First commit of version 0.1.1-2

This commit is contained in:
Bob Buxton 2020-03-11 15:15:47 +00:00
commit a0a273350e
44 changed files with 23581 additions and 0 deletions

11
CONTROL/control Normal file
View File

@ -0,0 +1,11 @@
Package: flexview
Priority: optional
Section: misc
Version: 0.1.1-2
Architecture: mipsel
Maintainer: mymsman
Depends: webif(>=1.4.0)
Description: Provides an alternative 'flexible view' of your recordings through the Web Interface.
Can sort and filter on most attributes. Has a customisable display format.
Tags: http://wiki.hummy.tv/wiki/FlexView

View File

@ -0,0 +1 @@
{"name":"default","curdirsel":null,"tablewidth":0,"tabledepth":0,"dircolwidth":0,"flcolwidth":0,"dircolhidden":false,"dirshrinktofit":false,"flshrinktofit":false,"datefmt":"d M y H:i","timefmt":"H:i:s","dirColChg":{},"flColChg":{},"sel":{},"exp":{"-media":true,"-media-My-Video":true},"excl":{}}

View File

@ -0,0 +1 @@
{"name":"fixed size table","curdirsel":null,"tablewidth":1302,"tabledepth":458,"dircolwidth":350,"flcolwidth":930,"dircolhidden":false,"dirshrinktofit":false,"flshrinktofit":false,"datefmt":"d M y H:i","timefmt":"H:i:s","dirColChg":{"title":{"width":177}},"flColChg":{},"sel":{},"excl":{},"exp":{"-media-My-Video":true}}

View File

@ -0,0 +1,98 @@
// Column definition for Directory tree table
{name:"id", key:true, width: 250, hidden:true},
{ name: "sel", label: 'Sel',
hidden:false, width: 20, sortable:false, search: false,
formatter: "checkbox", formatoptions: {disabled: false},
},
{ name: "title", label: "Directory",
hidden:false, width: 250, sorttype: "text",
searchoptions: {sopt: ["cn", "bw", "ew"]},
},
{ name:'opt', label: "Opt+", hidden:false, width:47,
sortable:false, search: false,
formatter: fmtDOpt
},
{ name: "dir", label: "Directory path", hidden: true,
width: 250, sorttype: "text",
searchoptions: {sopt: ["cn", "bw", "ew"]},
},
{ name: "psize", label: "Size", hidden:false, width: 70,
align: "right", sorttype: "number", index: "dsize" ,
searchoptions: {sopt: ["lt", "gt"],searchOperMenu: true, clearSearch: false},
},
{ name: "dsize", label: "unformatted size Kb",
hidden: true, width: 90,
align: "right", sorttype: "number" ,
searchoptions: {sopt: ["lt", "gt"],searchOperMenu: true, clearSearch: false},
},
{ name:'autodecryptR',
label: icons.autodecryptr + ' Recursive Auto-Decrypt',
hidden:false, width:22,
stype: 'select',
searchoptions: {value: {1:"Rec Auto-Decrypt",0:"Not Auto-Decrypt",'':"-   All"}, searchOperMenu: false, clearSearch: false},
formatter: fmtIcon, formatoptions: {img: icons.autodecryptr}
},
{ name:'autodecrypt',
label: icons.autodecrypt + ' Auto-Decrypt',
hidden:false, width:22,
stype: 'select',
searchoptions: {value: {1:"Auto-Decrypt",0:"Not Auto-Decrypt",'':"-   All"}, searchOperMenu: false, clearSearch: false},
formatter: fmtIcon, formatoptions: {img: icons.autodecrypt}
},
{ name:'autoshrinkR',
label: icons.autoshrinkr + ' Recursive Auto Shrink',
hidden:false, width:22,
stype: 'select',
searchoptions: {value: {1:"Rec Auto-Shrink",0:"Not Auto-Shrink",'':"-   All"}, searchOperMenu: false, clearSearch: false},
formatter: fmtIcon, formatoptions: {img: icons.autoshrinkr}
},
{ name:'autoshrink',
label: icons.autoshrink + ' Auto-Shrink',
hidden:false, width:22,
stype: 'select',
searchoptions: {value: {1:"Auto-Shrink",0:"Not Auto-Shrink",'':"-   All"}, searchOperMenu: false, clearSearch: false},
formatter: fmtIcon, formatoptions: {img: icons.autoshrink}
},
{ name:'autodedup',
label: icons.autodedup + ' Auto-Deduplicate',
hidden:false, width:22,
stype: 'select',
searchoptions: {value: {1:"Auto-Deduplicate",0:"Not Auto-Deduplicate",'':"-   All"}, searchOperMenu: false, clearSearch: false},
formatter: fmtIcon, formatoptions: {img: icons.autodedup}
},
{ name:'autoexpire',
label: icons.autoexpire + ' Auto-Expire',
hidden:false, width:22,
stype: 'select',
searchoptions: {value: {1:"Auto-Expire",0:"Not Auto-Expire",'':"-   All"}, searchOperMenu: false, clearSearch: false},
formatter: fmtIcon, formatoptions: {img: icons.autoexpire}
},
{ name:'autompg',
label: icons.autompg + ' Auto-mpg',
hidden:false, width:22,
stype: 'select',
searchoptions: {value: {1:"Auto-mpg",0:"Not Auto-mpg",'':"-   All"}, searchOperMenu: false, clearSearch: false},
formatter: fmtIcon, formatoptions: {img: icons.autompg}
},
{ name:'automp3',
label: icons.automp3 + ' Auto-mp3',
hidden:false, width:22,
stype: 'select',
searchoptions: {value: {1:"Auto-mp3",0:"Not Auto-mp3",'':"-   All"}, searchOperMenu: false, clearSearch: false},
formatter: fmtIcon, formatoptions: {img: icons.automp3}
},
{ name:'tvdbseriesid',
label: icons.tvdb + ' theTVDB',
hidden:false, width:25,
stype: 'select',
searchoptions: {value: {1:"theTVDB",0:"Not theTVDB",'':"-   All"}, searchOperMenu: false, clearSearch: false},
formatter: fmtIcon, formatoptions: {img: icons.tvdb}
},
{ name:'series',
label: icons.series + ' Series',
hidden:false, width:35,
stype: 'select',
searchoptions: {value: {1:"Series",0:"Not Series",'':"-   All"}, searchOperMenu: false, clearSearch: false},
formatter: fmtIcon, formatoptions: {img: icons.series}
},

View File

@ -0,0 +1,266 @@
// Column Definitions for File List table
{ name:"id", key:true, width: 250, hidden:true},
{ name: "sel", label: '<input type="checkbox" id="chkbox" value="Yes" selected=true onclick="selAll(event)" /> Sel',
hidden:false, width: 20, sortable:false, search: false,
formatter: fmtCheckbox,
},
{ name: "fname", label: "File Name", hidden: false,
width: 300, sorttype: "text",
searchoptions: {sopt: ["cn", "bw", "ew"],searchOperMenu: true},
formatter: fmtfname
},
{ name:'opt', label: "Opt+",
hidden:false, width:47, sortable:false, search: false,
formatter: fmtOpt
},
{ name: "dir", label: "Directory path", hidden: true,
width: 250, sorttype: "text",
searchoptions: {sopt: ["cn", "bw", "ew"]},
},
{ name: "title", label: "Title", hidden: true,
width: 250, sorttype: "text",
searchoptions: {sopt: ["cn", "bw", "ew"]},
},
{ name: "synopsis", label: "Synopsis", hidden: true,
width: 250, sorttype: "text",
searchoptions: {sopt: ["cn", "bw", "ew"]},
},
{ name: "channel_name", label: "Channel name", hidden: false,
width: 22, sorttype: "text",
searchoptions: {sopt: ["cn", "bw", "ew"], searchOperMenu: false, clearSearch: false},
formatter: fmtChannel
},
{ name: "channel_num", label: "Channel number", hidden: true,
width: 30, sorttype: "number",
searchoptions: {sopt: ["eq", "lt", "gt"], searchOperMenu: false, clearSearch: false},
},
{ name: "start", label: "Start time", width: 90, hidden: false,
align: "right", sorttype: "date",
formatter: "date",
formatoptions: {srcformat:"U" ,newformat: fvConfig.datetimefmt},
searchoptions: {sopt: ["lt", "gt"], clearSearch: false,
// dataInit is the client-side event that fires upon initializing the toolbar search field for a column
// use it to place a third party control to customize the toolbar
dataInit: function (element) {
$(element).datepicker({
id: 'start_datePicker',
dateFormat: fvConfig.datefmt,
showOn: 'focus'
});
}
}
},
{ name: "duration", label: "Duration", width: 50, hidden: false,
align: "right", sorttype: "date",
formatter: "date",
formatoptions: {srcformat:"U" ,newformat: fvConfig.timefmt},
searchoptions: {sopt: ["lt", "gt"], clearSearch: false},
},
{ name: "end", label: "End Time", width: 90, hidden: true,
align: "right", sorttype: "date",
formatter: "date",
formatoptions: {srcformat:"U" ,newformat: fvConfig.datetimefmt},
searchoptions: {sopt: ["lt", "gt"], clearSearch: false,
// dataInit is the client-side event that fires upon initializing the toolbar search field for a column
// use it to place a third party control to customize the toolbar
dataInit: function (element) {
$(element).datepicker({
id: 'end_datePicker',
dateFormat: fvConfig.datefmt,
showOn: 'focus'
});
}
}
},
{ name: "schedstart", label: "Scheduled Start time", width: 90, hidden: true,
align: "right", sorttype: "date",
formatter: "date",
formatoptions: {srcformat:"U" ,newformat: fvConfig.datetimefmt},
searchoptions: {sopt: ["lt", "gt"], clearSearch: false,
// dataInit is the client-side event that fires upon initializing the toolbar search field for a column
// use it to place a third party control to customize the toolbar
dataInit: function (element) {
$(element).datepicker({
id: 'schstart_datePicker',
dateFormat: fvConfig.datefmt,
showOn: 'focus'
});
}
}
},
{ name: "scheddur", label: "Scheduled Duration", width: 50, hidden: true,
align: "right", sorttype: "date",
formatter: "date",
formatoptions: {srcformat:"U" ,newformat: fvConfig.timefmt},
searchoptions: {sopt: ["lt", "gt"], clearSearch: false},
},
{ name: "resume", label: "Resume point", width: 50, hidden: true,
align: "right", sorttype: "date",
formatter: "date",
formatoptions: {srcformat:"U" ,newformat: fvConfig.timefmt},
searchoptions: {sopt: ["lt", "gt"], clearSearch: false},
},
{ name: "psize", label: "Size", width: 70, hidden: false,
align: "right", sorttype: "number", index: "fsize" ,
searchoptions: {sopt: ["lt", "gt"], clearSearch: false},
},
{ name: "fsize", label: "unformatted Size", hidden: true,
width: 70,
searchoptions: {sopt: ["lt", "gt"], clearSearch: false},
align: "right", sorttype: "number"
},
{ name: "bookmarks", label: icons.bookmark +" Bookmark count", hidden: false,
width: 35,
searchoptions: {sopt: ["gt", "lt", "eq"], clearSearch: false},
align: "right", sorttype: "number"
},
{ name: "seriesnum", label: "Series number", hidden: true,
width: 22,
searchoptions: {sopt: ["gt", "lt", "eq"], clearSearch: false},
align: "right", sorttype: "number"
},
{ name: "episodenum", label: "Episode number", hidden: true,
width: 22,
searchoptions: {sopt: ["gt", "lt", "eq"], clearSearch: false},
align: "right", sorttype: "number"
},
{ name: "episodetot", label: "Episode total", hidden: true,
width: 22,
searchoptions: {sopt: ["gt", "lt", "eq"], clearSearch: false},
align: "right", sorttype: "number"
},
{ name: "showrow", label: "show row", hidden: true,
width: 50, align: "right",
searchoptions: {sopt: ["eq"], defaultValue: "true", searchhidden: true, searchOperMenu: false, clearSearch: false},
},
{ name: "status", label: "Status", hidden: false,
width: 70, align: "right",
searchoptions: {sopt: ["cn", "bw", "eq"], searchhidden: true, searchOperMenu: false, clearSearch: false},
},
{ name: "definition", label: "Def", hidden: false,
width: 24, sorttype: "text",
stype: 'select',
searchoptions: {sopt: ["cn"],value: {"HD":icons.HD+'HD',"SD":icons.SD+'SD','':"-&nbsp;&nbsp; All"}, searchOperMenu: false, clearSearch: false},
formatter: 'select', editoptions: {value: {"HD":icons.HD,"SD":icons.SD,'':"Unknown"}},
},
{ name: "genre", label: "Genre", hidden: false,
width: 24, sorttype: "mumber",
stype: 'select',
searchoptions: {sopt: ["eq"],value: genrelist, searchOperMenu: false, clearSearch: false},
formatter: 'select', editoptions: {value: genrelist},
},
{ name:'flags_list', label: "Flags", hidden: true,
width:70,
searchoptions: {sopt: ["cn"], searchOperMenu: false, clearSearch: false},
},
{ name: "guidance", label: icons.guidance +" Guidance text", hidden: false,
width: 22, sorttype: "text",
searchoptions: {sopt: ["cn", "bw", "ew"], searchOperMenu: false, clearSearch: false},
formatter: fmtGuidance
},
{ name:'Guidance', hidden: true,
label: icons.guidance + ' Guidance flag',
width:22,
stype: 'select',
searchoptions: {value: {1:"Guidance",0:"No Guidance",'':"-&nbsp;&nbsp; All"}, searchOperMenu: false, clearSearch: false},
formatter: fmtIcon, formatoptions: {img: icons.guidance}
},
{ name:'GGuidance', hidden: true,
label: icons.Gguidance + ' General Guidance',
width:22,
stype: 'select',
searchoptions: {value: {1:"Guidance",0:"No Guidance",'':"-&nbsp;&nbsp; All"}, searchOperMenu: false, clearSearch: false},
formatter: fmtIcon, formatoptions: {img: icons.Gguidance}
},
{ name:'inuse', hidden: false,
label: icons.inuse + ' In use',
width:45,
stype: 'select',
searchoptions: {value: {1:"In use",0:"Not in use",'':"-&nbsp;&nbsp; All"}, searchOperMenu: false, clearSearch: false},
formatter: fmtIcon, formatoptions: {img: icons.inuse}
},
{ name:'New', hidden: false,
label: icons.new + ' New',
width:22,
stype: 'select',
searchoptions: {value: {1:"New",0:"Not New",'':"-&nbsp;&nbsp; All"}, searchOperMenu: false, clearSearch: false},
formatter: fmtIcon, formatoptions: {img: icons.new}
},
{ name:'ODEncrypted', hidden: true,
label: icons.ODE + ' On Disk Encrypted',
width:28,
stype: 'select',
searchoptions: {value: {1:"On Disk Encrypted",0:"Not On Disk Encrypted",'':"-&nbsp;&nbsp; All"}, searchOperMenu: false, clearSearch: false},
formatter: fmtIcon, formatoptions: {img: icons.ODE}
},
{ name:'Decrypted', hidden: false,
label: icons.DEC + ' Decrypted',
width:25,
stype: 'select',
searchoptions: {value: {1:"Decrypted",0:"Not Decrypted",'':"-&nbsp;&nbsp; All"}, searchOperMenu: false, clearSearch: false},
formatter: fmtIcon, formatoptions: {img: icons.DEC}
},
{ name:'Encrypted', hidden: false,
label: icons.ENC + ' Encrypted (Protected)',
width:25,
stype: 'select',
searchoptions: {value: {1:"Encrypted",0:"Not Encrypted",'':"-&nbsp;&nbsp; All"}, searchOperMenu: false, clearSearch: false},
formatter: fmtIcon, formatoptions: {img: icons.ENC}
},
{ name:'Radio', hidden: false,
label: icons.Radio + ' Radio',
width:22,
stype: 'select',
searchoptions: {value: {1:"Radio",0:"Not Radio",'':"-&nbsp;&nbsp; All"}, searchOperMenu: false, clearSearch: false},
formatter: fmtIcon, formatoptions: {img: icons.Radio}
},
{ name:'Locked', hidden: false,
label: icons.Locked + ' Locked',
width:22,
stype: 'select',
searchoptions: {value: {1:"Locked",0:"Not Locked",'':"-&nbsp;&nbsp; All"}, searchOperMenu: false, clearSearch: false},
formatter: fmtIcon, formatoptions: {img: icons.Locked}
},
{ name:'Shrunk', hidden: false,
label: icons.Shrunk + ' Shrunk',
width:22,
stype: 'select',
searchoptions: {value: {1:"Shrunk",0:"Not Shrunk",'':"-&nbsp;&nbsp; All"}, searchOperMenu: false, clearSearch: false},
formatter: fmtIcon, formatoptions: {img: icons.Shrunk}
},
{ name:'Deduped', hidden: false,
label: icons.Deduped + ' De-duped',
width:25,
stype: 'select',
searchoptions: {value: {1:"De-duped",0:"Not De-duped",'':"-&nbsp;&nbsp; All"}, searchOperMenu: false, clearSearch: false},
formatter: fmtIcon, formatoptions: {img: icons.Deduped}
},
{ name:'thmok', hidden: true,
label: icons.thumb + ' Thumbnail exists',
width:22,
stype: 'select',
searchoptions: {value: {1:"Thumbnail",0:"No Thumbnail",'':"-&nbsp;&nbsp; All"}, searchOperMenu: false, clearSearch: false},
formatter: fmtIcon, formatoptions: {img: icons.thumb}
},
{ name:'thmimg', hidden: true,
label: icons.thumb + ' Thumbnail image',
width:140,
stype: 'select', index: 'thmok',
searchoptions: {value: {1:"Thumbnail",0:"No Thumbnail",'':"-&nbsp;&nbsp; All"}, searchOperMenu: false, clearSearch: false},
formatter: fmtThumbnail,
},
{ name:'dlna',
label: icons.DLNA + ' DLNA indexed',
width:22,
hidden: false,
stype: 'select',
searchoptions: {value: {1:"DLNA",0:"No DLNA",'':"-&nbsp;&nbsp; All"}, searchOperMenu: false, clearSearch: false},
formatter: fmtIcon, formatoptions: {img: icons.DLNA}
},
{ name:'dlnaIndex',
label: icons.DLNA + ' DLNA index',
hidden: true,
width:50,
search:false,
formatter: "link"
},

View File

@ -0,0 +1,10 @@
# generate selection list for fvConfig files
set configl [glob -nocomplain /mod/etc/fvConfig_*.json]
proc genConfigList {} {
foreach file [lsort $::configl] {
set config [string range $file 18 end-5]
puts "<option value=\"$config\">$config</option>"
}
}

View File

@ -0,0 +1,130 @@
// Browse context menu enablement - extracted from browse.js
// extremely unfortunate the need to duplicate code
// but needs to be (re)done whenever grids are redrawn since elements
// being tracked are not created at document ready
function dirListReady() {
// Bind context menu to opt+ image
$('img.dopt').contextMenu(
{
menu: 'doptmenu',
leftButton: true,
beforeShow: preparedmenu
},
dmenuclick
);
$('img.doopt').contextMenu(
{
menu: 'dooptmenu',
leftButton: true,
beforeShow: preparedmenu
},
dmenuclick
);
};
function fileListReady() {
// Bind context menu to opt+ image
$('img.opt').contextMenu(
{
menu: 'optmenu',
leftButton: true,
beforeShow: preparemenu
},
menuclick
);
$('img.oopt').contextMenu(
{
menu: 'ooptmenu',
leftButton: true,
beforeShow: prepareomenu
},
omenuclick
);
};
// Cant trigger the imput.fs changed function from grid
function inputChanged() {
var num = $('input.fs:checked').size();
if (num > 0)
$('.onesel').enable();
else
$('.onesel').disable();
var num = $('input.fsts:checked').size();
if (num > 0)
$('.tsonesel').enable();
else
$('.tsonesel').disable();
if (num > 1)
$('.tstwosel').enable();
else
$('.tstwosel').disable();
};
// use browse media view dialog adapted for FV, may need changing if Browse dialog changes
function viewFile(rowid) {
// Use jqGrid column data instead of HTML attributes
var record = $("#filelist").jqGrid('getLocalRow',rowid);
var file = record.dir +'/'+ record.fname;
var file = encodeURI(record.dir +'/'+ record.fname);
var type = record.ftype;
var dlna = record.dlna;
var decr = record.Decrypted;
// e.preventDefault();
// var file = $(this).attr('file');
// var type = $(this).attr('type');
// var opt = $(this).nextAll('a').find('img.opt');
var url = '/browse/file.jim?file=' + file
+ '&type=' + type;
$.dialog.load(url);
$.dialog.attr('file', file);
$.dialog.attr('type', type);
if (type == 'ts' &&
(decr == 1 || dlna == 1))
// (opt.attr('odencd') == 0 || opt.attr('dlna') == 1))
$.dialog.dialog("option", "buttons", $buttonsp);
else
$.dialog.dialog("option", "buttons", $buttons);
$.dialog.dialog('open');
};
var $buttons = {
"Close" : function() {$(this).dialog('close');}
};
var $buttonsp = $.extend(
{"Play" : function() { //doplay();
window.location = '/play/play.jim?' +
'dir=' + encodeURIComponent(dir) +
'&file=' + encodeURIComponent(file);
}
},
$buttons);
$(function() {
// Create reusable dialogue.
if (!$.dialog)
$.dialog = $('#dialogue').dialog({
title: "Media Details",
modal: false, autoOpen: false,
height: 600, width: 700,
show: 'scale', hide: 'fade',
draggable: true, resizable: true,
buttons: $buttons,
close: function(e,u) { $('#dialogue').empty().html(
'<img src="/img/loading.gif">Retrieving data...'); }
});
});

View File

@ -0,0 +1,341 @@
/*Grid*/
.ui-jqgrid {
position: relative;
-moz-box-sizing: content-box;
-webkit-box-sizing: content-box;
box-sizing: content-box;
}
.ui-jqgrid .ui-jqgrid-view {position: relative;left:0; top: 0; padding: 0; font-size:11px; z-index:100;}
.ui-jqgrid .ui-common-table {border-width: 0px; border-style: none; border-spacing: 0px; padding: 0;}
/* caption*/
.ui-jqgrid .ui-jqgrid-titlebar {height:19px; padding: .3em .2em .2em .3em; position: relative; font-size: 12px; border-left: 0 none;border-right: 0 none; border-top: 0 none;}
.ui-jqgrid .ui-jqgrid-caption {text-align: left;}
.ui-jqgrid .ui-jqgrid-title { margin: .1em 0 .2em; }
.ui-jqgrid .ui-jqgrid-titlebar-close { position: absolute;top: 50%; width: 19px; margin: -10px 0 0 0; padding: 1px; height:18px; cursor:pointer;}
.ui-jqgrid .ui-jqgrid-titlebar-close span { display: block; margin: 1px; }
.ui-jqgrid .ui-jqgrid-titlebar-close:hover { padding: 0; }
/* header*/
.ui-jqgrid .ui-jqgrid-hdiv {position: relative; margin: 0;padding: 0; overflow: hidden; border-left: 0 none !important; border-top : 0 none !important; border-right : 0 none !important;}
.ui-jqgrid .ui-jqgrid-hbox {float: left; padding-right: 20px;}
.ui-jqgrid .ui-jqgrid-htable {table-layout:fixed;margin:0;border-collapse: separate;}
.ui-jqgrid .ui-jqgrid-htable th { height: 27px; padding: 0 2px 0 2px;}
.ui-jqgrid .ui-jqgrid-htable th div {overflow: hidden; position:relative;margin: .1em 0em .1em 0em;}
.ui-th-column, .ui-jqgrid .ui-jqgrid-htable th.ui-th-column {overflow: hidden;white-space: nowrap;text-align:center;border-top : 0 none;border-bottom : 0 none;}
.ui-th-column-header, .ui-jqgrid .ui-jqgrid-htable th.ui-th-column-header {overflow: hidden;white-space: nowrap;text-align:center;border-top : 0 none; height: 26px;}
.ui-th-ltr, .ui-jqgrid .ui-jqgrid-htable th.ui-th-ltr {border-left : 0 none;}
.ui-th-rtl, .ui-jqgrid .ui-jqgrid-htable th.ui-th-rtl {border-right : 0 none;}
.ui-first-th-ltr {border-right: 1px solid; }
.ui-first-th-rtl {border-left: 1px solid; }
.ui-jqgrid .ui-th-div-ie {white-space: nowrap; zoom :1; height:17px;}
.ui-jqgrid .ui-jqgrid-resize {height:20px !important;position: relative; cursor :e-resize;display: inline;overflow: hidden;}
.ui-jqgrid .ui-grid-ico-sort {overflow:hidden;position:absolute;display:inline; cursor: pointer !important;}
.ui-jqgrid .ui-icon-asc {margin-top:-3px; height:12px;}
.ui-jqgrid .ui-icon-desc {margin-top:3px;margin-left:-1px;height:12px;}
.ui-jqgrid .ui-i-asc {margin-top:0;height:18px;}
.ui-jqgrid .ui-i-desc {margin-top:0;margin-left:12px;height:18px;}
.ui-jqgrid .ui-single-sort-asc {margin-top:0;height:18px;}
.ui-jqgrid .ui-single-sort-desc {margin-top:-1px;height:18px;}
.ui-jqgrid .ui-jqgrid-sortable {cursor:pointer;height:14px}
.ui-jqgrid tr.ui-search-toolbar th { }
.ui-jqgrid .ui-search-table td.ui-search-clear { width:25px;}
.ui-jqgrid tr.ui-search-toolbar td > input { padding-right: 0px; width: 95%;}
.ui-jqgrid tr.ui-search-toolbar select {}
/* body */
.ui-jqgrid .ui-jqgrid-bdiv {position: relative; margin: 0; padding:0; overflow: auto; text-align:left;z-index: 101;}
.ui-jqgrid .ui-jqgrid-btable {table-layout:fixed; margin:0; outline-style: none; border-collapse: separate;}
.ui-jqgrid tr.jqgrow { outline-style: none; }
.ui-jqgrid tr.jqgroup { outline-style: none; }
.ui-jqgrid tr.jqgrow td {font-weight: normal; overflow: hidden; white-space: pre; height: 23px;padding: 1px 2px 1px 2px;border-bottom-width: 1px; border-bottom-color: inherit; border-bottom-style: solid;}
.ui-jqgrid tr.jqgfirstrow td {padding: 0 2px 0 2px;border-right-width: 1px; border-right-style: solid; height:auto;}
.ui-jqgrid tr.jqgroup td {font-weight: normal; overflow: hidden; white-space: pre; height: 22px;padding: 0 2px 0 2px;border-bottom-width: 1px; border-bottom-color: inherit; border-bottom-style: solid;}
.ui-jqgrid tr.jqfoot td {font-weight: bold; overflow: hidden; white-space: pre; height: 22px;padding: 0 2px 0 2px;border-bottom-width: 1px; border-bottom-color: inherit; border-bottom-style: solid;}
.ui-jqgrid tr.ui-row-ltr td {text-align:left;border-right-width: 1px; border-right-color: inherit; border-right-style: solid;}
.ui-jqgrid tr.ui-row-rtl td {text-align:right;border-left-width: 1px; border-left-color: inherit; border-left-style: solid;}
.ui-jqgrid td.jqgrid-rownum { padding: 0 2px 0 2px; margin: 0; border: 0 none;}
.ui-jqgrid .ui-jqgrid-resize-mark { width:2px; left:0; background-color:#777; cursor: e-resize; cursor: col-resize; position:absolute; top:0; height:100px; overflow:hidden; display:none; border:0 none; z-index: 99999;}
/* footer */
.ui-jqgrid .ui-jqgrid-sdiv {position: relative; margin: 0;padding: 0; overflow: hidden; border-left: 0 none !important; border-top : 0 none !important; border-right : 0 none !important;}
.ui-jqgrid .ui-jqgrid-ftable {table-layout:fixed; margin-bottom:0;border-collapse: separate;}
.ui-jqgrid tr.footrow td {font-weight: bold; overflow: hidden; white-space:nowrap; height: 20px;padding: 0 2px 0 2px;border-top-width: 1px; border-top-color: inherit; border-top-style: solid;border-bottom-width: 1px; border-bottom-color: inherit; border-bottom-style: solid;}
.ui-jqgrid tr.footrow-ltr td {text-align:left;border-right-width: 1px; border-right-color: inherit; border-right-style: solid;}
.ui-jqgrid tr.footrow-rtl td {text-align:right;border-left-width: 1px; border-left-color: inherit; border-left-style: solid;}
/* Pager*/
.ui-jqgrid .ui-jqgrid-pager { border-left: 0 none !important;border-right: 0 none !important; border-bottom: 0 none !important; border-top: 0 none; margin: 0 !important; padding: 0 !important; position: relative; height: auto; min-height: 28px; white-space: nowrap;overflow: hidden;font-size:11px; z-index:101}
.ui-jqgrid .ui-jqgrid-toppager .ui-pager-control, .ui-jqgrid .ui-jqgrid-pager .ui-pager-control {position: relative;border-left: 0;border-bottom: 0;border-top: 0; height: 28px;}
.ui-jqgrid .ui-pg-table {position: relative; padding: 1px 0; width:auto; margin: 0;}
.ui-jqgrid .ui-pg-table td {font-weight:normal; vertical-align:middle; padding:0px 1px;}
.ui-jqgrid .ui-pg-button { height:auto}
.ui-jqgrid .ui-pg-button span { display: block; margin: 2px; float:left;}
.ui-jqgrid .ui-pg-button:hover { padding: 0;}
.ui-jqgrid .ui-state-disabled:hover {padding:0px;}
.ui-jqgrid .ui-pg-input,.ui-jqgrid .ui-jqgrid-toppager .ui-pg-input { height:14px;width: auto;font-size:.9em; margin:0;line-height: inherit;border: none; padding: 3px 2px}
.ui-jqgrid .ui-pg-selbox, .ui-jqgrid .ui-jqgrid-toppager .ui-pg-selbox {font-size:.9em; line-height:inherit; display:block; height:19px; margin: 0; padding: 3px 0px; border:none;}
.ui-jqgrid .ui-separator {height: 18px; border-left: 2px solid #ccc ;}
.ui-separator-li {height: 2px; border : none;border-top: 2px solid #ccc ; margin: 0; padding: 0; width:100%}
.ui-jqgrid .dropdownmenu {
padding: 3px 0 3px 0;
margin-left: 4px;
}
.ui-jqgrid .ui-jqgrid-pager .ui-pg-div,
.ui-jqgrid .ui-jqgrid-toppager .ui-pg-div
{padding:1px 0;float:left;position:relative; line-height: 20px;}
.ui-jqgrid .ui-jqgrid-pager .ui-pg-button,
.ui-jqgrid .ui-jqgrid-toppager .ui-pg-button
{ cursor:pointer; }
.ui-jqgrid .ui-jqgrid-pager .ui-pg-div span.ui-icon,
.ui-jqgrid .ui-jqgrid-toppager .ui-pg-div span.ui-icon
{float:left;margin: 2px; width:18px;}
.ui-jqgrid td input, .ui-jqgrid td select, .ui-jqgrid td textarea { margin: 0; padding-top:5px;padding-bottom: 5px;}
.ui-jqgrid td textarea {width:auto;height:auto;}
.ui-jqgrid .ui-jqgrid-toppager {border-left: 0 none !important;border-right: 0 none !important; border-top: 0 none !important; margin: 0 !important; padding: 0 !important; position: relative;white-space: nowrap;overflow: hidden;}
.ui-jqgrid .ui-jqgrid-pager .ui-pager-table {
width:100%;
table-layout:fixed;
height:100%;
}
.ui-jqgrid .ui-jqgrid-pager .ui-paging-info {
font-weight: normal;
height:auto;
margin-top:3px;
margin-right:4px;
display: inline;
}
.ui-jqgrid .ui-jqgrid-pager .ui-paging-pager {
table-layout:auto;
height:100%;
}
.ui-jqgrid .ui-jqgrid-pager .navtable {
float:left;
table-layout:auto;
height:100%;
}
/*.ui-jqgrid .ui-jqgrid-toppager .ui-pg-div {padding:1px 0;float:left;position:relative; line-height: 20px; margin-right:3px;}
.ui-jqgrid .ui-jqgrid-toppager .ui-pg-button { cursor:pointer; }
.ui-jqgrid .ui-jqgrid-toppager .ui-pg-div span.ui-icon {float:left;margin: 2px; width:18px;}
*/
/*subgrid*/
.ui-jqgrid .ui-jqgrid-btable .ui-sgcollapsed span {display: block;}
.ui-jqgrid .ui-subgrid {margin:0;padding:0; width:100%;}
.ui-jqgrid .ui-subgrid table {table-layout: fixed;}
.ui-jqgrid .ui-subgrid tr.ui-subtblcell td {height:18px;border-right-width: 1px; border-right-color: inherit; border-right-style: solid;border-bottom-width: 1px; border-bottom-color: inherit; border-bottom-style: solid;}
.ui-jqgrid .ui-subgrid td.subgrid-data {border-top: 0 none !important; border-left: 0 none !important;}
.ui-jqgrid .ui-subgrid td.subgrid-cell {border-width: 0 1px 1px 0;}
.ui-jqgrid .ui-th-subgrid {height:20px;}
/* loading */
.ui-jqgrid .loading {position: absolute; top: 45%;left: 45%;width: auto;z-index:101;padding: 6px; margin: 5px;text-align: center;font-weight: bold;display: none;border-width: 2px !important; font-size:11px;}
.ui-jqgrid .jqgrid-overlay {display:none;z-index:100;}
/* IE * html .jqgrid-overlay {width: expression(this.parentNode.offsetWidth+'px');height: expression(this.parentNode.offsetHeight+'px');} */
* .jqgrid-overlay iframe {position:absolute;top:0;left:0;z-index:-1;}
/* IE width: expression(this.parentNode.offsetWidth+'px');height: expression(this.parentNode.offsetHeight+'px');}*/
/* end loading div */
/* toolbar */
.ui-jqgrid .ui-userdata {border-left: 0 none; border-right: 0 none; height : 27px;overflow: hidden; }
/*Modal Window */
.ui-jqdialog { font-size:11px !important; }
.ui-jqdialog { display: none; width: 300px; position: absolute; padding: .2em; font-size:11px; overflow:visible;}
.ui-jqdialog .ui-jqdialog-titlebar { padding: .3em .2em; position: relative; height:20px;}
.ui-jqdialog .ui-jqdialog-title { margin: .3em .2em .2em .2em;}
.ui-jqdialog .ui-jqdialog-titlebar-close { position: absolute; top: 50%; width: 19px; margin: -12px 0 0 0; padding: 1px; height: 18px; cursor:pointer;}
.ui-jqdialog .ui-jqdialog-titlebar-close span { display: block; margin: 1px; }
.ui-jqdialog .ui-jqdialog-titlebar-close:hover, .ui-jqdialog .ui-jqdialog-titlebar-close:focus { padding: 0; }
.ui-jqdialog-content, .ui-jqdialog .ui-jqdialog-content { border: 0; padding: .3em .2em; background: none; height:auto;}
.ui-jqdialog .ui-jqconfirm {padding: .4em 1em; border-width:3px;position:absolute;bottom:10px;right:10px;overflow:visible;display:none;height:80px;width:220px;text-align:center;}
.ui-jqdialog>.ui-resizable-se { bottom: -3px; right: -3px}
.ui-jqgrid>.ui-resizable-se { bottom: -3px; right: -3px }
/* end Modal window*/
/* Form edit */
.ui-jqdialog-content .FormGrid {margin: 0; overflow:auto;position:relative;}
.ui-jqdialog-content .EditTable { width: 100%; margin-bottom:0;}
.ui-jqdialog-content .DelTable { width: 100%; margin-bottom:0;}
.EditTable td input, .EditTable td select, .EditTable td textarea {margin: 0;}
.EditTable td textarea { width:auto; height:auto;}
.ui-jqdialog-content td.EditButton {text-align: right;border-top: 0 none;border-left: 0 none;border-right: 0 none; padding-bottom:5px; padding-top:5px;}
.ui-jqdialog-content td.navButton {text-align: center; border-left: 0 none;border-top: 0 none;border-right: 0 none; padding-bottom:5px; padding-top:5px;}
.ui-jqdialog-content input.FormElement {padding: .5em .3em; margin-bottom: 3px}
.ui-jqdialog-content select.FormElement {padding:.3em; margin-bottom: 3px;}
.ui-jqdialog-content .data-line {padding-top:.1em;border: 0 none;}
.ui-jqdialog-content .CaptionTD {vertical-align: middle;border: 0 none; padding: 2px;white-space: nowrap;}
.ui-jqdialog-content .DataTD {padding: 2px; border: 0 none; vertical-align: top;}
.ui-jqdialog-content .form-view-data {white-space:pre}
.fm-button { height: 18px; display: inline-block; margin:2px 4px 0 0; padding: .6em .5em .2em .5em; text-decoration:none !important; cursor:pointer; position: relative; text-align: center; zoom: 1; }
.fm-button-icon-left { padding-left: 1.9em; }
.fm-button-icon-right { padding-right: 1.9em; }
.fm-button-icon-left .ui-icon { right: auto; left: .2em; margin-left: 0; position: absolute; top: 50%; margin-top: -8px; }
.fm-button-icon-right .ui-icon { left: auto; right: .2em; margin-left: 0; position: absolute; top: 50%; margin-top: -8px;}
#nData, #pData { float: left; margin:3px;padding: 0; width: 15px; }
.ViewTable {
border-width: 0;
border-style: none;
border-spacing: 1px;
padding: 4px;
table-layout: fixed;
}
.ViewTable .CaptionTD, .ViewTable .DataTD {padding : 4px;}
/* End Eorm edit */
/*cell edit*/
.ui-jqgrid .edit-cell {
padding: 4px 0px 4px 4px;
}
.ui-jqgrid .selected-row, div.ui-jqgrid .selected-row td {font-style : normal;border-left: 0 none;}
/* inline edit actions button*/
.ui-inline-del.ui-state-hover span, .ui-inline-edit.ui-state-hover span,
.ui-inline-save.ui-state-hover span, .ui-inline-cancel.ui-state-hover span {
margin: -1px;
}
.ui-inline-del, .ui-inline-cancel {
margin-left: 8px;
}
.ui-jqgrid .inline-edit-cell {
padding: 4px 0px 4px 4px;
}
/* Tree Grid */
.ui-jqgrid .tree-wrap {float: left; position: relative;height: 18px;white-space: nowrap;overflow: hidden;}
.ui-jqgrid .tree-minus {position: absolute; height: 18px; width: 18px; overflow: hidden;}
.ui-jqgrid .tree-plus {position: absolute; height: 18px; width: 18px; overflow: hidden;}
.ui-jqgrid .tree-leaf {position: absolute; height: 18px; width: 18px;overflow: hidden;}
.ui-jqgrid .treeclick {cursor: pointer;}
/* moda dialog */
* iframe.jqm {position:absolute;top:0;left:0;z-index:-1;}
/* width: expression(this.parentNode.offsetWidth+'px');height: expression(this.parentNode.offsetHeight+'px');}*/
.ui-jqgrid-dnd tr td {border-right-width: 1px; border-right-color: inherit; border-right-style: solid; height:20px}
/* RTL Support */
.ui-jqgrid .ui-jqgrid-caption-rtl {text-align: right;}
.ui-jqgrid .ui-jqgrid-hbox-rtl {float: right; padding-left: 20px;}
.ui-jqgrid .ui-jqgrid-resize-ltr {float: right;margin: -2px -2px -2px 0;}
.ui-jqgrid .ui-jqgrid-resize-rtl {float: left;margin: -2px 0 -1px -3px;}
.ui-jqgrid .ui-sort-rtl {left:0;}
.ui-jqgrid .tree-wrap-ltr {float: left;}
.ui-jqgrid .tree-wrap-rtl {float: right;}
.ui-jqgrid .ui-ellipsis {-moz-text-overflow:ellipsis;text-overflow:ellipsis;}
/* Toolbar Search Menu , Nav menu*/
.ui-search-menu,
.ui-nav-menu {
position: absolute;
padding: 2px 5px;
z-index:99999;
-webkit-box-shadow: 7px 7px 5px 0px rgba(50, 50, 50, 0.75);
-moz-box-shadow: 7px 7px 5px 0px rgba(50, 50, 50, 0.75);
box-shadow: 7px 7px 5px 0px rgba(50, 50, 50, 0.75);
}
.ui-search-menu.ui-menu .ui-menu-item,
.ui-nav-menu.ui-menu .ui-menu-item
{
list-style-image: none;
padding-right: 0;
padding-left: 0;
}
.ui-search-menu.ui-menu .ui-menu-item a,
.ui-nav-menu.ui-menu .ui-menu-item a
{
display: block;
}
.ui-search-menu.ui-menu .ui-menu-item a.g-menu-item:hover,
.ui-nav-menu.ui-menu .ui-menu-item a.g-menu-item:hover
{
margin: -1px;
font-weight: normal;
}
.ui-jqgrid .ui-search-table { padding: 0; border: 0 none; height:20px; width:100%;}
.ui-jqgrid .ui-search-table .ui-search-oper { width:20px; }
a.g-menu-item, a.soptclass, a.clearsearchclass { cursor: pointer; }
.ui-jqgrid .ui-jqgrid-view input,
.ui-jqgrid .ui-jqgrid-view select,
.ui-jqgrid .ui-jqgrid-view textarea,
.ui-jqgrid .ui-jqgrid-view button {
font-size: 11px;
}
.ui-jqgrid .ui-scroll-popup {width: 95px;}
.ui-search-table select,
.ui-search-table input
{
padding: 4px 3px;
}
.ui-jqgrid .ui-pg-table .ui-pg-button.ui-state-disabled:hover > .ui-separator {
margin-left: 3px;
margin-right: 3px;
}
.ui-jqgrid .ui-pg-table .ui-pg-button.ui-state-disabled:hover > .ui-pg-div > .ui-icon {
margin-left: 3px;
margin-right: 3px;
}
/* Column menu */
.ui-jqgrid .ui-jqgrid-htable .colmenu {
position:absolute;
right:1px;
height:100%;
color : black;
}
.ui-jqgrid .ui-jqgrid-htable .colmenuspan {
display:inline-block;
}
.ui-jqgrid .ui-jqgrid-htable .ui-th-div {
height:17px;
margin-top:5px;
display:inine-block;
}
.column-menu, .ui-search-menu {
padding: 10px 10px;
}
.column-menu .divider {
background-color: #e5e5e5;
height: 1px;
padding:0 0;
margin: 5px 0;
overflow: hidden;
}
.ui-menu-item .ui-common-table .menu_icon {
white-space: pre;
padding-right: 8px;
width : auto;
}
.ui-menu-item .ui-common-table .menu_icon .ui-icon {
display : inline-block;
position: relative;
}
td.menu_text {
width: auto;
white-space: nowrap;
}
.ui-search-menu .ui-menu-item {
padding : 0 0;
}
.ui-col-menu .ui-menu-item td.menu_text{
padding-top: 0;
padding-bottom: 0;
padding-left : 1px;
}
.ui-col-menu .ui-menu-item td.menu_icon{
padding-top: 0;
padding-bottom: 0;
vertical-align: middle;
}
.ui-col-menu .ui-menu-item td.menu_icon input{
margin: 2px 0;
}
#search_menu .ui-menu-item div {
margin: 3px 0;
white-space: nowrap;
}
#search_menu .ui-menu-item div input,
#search_menu .ui-menu-item div select
{
padding: 3px 2px;
}
#search_menu .search_buttons {
display:inline-block;
width:50%;
}
#column_menu.ui-menu .ui-menu-item {
position :static;
}

View File

@ -0,0 +1,30 @@
/* Multiselect
----------------------------------*/
.ui-multiselect { border: solid 1px; font-size: 0.8em; }
.ui-multiselect ul { -moz-user-select: none; }
.ui-multiselect li { margin: 0; padding: 0; cursor: default; line-height: 20px; height: 20px; font-size: 11px; list-style: none; }
.ui-multiselect li a { color: #999; text-decoration: none; padding: 0; display: block; float: left; cursor: pointer;}
.ui-multiselect li.ui-draggable-dragging { padding-left: 10px; }
.ui-multiselect div.selected { position: relative; padding: 0; margin: 0; border: 0; float:left; }
.ui-multiselect ul.selected { position: relative; padding: 0; overflow: auto; overflow-x: hidden; background: #fff; margin: 0; list-style: none; border: 0; position: relative; width: 100%; }
.ui-multiselect ul.selected li { }
.ui-multiselect div.available { position: relative; padding: 0; margin: 0; border: 0; float:left; border-left: 1px solid; }
.ui-multiselect ul.available { position: relative; padding: 0; overflow: auto; overflow-x: hidden; background: #fff; margin: 0; list-style: none; border: 0; width: 100%; }
.ui-multiselect ul.available li { padding-left: 10px; }
.ui-multiselect .ui-state-default { border: none; margin-bottom: 1px; position: relative; padding-left: 20px;}
.ui-multiselect .ui-state-hover { border: none; }
.ui-multiselect .ui-widget-header {border: none; font-size: 11px; margin-bottom: 1px;}
.ui-multiselect .add-all { float: right; padding: 7px;}
.ui-multiselect .remove-all { float: right; padding: 7px;}
.ui-multiselect .search { float: left; padding: 4px;}
.ui-multiselect .count { float: left; padding: 7px;}
.ui-multiselect li span.ui-icon-arrowthick-2-n-s { position: absolute; left: 2px; }
.ui-multiselect li a.action { position: absolute; right: 2px; top: 2px; }
.ui-multiselect input.search { height: 14px; padding: 1px; opacity: 0.5; margin: 4px; width: 100px; }

View File

@ -0,0 +1,47 @@
#!/mod/bin/jimsh
######
# FlexView - Delete flexview cofig
# Author: MymsMan, 2016
######
package require cgi
source /mod/webif/lib/setup
require system.class settings.class
# logging options
if {[lindex $argv 0] eq "-d"} {
set argv [lrange $argv 1 end]
set loglevel 2
} else {
# Use same logging option as auto log
set settings [settings]
set loglevel [$settings _nval_setting "autolog"]
}
proc log {msg {level 2}} {
if {$level > $::loglevel} return
system plog flexview $msg
}
httpheader
set start [clock milliseconds]
set name [cgi_get name "default"]
set fname "/mod/etc/fvConfig_$name.json"
if {[catch {exec rm $fname
puts "Deleted configuration- $name<br>"} msg opts] } {
log "Config file error caught: $msg $opts" 0
puts "<b>File delete error $msg</b><br>"
}
source /mod/webif/plugin/flexview/configlist.jim
genConfigList
set end [clock milliseconds]
set elapse $(double($end-$start)/1000.0)
log "Delete | $elapse | $fname" 0

View File

@ -0,0 +1,22 @@
/* http://jsfiddle.net/7xwf1ysL/15/
(c) 2015 by DBJ.ORG, GPL/MIT applies
expr argument is any legal jQuery selector.
returns array of { element: , events: } objects
events: is jQuery events structure attached (as data)
to the element:
return is null if no events are found
*/
jQuery.events = function(expr) {
var rez = [],
evo;
jQuery(expr).each(
function() {
if (evo = jQuery._data(this, "events"))
rez.push({
element: this,
events: evo
});
});
return rez.length > 0 ? rez : null;
}

View File

@ -0,0 +1,44 @@
/* plugin style file to support column formatters and context menu actions */
.contextMenu li.new_dir a
{
background-image: url(/img/folder.png);
background-size: 16px 16px;
}
.contextMenu li.dedupdir a
{
background-image: url(/img/dedup.png);
}
.contextMenu li.theTVDB a
{
background-image: url(/img/tvdb-sm.png);
background-size: 16px 20px;
}
.contextMenu li.view a
{
background-image: url(/img/info.png);
background-size: 16px 16px;
}
.contextMenu li.view_file a ,
.contextMenu li.view_node a
{
background-image: url(/plugin/flexview/img/view.gif);
}
.contextMenu li.play a
{
background-image: url(/img/media/play.png);
background-size: 16px 16px;
}
.contextMenu li.reload_node a ,
.contextMenu li.reload_fdir a ,
.contextMenu li.reload_dir a
{
background-image: url(/plugin/flexview/img/refresh02.png);
}

View File

@ -0,0 +1,85 @@
# plugin jim code to define column formatters and context menu actions for flexview
# NB menu actions common to flexview and browse go in browse.hook
# Javascript and stylesheets for plugins
jscss /plugin/flexview/flexview.js /plugin/flexview/flexview.css
# Context menu items for video recordings
lappend plugins(menu) {view {desc "File Details"}}
lappend plugins(menu) {view_file {desc "View details"}}
lappend plugins(menu) {reload_fdir {desc "Reload folder"}}
lappend plugins(menu) {play {desc "Play"}}
# Context menu items for other file types
lappend plugins(omenu) {view_file {desc "View details"}}
lappend plugins(omenu) {reload_fdir {desc "Reload folder content"}}
# Context menu items for directories
lappend plugins(dmenu) {view_node {desc "View details"}}
lappend plugins(dmenu) {reload_node {desc "Reload tree node"}}
lappend plugins(dmenu) {reload_dir {desc "Reload folder"}}
lappend plugins(dmenu) {dedupdir {desc "De-duplicate folder"}}
lappend plugins(dmenu) {new_dir {desc "New sub-folder"}}
if {[system has tvdb]} {
lappend plugins(menu) {theTVDB {desc "&nbsp; change episode"}}
lappend plugins(dmenu) {theTVDB {desc "&nbsp; Add/change TVDB series"}}
}
## Bottom button definitions
#lappend plugins(buttons) "flexview {
# desc {<img src=/plugin/flexview/img/icon16.png>flexview Rules}
# link {/plugin/flexview/edit.jim?dir=[cgi_quote_url $dir]}
#}"
#
#
## Map browse context menu attributes to flexview column
#lappend plugins(dmenuattr) "forceflatview" "flatview"
#lappend plugins(dmenuattr) "noflat" "noflatten"
#lappend plugins(omenuattr) "detads" "Addetection"
#
#
## Icon name / icon
#lappend plugins(icons) "detectads"
#lappend plugins(icons) [_addicon "/plugin/detectads/img/detectads.png" "Ad Detection"]
#
#lappend plugins(icons) "flatview"
#lappend plugins(icons) [_addicon "/plugin/flatview/steamroller.png" "Force-flatview" "fvIcon"]
#
#lappend plugins(icons) "noflatten"
#lappend plugins(icons) [_addicon "/plugin/flatview/tyre.png" "No-flatten" "fvIcon"]
#
#
## File list column - see jqGrid documentation for column options
#lappend plugins(flcolumn) {
#{ name:'Addetection',
# hidden:false, width:22,
# stype: 'select',
# searchoptions: {value: {1:"Ad Detection",0:"No Ad Detection",'':"- &nbsp; All"}, searchOperMenu: false, clearSearch: false},
# label: icons.detectads + ' Ad Detection',
# formatter: fmtIcon, formatoptions: {img: icons.detectads}
# },
#}
#
## Directory tree column - see jqGrid documentation for column options
#lappend plugins(dircolumn) {
#{ name:'flatview',
# hidden:false, width:22,
# stype: 'select',
# searchoptions: {value: {1:"Force-FlatView",0:"No Force-FlatView",'':"- &nbsp; All"}, searchOperMenu: false, clearSearch: false},
# label: icons.flatview + ' Force-FlatView',
# formatter: fmtIcon, formatoptions: {img: icons.flatview}
# },
#}
#
#lappend plugins(dircolumn) {
#{ name:'noflatten',
# hidden:false, width:22,
# stype: 'select',
# searchoptions: {value: {1:"No-Flatten",0:"Allow Flatten",'':"- &nbsp; All"}, searchOperMenu: false, clearSearch: false},
# label: icons.noflatten + ' No-Flatten',
# formatter: fmtIcon, formatoptions: {img: icons.noflatten}
# },
#}

View File

@ -0,0 +1,73 @@
// plugin javascript code to support column formatters and context menu actions
plugins.menu.view_file = function(file) {
var rowid= getRowId(decodeURI(file));
viewRow('#filelist',rowid);
};
plugins.menu.reload_fdir = function(file1) {
var file =decodeURI(file1);
var dir = file.substr(0,file.lastIndexOf("/"));
reloadDir(dir);
};
plugins.dmenu.reload_dir = function(dir1) {
var dir = decodeURI(dir1);
reloadDir(dir);
};
plugins.dmenu.reload_node = function(dir) {
var rowid= getRowId(decodeURI(dir));
reloadNode(rowid);
};
plugins.dmenu.view_node = function(dir) {
var rowid= getRowId(decodeURI(dir));
viewRow('#dirlist',rowid);
};
plugins.menu.view = function(file) {
var rowid= getRowId(decodeURI(file));
viewFile(rowid);
};
plugins.menu.play = function(file) {
window.location = '/play/play.jim?file=' + file;
};
plugins.menu.theTVDB = function(file) {
window.location = '/browse/tvdb/episode.jim?file=' + file;
};
plugins.dmenu.dedupdir = function(dir) {
window.location = '/dedup/dedup.jim?dir=' + dir;
};
plugins.dmenu.new_dir = function(dir) {
// set dir field in form, no id :-(
var cdir = $("form#newdirform_form input:first").val();
$('form#newdirform_form input:first').val(decodeURI(dir));
$('#newdirform').dialog({
autoOpen: true,
height: 'auto', width: 'auto',
modal: true,
buttons: {
"Create": newdir_submit,
"Cancel": function() {
$(this).dialog('close');
}
},
close: function() { $('#newdirname').val(''); }
});
};
plugins.dmenu.theTVDB = function(dir) {
var cdir = window.dir;
window.dir = decodeURI(dir); // Set global var
$('#tvdbsetseries').show().enable().trigger('click').hide();
//window.dir = cdir; // Set global var
};

358
webif/plugin/flexview/genJSON.jim Executable file
View File

@ -0,0 +1,358 @@
#!/mod/bin/jimsh
######
# FlexView - Generate directory tree in jqGrid JSON format
# Author: MymsMan, 2015/16
######
package require cgi
source /mod/webif/lib/setup
require ts.class pretty_size system.class settings.class escape browse.class \
plugin epg.class classdump
set dustbin [system dustbin 1]
set root [system mediaroot]
set nicesplice [system pkginst nicesplice]
set model [system model]
set dlnaok [system dlnastatus]
set ignore {.nts .thm .hmi}
set include {.ts .avi .mpg .mpeg .wmv .mkv .mp3 .mp4 .mov .hmt .m4v .m4a}
# logging options
if {[lindex $argv 0] eq "-d"} {
set argv [lrange $argv 1 end]
set loglevel 2
} else {
# Use same logging option as auto log
set settings [settings]
set loglevel [$settings _nval_setting "autolog"]
}
proc log {msg {level 2}} {
if {$level > $::loglevel} return
system plog flexview $msg
}
######
# Common JSON code.
# Author: Martin Wink, 2014.
######
#
# Initialize a map from control characters to JSON escaped characters.
# Initially all non-null control characters to \u00xx sequences.
for {set i 1} {$i < 32} {incr i} {
set json_escape_map([format %c $i]) \\u[format %04x $i]
}
# Then overwrite certain well known control characters with shorter versions.
set json_escape_map([format %c 8]) \\b; # backspace
set json_escape_map([format %c 9]) \\t; # tab
set json_escape_map([format %c 10]) \\n; # lf
set json_escape_map([format %c 12]) \\f; # ff
set json_escape_map([format %c 13]) \\r; # cr
# Other special sequences
set json_escape_map(\") {\"}
set json_escape_map(\\) {\\}
set json_escape_map(/) {\/}
#
# Escape a string for inclusion in JSON output.
#
proc escape_json_string { in } {
return [string map $::json_escape_map $in]
}
###### end Common JSON code.
set lbrc "\{"
set rbrc "\}"
set lbrk "\["
set rbrk "\]"
set quot "\""
set nul ""
set comma ","
# id's are used as html id so shouldn't contain space, dot etc to avoid confusing jquery
# convert most specials to -
for {set i 1} {$i < 48} {incr i} {
set id_escape_map([format %c $i]) {-}
}
for {set i 58} {$i < 65} {incr i} {
set id_escape_map([format %c $i]) {-}
}
for {set i 91} {$i < 95} {incr i} {
set id_escape_map([format %c $i]) {-}
}
for {set i 123} {$i < 128} {incr i} {
set id_escape_map([format %c $i]) {-}
}
proc idQstring {str} {
# Return quoted and escaped id string
global quot
return "$quot[string map $::id_escape_map $str]$quot"
}
proc Qstring {str} {
# Return quoted and escaped string
global quot
return "$quot[escape_json_string $str]$quot"
}
proc json-key {key value {sep ","}} {
global quot
set jkey "$sep $quot$key$quot: $value"
return $jkey
}
proc p1l {str} {
puts -nonewline $str
}
proc pnl {str} {
puts $str
}
proc genDirList {dir ddir {depth 0} {expand 0} {i 0} {mode "children"}} {
global lbrc rbrc lbrk rbrk quot nul comma
if {[catch {set dsize [exec /mod/bin/busybox/du -s "$dir/"]} msg]} {
puts "Error, $dir, $msg"
return
}
lassign $dsize dsize
set psize [pretty_size $($dsize * 1024)]
set alist {}
set dlist {}
set flist {}
foreach entry [lsort [readdir -nocomplain $dir]] {
if {[string index $entry 0] eq "."} {
lappend alist $entry
continue
}
if {[file isdirectory "$dir/$entry"]} {
lappend dlist $entry
if {$depth <= 0} {
# quit if not loading
break
}
} else {
lappend flist $entry
}
}
set ssep $nul
if {$mode eq "parent"} {
# Basic fields for all directories
p1l [json-key title [Qstring $ddir] $lbrc]
set ssep $comma
p1l [json-key id [idQstring $dir] ]
p1l [json-key dir [Qstring $dir] ]
p1l [json-key level $i]
if {$i} {
p1l [json-key parent [idQstring [file dirname $dir]] ]
} else {
p1l [json-key parent null ]
}
p1l [json-key listloaded 0]
p1l [json-key opt [Qstring dopt] ]
p1l [json-key dsize $dsize ]
p1l [json-key psize [Qstring $psize] ]
# If subdirectories exist show whether node expanded or not
if {[llength $dlist]} {
p1l [json-key isLeaf false ]
if {$expand >0} {
p1l [json-key expanded true ]
} else {
p1l [json-key expanded false ]
}
} else {
p1l [json-key isLeaf true ]
}
# For each hidden file (dir attribute) create a flag (and content?) column
foreach attr $alist {
p1l [json-key [string range $attr 1 end] 1]
}
pnl "$rbrc"
}
# Recurse into subdirectories if they are to be expanded
if {[llength $dlist] && $depth > 0} {
incr depth -1
incr expand -1
incr i
foreach subd $dlist {
p1l $ssep
genDirList "$dir/$subd" $subd $depth $expand $i parent
set ssep $comma
}
# if {[llength $flist]} {
# p1l "$ssep [json-key title [Qstring -Files-] $lbrc]"
# pnl "[json-key folder false $comma] $rbrc"
# }
}
}
proc genFileDetails {dir file} {
global lbrc rbrc lbrk rbrk quot nul comma dinuse
set ext [string tolower [file extension $file]]
set base [file rootname $file]
set fsize [file size "$dir/$file"]
set psize [pretty_size $fsize]
# Basic attributes for all file types
p1l [json-key id [idQstring "$dir/$file"] $lbrc ]
p1l [json-key fname [Qstring $file] ]
p1l [json-key dir [Qstring "$dir"] ]
p1l [json-key sel false]
p1l [json-key showrow true]
p1l [json-key fsize $fsize ]
p1l [json-key psize [Qstring $psize] ]
if {$ext eq ".ts"} {
set omenu opt
set type ts
} else {
set omenu oopt
set type gen
}
p1l [json-key ftype [Qstring $type] ]
pnl [json-key opt [Qstring $omenu] ]
if {[file exists "$dir/$base.hmt"]} {
# Generate hmt related attributes
# Use HMT utility rather than TS class so we can handle orphaned HMT (and write less code)
set line [exec hmt -p "$dir/$file"]
set vars [split $line "\t"]
set vnames {title synopsis definition channel_num channel_name
start end flags_list guidance bookmarks schedstart scheddur
genre resume status seriesnum episodenum episodetot}
set ix 0
foreach vn $vnames {
p1l [json-key $vn [Qstring [lindex $vars $ix]] ]
set $vn [lindex $vars $ix]
incr ix
}
if {[string is integer -strict $end]} {
# calc duration
set duration $($end-$start)
p1l [json-key duration $duration ]
}
if {$flags_list != ""} {
# Individual Flag entries
set flags [split [string range $flags_list 0 end-1] ,]
foreach flag $flags {
if {$flag in {HD SD {Unlimited Copies}}} {continue}
p1l [json-key $flag 1]
}
# Need to special case Decrypt since it is the absence of ODE flag
if {"ODEncrypted" ni $flags} {p1l [json-key Decrypted 1]}
}
}
if {[file exists "$dir/$base.thm"]} {
# Generate thumbnail related attributes
p1l [json-key thmok 1]
}
# Possibly use SQL call to retrieve all DLNA for directory?
set dlnaIndex [system dlnaurl [file normalize $dir/$file]]
if {$dlnaIndex != ""} {
p1l [json-key dlna 1]
p1l [json-key dlnaIndex [Qstring $dlnaIndex] ]
}
# Check for file inuse
if {$file in $dinuse} {
p1l [json-key inuse 1]
}
pnl "$rbrc"
}
proc genFileList {dirlist } {
global lbrc rbrc lbrk rbrk quot nul comma dinuse
set ssep " "
set dirl [split $dirlist {,}]
foreach dir $dirl {
set alist {}
set dlist {}
set flist {}
set dinuse [system dirinuse $dir]
foreach entry [lsort [readdir -nocomplain $dir]] {
if {[string index $entry 0] eq "."} {
lappend alist $entry
continue
}
if {[file isdirectory "$dir/$entry"]} {
lappend dlist $entry
} else {
set ext [string tolower [file extension $entry]]
if {$ext ni $::include} { continue }
set base [file rootname $entry]
if {$ext eq ".hmt"} {
if {[file exists "$dir/$base.ts"]} { continue }
}
lappend flist $entry
}
}
if {[llength $flist]} {
foreach file $flist {
p1l $ssep
genFileDetails $dir $file
set ssep $comma
}
}
}
}
httpheader "application/json"
set dir [cgi_get dir "/media"]
set dir [cgi_get dir $root]
set level [cgi_get n_level 0]
set mode [cgi_get mode "parent"]
set mode [cgi_get mode "filelist"]
pnl "[json-key rows $lbrk $lbrc]"
set start [clock milliseconds]
if {$mode == "filelist"} {
genFileList $dir
} else {
genDirList $dir [file tail $dir] 1 1 $level $mode
}
set end [clock milliseconds]
set elapse $(double($end-$start)/1000.0)
log "$mode | $elapse | $dir" 0
pnl "$rbrk $rbrc"

View File

@ -0,0 +1,7 @@
register_module "flexview" "/plugin/flexview/"
#system plog flexview "gohook mod $module last $lastmodule"
if {$module eq "browse" && $lastmodule eq "flexview"} {
override_module "flexview"
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 69 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 69 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 566 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 225 B

403
webif/plugin/flexview/index.jim Executable file
View File

@ -0,0 +1,403 @@
#!/mod/bin/jimsh
package require cgi
source /mod/webif/lib/setup
require ts.class pretty_size system.class settings.class escape browse.class \
plugin epg.class classdump
set mroot [system mediaroot]
set dir [cgi_get topdir $mroot]
set model [system model]
set dustbin [system dustbin 1]
set nicesplice [system pkginst nicesplice]
set settings [settings]
set curconfig [$settings _tval_setting "fvConfig"]
set curconfig [cgi_get config $curconfig]
if {$curconfig == 0} {set curconfig "default"}
jqplugin contextMenu bar enadis blockui dialogue
header
jscss js/i18n/grid.locale-en.js
puts {
<script type="text/javascript">
jQuery.jgrid.no_legacy_api = true;
</script>
}
#jscss js/jquery.jqGrid.min.js css/ui.jqgrid.css
jscss js/jquery.jqGrid.js css/ui.jqgrid.css
jscss plugins/ui.multiselect.js plugins/ui.multiselect.css
jscss /browse/script.js /browse/style.css
jscss contextmenu.js
jscss script.js style.css
if {[system has tvdb]} { jscss /browse/tvdb/script.js /browse/tvdb/style.css }
set plugins { dmenu {} menu {} omenu {} buttons {} dircolumn {} flcolumn {} icons {} dmenuattr {} menuattr {} omenuattr {} }
eval_plugins browse
eval_plugins flexview
puts "
<script type=text/javascript>
var dir = '[string map {' {\'}} $dir]';
var mediaroot = '$mroot';
"
# Table column models & default config
puts {
var plugin = {};
// Default config
var fvConfig = {};
fvConfig.name = 'basic';
fvConfig.curdirsel = null;
fvConfig.tablewidth =0;
fvConfig.tabledepth =0;
fvConfig.dircolwidth =0;
fvConfig.flcolwidth =0
fvConfig.dircolhidden = false;
fvConfig.dirshrinktofit = false;
fvConfig.flshrinktofit = false;
fvConfig.datefmt = "d M y";
fvConfig.datetimefmt = "d M y H:i";
fvConfig.timefmt = "H:i:s";
fvConfig.dirColChg={};
fvConfig.flColChg={};
fvConfig.sel={};
fvConfig.excl={};
fvConfig.exp={};
fvConfig.exp['-media']=true;
fvConfig.exp['-media-My-Video']=true;
fvConfig.saveInt=15000;
fvConfig.reloadInt=60;
}
set fname "/mod/etc/fvConfig_$curconfig.json"
if {[catch {set fd [open $fname r]
set fvconfig [read $fd ]
#puts "Object.assign(fvConfig,$fvconfig);"
puts "\$.extend(fvConfig,$fvconfig);"
puts "fvConfig.name = '$curconfig';"
close $fd} msg opts]} {
puts "console.error('Config file error caught: $msg');"
}
puts {
// Icons
var icons = {};
}
set icon [_addicon "/img/compressr.png" "Recursive Auto-shrink"]
puts "icons.autoshrinkr = '$icon';"
set icon [_addicon "/img/compress.png" "Auto-shrink"]
puts "icons.autoshrink = '$icon';"
set icon [_addicon "/img/dedup.png" "Auto-dedup"]
puts "icons.autodedup = '$icon';"
set icon [_addicon "/img/decryptr.png" "Recursive Auto-decrypt"]
puts "icons.autodecryptr = '$icon';"
set icon [_addicon "/img/decrypt.png" "Auto-decrypt"]
puts "icons.autodecrypt = '$icon';"
set icon [_addicon "/img/mpg.png" "Auto-MPG Extract"]
puts "icons.autompg = '$icon';"
set icon [_addicon "/img/mp3.png" "Auto-Audio Extract"]
puts "icons.automp3 = '$icon';"
set icon [_addicon "/img/clock.png" "Auto-Expire"]
puts "icons.autoexpire = '$icon';"
set icon [_addicon "/img/tvdb-sm.png" "TheTVDB"]
puts "icons.tvdb = '$icon';"
set icon [_addicon "/images/175_1_11_Series_Record.png" "Series"]
puts "icons.series = '$icon';"
set icon [_addicon "/images/172_1_00_HD.png" "High Definition"]
puts "icons.HD = '$icon';"
set icon [_addicon "/images/172_1_26_SD.png" "Standard Definition"]
puts "icons.SD = '$icon';"
set icon [_addicon "/images/178_1_00_Icon_Lock.png" "Locked"]
puts "icons.Locked = '$icon';"
set icon [_addicon "/images/749_1_26_Video_Encryption.png" "Protected"]
puts "icons.ENC = '$icon';"
set icon [_addicon "/img/Decrypted.png" "Decrypted"]
puts "icons.DEC = '$icon';"
set icon [_addicon "/plugin/flexview/img/NotDecrypted.png" "On disk encrypted"]
puts "icons.ODE = '$icon';"
#set icon [_addicon "/img/Guidance_purple.png" "Guidance"]
set icon [_addicon "/img/Guidance_purple.png"]
puts "icons.guidance = '$icon';"
#set icon [_addicon "/img/Guidance_blue.png" "General Guidance"]
set icon [_addicon "/img/Guidance_blue.png"]
puts "icons.Gguidance = '$icon';"
set icon [_addicon "/img/compress.png" "Shrunk"]
puts "icons.Shrunk = '$icon';"
set icon [_addicon "/img/dedup.png" "De-duped"]
puts "icons.Deduped = '$icon';"
set icon [_addicon "/img/dlna.png" "Indexed by DLNA Server"]
puts "icons.DLNA = '$icon';"
set icon [_addicon "/images/743_1_10_Video_New.png" "New (unwatched)"]
puts "icons.new = '$icon';"
set icon [_addicon "/img/context/thumb.png" "Thumbnail"]
puts "icons.thumb = '$icon';"
set icon [_addicon "/img/context/bookmark.png" "Bookmark"]
puts "icons.bookmark = '$icon';"
set icon [_addicon "/img/inuse.png" "In Use"]
puts "icons.inuse = '$icon';"
set icon [_addicon "/img/radio.png" "Radio Programme"]
puts "icons.Radio = '$icon';"
foreach {icon image} $plugins(icons) {
# plugin icons
puts "icons.$icon = '$image';"
}
# opt+ menu attr values to table column mapping
# browse Opt+ menus use a set of addon keywords in HTML element to pass values to menu
# Define mapping with table rows to allow element to be built correctly
# plugins can extend the list
puts {var dirmenuattr = {
autoshrink: 'autoshrink',
autoshrinkR: 'autoshrinkR',
autodedup: 'autodedup',
autodecrypt: 'autodecrypt',
autodecryptR: 'autodecryptR',
autompg: 'autompg',
automp3: 'automp3',
autoexpire: 'autoexpire',
tvdbseriesid: 'tvdb'};
}
foreach {attr column} $plugins(dmenuattr) {
# plugin attribute
puts "dirmenuattr.$attr = '$column';"
}
puts {var optmenuattr = {
def: 'definition',
locked: 'Locked',
encd: 'Encrypted',
odencd: 'ODEncrypted',
shrunk: 'Shrunk',
bx: 'bookmarks',
type: 'ftype',
rsize: 'fsize',
dlna: 'dlna',
thmok: 'thmok',
new: 'New'};
}
foreach {attr column} $plugins(omenuattr) {
# plugin attribute
puts "optmenuattr.$attr = '$column';"
}
# Genre icon list
set glist [ts genrelist]
puts "var genrelist = {"
foreach {gcode genreval} $glist {
lassign $genreval txt img
if {$img eq "Unclassified"} {
set icon [_addicon "/images/173_3_26_G3_$img.png" $txt "fvICON va genre"]
} else {
set icon [_addicon "/images/173_3_00_G3_$img.png" $txt "fvICON va genre"]
}
puts "$gcode: '$icon $txt',"
}
puts "'':'- &nbsp;&nbsp; All'};"
puts {
// Table column models
var dirColModel= [
}
set fd [open "DirColModel.json" r] ; # std columns
set data [read $fd]
puts $data
close $fd
#puts "// $plugins "
foreach plugin $plugins(dircolumn) {
# plugin columns
puts $plugin
}
puts {
];
var flColModel= [
}
set fd [open "FlColModel.json" r] ; # std columns
set data [read $fd]
puts $data
close $fd
#puts "// $plugins "
foreach plugin $plugins(flcolumn) {
# plugin columns
puts $plugin
}
puts {
];
var flData = [];
var flLoaded = {};
var flLoadTime = {};
var dirstart;
</script>
}
source /mod/webif/html/browse/assets.jim
if {[system has tvdb]} { source /mod/webif/html/browse/tvdb/assets.jim }
puts "<button style=\"display:none\" id=tvdbsetseries>TVDB</button>"
puts "<span style=\"display:none\" id=dir>$dir</span>"
puts "<span style=\"display:none\" id=mediaroot>$mroot</span>"
# Top option buttons
puts "<div class=brow>"
puts {
<button id=reload><img src=/plugin/flexview/img/refresh02.png>Reload grids</button>
<button id=load>Load</button>
<button id=save>Save</button>
<div id="topspin" class=spinner style="float: right"><img border=0 src=/img/loading.gif>
Loading... Please wait...</div>
<div id="topmsg" class=blood style="float: right"></div>
</div>
<div id=configload title="Load configuration" style="display: none">
<select id=configllist class="ui-widget ui-corner-all">
}
source /mod/webif/plugin/flexview/configlist.jim
genConfigList
puts {
</select>
</div>
<div id=configsave title="Save configuration as" style="display: none">
<datalist id=configslist>
}
genConfigList
puts "
</datalist>
<input id=configname type=text list=configslist value=\"$curconfig\" size=50 class=\"ui-widget ui-corner-all\">
</div>
"
# Directory and filelist grids
puts {
<div style="clear: both" >
<table id="fvtable" border="1" class="ui-widget ui-corner-all">
<col id=dircol> <col id=flcol>
<thead border="1" class="ui-widget-header">
<tr>
<th id="dirhead" class="ui-widget ui-corner-all">Directory tree view
<div id="dirspin" class=spinner style="float: left"><img border=0 src=/img/loading.gif>
Loading... Please wait...</div>
<span style="float: right">
<button id=dircolumn>Columns</button>
<button id=hide>Hide Tree</button>
</span>
</th>
<th id="flhead" class="ui-widget ui-corner-all">File list view
<div id="flspin" class=spinner style="float: left"><img border=0 src=/img/loading.gif>
Loading... Please wait...</div>
<span style="float: right">
<button id=flcolumn>Columns</button>
<button id=show>Show Tree</button>
</span>
</th>
</tr>
</thead>
<tbody>
<tr id="dirrow">
<td id="dircell">
<!-- Add a <table> element where the tree should appear: -->
<table id="dirlist">
<tr> <td></td> </tr>
</table>
</td>
<td id="flcell">
<!-- Add a <table> element where the file list should appear: -->
<table id="filelist">
<tr> <td></td> </tr>
</table>
</td>
</tr>
</tbody>
</table>
</div>
}
# Bottom browse action buttons
puts "<div class=brow>"
puts {
<button id=copy class=onesel>Copy</button>
<button id=cut class=onesel>Cut</button>
<button id=delete class=onesel>Delete</button>
<div id=deletewait class=blood style="display: none">
<img src=/img/loading.gif>Deleting may take some time, please be patient...
</div>
}
# Join
if {$nicesplice} {
puts { <button id=join class=tstwosel>Join</button> }
}
# Queue
set queueactions(decrypt) "Decryption"
set queueactions(shrink) "Shrink"
set queueactions(mp2) "Audio-Extraction (mp2, fast)"
set queueactions(mp3) "Audio-Extraction (mp3, slow)"
set queueactions(mpg) "Conversion to MPG"
eval_plugins queueactions
puts {
<button id=so_queue class=tsonesel><img src=/img/queueps.png class=qb>
Queue for</button>
<select id=so_queueactions
class="tsonesel text ui-widget-content ui-corner-all">
}
set rqueueactions [lreverse $queueactions]
foreach v [lsort [dict keys $rqueueactions]] {
set k $rqueueactions($v)
puts "<option value=$k>$v</option>"
}
puts {
</select>
}
puts {
<button id=queue><img src=/img/queueps.png class=qb> View Queue</button>
}
# Plugin buttons
if {[llength $plugins(buttons)]} {
puts "</div><div class=buttonrow>"
foreach plugin $plugins(buttons) {
lassign $plugin tag options
puts "
<button class=plugin act=\"$options(link)\">$options(desc)</button>
"
}
}
puts "</div>"
# clipboard
puts {
<fieldset class=left style="margin: 0 1em 1em 1em; float: right">
<legend style="font-size: 1.5em; padding: 0 0.5em 0.5em 0.5em; color: #1e5bbd;">
File Clipboard
</legend>
<div id=clipboard>
<img src=/img/loading.gif> <i>Loading...</i>
</div>
</fieldset>
}
footer

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,220 @@
/**
* jqGrid English Translation
* Tony Tomov tony@trirand.com
* http://trirand.com/blog/
* Dual licensed under the MIT and GPL licenses:
* http://www.opensource.org/licenses/mit-license.php
* http://www.gnu.org/licenses/gpl.html
**/
/*global jQuery, define */
(function( factory ) {
"use strict";
if ( typeof define === "function" && define.amd ) {
// AMD. Register as an anonymous module.
define([
"jquery",
"../grid.base"
], factory );
} else {
// Browser globals
factory( jQuery );
}
}(function( $ ) {
$.jgrid = $.jgrid || {};
if(!$.jgrid.hasOwnProperty("regional")) {
$.jgrid.regional = [];
}
$.jgrid.regional["en"] = {
defaults : {
recordtext: "View {0} - {1} of {2}",
emptyrecords: "No records to view",
loadtext: "Loading...",
savetext: "Saving...",
pgtext : "Page {0} of {1}",
pgfirst : "First Page",
pglast : "Last Page",
pgnext : "Next Page",
pgprev : "Previous Page",
pgrecs : "Records per Page",
showhide: "Toggle Expand Collapse Grid",
// mobile
pagerCaption : "Grid::Page Settings",
pageText : "Page:",
recordPage : "Records per Page",
nomorerecs : "No more records...",
scrollPullup: "Pull up to load more...",
scrollPulldown : "Pull down to refresh...",
scrollRefresh : "Release to refresh..."
},
search : {
caption: "Search...",
Find: "Find",
Reset: "Reset",
odata: [{ oper:'eq', text:'equal'},{ oper:'ne', text:'not equal'},{ oper:'lt', text:'less'},{ oper:'le', text:'less or equal'},{ oper:'gt', text:'greater'},{ oper:'ge', text:'greater or equal'},{ oper:'bw', text:'begins with'},{ oper:'bn', text:'does not begin with'},{ oper:'in', text:'is in'},{ oper:'ni', text:'is not in'},{ oper:'ew', text:'ends with'},{ oper:'en', text:'does not end with'},{ oper:'cn', text:'contains'},{ oper:'nc', text:'does not contain'},{ oper:'nu', text:'is null'},{ oper:'nn', text:'is not null'}, {oper:'bt', text:'between'}],
groupOps: [{ op: "AND", text: "all" },{ op: "OR", text: "any" }],
operandTitle : "Click to select search operation.",
resetTitle : "Reset Search Value"
},
edit : {
addCaption: "Add Record",
editCaption: "Edit Record",
bSubmit: "Submit",
bCancel: "Cancel",
bClose: "Close",
saveData: "Data has been changed! Save changes?",
bYes : "Yes",
bNo : "No",
bExit : "Cancel",
msg: {
required:"Field is required",
number:"Please, enter valid number",
minValue:"value must be greater than or equal to ",
maxValue:"value must be less than or equal to",
email: "is not a valid e-mail",
integer: "Please, enter valid integer value",
date: "Please, enter valid date value",
url: "is not a valid URL. Prefix required ('http://' or 'https://')",
nodefined : " is not defined!",
novalue : " return value is required!",
customarray : "Custom function should return array!",
customfcheck : "Custom function should be present in case of custom checking!"
}
},
view : {
caption: "View Record",
bClose: "Close"
},
del : {
caption: "Delete",
msg: "Delete selected record(s)?",
bSubmit: "Delete",
bCancel: "Cancel"
},
nav : {
edittext: "",
edittitle: "Edit selected row",
addtext:"",
addtitle: "Add new row",
deltext: "",
deltitle: "Delete selected row",
searchtext: "",
searchtitle: "Find records",
refreshtext: "",
refreshtitle: "Reload Grid",
alertcap: "Warning",
alerttext: "Please, select row",
viewtext: "",
viewtitle: "View selected row",
savetext: "",
savetitle: "Save row",
canceltext: "",
canceltitle : "Cancel row editing",
selectcaption : "Actions..."
},
col : {
caption: "Select columns",
bSubmit: "Ok",
bCancel: "Cancel"
},
errors : {
errcap : "Error",
nourl : "No url is set",
norecords: "No records to process",
model : "Length of colNames <> colModel!"
},
formatter : {
integer : {thousandsSeparator: ",", defaultValue: '0'},
number : {decimalSeparator:".", thousandsSeparator: ",", decimalPlaces: 2, defaultValue: '0.00'},
currency : {decimalSeparator:".", thousandsSeparator: ",", decimalPlaces: 2, prefix: "", suffix:"", defaultValue: '0.00'},
date : {
dayNames: [
"Sun", "Mon", "Tue", "Wed", "Thr", "Fri", "Sat",
"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"
],
monthNames: [
"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec",
"January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"
],
AmPm : ["am","pm","AM","PM"],
S: function (j) {return j < 11 || j > 13 ? ['st', 'nd', 'rd', 'th'][Math.min((j - 1) % 10, 3)] : 'th';},
srcformat: 'Y-m-d',
newformat: 'n/j/Y',
parseRe : /[#%\\\/:_;.,\t\s-]/,
masks : {
// see http://php.net/manual/en/function.date.php for PHP format used in jqGrid
// and see http://docs.jquery.com/UI/Datepicker/formatDate
// and https://github.com/jquery/globalize#dates for alternative formats used frequently
// one can find on https://github.com/jquery/globalize/tree/master/lib/cultures many
// information about date, time, numbers and currency formats used in different countries
// one should just convert the information in PHP format
ISO8601Long:"Y-m-d H:i:s",
ISO8601Short:"Y-m-d",
// short date:
// n - Numeric representation of a month, without leading zeros
// j - Day of the month without leading zeros
// Y - A full numeric representation of a year, 4 digits
// example: 3/1/2012 which means 1 March 2012
ShortDate: "n/j/Y", // in jQuery UI Datepicker: "M/d/yyyy"
// long date:
// l - A full textual representation of the day of the week
// F - A full textual representation of a month
// d - Day of the month, 2 digits with leading zeros
// Y - A full numeric representation of a year, 4 digits
LongDate: "l, F d, Y", // in jQuery UI Datepicker: "dddd, MMMM dd, yyyy"
// long date with long time:
// l - A full textual representation of the day of the week
// F - A full textual representation of a month
// d - Day of the month, 2 digits with leading zeros
// Y - A full numeric representation of a year, 4 digits
// g - 12-hour format of an hour without leading zeros
// i - Minutes with leading zeros
// s - Seconds, with leading zeros
// A - Uppercase Ante meridiem and Post meridiem (AM or PM)
FullDateTime: "l, F d, Y g:i:s A", // in jQuery UI Datepicker: "dddd, MMMM dd, yyyy h:mm:ss tt"
// month day:
// F - A full textual representation of a month
// d - Day of the month, 2 digits with leading zeros
MonthDay: "F d", // in jQuery UI Datepicker: "MMMM dd"
// short time (without seconds)
// g - 12-hour format of an hour without leading zeros
// i - Minutes with leading zeros
// A - Uppercase Ante meridiem and Post meridiem (AM or PM)
ShortTime: "g:i A", // in jQuery UI Datepicker: "h:mm tt"
// long time (with seconds)
// g - 12-hour format of an hour without leading zeros
// i - Minutes with leading zeros
// s - Seconds, with leading zeros
// A - Uppercase Ante meridiem and Post meridiem (AM or PM)
LongTime: "g:i:s A", // in jQuery UI Datepicker: "h:mm:ss tt"
SortableDateTime: "Y-m-d\\TH:i:s",
UniversalSortableDateTime: "Y-m-d H:i:sO",
// month with year
// Y - A full numeric representation of a year, 4 digits
// F - A full textual representation of a month
YearMonth: "F, Y" // in jQuery UI Datepicker: "MMMM, yyyy"
},
reformatAfterEdit : false,
userLocalTime : false
},
baseLinkUrl: '',
showAction: '',
target: '',
checkbox : {disabled:true},
idName : 'id'
},
colmenu : {
sortasc : "Sort Ascending",
sortdesc : "Sort Descending",
columns : "Columns",
filter : "Filter",
grouping : "Group By",
ungrouping : "Ungroup",
searchTitle : "Get items with value that:",
freeze : "Freeze",
unfreeze : "Unfreeze",
reorder : "Move to reorder"
}
};
}));

View File

@ -0,0 +1,43 @@
Installation
Download the jqGrid package from the www.trirand/blog site section downloads.
Note the new download manager where you can choose which modules you want to
include in the download.
In order to use jqGrid 3.5, first a UI theme css file should be loaded.
Download the desired theme (or build a custom one) from jQueryUI site
(www.jqueryui.com) and point in your link tag in head section the path to the
theme css
<link rel="stylesheet" type="text/css" media="screen" href="path_to_ui_css_file/jquery-ui-1.7.1.custom.css" />
where the path_to_ui_css_file is a valid path to the ui theme file
Extract the jqGrid package and copy the ui.jqgrid.css from css directory to
your webserver directory. It is not necessary that the jqgrid css file is in
the same directory as those of the jquery ui css.
<link rel="stylesheet" type="text/css" media="screen" href="path_to_jqgrid_css_file/ui.jqgrid.css" />
Starting with this version, jqGrid does not use a loader (which loads the
needed files one by one), but all the needed code is contained in one file.
The desired modules can be built using the jqGrid download manager from the
site pointed above. In order to use this, first a language file should be
loaded and then the jqgrid file.
Copy the desired language file from js/i18n directory to your web server
directory where you store the java script files. Every language file is
named grid.locale-XX.js, where XX is a two-letter code for the language.
Copy the jquery.jqGid.min.js file to the same or other valid directory in
your web server
Include both the files in script tags in the head section
<script src="path_to_js_files/grid.locale-en.js" type="text/javascript"></script>
<script src="path_to_js_files/jquery.jqGrid.min.js" type="text/javascript"></script>
For debugging purposes, I have created a grid.loader.js which does the same
loading of the files as in previous versions. The location of the file is in
src directory of the package. In order to use this, the variable pathojsfiles
should be adjusted to point to the appropriate folder - see 3.4.x docs.

View File

@ -0,0 +1,485 @@
/*
**
* formatter for values but most of the values if for jqGrid
* Some of this was inspired and based on how YUI does the table datagrid but in jQuery fashion
* we are trying to keep it as light as possible
* Joshua Burnett josh@9ci.com
* http://www.greenbill.com
*
* Changes from Tony Tomov tony@trirand.com
* Dual licensed under the MIT and GPL licenses:
* http://www.opensource.org/licenses/mit-license.php
* http://www.gnu.org/licenses/gpl-2.0.html
*
**/
/*jshint eqeqeq:false */
/*global jQuery, define */
(function( factory ) {
"use strict";
if ( typeof define === "function" && define.amd ) {
// AMD. Register as an anonymous module.
define([
"jquery",
"./grid.base"
], factory );
} else {
// Browser globals
factory( jQuery );
}
}(function( $ ) {
"use strict";
//module begin
$.fmatter = {};
//opts can be id:row id for the row, rowdata:the data for the row, colmodel:the column model for this column
//example {id:1234,}
$.extend($.fmatter,{
isBoolean : function(o) {
return typeof o === 'boolean';
},
isObject : function(o) {
return (o && (typeof o === 'object' || $.isFunction(o))) || false;
},
isString : function(o) {
return typeof o === 'string';
},
isNumber : function(o) {
return typeof o === 'number' && isFinite(o);
},
isValue : function (o) {
return (this.isObject(o) || this.isString(o) || this.isNumber(o) || this.isBoolean(o));
},
isEmpty : function(o) {
if(!this.isString(o) && this.isValue(o)) {
return false;
}
if (!this.isValue(o)){
return true;
}
o = $.trim(o).replace(/\&nbsp\;/ig,'').replace(/\&#160\;/ig,'');
return o==="";
}
});
$.fn.fmatter = function(formatType, cellval, opts, rwd, act) {
// build main options before element iteration
var v=cellval;
opts = $.extend({}, $.jgrid.getRegional(this, 'formatter') , opts);
try {
v = $.fn.fmatter[formatType].call(this, cellval, opts, rwd, act);
} catch(fe){}
return v;
};
$.fmatter.util = {
// Taken from YAHOO utils
NumberFormat : function(nData,opts) {
if(!$.fmatter.isNumber(nData)) {
nData *= 1;
}
if($.fmatter.isNumber(nData)) {
var bNegative = (nData < 0);
var sOutput = String(nData);
var sDecimalSeparator = opts.decimalSeparator || ".";
var nDotIndex;
if($.fmatter.isNumber(opts.decimalPlaces)) {
// Round to the correct decimal place
var nDecimalPlaces = opts.decimalPlaces;
var nDecimal = Math.pow(10, nDecimalPlaces);
sOutput = String(Math.round(nData*nDecimal)/nDecimal);
nDotIndex = sOutput.lastIndexOf(".");
if(nDecimalPlaces > 0) {
// Add the decimal separator
if(nDotIndex < 0) {
sOutput += sDecimalSeparator;
nDotIndex = sOutput.length-1;
}
// Replace the "."
else if(sDecimalSeparator !== "."){
sOutput = sOutput.replace(".",sDecimalSeparator);
}
// Add missing zeros
while((sOutput.length - 1 - nDotIndex) < nDecimalPlaces) {
sOutput += "0";
}
}
}
if(opts.thousandsSeparator) {
var sThousandsSeparator = opts.thousandsSeparator;
nDotIndex = sOutput.lastIndexOf(sDecimalSeparator);
nDotIndex = (nDotIndex > -1) ? nDotIndex : sOutput.length;
var sNewOutput = sOutput.substring(nDotIndex);
var nCount = -1, i;
for (i=nDotIndex; i>0; i--) {
nCount++;
if ((nCount%3 === 0) && (i !== nDotIndex) && (!bNegative || (i > 1))) {
sNewOutput = sThousandsSeparator + sNewOutput;
}
sNewOutput = sOutput.charAt(i-1) + sNewOutput;
}
sOutput = sNewOutput;
}
// Prepend prefix
sOutput = (opts.prefix) ? opts.prefix + sOutput : sOutput;
// Append suffix
sOutput = (opts.suffix) ? sOutput + opts.suffix : sOutput;
return sOutput;
}
return nData;
}
};
$.fn.fmatter.defaultFormat = function(cellval, opts) {
return ($.fmatter.isValue(cellval) && cellval!=="" ) ? cellval : opts.defaultValue || "&#160;";
};
$.fn.fmatter.email = function(cellval, opts) {
if(!$.fmatter.isEmpty(cellval)) {
return "<a href=\"mailto:" + cellval + "\">" + cellval + "</a>";
}
return $.fn.fmatter.defaultFormat(cellval,opts );
};
$.fn.fmatter.checkbox =function(cval, opts) {
var op = $.extend({},opts.checkbox), ds;
if(opts.colModel !== undefined && opts.colModel.formatoptions !== undefined) {
op = $.extend({},op,opts.colModel.formatoptions);
}
if(op.disabled===true) {ds = "disabled=\"disabled\"";} else {ds="";}
if($.fmatter.isEmpty(cval) || cval === undefined ) {cval = $.fn.fmatter.defaultFormat(cval,op);}
cval=String(cval);
cval=(cval+"").toLowerCase();
var bchk = cval.search(/(false|f|0|no|n|off|undefined)/i)<0 ? " checked='checked' " : "";
return "<input type=\"checkbox\" " + bchk + " value=\""+ cval+"\" offval=\"no\" "+ds+ "/>";
};
$.fn.fmatter.link = function(cellval, opts) {
var op = {target:opts.target};
var target = "";
if(opts.colModel !== undefined && opts.colModel.formatoptions !== undefined) {
op = $.extend({},op,opts.colModel.formatoptions);
}
if(op.target) {target = 'target=' + op.target;}
if(!$.fmatter.isEmpty(cellval)) {
return "<a "+target+" href=\"" + cellval + "\">" + cellval + "</a>";
}
return $.fn.fmatter.defaultFormat(cellval,opts);
};
$.fn.fmatter.showlink = function(cellval, opts) {
var op = {baseLinkUrl: opts.baseLinkUrl,showAction:opts.showAction, addParam: opts.addParam || "", target: opts.target, idName: opts.idName},
target = "", idUrl;
if(opts.colModel !== undefined && opts.colModel.formatoptions !== undefined) {
op = $.extend({},op,opts.colModel.formatoptions);
}
if(op.target) {target = 'target=' + op.target;}
idUrl = op.baseLinkUrl+op.showAction + '?'+ op.idName+'='+opts.rowId+op.addParam;
if($.fmatter.isString(cellval) || $.fmatter.isNumber(cellval)) { //add this one even if its blank string
return "<a "+target+" href=\"" + idUrl + "\">" + cellval + "</a>";
}
return $.fn.fmatter.defaultFormat(cellval,opts);
};
$.fn.fmatter.integer = function(cellval, opts) {
var op = $.extend({},opts.integer);
if(opts.colModel !== undefined && opts.colModel.formatoptions !== undefined) {
op = $.extend({},op,opts.colModel.formatoptions);
}
if($.fmatter.isEmpty(cellval)) {
return op.defaultValue;
}
return $.fmatter.util.NumberFormat(cellval,op);
};
$.fn.fmatter.number = function (cellval, opts) {
var op = $.extend({},opts.number);
if(opts.colModel !== undefined && opts.colModel.formatoptions !== undefined) {
op = $.extend({},op,opts.colModel.formatoptions);
}
if($.fmatter.isEmpty(cellval)) {
return op.defaultValue;
}
return $.fmatter.util.NumberFormat(cellval,op);
};
$.fn.fmatter.currency = function (cellval, opts) {
var op = $.extend({},opts.currency);
if(opts.colModel !== undefined && opts.colModel.formatoptions !== undefined) {
op = $.extend({},op,opts.colModel.formatoptions);
}
if($.fmatter.isEmpty(cellval)) {
return op.defaultValue;
}
return $.fmatter.util.NumberFormat(cellval,op);
};
$.fn.fmatter.date = function (cellval, opts, rwd, act) {
var op = $.extend({},opts.date);
if(opts.colModel !== undefined && opts.colModel.formatoptions !== undefined) {
op = $.extend({},op,opts.colModel.formatoptions);
}
if(!op.reformatAfterEdit && act === 'edit'){
return $.fn.fmatter.defaultFormat(cellval, opts);
}
if(!$.fmatter.isEmpty(cellval)) {
return $.jgrid.parseDate.call(this, op.srcformat,cellval,op.newformat,op);
}
return $.fn.fmatter.defaultFormat(cellval, opts);
};
$.fn.fmatter.select = function (cellval,opts) {
// jqGrid specific
cellval = String(cellval);
var oSelect = false, ret=[], sep, delim;
if(opts.colModel.formatoptions !== undefined){
oSelect= opts.colModel.formatoptions.value;
sep = opts.colModel.formatoptions.separator === undefined ? ":" : opts.colModel.formatoptions.separator;
delim = opts.colModel.formatoptions.delimiter === undefined ? ";" : opts.colModel.formatoptions.delimiter;
} else if(opts.colModel.editoptions !== undefined){
oSelect= opts.colModel.editoptions.value;
sep = opts.colModel.editoptions.separator === undefined ? ":" : opts.colModel.editoptions.separator;
delim = opts.colModel.editoptions.delimiter === undefined ? ";" : opts.colModel.editoptions.delimiter;
}
if (oSelect) {
var msl = (opts.colModel.editoptions != null && opts.colModel.editoptions.multiple === true) === true ? true : false,
scell = [], sv;
if(msl) {scell = cellval.split(",");scell = $.map(scell,function(n){return $.trim(n);});}
if ($.fmatter.isString(oSelect)) {
// mybe here we can use some caching with care ????
var so = oSelect.split(delim), j=0, i;
for(i=0; i<so.length;i++){
sv = so[i].split(sep);
if(sv.length > 2 ) {
sv[1] = $.map(sv,function(n,i){if(i>0) {return n;}}).join(sep);
}
if(msl) {
if($.inArray(sv[0],scell)>-1) {
ret[j] = sv[1];
j++;
}
} else if($.trim(sv[0]) === $.trim(cellval)) {
ret[0] = sv[1];
break;
}
}
} else if($.fmatter.isObject(oSelect)) {
// this is quicker
if(msl) {
ret = $.map(scell, function(n){
return oSelect[n];
});
} else {
ret[0] = oSelect[cellval] || "";
}
}
}
cellval = ret.join(", ");
return cellval === "" ? $.fn.fmatter.defaultFormat(cellval,opts) : cellval;
};
$.fn.fmatter.rowactions = function(act) {
var $tr = $(this).closest("tr.jqgrow"),
rid = $tr.attr("id"),
$id = $(this).closest("table.ui-jqgrid-btable").attr('id').replace(/_frozen([^_]*)$/,'$1'),
$grid = $("#"+$id),
$t = $grid[0],
p = $t.p,
cm = p.colModel[$.jgrid.getCellIndex(this)],
$actionsDiv = cm.frozen ? $("tr#"+rid+" td:eq("+$.jgrid.getCellIndex(this)+") > div",$grid) :$(this).parent(),
op = {
extraparam: {}
},
saverow = function(rowid, res) {
if($.isFunction(op.afterSave)) { op.afterSave.call($t, rowid, res); }
$actionsDiv.find("div.ui-inline-edit,div.ui-inline-del").show();
$actionsDiv.find("div.ui-inline-save,div.ui-inline-cancel").hide();
},
restorerow = function(rowid) {
if($.isFunction(op.afterRestore)) { op.afterRestore.call($t, rowid); }
$actionsDiv.find("div.ui-inline-edit,div.ui-inline-del").show();
$actionsDiv.find("div.ui-inline-save,div.ui-inline-cancel").hide();
};
if (cm.formatoptions !== undefined) {
op = $.extend(op,cm.formatoptions);
}
if (p.editOptions !== undefined) {
op.editOptions = p.editOptions;
}
if (p.delOptions !== undefined) {
op.delOptions = p.delOptions;
}
if ($tr.hasClass("jqgrid-new-row")){
op.extraparam[p.prmNames.oper] = p.prmNames.addoper;
}
var actop = {
keys: op.keys,
oneditfunc: op.onEdit,
successfunc: op.onSuccess,
url: op.url,
extraparam: op.extraparam,
aftersavefunc: saverow,
errorfunc: op.onError,
afterrestorefunc: restorerow,
restoreAfterError: op.restoreAfterError,
mtype: op.mtype
};
switch(act)
{
case 'edit':
$grid.jqGrid('editRow', rid, actop);
$actionsDiv.find("div.ui-inline-edit,div.ui-inline-del").hide();
$actionsDiv.find("div.ui-inline-save,div.ui-inline-cancel").show();
$grid.triggerHandler("jqGridAfterGridComplete");
break;
case 'save':
if ($grid.jqGrid('saveRow', rid, actop)) {
$actionsDiv.find("div.ui-inline-edit,div.ui-inline-del").show();
$actionsDiv.find("div.ui-inline-save,div.ui-inline-cancel").hide();
$grid.triggerHandler("jqGridAfterGridComplete");
}
break;
case 'cancel' :
$grid.jqGrid('restoreRow', rid, restorerow);
$actionsDiv.find("div.ui-inline-edit,div.ui-inline-del").show();
$actionsDiv.find("div.ui-inline-save,div.ui-inline-cancel").hide();
$grid.triggerHandler("jqGridAfterGridComplete");
break;
case 'del':
$grid.jqGrid('delGridRow', rid, op.delOptions);
break;
case 'formedit':
$grid.jqGrid('setSelection', rid);
$grid.jqGrid('editGridRow', rid, op.editOptions);
break;
}
};
$.fn.fmatter.actions = function(cellval,opts) {
var op={keys:false, editbutton:true, delbutton:true, editformbutton: false},
rowid=opts.rowId, str="",ocl,
nav = $.jgrid.getRegional(this, 'nav'),
classes = $.jgrid.styleUI[(opts.styleUI || 'jQueryUI')].fmatter,
common = $.jgrid.styleUI[(opts.styleUI || 'jQueryUI')].common;
if(opts.colModel.formatoptions !== undefined) {
op = $.extend(op,opts.colModel.formatoptions);
}
if(rowid === undefined || $.fmatter.isEmpty(rowid)) {return "";}
var hover = "onmouseover=jQuery(this).addClass('" + common.hover +"'); onmouseout=jQuery(this).removeClass('" + common.hover +"'); ";
if(op.editformbutton){
ocl = "id='jEditButton_"+rowid+"' onclick=jQuery.fn.fmatter.rowactions.call(this,'formedit'); " + hover;
str += "<div title='"+nav.edittitle+"' style='float:left;cursor:pointer;' class='ui-pg-div ui-inline-edit' "+ocl+"><span class='" + common.icon_base +" "+classes.icon_edit +"'></span></div>";
} else if(op.editbutton){
ocl = "id='jEditButton_"+rowid+"' onclick=jQuery.fn.fmatter.rowactions.call(this,'edit'); " + hover;
str += "<div title='"+nav.edittitle+"' style='float:left;cursor:pointer;' class='ui-pg-div ui-inline-edit' "+ocl+"><span class='" + common.icon_base +" "+classes.icon_edit +"'></span></div>";
}
if(op.delbutton) {
ocl = "id='jDeleteButton_"+rowid+"' onclick=jQuery.fn.fmatter.rowactions.call(this,'del'); " + hover;
str += "<div title='"+nav.deltitle+"' style='float:left;' class='ui-pg-div ui-inline-del' "+ocl+"><span class='" + common.icon_base +" "+classes.icon_del +"'></span></div>";
}
ocl = "id='jSaveButton_"+rowid+"' onclick=jQuery.fn.fmatter.rowactions.call(this,'save'); " + hover;
str += "<div title='"+nav.savetitle+"' style='float:left;display:none' class='ui-pg-div ui-inline-save' "+ocl+"><span class='" + common.icon_base +" "+classes.icon_save +"'></span></div>";
ocl = "id='jCancelButton_"+rowid+"' onclick=jQuery.fn.fmatter.rowactions.call(this,'cancel'); " + hover;
str += "<div title='"+nav.canceltitle+"' style='float:left;display:none;' class='ui-pg-div ui-inline-cancel' "+ocl+"><span class='" + common.icon_base +" "+classes.icon_cancel +"'></span></div>";
return "<div style='margin-left:8px;'>" + str + "</div>";
};
$.unformat = function (cellval,options,pos,cnt) {
// specific for jqGrid only
var ret, formatType = options.colModel.formatter,
op =options.colModel.formatoptions || {}, sep,
re = /([\.\*\_\'\(\)\{\}\+\?\\])/g,
unformatFunc = options.colModel.unformat||($.fn.fmatter[formatType] && $.fn.fmatter[formatType].unformat);
if(unformatFunc !== undefined && $.isFunction(unformatFunc) ) {
ret = unformatFunc.call(this, $(cellval).text(), options, cellval);
} else if(formatType !== undefined && $.fmatter.isString(formatType) ) {
var opts = $.jgrid.getRegional(this, 'formatter') || {}, stripTag;
switch(formatType) {
case 'integer' :
op = $.extend({},opts.integer,op);
sep = op.thousandsSeparator.replace(re,"\\$1");
stripTag = new RegExp(sep, "g");
ret = $(cellval).text().replace(stripTag,'');
break;
case 'number' :
op = $.extend({},opts.number,op);
sep = op.thousandsSeparator.replace(re,"\\$1");
stripTag = new RegExp(sep, "g");
ret = $(cellval).text().replace(stripTag,"").replace(op.decimalSeparator,'.');
break;
case 'currency':
op = $.extend({},opts.currency,op);
sep = op.thousandsSeparator.replace(re,"\\$1");
stripTag = new RegExp(sep, "g");
ret = $(cellval).text();
if (op.prefix && op.prefix.length) {
ret = ret.substr(op.prefix.length);
}
if (op.suffix && op.suffix.length) {
ret = ret.substr(0, ret.length - op.suffix.length);
}
ret = ret.replace(stripTag,'').replace(op.decimalSeparator,'.');
break;
case 'checkbox':
var cbv = (options.colModel.editoptions) ? options.colModel.editoptions.value.split(":") : ["Yes","No"];
ret = $('input',cellval).is(":checked") ? cbv[0] : cbv[1];
break;
case 'select' :
ret = $.unformat.select(cellval,options,pos,cnt);
break;
case 'actions':
return "";
default:
ret= $(cellval).text();
}
}
return ret !== undefined ? ret : cnt===true ? $(cellval).text() : $.jgrid.htmlDecode($(cellval).html());
};
$.unformat.select = function (cellval,options,pos,cnt) {
// Spacial case when we have local data and perform a sort
// cnt is set to true only in sortDataArray
var ret = [];
var cell = $(cellval).text();
if(cnt===true) {return cell;}
var op = $.extend({}, options.colModel.formatoptions !== undefined ? options.colModel.formatoptions: options.colModel.editoptions),
sep = op.separator === undefined ? ":" : op.separator,
delim = op.delimiter === undefined ? ";" : op.delimiter;
if(op.value){
var oSelect = op.value,
msl = op.multiple === true ? true : false,
scell = [], sv;
if(msl) {scell = cell.split(",");scell = $.map(scell,function(n){return $.trim(n);});}
if ($.fmatter.isString(oSelect)) {
var so = oSelect.split(delim), j=0, i;
for(i=0; i<so.length;i++){
sv = so[i].split(sep);
if(sv.length > 2 ) {
sv[1] = $.map(sv,function(n,i){if(i>0) {return n;}}).join(sep);
}
if(msl) {
if($.inArray($.trim(sv[1]),scell)>-1) {
ret[j] = sv[0];
j++;
}
} else if($.trim(sv[1]) === $.trim(cell)) {
ret[0] = sv[0];
break;
}
}
} else if($.fmatter.isObject(oSelect) || $.isArray(oSelect) ){
if(!msl) {scell[0] = cell;}
ret = $.map(scell, function(n){
var rv;
$.each(oSelect, function(i,val){
if (val === n) {
rv = i;
return false;
}
});
if( rv !== undefined ) {return rv;}
});
}
return ret.join(", ");
}
return cell || "";
};
$.unformat.date = function (cellval, opts) {
var op = $.jgrid.getRegional(this, 'formatter.date') || {};
if(opts.formatoptions !== undefined) {
op = $.extend({},op,opts.formatoptions);
}
if(!$.fmatter.isEmpty(cellval)) {
return $.jgrid.parseDate.call(this, op.newformat,cellval,op.srcformat,op);
}
return $.fn.fmatter.defaultFormat(cellval, opts);
};
//module end
}));

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,48 @@
#!/mod/bin/jimsh
######
# FlexView - Load flexview cofig in jqGrid JSON format
# Author: MymsMan, 2016
######
package require cgi
source /mod/webif/lib/setup
require system.class settings.class
# logging options
if {[lindex $argv 0] eq "-d"} {
set argv [lrange $argv 1 end]
set loglevel 2
} else {
# Use same logging option as auto log
set settings [settings]
set loglevel [$settings _nval_setting "autolog"]
}
proc log {msg {level 2}} {
if {$level > $::loglevel} return
system plog flexview $msg
}
httpheader "application/json"
set start [clock milliseconds]
set name [cgi_get name "default"]
set fname "/mod/etc/fvConfig_$name.json"
if {[catch {set fd [open $fname r]
set fvconfig [read $fd]
puts $fvconfig
$fd close} msg opts]} {
log "Config file error caught: $msg $opts" 0
puts "{\"errmsg\": \"File load error $msg\"}"
}
[settings new] _tval_setting "fvConfig" $name
set end [clock milliseconds]
set elapse $(double($end-$start)/1000.0)
log "Load | $elapse | $fname" 0

View File

@ -0,0 +1,7 @@
menuitem \
"Flexible View" \
"/plugin/flexview/img/flexview.png" \
"/go/flexview"
# "/plugin/flexview/index.jim"

View File

@ -0,0 +1,704 @@
(function($){
/*
* jqGrid methods without support. Use as you wish
* Tony Tomov tony@trirand.com
* http://trirand.com/blog/
* Dual licensed under the MIT and GPL licenses:
* http://www.opensource.org/licenses/mit-license.php
* http://www.gnu.org/licenses/gpl-2.0.html
*
* This list of deprecated methods.
* If you instead want to use them, please include this file after the grid main file.
* Some methods will be then overwritten.
*
*/
/*global jQuery, $ */
$.jgrid.extend({
// This is the ols search Filter method used in navigator.
searchGrid : function (p) {
p = $.extend({
recreateFilter: false,
drag: true,
sField:'searchField',
sValue:'searchString',
sOper: 'searchOper',
sFilter: 'filters',
loadDefaults: true, // this options activates loading of default filters from grid's postData for Multipe Search only.
beforeShowSearch: null,
afterShowSearch : null,
onInitializeSearch: null,
closeAfterSearch : false,
closeAfterReset: false,
closeOnEscape : false,
multipleSearch : false,
cloneSearchRowOnAdd: true,
// translation
// if you want to change or remove the order change it in sopt
// ['bw','eq','ne','lt','le','gt','ge','ew','cn']
sopt: null,
// Note: stringResult is intentionally declared "undefined by default".
// you are velcome to define stringResult expressly in the options you pass to searchGrid()
// stringResult is a "safeguard" measure to insure we post sensible data when communicated as form-encoded
// see http://github.com/tonytomov/jqGrid/issues/#issue/36
//
// If this value is not expressly defined in the incoming options,
// lower in the code we will infer the value based on value of multipleSearch
stringResult: undefined,
onClose : null,
// useDataProxy allows ADD, EDIT and DEL code to bypass calling $.ajax
// directly when grid's 'dataProxy' property (grid.p.dataProxy) is a function.
// Used for "editGridRow" and "delGridRow" below and automatically flipped to TRUE
// when ajax setting's 'url' (grid's 'editurl') property is undefined.
// When 'useDataProxy' is true, instead of calling $.ajax.call(gridDOMobj, o, i) we call
// gridDOMobj.p.dataProxy.call(gridDOMobj, o, i)
//
// Behavior is extremely similar to when 'datatype' is a function, but arguments are slightly different.
// Normally the following is fed to datatype.call(a, b, c):
// a = Pointer to grid's table DOM element, b = grid.p.postdata, c = "load_"+grid's ID
// In cases of "edit" and "del" the following is fed:
// a = Pointer to grid's table DOM element (same),
// b = extended Ajax Options including postdata in "data" property. (different object type)
// c = "set_"+grid's ID in case of "edit" and "del_"+grid's ID in case of "del" (same type, different content)
// The major difference is that complete ajax options object, with attached "complete" and "error"
// callback functions is fed instead of only post data.
// This allows you to emulate a $.ajax call (including calling "complete"/"error"),
// while retrieving the data locally in the browser.
useDataProxy: false,
overlay : true
}, $.jgrid.search, p || {});
return this.each(function() {
var $t = this;
if(!$t.grid) {return;}
var fid = "fbox_"+$t.p.id,
showFrm = true;
function applyDefaultFilters(gridDOMobj, filterSettings) {
/*
gridDOMobj = ointer to grid DOM object ( $(#list)[0] )
What we need from gridDOMobj:
gridDOMobj.SearchFilter is the pointer to the Search box, once it's created.
gridDOMobj.p.postData - dictionary of post settings. These can be overriden at grid creation to
contain default filter settings. We will parse these and will populate the search with defaults.
filterSettings - same settings object you (would) pass to $().jqGrid('searchGrid', filterSettings);
*/
// Pulling default filter settings out of postData property of grid's properties.:
var defaultFilters = gridDOMobj.p.postData[filterSettings.sFilter];
// example of what we might get: {"groupOp":"and","rules":[{"field":"amount","op":"eq","data":"100"}]}
// suppose we have imported this with grid import, the this is a string.
if(typeof(defaultFilters) == "string") {
defaultFilters = $.jgrid.parse(defaultFilters);
}
if (defaultFilters) {
if (defaultFilters.groupOp) {
gridDOMobj.SearchFilter.setGroupOp(defaultFilters.groupOp);
}
if (defaultFilters.rules) {
var f, i = 0, li = defaultFilters.rules.length, success = false;
for (; i < li; i++) {
f = defaultFilters.rules[i];
// we are not trying to counter all issues with filter declaration here. Just the basics to avoid lookup exceptions.
if (f.field !== undefined && f.op !== undefined && f.data !== undefined) {
success = gridDOMobj.SearchFilter.setFilter({
'sfref':gridDOMobj.SearchFilter.$.find(".sf:last"),
'filter':$.extend({},f)
});
if (success) { gridDOMobj.SearchFilter.add(); }
}
}
}
}
} // end of applyDefaultFilters
function hideFilter(selector) {
if(p.onClose){
var fclm = p.onClose(selector);
if(typeof fclm == 'boolean' && !fclm) { return; }
}
selector.hide();
if(p.overlay === true) {
$(".jqgrid-overlay:first","#gbox_"+$t.p.id).hide();
}
}
function showFilter(){
var fl = $(".ui-searchFilter").length;
if(fl > 1) {
var zI = $("#"+fid).css("zIndex");
$("#"+fid).css({zIndex:parseInt(zI,10)+fl});
}
$("#"+fid).show();
if(p.overlay === true) {
$(".jqgrid-overlay:first","#gbox_"+$t.p.id).show();
}
try{$(':input:visible',"#"+fid)[0].focus();}catch(_){}
}
function searchFilters(filters) {
var hasFilters = (filters !== undefined),
grid = $("#"+$t.p.id),
sdata={};
if(p.multipleSearch===false) {
sdata[p.sField] = filters.rules[0].field;
sdata[p.sValue] = filters.rules[0].data;
sdata[p.sOper] = filters.rules[0].op;
if(sdata.hasOwnProperty(p.sFilter) ) {
delete sdata[p.sFilter];
}
} else {
sdata[p.sFilter] = filters;
$.each([p.sField, p.sValue, p.sOper], function(i, n){
if(sdata.hasOwnProperty(n)) { delete sdata[n];}
});
}
grid[0].p.search = hasFilters;
$.extend(grid[0].p.postData,sdata);
grid.trigger("reloadGrid",[{page:1}]);
if(p.closeAfterSearch) { hideFilter($("#"+fid)); }
}
function resetFilters(op) {
var reload = op && op.hasOwnProperty("reload") ? op.reload : true,
grid = $("#"+$t.p.id),
sdata={};
grid[0].p.search = false;
if(p.multipleSearch===false) {
sdata[p.sField] = sdata[p.sValue] = sdata[p.sOper] = "";
} else {
sdata[p.sFilter] = "";
}
$.extend(grid[0].p.postData,sdata);
if(reload) {
grid.trigger("reloadGrid",[{page:1}]);
}
if(p.closeAfterReset) { hideFilter($("#"+fid)); }
}
if($.fn.searchFilter) {
if(p.recreateFilter===true) {$("#"+fid).remove();}
if( $("#"+fid).html() !== null ) {
if ( $.isFunction(p.beforeShowSearch) ) {
showFrm = p.beforeShowSearch($("#"+fid));
if(typeof(showFrm) == "undefined") {
showFrm = true;
}
}
if(showFrm === false) { return; }
showFilter();
if( $.isFunction(p.afterShowSearch) ) { p.afterShowSearch($("#"+fid)); }
} else {
var fields = [],
colNames = $("#"+$t.p.id).jqGrid("getGridParam","colNames"),
colModel = $("#"+$t.p.id).jqGrid("getGridParam","colModel"),
stempl = ['eq','ne','lt','le','gt','ge','bw','bn','in','ni','ew','en','cn','nc'],
j,pos,k,oprtr=[];
if (p.sopt !==null) {
k=0;
for(j=0;j<p.sopt.length;j++) {
if( (pos= $.inArray(p.sopt[j],stempl)) != -1 ){
oprtr[k] = {op:p.sopt[j],text: p.odata[pos]};
k++;
}
}
} else {
for(j=0;j<stempl.length;j++) {
oprtr[j] = {op:stempl[j],text: p.odata[j]};
}
}
$.each(colModel, function(i, v) {
var searchable = (typeof v.search === 'undefined') ? true: v.search ,
hidden = (v.hidden === true),
soptions = $.extend({}, {text: colNames[i], itemval: v.index || v.name}, this.searchoptions),
ignoreHiding = (soptions.searchhidden === true);
if(typeof soptions.sopt !== 'undefined') {
k=0;
soptions.ops =[];
if(soptions.sopt.length>0) {
for(j=0;j<soptions.sopt.length;j++) {
if( (pos= $.inArray(soptions.sopt[j],stempl)) != -1 ){
soptions.ops[k] = {op:soptions.sopt[j],text: p.odata[pos]};
k++;
}
}
}
}
if(typeof(this.stype) === 'undefined') { this.stype='text'; }
if(this.stype == 'select') {
if ( soptions.dataUrl !== undefined) {}
else {
var eov;
if(soptions.value) {
eov = soptions.value;
} else if(this.editoptions) {
eov = this.editoptions.value;
}
if(eov) {
soptions.dataValues =[];
if(typeof(eov) === 'string') {
var so = eov.split(";"),sv;
for(j=0;j<so.length;j++) {
sv = so[j].split(":");
soptions.dataValues[j] ={value:sv[0],text:sv[1]};
}
} else if (typeof(eov) === 'object') {
j=0;
for (var key in eov) {
if(eov.hasOwnProperty(key)) {
soptions.dataValues[j] ={value:key,text:eov[key]};
j++;
}
}
}
}
}
}
if ((ignoreHiding && searchable) || (searchable && !hidden)) {
fields.push(soptions);
}
});
if(fields.length>0){
$("<div id='"+fid+"' role='dialog' tabindex='-1'></div>").insertBefore("#gview_"+$t.p.id);
// Before we create searchFilter we need to decide if we want to get back a string or a JS object.
// see http://github.com/tonytomov/jqGrid/issues/#issue/36 for background on the issue.
// If p.stringResult is defined, it was explisitly passed to us by user. Honor the choice, whatever it is.
if (p.stringResult===undefined) {
// to provide backward compatibility, inferring stringResult value from multipleSearch
p.stringResult = p.multipleSearch;
}
// we preserve the return value here to retain access to .add() and other good methods of search form.
$t.SearchFilter = $("#"+fid).searchFilter(fields, { groupOps: p.groupOps, operators: oprtr, onClose:hideFilter, resetText: p.Reset, searchText: p.Find, windowTitle: p.caption, rulesText:p.rulesText, matchText:p.matchText, onSearch: searchFilters, onReset: resetFilters,stringResult:p.stringResult, ajaxSelectOptions: $.extend({},$.jgrid.ajaxOptions,$t.p.ajaxSelectOptions ||{}), clone: p.cloneSearchRowOnAdd });
$(".ui-widget-overlay","#"+fid).remove();
if($t.p.direction=="rtl") { $(".ui-closer","#"+fid).css("float","left"); }
if (p.drag===true) {
$("#"+fid+" table thead tr:first td:first").css('cursor','move');
if(jQuery.fn.jqDrag) {
$("#"+fid).jqDrag($("#"+fid+" table thead tr:first td:first"));
} else {
try {
$("#"+fid).draggable({handle: $("#"+fid+" table thead tr:first td:first")});
} catch (e) {}
}
}
if(p.multipleSearch === false) {
$(".ui-del, .ui-add, .ui-del, .ui-add-last, .matchText, .rulesText", "#"+fid).hide();
$("select[name='groupOp']","#"+fid).hide();
}
if (p.multipleSearch === true && p.loadDefaults === true) {
applyDefaultFilters($t, p);
}
if ( $.isFunction(p.onInitializeSearch) ) { p.onInitializeSearch( $("#"+fid) ); }
if ( $.isFunction(p.beforeShowSearch) ) {
showFrm = p.beforeShowSearch($("#"+fid));
if(typeof(showFrm) == "undefined") {
showFrm = true;
}
}
if(showFrm === false) { return; }
showFilter();
if( $.isFunction(p.afterShowSearch) ) { p.afterShowSearch($("#"+fid)); }
if(p.closeOnEscape===true){
$("#"+fid).keydown( function( e ) {
if( e.which == 27 ) {
hideFilter($("#"+fid));
}
if (e.which == 13) {
$(".ui-search", this).click();
}
});
}
}
}
}
});
},
// methods taken from grid.custom.
updateGridRows : function (data, rowidname, jsonreader) {
var nm, success=false, title;
this.each(function(){
var t = this, vl, ind, srow, sid;
if(!t.grid) {return false;}
if(!rowidname) { rowidname = "id"; }
if( data && data.length >0 ) {
$(data).each(function(j){
srow = this;
ind = t.rows.namedItem(srow[rowidname]);
if(ind) {
sid = srow[rowidname];
if(jsonreader === true){
if(t.p.jsonReader.repeatitems === true) {
if(t.p.jsonReader.cell) {srow = srow[t.p.jsonReader.cell];}
for (var k=0;k<srow.length;k++) {
vl = t.formatter( sid, srow[k], k, srow, 'edit');
title = t.p.colModel[k].title ? {"title":$.jgrid.stripHtml(vl)} : {};
if(t.p.treeGrid===true && nm == t.p.ExpandColumn) {
$("td:eq("+k+") > span:first",ind).html(vl).attr(title);
} else {
$("td:eq("+k+")",ind).html(vl).attr(title);
}
}
success = true;
return true;
}
}
$(t.p.colModel).each(function(i){
nm = jsonreader===true ? this.jsonmap || this.name :this.name;
if( srow[nm] !== undefined) {
vl = t.formatter( sid, srow[nm], i, srow, 'edit');
title = this.title ? {"title":$.jgrid.stripHtml(vl)} : {};
if(t.p.treeGrid===true && nm == t.p.ExpandColumn) {
$("td:eq("+i+") > span:first",ind).html(vl).attr(title);
} else {
$("td:eq("+i+")",ind).html(vl).attr(title);
}
success = true;
}
});
}
});
}
});
return success;
},
// Form search - sorry for this method. Instead use ne jqFilter method.
filterGrid : function(gridid,p){
p = $.extend({
gridModel : false,
gridNames : false,
gridToolbar : false,
filterModel: [], // label/name/stype/defval/surl/sopt
formtype : "horizontal", // horizontal/vertical
autosearch: true, // if set to false a serch button should be enabled.
formclass: "filterform",
tableclass: "filtertable",
buttonclass: "filterbutton",
searchButton: "Search",
clearButton: "Clear",
enableSearch : false,
enableClear: false,
beforeSearch: null,
afterSearch: null,
beforeClear: null,
afterClear: null,
url : '',
marksearched: true
},p || {});
return this.each(function(){
var self = this;
this.p = p;
if(this.p.filterModel.length === 0 && this.p.gridModel===false) { alert("No filter is set"); return;}
if( !gridid) {alert("No target grid is set!"); return;}
this.p.gridid = gridid.indexOf("#") != -1 ? gridid : "#"+gridid;
var gcolMod = $(this.p.gridid).jqGrid("getGridParam",'colModel');
if(gcolMod) {
if( this.p.gridModel === true) {
var thegrid = $(this.p.gridid)[0];
var sh;
// we should use the options search, edittype, editoptions
// additionally surl and defval can be added in grid colModel
$.each(gcolMod, function (i,n) {
var tmpFil = [];
this.search = this.search === false ? false : true;
if(this.editrules && this.editrules.searchhidden === true) {
sh = true;
} else {
if(this.hidden === true ) {
sh = false;
} else {
sh = true;
}
}
if( this.search === true && sh === true) {
if(self.p.gridNames===true) {
tmpFil.label = thegrid.p.colNames[i];
} else {
tmpFil.label = '';
}
tmpFil.name = this.name;
tmpFil.index = this.index || this.name;
// we support only text and selects, so all other to text
tmpFil.stype = this.edittype || 'text';
if(tmpFil.stype != 'select' ) {
tmpFil.stype = 'text';
}
tmpFil.defval = this.defval || '';
tmpFil.surl = this.surl || '';
tmpFil.sopt = this.editoptions || {};
tmpFil.width = this.width;
self.p.filterModel.push(tmpFil);
}
});
} else {
$.each(self.p.filterModel,function(i,n) {
for(var j=0;j<gcolMod.length;j++) {
if(this.name == gcolMod[j].name) {
this.index = gcolMod[j].index || this.name;
break;
}
}
if(!this.index) {
this.index = this.name;
}
});
}
} else {
alert("Could not get grid colModel"); return;
}
var triggerSearch = function() {
var sdata={}, j=0, v;
var gr = $(self.p.gridid)[0], nm;
gr.p.searchdata = {};
if($.isFunction(self.p.beforeSearch)){self.p.beforeSearch();}
$.each(self.p.filterModel,function(i,n){
nm = this.index;
if(this.stype === 'select') {
v = $("select[name="+nm+"]",self).val();
if(v) {
sdata[nm] = v;
if(self.p.marksearched){
$("#jqgh_"+this.name,gr.grid.hDiv).addClass("dirty-cell");
}
j++;
} else {
if(self.p.marksearched){
$("#jqgh_"+this.name,gr.grid.hDiv).removeClass("dirty-cell");
}
try {
delete gr.p.postData[this.index];
} catch (e) {}
}
} else {
v = $("input[name="+nm+"]",self).val();
if(v) {
sdata[nm] = v;
if(self.p.marksearched){
$("#jqgh_"+this.name,gr.grid.hDiv).addClass("dirty-cell");
}
j++;
} else {
if(self.p.marksearched){
$("#jqgh_"+this.name,gr.grid.hDiv).removeClass("dirty-cell");
}
try {
delete gr.p.postData[this.index];
} catch(x) {}
}
}
});
var sd = j>0 ? true : false;
$.extend(gr.p.postData,sdata);
var saveurl;
if(self.p.url) {
saveurl = $(gr).jqGrid("getGridParam",'url');
$(gr).jqGrid("setGridParam",{url:self.p.url});
}
$(gr).jqGrid("setGridParam",{search:sd}).trigger("reloadGrid",[{page:1}]);
if(saveurl) {$(gr).jqGrid("setGridParam",{url:saveurl});}
if($.isFunction(self.p.afterSearch)){self.p.afterSearch();}
};
var clearSearch = function(){
var sdata={}, v, j=0;
var gr = $(self.p.gridid)[0], nm;
if($.isFunction(self.p.beforeClear)){self.p.beforeClear();}
$.each(self.p.filterModel,function(i,n){
nm = this.index;
v = (this.defval) ? this.defval : "";
if(!this.stype){this.stype='text';}
switch (this.stype) {
case 'select' :
var v1;
$("select[name="+nm+"] option",self).each(function (i){
if(i===0) { this.selected = true; }
if ($(this).text() == v) {
this.selected = true;
v1 = $(this).val();
return false;
}
});
if(v1) {
// post the key and not the text
sdata[nm] = v1;
if(self.p.marksearched){
$("#jqgh_"+this.name,gr.grid.hDiv).addClass("dirty-cell");
}
j++;
} else {
if(self.p.marksearched){
$("#jqgh_"+this.name,gr.grid.hDiv).removeClass("dirty-cell");
}
try {
delete gr.p.postData[this.index];
} catch (e) {}
}
break;
case 'text':
$("input[name="+nm+"]",self).val(v);
if(v) {
sdata[nm] = v;
if(self.p.marksearched){
$("#jqgh_"+this.name,gr.grid.hDiv).addClass("dirty-cell");
}
j++;
} else {
if(self.p.marksearched){
$("#jqgh_"+this.name,gr.grid.hDiv).removeClass("dirty-cell");
}
try {
delete gr.p.postData[this.index];
} catch (k) {}
}
break;
}
});
var sd = j>0 ? true : false;
$.extend(gr.p.postData,sdata);
var saveurl;
if(self.p.url) {
saveurl = $(gr).jqGrid("getGridParam",'url');
$(gr).jqGrid("setGridParam",{url:self.p.url});
}
$(gr).jqGrid("setGridParam",{search:sd}).trigger("reloadGrid",[{page:1}]);
if(saveurl) {$(gr).jqGrid("setGridParam",{url:saveurl});}
if($.isFunction(self.p.afterClear)){self.p.afterClear();}
};
var tbl;
var formFill = function(){
var tr = document.createElement("tr");
var tr1, sb, cb,tl,td;
if(self.p.formtype=='horizontal'){
$(tbl).append(tr);
}
$.each(self.p.filterModel,function(i,n){
tl = document.createElement("td");
$(tl).append("<label for='"+this.name+"'>"+this.label+"</label>");
td = document.createElement("td");
var $t=this;
if(!this.stype) { this.stype='text';}
switch (this.stype)
{
case "select":
if(this.surl) {
// data returned should have already constructed html select
$(td).load(this.surl,function(){
if($t.defval) { $("select",this).val($t.defval); }
$("select",this).attr({name:$t.index || $t.name, id: "sg_"+$t.name});
if($t.sopt) { $("select",this).attr($t.sopt); }
if(self.p.gridToolbar===true && $t.width) {
$("select",this).width($t.width);
}
if(self.p.autosearch===true){
$("select",this).change(function(e){
triggerSearch();
return false;
});
}
});
} else {
// sopt to construct the values
if($t.sopt.value) {
var oSv = $t.sopt.value;
var elem = document.createElement("select");
$(elem).attr({name:$t.index || $t.name, id: "sg_"+$t.name}).attr($t.sopt);
var so, sv, ov;
if(typeof oSv === "string") {
so = oSv.split(";");
for(var k=0; k<so.length;k++){
sv = so[k].split(":");
ov = document.createElement("option");
ov.value = sv[0]; ov.innerHTML = sv[1];
if (sv[1]==$t.defval) { ov.selected ="selected"; }
elem.appendChild(ov);
}
} else if(typeof oSv === "object" ) {
for ( var key in oSv) {
if(oSv.hasOwnProperty(key)) {
i++;
ov = document.createElement("option");
ov.value = key; ov.innerHTML = oSv[key];
if (oSv[key]==$t.defval) { ov.selected ="selected"; }
elem.appendChild(ov);
}
}
}
if(self.p.gridToolbar===true && $t.width) {
$(elem).width($t.width);
}
$(td).append(elem);
if(self.p.autosearch===true){
$(elem).change(function(e){
triggerSearch();
return false;
});
}
}
}
break;
case 'text':
var df = this.defval ? this.defval: "";
$(td).append("<input type='text' name='"+(this.index || this.name)+"' id='sg_"+this.name+"' value='"+df+"'/>");
if($t.sopt) { $("input",td).attr($t.sopt); }
if(self.p.gridToolbar===true && $t.width) {
if($.browser.msie) {
$("input",td).width($t.width-4);
} else {
$("input",td).width($t.width-2);
}
}
if(self.p.autosearch===true){
$("input",td).keypress(function(e){
var key = e.charCode ? e.charCode : e.keyCode ? e.keyCode : 0;
if(key == 13){
triggerSearch();
return false;
}
return this;
});
}
break;
}
if(self.p.formtype=='horizontal'){
if(self.p.gridToolbar===true && self.p.gridNames===false) {
$(tr).append(td);
} else {
$(tr).append(tl).append(td);
}
$(tr).append(td);
} else {
tr1 = document.createElement("tr");
$(tr1).append(tl).append(td);
$(tbl).append(tr1);
}
});
td = document.createElement("td");
if(self.p.enableSearch === true){
sb = "<input type='button' id='sButton' class='"+self.p.buttonclass+"' value='"+self.p.searchButton+"'/>";
$(td).append(sb);
$("input#sButton",td).click(function(){
triggerSearch();
return false;
});
}
if(self.p.enableClear === true) {
cb = "<input type='button' id='cButton' class='"+self.p.buttonclass+"' value='"+self.p.clearButton+"'/>";
$(td).append(cb);
$("input#cButton",td).click(function(){
clearSearch();
return false;
});
}
if(self.p.enableClear === true || self.p.enableSearch === true) {
if(self.p.formtype=='horizontal') {
$(tr).append(td);
} else {
tr1 = document.createElement("tr");
$(tr1).append("<td>&#160;</td>").append(td);
$(tbl).append(tr1);
}
}
};
var frm = $("<form name='SearchForm' style=display:inline;' class='"+this.p.formclass+"'></form>");
tbl =$("<table class='"+this.p.tableclass+"' cellspacing='0' cellpading='0' border='0'><tbody></tbody></table>");
$(frm).append(tbl);
formFill();
$(this).append(frm);
this.triggerSearch = triggerSearch;
this.clearSearch = clearSearch;
});
}
});
})(jQuery);

View File

@ -0,0 +1,64 @@
;(function($){
/**
* jqGrid extension
* Paul Tiseo ptiseo@wasteconsultants.com
*
* Dual licensed under the MIT and GPL licenses:
* http://www.opensource.org/licenses/mit-license.php
* http://www.gnu.org/licenses/gpl-2.0.html
**/
$.jgrid.extend({
getPostData : function(){
var $t = this[0];
if(!$t.grid) { return; }
return $t.p.postData;
},
setPostData : function( newdata ) {
var $t = this[0];
if(!$t.grid) { return; }
// check if newdata is correct type
if ( typeof(newdata) === 'object' ) {
$t.p.postData = newdata;
}
else {
alert("Error: cannot add a non-object postData value. postData unchanged.");
}
},
appendPostData : function( newdata ) {
var $t = this[0];
if(!$t.grid) { return; }
// check if newdata is correct type
if ( typeof(newdata) === 'object' ) {
$.extend($t.p.postData, newdata);
}
else {
alert("Error: cannot append a non-object postData value. postData unchanged.");
}
},
setPostDataItem : function( key, val ) {
var $t = this[0];
if(!$t.grid) { return; }
$t.p.postData[key] = val;
},
getPostDataItem : function( key ) {
var $t = this[0];
if(!$t.grid) { return; }
return $t.p.postData[key];
},
removePostDataItem : function( key ) {
var $t = this[0];
if(!$t.grid) { return; }
delete $t.p.postData[key];
},
getUserData : function(){
var $t = this[0];
if(!$t.grid) { return; }
return $t.p.userData;
},
getUserDataItem : function( key ) {
var $t = this[0];
if(!$t.grid) { return; }
return $t.p.userData[key];
}
});
})(jQuery);

View File

@ -0,0 +1,126 @@
;(function($){
/**
* jqGrid extension for manipulating columns properties
* Piotr Roznicki roznicki@o2.pl
* http://www.roznicki.prv.pl
* Dual licensed under the MIT and GPL licenses:
* http://www.opensource.org/licenses/mit-license.php
* http://www.gnu.org/licenses/gpl-2.0.html
**/
$.jgrid.extend({
setColumns : function(p) {
p = $.extend({
top : 0,
left: 0,
width: 200,
height: 'auto',
dataheight: 'auto',
modal: false,
drag: true,
beforeShowForm: null,
afterShowForm: null,
afterSubmitForm: null,
closeOnEscape : true,
ShrinkToFit : false,
jqModal : false,
saveicon: [true,"left","ui-icon-disk"],
closeicon: [true,"left","ui-icon-close"],
onClose : null,
colnameview : true,
closeAfterSubmit : true,
updateAfterCheck : false,
recreateForm : false
}, $.jgrid.col, p ||{});
return this.each(function(){
var $t = this;
if (!$t.grid ) { return; }
var onBeforeShow = typeof p.beforeShowForm === 'function' ? true: false;
var onAfterShow = typeof p.afterShowForm === 'function' ? true: false;
var onAfterSubmit = typeof p.afterSubmitForm === 'function' ? true: false;
var gID = $t.p.id,
dtbl = "ColTbl_"+gID,
IDs = {themodal:'colmod'+gID,modalhead:'colhd'+gID,modalcontent:'colcnt'+gID, scrollelm: dtbl};
if(p.recreateForm===true && $("#"+IDs.themodal).html() != null) {
$("#"+IDs.themodal).remove();
}
if ( $("#"+IDs.themodal).html() != null ) {
if(onBeforeShow) { p.beforeShowForm($("#"+dtbl)); }
$.jgrid.viewModal("#"+IDs.themodal,{gbox:"#gbox_"+gID,jqm:p.jqModal, jqM:false, modal:p.modal});
if(onAfterShow) { p.afterShowForm($("#"+dtbl)); }
} else {
var dh = isNaN(p.dataheight) ? p.dataheight : p.dataheight+"px";
var formdata = "<div id='"+dtbl+"' class='formdata' style='width:100%;overflow:auto;position:relative;height:"+dh+";'>";
formdata += "<table class='ColTable' cellspacing='1' cellpading='2' border='0'><tbody>";
for(i=0;i<this.p.colNames.length;i++){
if(!$t.p.colModel[i].hidedlg) { // added from T. Tomov
formdata += "<tr><td style='white-space: pre;'><input type='checkbox' style='margin-right:5px;' id='col_" + this.p.colModel[i].name + "' class='cbox' value='T' " +
((this.p.colModel[i].hidden===false)?"checked":"") + "/>" + "<label for='col_" + this.p.colModel[i].name + "'>" + this.p.colNames[i] + ((p.colnameview) ? " (" + this.p.colModel[i].name + ")" : "" )+ "</label></td></tr>";
}
}
formdata += "</tbody></table></div>"
var bS = !p.updateAfterCheck ? "<a href='javascript:void(0)' id='dData' class='fm-button ui-state-default ui-corner-all'>"+p.bSubmit+"</a>" : "",
bC ="<a href='javascript:void(0)' id='eData' class='fm-button ui-state-default ui-corner-all'>"+p.bCancel+"</a>";
formdata += "<table border='0' class='EditTable' id='"+dtbl+"_2'><tbody><tr style='display:block;height:3px;'><td></td></tr><tr><td class='DataTD ui-widget-content'></td></tr><tr><td class='ColButton EditButton'>"+bS+"&#160;"+bC+"</td></tr></tbody></table>";
p.gbox = "#gbox_"+gID;
$.jgrid.createModal(IDs,formdata,p,"#gview_"+$t.p.id,$("#gview_"+$t.p.id)[0]);
if(p.saveicon[0]==true) {
$("#dData","#"+dtbl+"_2").addClass(p.saveicon[1] == "right" ? 'fm-button-icon-right' : 'fm-button-icon-left')
.append("<span class='ui-icon "+p.saveicon[2]+"'></span>");
}
if(p.closeicon[0]==true) {
$("#eData","#"+dtbl+"_2").addClass(p.closeicon[1] == "right" ? 'fm-button-icon-right' : 'fm-button-icon-left')
.append("<span class='ui-icon "+p.closeicon[2]+"'></span>");
}
if(!p.updateAfterCheck) {
$("#dData","#"+dtbl+"_2").click(function(e){
for(i=0;i<$t.p.colModel.length;i++){
if(!$t.p.colModel[i].hidedlg) { // added from T. Tomov
var nm = $t.p.colModel[i].name.replace(/\./g, "\\.");
if($("#col_" + nm,"#"+dtbl).attr("checked")) {
$($t).jqGrid("showCol",$t.p.colModel[i].name);
$("#col_" + nm,"#"+dtbl).attr("defaultChecked",true); // Added from T. Tomov IE BUG
} else {
$($t).jqGrid("hideCol",$t.p.colModel[i].name);
$("#col_" + nm,"#"+dtbl).attr("defaultChecked",""); // Added from T. Tomov IE BUG
}
}
}
if(p.ShrinkToFit===true) {
$($t).jqGrid("setGridWidth",$t.grid.width-0.001,true);
}
if(p.closeAfterSubmit) $.jgrid.hideModal("#"+IDs.themodal,{gb:"#gbox_"+gID,jqm:p.jqModal, onClose: p.onClose});
if (onAfterSubmit) { p.afterSubmitForm($("#"+dtbl)); }
return false;
});
} else {
$(":input","#"+dtbl).click(function(e){
var cn = this.id.substr(4);
if(cn){
if(this.checked) {
$($t).jqGrid("showCol",cn);
} else {
$($t).jqGrid("hideCol",cn);
}
if(p.ShrinkToFit===true) {
$($t).jqGrid("setGridWidth",$t.grid.width-0.001,true);
}
}
return this;
});
}
$("#eData", "#"+dtbl+"_2").click(function(e){
$.jgrid.hideModal("#"+IDs.themodal,{gb:"#gbox_"+gID,jqm:p.jqModal, onClose: p.onClose});
return false;
});
$("#dData, #eData","#"+dtbl+"_2").hover(
function(){$(this).addClass('ui-state-hover');},
function(){$(this).removeClass('ui-state-hover');}
);
if(onBeforeShow) { p.beforeShowForm($("#"+dtbl)); }
$.jgrid.viewModal("#"+IDs.themodal,{gbox:"#gbox_"+gID,jqm:p.jqModal, jqM: true, modal:p.modal});
if(onAfterShow) { p.afterShowForm($("#"+dtbl)); }
}
});
}
});
})(jQuery);

View File

@ -0,0 +1,147 @@
/*
* ContextMenu - jQuery plugin for right-click context menus
*
* Author: Chris Domigan
* Contributors: Dan G. Switzer, II
* Parts of this plugin are inspired by Joern Zaefferer's Tooltip plugin
*
* Dual licensed under the MIT and GPL licenses:
* http://www.opensource.org/licenses/mit-license.php
* http://www.gnu.org/licenses/gpl.html
*
* Version: r2
* Date: 16 July 2007
*
* For documentation visit http://www.trendskitchens.co.nz/jquery/contextmenu/
*
*/
(function($) {
var menu, shadow, content, hash, currentTarget;
var defaults = {
menuStyle: {
listStyle: 'none',
padding: '1px',
margin: '0px',
backgroundColor: '#fff',
border: '1px solid #999',
width: '100px'
},
itemStyle: {
margin: '0px',
color: '#000',
display: 'block',
cursor: 'default',
padding: '3px',
border: '1px solid #fff',
backgroundColor: 'transparent'
},
itemHoverStyle: {
border: '1px solid #0a246a',
backgroundColor: '#b6bdd2'
},
eventPosX: 'pageX',
eventPosY: 'pageY',
shadow : true,
onContextMenu: null,
onShowMenu: null
};
$.fn.contextMenu = function(id, options) {
if (!menu) { // Create singleton menu
menu = $('<div id="jqContextMenu"></div>')
.hide()
.css({position:'absolute', zIndex:'500'})
.appendTo('body')
.bind('click', function(e) {
e.stopPropagation();
});
}
if (!shadow) {
shadow = $('<div></div>')
.css({backgroundColor:'#000',position:'absolute',opacity:0.2,zIndex:499})
.appendTo('body')
.hide();
}
hash = hash || [];
hash.push({
id : id,
menuStyle: $.extend({}, defaults.menuStyle, options.menuStyle || {}),
itemStyle: $.extend({}, defaults.itemStyle, options.itemStyle || {}),
itemHoverStyle: $.extend({}, defaults.itemHoverStyle, options.itemHoverStyle || {}),
bindings: options.bindings || {},
shadow: options.shadow || options.shadow === false ? options.shadow : defaults.shadow,
onContextMenu: options.onContextMenu || defaults.onContextMenu,
onShowMenu: options.onShowMenu || defaults.onShowMenu,
eventPosX: options.eventPosX || defaults.eventPosX,
eventPosY: options.eventPosY || defaults.eventPosY
});
var index = hash.length - 1;
$(this).bind('contextmenu', function(e) {
// Check if onContextMenu() defined
var bShowContext = (!!hash[index].onContextMenu) ? hash[index].onContextMenu(e) : true;
currentTarget = e.target;
if (bShowContext) {
display(index, this, e );
return false;
}
});
return this;
};
function display(index, trigger, e ) {
var cur = hash[index];
content = $('#'+cur.id).find('ul:first').clone(true);
content.css(cur.menuStyle).find('li').css(cur.itemStyle).hover(
function() {
$(this).css(cur.itemHoverStyle);
},
function(){
$(this).css(cur.itemStyle);
}
).find('img').css({verticalAlign:'middle',paddingRight:'2px'});
// Send the content to the menu
menu.html(content);
// if there's an onShowMenu, run it now -- must run after content has been added
// if you try to alter the content variable before the menu.html(), IE6 has issues
// updating the content
if (!!cur.onShowMenu) menu = cur.onShowMenu(e, menu);
$.each(cur.bindings, function(id, func) {
$('#'+id, menu).bind('click', function() {
hide();
func(trigger, currentTarget);
});
});
menu.css({'left':e[cur.eventPosX],'top':e[cur.eventPosY]}).show();
if (cur.shadow) shadow.css({width:menu.width(),height:menu.height(),left:e.pageX+2,top:e.pageY+2}).show();
$(document).one('click', hide);
}
function hide() {
menu.hide();
shadow.hide();
}
// Apply defaults
$.contextMenu = {
defaults : function(userDefaults) {
$.each(userDefaults, function(i, val) {
if (typeof val == 'object' && defaults[i]) {
$.extend(defaults[i], val);
}
else defaults[i] = val;
});
}
};
})(jQuery);
$(function() {
$('div.contextMenu').hide();
});

View File

@ -0,0 +1,716 @@
/* Plugin: searchFilter v1.2.9
* Author: Kasey Speakman (kasey@cornerspeed.com)
* License: Dual Licensed, MIT and GPL v2 (http://www.gnu.org/licenses/gpl-2.0.html)
*
* REQUIREMENTS:
* jQuery 1.3+ (http://jquery.com/)
* A Themeroller Theme (http://jqueryui.com/themeroller/)
*
* SECURITY WARNING
* You should always implement server-side checking to ensure that
* the query will fail when forged/invalid data is received.
* Clever users can send any value they want through JavaScript and HTTP POST/GET.
*
* THEMES
* Simply include the CSS file for your Themeroller theme.
*
* DESCRIPTION
* This plugin creates a new searchFilter object in the specified container
*
* INPUT TYPE
* fields: an array of field objects. each object has the following properties:
* text: a string containing the display name of the field (e.g. "Field 1")
* itemval: a string containing the actual field name (e.g. "field1")
* optional properties:
* ops: an array of operators in the same format as jQuery.fn.searchFilter.defaults.operators
* that is: [ { op: 'gt', text: 'greater than'}, { op:'lt', text: 'less than'}, ... ]
* if not specified, the passed-in options used, and failting that, jQuery.fn.searchFilter.defaults.operators will be used
* *** NOTE ***
* Specifying a dataUrl or dataValues property means that a <select ...> (drop-down-list) will be generated
* instead of a text input <input type='text'.../> where the user would normally type in their search data
* ************
* dataUrl: a url that will return the html select for this field, this url will only be called once for this field
* dataValues: the possible values for this field in the form [ { text: 'Data Display Text', value: 'data_actual_value' }, { ... } ]
* dataInit: a function that you can use to initialize the data field. this function is passed the jQuery-fied data element
* dataEvents: list of events to apply to the data element. uses $("#id").bind(type, [data], fn) to bind events to data element
* *** JSON of this object could look like this: ***
* var fields = [
* {
* text: 'Field Display Name',
* itemval: 'field_actual_name',
* // below this are optional values
* ops: [ // this format is the same as jQuery.fn.searchFilter.defaults.operators
* { op: 'gt', text: 'greater than' },
* { op: 'lt', text: 'less than' }
* ],
* dataUrl: 'http://server/path/script.php?propName=propValue', // using this creates a select for the data input instead of an input type='text'
* dataValues: [ // using this creates a select for the data input instead of an input type='text'
* { text: 'Data Value Display Name', value: 'data_actual_value' },
* { ... }
* ],
* dataInit: function(jElem) { jElem.datepicker(options); },
* dataEvents: [ // these are the same options that you pass to $("#id").bind(type, [data], fn)
* { type: 'click', data: { i: 7 }, fn: function(e) { console.log(e.data.i); } },
* { type: 'keypress', fn: function(e) { console.log('keypress'); } }
* ]
* },
* { ... }
* ]
* options: name:value properties containing various creation options
* see jQuery.fn.searchFilter.defaults for the overridable options
*
* RETURN TYPE: This plugin returns a SearchFilter object, which has additional SearchFilter methods:
* Methods
* add: Adds a filter. added to the end of the list unless a jQuery event object or valid row number is passed.
* del: Removes a filter. removed from the end of the list unless a jQuery event object or valid row number is passed.
* reset: resets filters back to original state (only one blank filter), and calls onReset
* search: puts the search rules into an object and calls onSearch with it
* close: calls the onClose event handler
*
* USAGE
* HTML
* <head>
* ...
* <script src="path/to/jquery.min.js" type="text/javascript"></script>
* <link href="path/to/themeroller.css" rel="Stylesheet" type="text/css" />
* <script src="path/to/jquery.searchFilter.js" type="text/javascript"></script>
* <link href="path/to/jquery.searchFilter.css" rel="Stylesheet" type="text/css" />
* ...
* </head>
* <body>
* ...
* <div id='mySearch'></div>
* ...
* </body>
* JQUERY
* Methods
* initializing: $("#mySearch").searchFilter([{text: "Field 1", value: "field1"},{text: "Field 2", value: "field2"}], {onSearch: myFilterRuleReceiverFn, onReset: myFilterResetFn });
* Manual Methods (there's no need to call these methods unless you are trying to manipulate searchFilter with script)
* add: $("#mySearch").searchFilter().add(); // appends a blank filter
* $("#mySearch").searchFilter().add(0); // copies the first filter as second
* del: $("#mySearch").searchFilter().del(); // removes the bottom filter
* $("#mySearch").searchFilter().del(1); // removes the second filter
* search: $("#mySearch").searchFilter().search(); // invokes onSearch, passing it a ruleGroup object
* reset: $("#mySearch").searchFilter().reset(); // resets rules and invokes onReset
* close: $("#mySearch").searchFilter().close(); // without an onClose handler, equivalent to $("#mySearch").hide();
*
* NOTE: You can get the jQuery object back from the SearchFilter object by chaining .$
* Example
* $("#mySearch").searchFilter().add().add().reset().$.hide();
* Verbose Example
* $("#mySearch") // gets jQuery object for the HTML element with id="mySearch"
* .searchFilter() // gets the SearchFilter object for an existing search filter
* .add() // adds a new filter to the end of the list
* .add() // adds another new filter to the end of the list
* .reset() // resets filters back to original state, triggers onReset
* .$ // returns jQuery object for $("#mySearch")
* .hide(); // equivalent to $("#mySearch").hide();
*/
jQuery.fn.searchFilter = function(fields, options) {
function SearchFilter(jQ, fields, options) {
//---------------------------------------------------------------
// PUBLIC VARS
//---------------------------------------------------------------
this.$ = jQ; // makes the jQuery object available as .$ from the return value
//---------------------------------------------------------------
// PUBLIC FUNCTIONS
//---------------------------------------------------------------
this.add = function(i) {
if (i == null) jQ.find(".ui-add-last").click();
else jQ.find(".sf:eq(" + i + ") .ui-add").click();
return this;
};
this.del = function(i) {
if (i == null) jQ.find(".sf:last .ui-del").click();
else jQ.find(".sf:eq(" + i + ") .ui-del").click();
return this;
};
this.search = function(e) {
jQ.find(".ui-search").click();
return this;
};
this.reset = function(o) {
if(o===undefined) o = false;
jQ.find(".ui-reset").trigger('click',[o]);
return this;
};
this.close = function() {
jQ.find(".ui-closer").click();
return this;
};
//---------------------------------------------------------------
// "CONSTRUCTOR" (in air quotes)
//---------------------------------------------------------------
if (fields != null) { // type coercion matches undefined as well as null
//---------------------------------------------------------------
// UTILITY FUNCTIONS
//---------------------------------------------------------------
function hover() {
jQuery(this).toggleClass("ui-state-hover");
return false;
}
function active(e) {
jQuery(this).toggleClass("ui-state-active", (e.type == "mousedown"));
return false;
}
function buildOpt(value, text) {
return "<option value='" + value + "'>" + text + "</option>";
}
function buildSel(className, options, isHidden) {
return "<select class='" + className + "'" + (isHidden ? " style='display:none;'" : "") + ">" + options + "</select>";
}
function initData(selector, fn) {
var jElem = jQ.find("tr.sf td.data " + selector);
if (jElem[0] != null)
fn(jElem);
}
function bindDataEvents(selector, events) {
var jElem = jQ.find("tr.sf td.data " + selector);
if (jElem[0] != null) {
jQuery.each(events, function() {
if (this.data != null)
jElem.bind(this.type, this.data, this.fn);
else
jElem.bind(this.type, this.fn);
});
}
}
//---------------------------------------------------------------
// SUPER IMPORTANT PRIVATE VARS
//---------------------------------------------------------------
// copies jQuery.fn.searchFilter.defaults.options properties onto an empty object, then options onto that
var opts = jQuery.extend({}, jQuery.fn.searchFilter.defaults, options);
// this is keeps track of the last asynchronous setup
var highest_late_setup = -1;
//---------------------------------------------------------------
// CREATION PROCESS STARTS
//---------------------------------------------------------------
// generate the global ops
var gOps_html = "";
jQuery.each(opts.groupOps, function() { gOps_html += buildOpt(this.op, this.text); });
gOps_html = "<select name='groupOp'>" + gOps_html + "</select>";
/* original content - doesn't minify very well
jQ
.html("") // clear any old content
.addClass("ui-searchFilter") // add classes
.append( // add content
"\
<div class='ui-widget-overlay' style='z-index: -1'>&nbsp;</div>\
<table class='ui-widget-content ui-corner-all'>\
<thead>\
<tr>\
<td colspan='5' class='ui-widget-header ui-corner-all' style='line-height: 18px;'>\
<div class='ui-closer ui-state-default ui-corner-all ui-helper-clearfix' style='float: right;'>\
<span class='ui-icon ui-icon-close'></span>\
</div>\
" + opts.windowTitle + "\
</td>\
</tr>\
</thead>\
<tbody>\
<tr class='sf'>\
<td class='fields'></td>\
<td class='ops'></td>\
<td class='data'></td>\
<td><div class='ui-del ui-state-default ui-corner-all'><span class='ui-icon ui-icon-minus'></span></div></td>\
<td><div class='ui-add ui-state-default ui-corner-all'><span class='ui-icon ui-icon-plus'></span></div></td>\
</tr>\
<tr>\
<td colspan='5' class='divider'><div>&nbsp;</div></td>\
</tr>\
</tbody>\
<tfoot>\
<tr>\
<td colspan='3'>\
<span class='ui-reset ui-state-default ui-corner-all' style='display: inline-block; float: left;'><span class='ui-icon ui-icon-arrowreturnthick-1-w' style='float: left;'></span><span style='line-height: 18px; padding: 0 7px 0 3px;'>" + opts.resetText + "</span></span>\
<span class='ui-search ui-state-default ui-corner-all' style='display: inline-block; float: right;'><span class='ui-icon ui-icon-search' style='float: left;'></span><span style='line-height: 18px; padding: 0 7px 0 3px;'>" + opts.searchText + "</span></span>\
<span class='matchText'>" + opts.matchText + "</span> \
" + gOps_html + " \
<span class='rulesText'>" + opts.rulesText + "</span>\
</td>\
<td>&nbsp;</td>\
<td><div class='ui-add-last ui-state-default ui-corner-all'><span class='ui-icon ui-icon-plusthick'></span></div></td>\
</tr>\
</tfoot>\
</table>\
");
/* end hard-to-minify code */
/* begin easier to minify code */
jQ.html("").addClass("ui-searchFilter").append("<div class='ui-widget-overlay' style='z-index: -1'>&#160;</div><table class='ui-widget-content ui-corner-all'><thead><tr><td colspan='5' class='ui-widget-header ui-corner-all' style='line-height: 18px;'><div class='ui-closer ui-state-default ui-corner-all ui-helper-clearfix' style='float: right;'><span class='ui-icon ui-icon-close'></span></div>" + opts.windowTitle + "</td></tr></thead><tbody><tr class='sf'><td class='fields'></td><td class='ops'></td><td class='data'></td><td><div class='ui-del ui-state-default ui-corner-all'><span class='ui-icon ui-icon-minus'></span></div></td><td><div class='ui-add ui-state-default ui-corner-all'><span class='ui-icon ui-icon-plus'></span></div></td></tr><tr><td colspan='5' class='divider'><hr class='ui-widget-content' style='margin:1px'/></td></tr></tbody><tfoot><tr><td colspan='3'><span class='ui-reset ui-state-default ui-corner-all' style='display: inline-block; float: left;'><span class='ui-icon ui-icon-arrowreturnthick-1-w' style='float: left;'></span><span style='line-height: 18px; padding: 0 7px 0 3px;'>" + opts.resetText + "</span></span><span class='ui-search ui-state-default ui-corner-all' style='display: inline-block; float: right;'><span class='ui-icon ui-icon-search' style='float: left;'></span><span style='line-height: 18px; padding: 0 7px 0 3px;'>" + opts.searchText + "</span></span><span class='matchText'>" + opts.matchText + "</span> " + gOps_html + " <span class='rulesText'>" + opts.rulesText + "</span></td><td>&#160;</td><td><div class='ui-add-last ui-state-default ui-corner-all'><span class='ui-icon ui-icon-plusthick'></span></div></td></tr></tfoot></table>");
/* end easier-to-minify code */
var jRow = jQ.find("tr.sf");
var jFields = jRow.find("td.fields");
var jOps = jRow.find("td.ops");
var jData = jRow.find("td.data");
// generate the defaults
var default_ops_html = "";
jQuery.each(opts.operators, function() { default_ops_html += buildOpt(this.op, this.text); });
default_ops_html = buildSel("default", default_ops_html, true);
jOps.append(default_ops_html);
var default_data_html = "<input type='text' class='default' style='display:none;' />";
jData.append(default_data_html);
// generate the field list as a string
var fields_html = "";
var has_custom_ops = false;
var has_custom_data = false;
jQuery.each(fields, function(i) {
var field_num = i;
fields_html += buildOpt(this.itemval, this.text);
// add custom ops if they exist
if (this.ops != null) {
has_custom_ops = true;
var custom_ops = "";
jQuery.each(this.ops, function() { custom_ops += buildOpt(this.op, this.text); });
custom_ops = buildSel("field" + field_num, custom_ops, true);
jOps.append(custom_ops);
}
// add custom data if it is given
if (this.dataUrl != null) {
if (i > highest_late_setup) highest_late_setup = i;
has_custom_data = true;
var dEvents = this.dataEvents;
var iEvent = this.dataInit;
var bs = this.buildSelect;
jQuery.ajax(jQuery.extend({
url : this.dataUrl,
complete: function(data) {
var $d;
if(bs != null) $d =jQuery("<div />").append(bs(data));
else $d = jQuery("<div />").append(data.responseText);
$d.find("select").addClass("field" + field_num).hide();
jData.append($d.html());
if (iEvent) initData(".field" + i, iEvent);
if (dEvents) bindDataEvents(".field" + i, dEvents);
if (i == highest_late_setup) { // change should get called no more than twice when this searchFilter is constructed
jQ.find("tr.sf td.fields select[name='field']").change();
}
}
},opts.ajaxSelectOptions));
} else if (this.dataValues != null) {
has_custom_data = true;
var custom_data = "";
jQuery.each(this.dataValues, function() { custom_data += buildOpt(this.value, this.text); });
custom_data = buildSel("field" + field_num, custom_data, true);
jData.append(custom_data);
} else if (this.dataEvents != null || this.dataInit != null) {
has_custom_data = true;
var custom_data = "<input type='text' class='field" + field_num + "' />";
jData.append(custom_data);
}
// attach events to data if they exist
if (this.dataInit != null && i != highest_late_setup)
initData(".field" + i, this.dataInit);
if (this.dataEvents != null && i != highest_late_setup)
bindDataEvents(".field" + i, this.dataEvents);
});
fields_html = "<select name='field'>" + fields_html + "</select>";
jFields.append(fields_html);
// setup the field select with an on-change event if there are custom ops or data
var jFSelect = jFields.find("select[name='field']");
if (has_custom_ops) jFSelect.change(function(e) {
var index = e.target.selectedIndex;
var td = jQuery(e.target).parents("tr.sf").find("td.ops");
td.find("select").removeAttr("name").hide(); // disown and hide all elements
var jElem = td.find(".field" + index);
if (jElem[0] == null) jElem = td.find(".default"); // if there's not an element for that field, use the default one
jElem.attr("name", "op").show();
return false;
});
else jOps.find(".default").attr("name", "op").show();
if (has_custom_data) jFSelect.change(function(e) {
var index = e.target.selectedIndex;
var td = jQuery(e.target).parents("tr.sf").find("td.data");
td.find("select,input").removeClass("vdata").hide(); // disown and hide all elements
var jElem = td.find(".field" + index);
if (jElem[0] == null) jElem = td.find(".default"); // if there's not an element for that field, use the default one
jElem.show().addClass("vdata");
return false;
});
else jData.find(".default").show().addClass("vdata");
// go ahead and call the change event and setup the ops and data values
if (has_custom_ops || has_custom_data) jFSelect.change();
// bind events
jQ.find(".ui-state-default").hover(hover, hover).mousedown(active).mouseup(active); // add hover/active effects to all buttons
jQ.find(".ui-closer").click(function(e) {
opts.onClose(jQuery(jQ.selector));
return false;
});
jQ.find(".ui-del").click(function(e) {
var row = jQuery(e.target).parents(".sf");
if (row.siblings(".sf").length > 0) { // doesn't remove if there's only one filter left
if (opts.datepickerFix === true && jQuery.fn.datepicker !== undefined)
row.find(".hasDatepicker").datepicker("destroy"); // clean up datepicker's $.data mess
row.remove(); // also unbinds
} else { // resets the filter if it's the last one
row.find("select[name='field']")[0].selectedIndex = 0;
row.find("select[name='op']")[0].selectedIndex = 0;
row.find(".data input").val(""); // blank all input values
row.find(".data select").each(function() { this.selectedIndex = 0; }); // select first option on all selects
row.find("select[name='field']").change(function(event){event.stopPropagation();}); // trigger any change events
}
return false;
});
jQ.find(".ui-add").click(function(e) {
var row = jQuery(e.target).parents(".sf");
var newRow = row.clone(true).insertAfter(row);
newRow.find(".ui-state-default").removeClass("ui-state-hover ui-state-active");
if (opts.clone) {
newRow.find("select[name='field']")[0].selectedIndex = row.find("select[name='field']")[0].selectedIndex;
var stupid_browser = (newRow.find("select[name='op']")[0] == null); // true for IE6
if (!stupid_browser)
newRow.find("select[name='op']").focus()[0].selectedIndex = row.find("select[name='op']")[0].selectedIndex;
var jElem = newRow.find("select.vdata");
if (jElem[0] != null) // select doesn't copy it's selected index when cloned
jElem[0].selectedIndex = row.find("select.vdata")[0].selectedIndex;
} else {
newRow.find(".data input").val(""); // blank all input values
newRow.find("select[name='field']").focus();
}
if (opts.datepickerFix === true && jQuery.fn.datepicker !== undefined) { // using $.data to associate data with document elements is Not Good
row.find(".hasDatepicker").each(function() {
var settings = jQuery.data(this, "datepicker").settings;
newRow.find("#" + this.id).unbind().removeAttr("id").removeClass("hasDatepicker").datepicker(settings);
});
}
newRow.find("select[name='field']").change(function(event){event.stopPropagation();} );
return false;
});
jQ.find(".ui-search").click(function(e) {
var ui = jQuery(jQ.selector); // pointer to search box wrapper element
var ruleGroup;
var group_op = ui.find("select[name='groupOp'] :selected").val(); // puls "AND" or "OR"
if (!opts.stringResult) {
ruleGroup = {
groupOp: group_op,
rules: []
};
} else {
ruleGroup = "{\"groupOp\":\"" + group_op + "\",\"rules\":[";
}
ui.find(".sf").each(function(i) {
var tField = jQuery(this).find("select[name='field'] :selected").val();
var tOp = jQuery(this).find("select[name='op'] :selected").val();
var tData = jQuery(this).find("input.vdata,select.vdata :selected").val();
tData += "";
if (!opts.stringResult) {
ruleGroup.rules.push({
field: tField,
op: tOp,
data: tData
});
} else {
tData = tData.replace(/\\/g,'\\\\').replace(/\"/g,'\\"');
if (i > 0) ruleGroup += ",";
ruleGroup += "{\"field\":\"" + tField + "\",";
ruleGroup += "\"op\":\"" + tOp + "\",";
ruleGroup += "\"data\":\"" + tData + "\"}";
}
});
if (opts.stringResult) ruleGroup += "]}";
opts.onSearch(ruleGroup);
return false;
});
jQ.find(".ui-reset").click(function(e,op) {
var ui = jQuery(jQ.selector);
ui.find(".ui-del").click(); // removes all filters, resets the last one
ui.find("select[name='groupOp']")[0].selectedIndex = 0; // changes the op back to the default one
opts.onReset(op);
return false;
});
jQ.find(".ui-add-last").click(function() {
var row = jQuery(jQ.selector + " .sf:last");
var newRow = row.clone(true).insertAfter(row);
newRow.find(".ui-state-default").removeClass("ui-state-hover ui-state-active");
newRow.find(".data input").val(""); // blank all input values
newRow.find("select[name='field']").focus();
if (opts.datepickerFix === true && jQuery.fn.datepicker !== undefined) { // using $.data to associate data with document elements is Not Good
row.find(".hasDatepicker").each(function() {
var settings = jQuery.data(this, "datepicker").settings;
newRow.find("#" + this.id).unbind().removeAttr("id").removeClass("hasDatepicker").datepicker(settings);
});
}
newRow.find("select[name='field']").change(function(event){event.stopPropagation();});
return false;
});
this.setGroupOp = function(setting) {
/* a "setter" for groupping argument.
* ("AND" or "OR")
*
* Inputs:
* setting - a string
*
* Returns:
* Does not return anything. May add success / failure reporting in future versions.
*
* author: Daniel Dotsenko (dotsa@hotmail.com)
*/
selDOMobj = jQ.find("select[name='groupOp']")[0];
var indexmap = {}, l = selDOMobj.options.length, i;
for (i=0; i<l; i++) {
indexmap[selDOMobj.options[i].value] = i;
}
selDOMobj.selectedIndex = indexmap[setting];
jQuery(selDOMobj).change(function(event){event.stopPropagation();});
};
this.setFilter = function(settings) {
/* a "setter" for an arbitrary SearchFilter's filter line.
* designed to abstract the DOM manipulations required to infer
* a particular filter is a fit to the search box.
*
* Inputs:
* settings - an "object" (dictionary)
* index (optional*) (to be implemented in the future) : signed integer index (from top to bottom per DOM) of the filter line to fill.
* Negative integers (rooted in -1 and lower) denote position of the line from the bottom.
* sfref (optional*) : DOM object referencing individual '.sf' (normally a TR element) to be populated. (optional)
* filter (mandatory) : object (dictionary) of form {'field':'field_value','op':'op_value','data':'data value'}
*
* * It is mandatory to have either index or sfref defined.
*
* Returns:
* Does not return anything. May add success / failure reporting in future versions.
*
* author: Daniel Dotsenko (dotsa@hotmail.com)
*/
var o = settings['sfref'], filter = settings['filter'];
// setting up valueindexmap that we will need to manipulate SELECT elements.
var fields = [], i, j , l, lj, li,
valueindexmap = {};
// example of valueindexmap:
// {'field1':{'index':0,'ops':{'eq':0,'ne':1}},'fieldX':{'index':1,'ops':{'eq':0,'ne':1},'data':{'true':0,'false':1}}},
// if data is undefined it's a INPUT field. If defined, it's SELECT
selDOMobj = o.find("select[name='field']")[0];
for (i=0, l=selDOMobj.options.length; i<l; i++) {
valueindexmap[selDOMobj.options[i].value] = {'index':i,'ops':{}};
fields.push(selDOMobj.options[i].value);
}
for (i=0, li=fields.length; i < li; i++) {
selDOMobj = o.find(".ops > select[class='field"+i+"']")[0];
if (selDOMobj) {
for (j=0, lj=selDOMobj.options.length; j<lj; j++) {
valueindexmap[fields[i]]['ops'][selDOMobj.options[j].value] = j;
}
}
selDOMobj = o.find(".data > select[class='field"+i+"']")[0];
if (selDOMobj) {
valueindexmap[fields[i]]['data'] = {}; // this setting is the flag that 'data' is contained in a SELECT
for (j=0, lj=selDOMobj.options.length; j<lj; j++) {
valueindexmap[fields[i]]['data'][selDOMobj.options[j].value] = j;
}
}
} // done populating valueindexmap
// preparsing the index values for SELECT elements.
var fieldvalue, fieldindex, opindex, datavalue, dataindex;
fieldvalue = filter['field'];
if (valueindexmap[fieldvalue]) {
fieldindex = valueindexmap[fieldvalue]['index'];
}
if (fieldindex != null) {
opindex = valueindexmap[fieldvalue]['ops'][filter['op']];
if(opindex === undefined) {
for(i=0,li=options.operators.length; i<li;i++) {
if(options.operators[i].op == filter.op ){
opindex = i;
break;
}
}
}
datavalue = filter['data'];
if (valueindexmap[fieldvalue]['data'] == null) {
dataindex = -1; // 'data' is not SELECT, Making the var 'defined'
} else {
dataindex = valueindexmap[fieldvalue]['data'][datavalue]; // 'undefined' may come from here.
}
}
// only if values for 'field' and 'op' and 'data' are 'found' in mapping...
if (fieldindex != null && opindex != null && dataindex != null) {
o.find("select[name='field']")[0].selectedIndex = fieldindex;
o.find("select[name='field']").change();
o.find("select[name='op']")[0].selectedIndex = opindex;
o.find("input.vdata").val(datavalue); // if jquery does not find any INPUT, it does not set any. This means we deal with SELECT
o = o.find("select.vdata")[0];
if (o) {
o.selectedIndex = dataindex;
}
return true
} else {
return false
}
}; // end of this.setFilter fn
} // end of if fields != null
}
return new SearchFilter(this, fields, options);
};
jQuery.fn.searchFilter.version = '1.2.9';
/* This property contains the default options */
jQuery.fn.searchFilter.defaults = {
/*
* PROPERTY
* TYPE: boolean
* DESCRIPTION: clone a row if it is added from an existing row
* when false, any new added rows will be blank.
*/
clone: true,
/*
* PROPERTY
* TYPE: boolean
* DESCRIPTION: current version of datepicker uses a data store,
* which is incompatible with $().clone(true)
*/
datepickerFix: true,
/*
* FUNCTION
* DESCRIPTION: the function that will be called when the user clicks Reset
* INPUT TYPE: JS object if stringResult is false, otherwise is JSON string
*/
onReset: function(data) { alert("Reset Clicked. Data Returned: " + data) },
/*
* FUNCTION
* DESCRIPTION: the function that will be called when the user clicks Search
* INPUT TYPE: JS object if stringResult is false, otherwise is JSON string
*/
onSearch: function(data) { alert("Search Clicked. Data Returned: " + data) },
/*
* FUNCTION
* DESCRIPTION: the function that will be called when the user clicks the Closer icon
* or the close() function is called
* if left null, it simply does a .hide() on the searchFilter
* INPUT TYPE: a jQuery object for the searchFilter
*/
onClose: function(jElem) { jElem.hide(); },
/*
* PROPERTY
* TYPE: array of objects, each object has the properties op and text
* DESCRIPTION: the selectable operators that are applied between rules
* e.g. for {op:"AND", text:"all"}
* the search filter box will say: match all rules
* the server should interpret this as putting the AND op between each rule:
* rule1 AND rule2 AND rule3
* text will be the option text, and op will be the option value
*/
groupOps: [
{ op: "AND", text: "all" },
{ op: "OR", text: "any" }
],
/*
* PROPERTY
* TYPE: array of objects, each object has the properties op and text
* DESCRIPTION: the operators that will appear as drop-down options
* text will be the option text, and op will be the option value
*/
operators: [
{ op: "eq", text: "is equal to" },
{ op: "ne", text: "is not equal to" },
{ op: "lt", text: "is less than" },
{ op: "le", text: "is less or equal to" },
{ op: "gt", text: "is greater than" },
{ op: "ge", text: "is greater or equal to" },
{ op: "in", text: "is in" },
{ op: "ni", text: "is not in" },
{ op: "bw", text: "begins with" },
{ op: "bn", text: "does not begin with" },
{ op: "ew", text: "ends with" },
{ op: "en", text: "does not end with" },
{ op: "cn", text: "contains" },
{ op: "nc", text: "does not contain" }
],
/*
* PROPERTY
* TYPE: string
* DESCRIPTION: part of the phrase: _match_ ANY/ALL rules
*/
matchText: "match",
/*
* PROPERTY
* TYPE: string
* DESCRIPTION: part of the phrase: match ANY/ALL _rules_
*/
rulesText: "rules",
/*
* PROPERTY
* TYPE: string
* DESCRIPTION: the text that will be displayed in the reset button
*/
resetText: "Reset",
/*
* PROPERTY
* TYPE: string
* DESCRIPTION: the text that will be displayed in the search button
*/
searchText: "Search",
/*
* PROPERTY
* TYPE: boolean
* DESCRIPTION: a flag that, when set, will make the onSearch and onReset return strings instead of objects
*/
stringResult: true,
/*
* PROPERTY
* TYPE: string
* DESCRIPTION: the title of the searchFilter window
*/
windowTitle: "Search Rules",
/*
* PROPERTY
* TYPE: object
* DESCRIPTION: options to extend the ajax request
*/
ajaxSelectOptions : {}
}; /* end of searchFilter */

View File

@ -0,0 +1,383 @@
/**
* TableDnD plug-in for JQuery, allows you to drag and drop table rows
* You can set up various options to control how the system will work
* Copyright (c) Denis Howlett <denish@isocra.com>
* Licensed like jQuery, see http://docs.jquery.com/License.
*
* Configuration options:
*
* onDragStyle
* This is the style that is assigned to the row during drag. There are limitations to the styles that can be
* associated with a row (such as you can't assign a border--well you can, but it won't be
* displayed). (So instead consider using onDragClass.) The CSS style to apply is specified as
* a map (as used in the jQuery css(...) function).
* onDropStyle
* This is the style that is assigned to the row when it is dropped. As for onDragStyle, there are limitations
* to what you can do. Also this replaces the original style, so again consider using onDragClass which
* is simply added and then removed on drop.
* onDragClass
* This class is added for the duration of the drag and then removed when the row is dropped. It is more
* flexible than using onDragStyle since it can be inherited by the row cells and other content. The default
* is class is tDnD_whileDrag. So to use the default, simply customise this CSS class in your
* stylesheet.
* onDrop
* Pass a function that will be called when the row is dropped. The function takes 2 parameters: the table
* and the row that was dropped. You can work out the new order of the rows by using
* table.rows.
* onDragStart
* Pass a function that will be called when the user starts dragging. The function takes 2 parameters: the
* table and the row which the user has started to drag.
* onAllowDrop
* Pass a function that will be called as a row is over another row. If the function returns true, allow
* dropping on that row, otherwise not. The function takes 2 parameters: the dragged row and the row under
* the cursor. It returns a boolean: true allows the drop, false doesn't allow it.
* scrollAmount
* This is the number of pixels to scroll if the user moves the mouse cursor to the top or bottom of the
* window. The page should automatically scroll up or down as appropriate (tested in IE6, IE7, Safari, FF2,
* FF3 beta
* dragHandle
* This is the name of a class that you assign to one or more cells in each row that is draggable. If you
* specify this class, then you are responsible for setting cursor: move in the CSS and only these cells
* will have the drag behaviour. If you do not specify a dragHandle, then you get the old behaviour where
* the whole row is draggable.
*
* Other ways to control behaviour:
*
* Add class="nodrop" to any rows for which you don't want to allow dropping, and class="nodrag" to any rows
* that you don't want to be draggable.
*
* Inside the onDrop method you can also call $.tableDnD.serialize() this returns a string of the form
* <tableID>[]=<rowID1>&<tableID>[]=<rowID2> so that you can send this back to the server. The table must have
* an ID as must all the rows.
*
* Other methods:
*
* $("...").tableDnDUpdate()
* Will update all the matching tables, that is it will reapply the mousedown method to the rows (or handle cells).
* This is useful if you have updated the table rows using Ajax and you want to make the table draggable again.
* The table maintains the original configuration (so you don't have to specify it again).
*
* $("...").tableDnDSerialize()
* Will serialize and return the serialized string as above, but for each of the matching tables--so it can be
* called from anywhere and isn't dependent on the currentTable being set up correctly before calling
*
* Known problems:
* - Auto-scoll has some problems with IE7 (it scrolls even when it shouldn't), work-around: set scrollAmount to 0
*
* Version 0.2: 2008-02-20 First public version
* Version 0.3: 2008-02-07 Added onDragStart option
* Made the scroll amount configurable (default is 5 as before)
* Version 0.4: 2008-03-15 Changed the noDrag/noDrop attributes to nodrag/nodrop classes
* Added onAllowDrop to control dropping
* Fixed a bug which meant that you couldn't set the scroll amount in both directions
* Added serialize method
* Version 0.5: 2008-05-16 Changed so that if you specify a dragHandle class it doesn't make the whole row
* draggable
* Improved the serialize method to use a default (and settable) regular expression.
* Added tableDnDupate() and tableDnDSerialize() to be called when you are outside the table
*/
jQuery.tableDnD = {
/** Keep hold of the current table being dragged */
currentTable : null,
/** Keep hold of the current drag object if any */
dragObject: null,
/** The current mouse offset */
mouseOffset: null,
/** Remember the old value of Y so that we don't do too much processing */
oldY: 0,
/** Actually build the structure */
build: function(options) {
// Set up the defaults if any
this.each(function() {
// This is bound to each matching table, set up the defaults and override with user options
this.tableDnDConfig = jQuery.extend({
onDragStyle: null,
onDropStyle: null,
// Add in the default class for whileDragging
onDragClass: "tDnD_whileDrag",
onDrop: null,
onDragStart: null,
scrollAmount: 5,
serializeRegexp: /[^\-]*$/, // The regular expression to use to trim row IDs
serializeParamName: null, // If you want to specify another parameter name instead of the table ID
dragHandle: null // If you give the name of a class here, then only Cells with this class will be draggable
}, options || {});
// Now make the rows draggable
jQuery.tableDnD.makeDraggable(this);
});
// Now we need to capture the mouse up and mouse move event
// We can use bind so that we don't interfere with other event handlers
jQuery(document)
.bind('mousemove', jQuery.tableDnD.mousemove)
.bind('mouseup', jQuery.tableDnD.mouseup);
// Don't break the chain
return this;
},
/** This function makes all the rows on the table draggable apart from those marked as "NoDrag" */
makeDraggable: function(table) {
var config = table.tableDnDConfig;
if (table.tableDnDConfig.dragHandle) {
// We only need to add the event to the specified cells
var cells = jQuery("td."+table.tableDnDConfig.dragHandle, table);
cells.each(function() {
// The cell is bound to "this"
jQuery(this).mousedown(function(ev) {
jQuery.tableDnD.dragObject = this.parentNode;
jQuery.tableDnD.currentTable = table;
jQuery.tableDnD.mouseOffset = jQuery.tableDnD.getMouseOffset(this, ev);
if (config.onDragStart) {
// Call the onDrop method if there is one
config.onDragStart(table, this);
}
return false;
});
})
} else {
// For backwards compatibility, we add the event to the whole row
var rows = jQuery("tr", table); // get all the rows as a wrapped set
rows.each(function() {
// Iterate through each row, the row is bound to "this"
var row = jQuery(this);
if (! row.hasClass("nodrag")) {
row.mousedown(function(ev) {
if (ev.target.tagName == "TD") {
jQuery.tableDnD.dragObject = this;
jQuery.tableDnD.currentTable = table;
jQuery.tableDnD.mouseOffset = jQuery.tableDnD.getMouseOffset(this, ev);
if (config.onDragStart) {
// Call the onDrop method if there is one
config.onDragStart(table, this);
}
return false;
}
}).css("cursor", "move"); // Store the tableDnD object
}
});
}
},
updateTables: function() {
this.each(function() {
// this is now bound to each matching table
if (this.tableDnDConfig) {
jQuery.tableDnD.makeDraggable(this);
}
})
},
/** Get the mouse coordinates from the event (allowing for browser differences) */
mouseCoords: function(ev){
if(ev.pageX || ev.pageY){
return {x:ev.pageX, y:ev.pageY};
}
return {
x:ev.clientX + document.body.scrollLeft - document.body.clientLeft,
y:ev.clientY + document.body.scrollTop - document.body.clientTop
};
},
/** Given a target element and a mouse event, get the mouse offset from that element.
To do this we need the element's position and the mouse position */
getMouseOffset: function(target, ev) {
ev = ev || window.event;
var docPos = this.getPosition(target);
var mousePos = this.mouseCoords(ev);
return {x:mousePos.x - docPos.x, y:mousePos.y - docPos.y};
},
/** Get the position of an element by going up the DOM tree and adding up all the offsets */
getPosition: function(e){
var left = 0;
var top = 0;
/** Safari fix -- thanks to Luis Chato for this! */
if (e.offsetHeight == 0) {
/** Safari 2 doesn't correctly grab the offsetTop of a table row
this is detailed here:
http://jacob.peargrove.com/blog/2006/technical/table-row-offsettop-bug-in-safari/
the solution is likewise noted there, grab the offset of a table cell in the row - the firstChild.
note that firefox will return a text node as a first child, so designing a more thorough
solution may need to take that into account, for now this seems to work in firefox, safari, ie */
e = e.firstChild; // a table cell
}
if (e && e.offsetParent) {
while (e.offsetParent){
left += e.offsetLeft;
top += e.offsetTop;
e = e.offsetParent;
}
left += e.offsetLeft;
top += e.offsetTop;
}
return {x:left, y:top};
},
mousemove: function(ev) {
if (jQuery.tableDnD.dragObject == null) {
return;
}
var dragObj = jQuery(jQuery.tableDnD.dragObject);
var config = jQuery.tableDnD.currentTable.tableDnDConfig;
var mousePos = jQuery.tableDnD.mouseCoords(ev);
var y = mousePos.y - jQuery.tableDnD.mouseOffset.y;
//auto scroll the window
var yOffset = window.pageYOffset;
if (document.all) {
// Windows version
//yOffset=document.body.scrollTop;
if (typeof document.compatMode != 'undefined' &&
document.compatMode != 'BackCompat') {
yOffset = document.documentElement.scrollTop;
}
else if (typeof document.body != 'undefined') {
yOffset=document.body.scrollTop;
}
}
if (mousePos.y-yOffset < config.scrollAmount) {
window.scrollBy(0, -config.scrollAmount);
} else {
var windowHeight = window.innerHeight ? window.innerHeight
: document.documentElement.clientHeight ? document.documentElement.clientHeight : document.body.clientHeight;
if (windowHeight-(mousePos.y-yOffset) < config.scrollAmount) {
window.scrollBy(0, config.scrollAmount);
}
}
if (y != jQuery.tableDnD.oldY) {
// work out if we're going up or down...
var movingDown = y > jQuery.tableDnD.oldY;
// update the old value
jQuery.tableDnD.oldY = y;
// update the style to show we're dragging
if (config.onDragClass) {
dragObj.addClass(config.onDragClass);
} else {
dragObj.css(config.onDragStyle);
}
// If we're over a row then move the dragged row to there so that the user sees the
// effect dynamically
var currentRow = jQuery.tableDnD.findDropTargetRow(dragObj, y);
if (currentRow) {
// TODO worry about what happens when there are multiple TBODIES
if (movingDown && jQuery.tableDnD.dragObject != currentRow) {
jQuery.tableDnD.dragObject.parentNode.insertBefore(jQuery.tableDnD.dragObject, currentRow.nextSibling);
} else if (! movingDown && jQuery.tableDnD.dragObject != currentRow) {
jQuery.tableDnD.dragObject.parentNode.insertBefore(jQuery.tableDnD.dragObject, currentRow);
}
}
}
return false;
},
/** We're only worried about the y position really, because we can only move rows up and down */
findDropTargetRow: function(draggedRow, y) {
var rows = jQuery.tableDnD.currentTable.rows;
for (var i=0; i<rows.length; i++) {
var row = rows[i];
var rowY = this.getPosition(row).y;
var rowHeight = parseInt(row.offsetHeight)/2;
if (row.offsetHeight == 0) {
rowY = this.getPosition(row.firstChild).y;
rowHeight = parseInt(row.firstChild.offsetHeight)/2;
}
// Because we always have to insert before, we need to offset the height a bit
if ((y > rowY - rowHeight) && (y < (rowY + rowHeight))) {
// that's the row we're over
// If it's the same as the current row, ignore it
if (row == draggedRow) {return null;}
var config = jQuery.tableDnD.currentTable.tableDnDConfig;
if (config.onAllowDrop) {
if (config.onAllowDrop(draggedRow, row)) {
return row;
} else {
return null;
}
} else {
// If a row has nodrop class, then don't allow dropping (inspired by John Tarr and Famic)
var nodrop = jQuery(row).hasClass("nodrop");
if (! nodrop) {
return row;
} else {
return null;
}
}
return row;
}
}
return null;
},
mouseup: function(e) {
if (jQuery.tableDnD.currentTable && jQuery.tableDnD.dragObject) {
var droppedRow = jQuery.tableDnD.dragObject;
var config = jQuery.tableDnD.currentTable.tableDnDConfig;
// If we have a dragObject, then we need to release it,
// The row will already have been moved to the right place so we just reset stuff
if (config.onDragClass) {
jQuery(droppedRow).removeClass(config.onDragClass);
} else {
jQuery(droppedRow).css(config.onDropStyle);
}
jQuery.tableDnD.dragObject = null;
if (config.onDrop) {
// Call the onDrop method if there is one
config.onDrop(jQuery.tableDnD.currentTable, droppedRow);
}
jQuery.tableDnD.currentTable = null; // let go of the table too
}
},
serialize: function() {
if (jQuery.tableDnD.currentTable) {
return jQuery.tableDnD.serializeTable(jQuery.tableDnD.currentTable);
} else {
return "Error: No Table id set, you need to set an id on your table and every row";
}
},
serializeTable: function(table) {
var result = "";
var tableId = table.id;
var rows = table.rows;
for (var i=0; i<rows.length; i++) {
if (result.length > 0) result += "&";
var rowId = rows[i].id;
if (rowId && rowId && table.tableDnDConfig && table.tableDnDConfig.serializeRegexp) {
rowId = rowId.match(table.tableDnDConfig.serializeRegexp)[0];
}
result += tableId + '[]=' + rowId;
}
return result;
},
serializeTables: function() {
var result = "";
this.each(function() {
// this is now bound to each matching table
result += jQuery.tableDnD.serializeTable(this);
});
return result;
}
}
jQuery.fn.extend(
{
tableDnD : jQuery.tableDnD.build,
tableDnDUpdate : jQuery.tableDnD.updateTables,
tableDnDSerialize: jQuery.tableDnD.serializeTables
}
);

View File

@ -0,0 +1,6 @@
.ui-searchFilter { display: none; position: absolute; z-index: 770; overflow: visible;}
.ui-searchFilter table {position:relative; margin:0em; width:auto}
.ui-searchFilter table td {margin: 0em; padding: 1px;}
.ui-searchFilter table td input, .ui-searchFilter table td select {margin: 0.1em;}
.ui-searchFilter .ui-state-default { cursor: pointer; }
.ui-searchFilter .divider hr {margin: 1px; }

View File

@ -0,0 +1,30 @@
/* Multiselect
----------------------------------*/
.ui-multiselect { border: solid 1px; font-size: 0.8em; }
.ui-multiselect ul { -moz-user-select: none; }
.ui-multiselect li { margin: 0; padding: 0; cursor: default; line-height: 20px; height: 20px; font-size: 11px; list-style: none; }
.ui-multiselect li a { color: #999; text-decoration: none; padding: 0; display: block; float: left; cursor: pointer;}
.ui-multiselect li.ui-draggable-dragging { padding-left: 10px; }
.ui-multiselect div.selected { position: relative; padding: 0; margin: 0; border: 0; float:left; }
.ui-multiselect ul.selected { position: relative; padding: 0; overflow: auto; overflow-x: hidden; background: #fff; margin: 0; list-style: none; border: 0; position: relative; width: 100%; }
.ui-multiselect ul.selected li { }
.ui-multiselect div.available { position: relative; padding: 0; margin: 0; border: 0; float:left; border-left: 1px solid; }
.ui-multiselect ul.available { position: relative; padding: 0; overflow: auto; overflow-x: hidden; background: #fff; margin: 0; list-style: none; border: 0; width: 100%; }
.ui-multiselect ul.available li { padding-left: 10px; }
.ui-multiselect .ui-state-default { border: none; margin-bottom: 1px; position: relative; padding-left: 20px;}
.ui-multiselect .ui-state-hover { border: none; }
.ui-multiselect .ui-widget-header {border: none; font-size: 11px; margin-bottom: 1px;}
.ui-multiselect .add-all { float: right; padding: 7px;}
.ui-multiselect .remove-all { float: right; padding: 7px;}
.ui-multiselect .search { float: left; padding: 4px;}
.ui-multiselect .count { float: left; padding: 7px;}
.ui-multiselect li span.ui-icon-arrowthick-2-n-s { position: absolute; left: 2px; }
.ui-multiselect li a.action { position: absolute; right: 2px; top: 2px; }
.ui-multiselect input.search { height: 14px; padding: 1px; opacity: 0.5; margin: 4px; width: 100px; }

View File

@ -0,0 +1,342 @@
/*
* jQuery UI Multiselect
*
* Authors:
* Michael Aufreiter (quasipartikel.at)
* Yanick Rochon (yanick.rochon[at]gmail[dot]com)
*
* Dual licensed under the MIT (MIT-LICENSE.txt)
* and GPL (GPL-LICENSE.txt) licenses.
*
* http://www.quasipartikel.at/multiselect/
*
*
* Depends:
* ui.core.js
* ui.sortable.js
*
* Optional:
* localization (http://plugins.jquery.com/project/localisation)
* scrollTo (http://plugins.jquery.com/project/ScrollTo)
*
* Todo:
* Make batch actions faster
* Implement dynamic insertion through remote calls
*/
(function($) {
$.widget("ui.multiselect", {
options: {
sortable: true,
searchable: true,
doubleClickable: true,
animated: 'fast',
show: 'slideDown',
hide: 'slideUp',
dividerLocation: 0.6,
availableFirst: false,
nodeComparator: function(node1,node2) {
var text1 = node1.text(),
text2 = node2.text();
return text1 == text2 ? 0 : (text1 < text2 ? -1 : 1);
}
},
_create: function() {
this.element.hide();
this.id = this.element.attr("id");
this.container = $('<div class="ui-multiselect ui-helper-clearfix ui-widget"></div>').insertAfter(this.element);
this.count = 0; // number of currently selected options
this.selectedContainer = $('<div class="selected"></div>').appendTo(this.container);
this.availableContainer = $('<div class="available"></div>')[this.options.availableFirst?'prependTo': 'appendTo'](this.container);
this.selectedActions = $('<div class="actions ui-widget-header ui-helper-clearfix"><span class="count">0 '+$.ui.multiselect.locale.itemsCount+'</span><a href="#" class="remove-all">'+$.ui.multiselect.locale.removeAll+'</a></div>').appendTo(this.selectedContainer);
this.availableActions = $('<div class="actions ui-widget-header ui-helper-clearfix"><input type="text" class="search empty ui-widget-content ui-corner-all"/><a href="#" class="add-all">'+$.ui.multiselect.locale.addAll+'</a></div>').appendTo(this.availableContainer);
this.selectedList = $('<ul class="selected connected-list"><li class="ui-helper-hidden-accessible"></li></ul>').bind('selectstart', function(){return false;}).appendTo(this.selectedContainer);
this.availableList = $('<ul class="available connected-list"><li class="ui-helper-hidden-accessible"></li></ul>').bind('selectstart', function(){return false;}).appendTo(this.availableContainer);
var that = this;
// set dimensions
this.container.width(this.element.width()+1);
this.selectedContainer.width(Math.floor(this.element.width()*this.options.dividerLocation));
this.availableContainer.width(Math.floor(this.element.width()*(1-this.options.dividerLocation)));
// fix list height to match <option> depending on their individual header's heights
this.selectedList.height(Math.max(this.element.height()-this.selectedActions.height(),1));
this.availableList.height(Math.max(this.element.height()-this.availableActions.height(),1));
if ( !this.options.animated ) {
this.options.show = 'show';
this.options.hide = 'hide';
}
this.useProp = !!$.fn.prop;
// init lists
this._populateLists(this.element.find('option'));
// make selection sortable
if (this.options.sortable) {
this.selectedList.sortable({
placeholder: 'ui-state-highlight',
axis: 'y',
update: function(event, ui) {
// apply the new sort order to the original selectbox
that.selectedList.find('li').each(function() {
if ($(this).data('optionLink'))
$(this).data('optionLink').remove().appendTo(that.element);
});
},
receive: function(event, ui) {
ui.item.data('optionLink')[ this.useProp ? 'prop' : 'attr' ]('selected', true);
// increment count
that.count += 1;
that._updateCount();
// workaround, because there's no way to reference
// the new element, see http://dev.jqueryui.com/ticket/4303
that.selectedList.children('.ui-draggable').each(function() {
$(this).removeClass('ui-draggable');
$(this).data('optionLink', ui.item.data('optionLink'));
$(this).data('idx', ui.item.data('idx'));
that._applyItemState($(this), true);
});
// workaround according to http://dev.jqueryui.com/ticket/4088
setTimeout(function() { ui.item.remove(); }, 1);
}
});
}
// set up livesearch
if (this.options.searchable) {
this._registerSearchEvents(this.availableContainer.find('input.search'));
} else {
$('.search').hide();
}
// batch actions
this.container.find(".remove-all").click(function() {
that._populateLists(that.element.find('option').removeAttr('selected'));
return false;
});
this.container.find(".add-all").click(function() {
var options = that.element.find('option').not(":selected");
if (that.availableList.children('li:hidden').length > 1) {
that.availableList.children('li').each(function(i) {
if ($(this).is(":visible")) $(options[i-1])[ that.useProp ? 'prop' : 'attr' ]('selected', true);
});
} else {
options[ that.useProp ? 'prop' : 'attr' ]('selected', true);
}
that._populateLists(that.element.find('option'));
return false;
});
},
destroy: function() {
this.element.show();
this.container.remove();
$.Widget.prototype.destroy.apply(this, arguments);
},
_populateLists: function(options) {
this.selectedList.children('.ui-element').remove();
this.availableList.children('.ui-element').remove();
this.count = 0;
var that = this;
var items = $(options.map(function(i) {
var item = that._getOptionNode(this).appendTo(this.selected ? that.selectedList : that.availableList).show();
if (this.selected) that.count += 1;
that._applyItemState(item, this.selected);
item.data('idx', i);
return item[0];
}));
// update count
this._updateCount();
that._filter.apply(this.availableContainer.find('input.search'), [that.availableList]);
},
_updateCount: function() {
this.element.trigger('change');
this.selectedContainer.find('span.count').text(this.count+" "+$.ui.multiselect.locale.itemsCount);
},
_getOptionNode: function(option) {
option = $(option);
var node = $('<li class="ui-state-default ui-element" title="'+option.text()+'"><span class="ui-icon"/>'+option.text()+'<a href="#" class="action"><span class="ui-corner-all ui-icon"/></a></li>').hide();
node.data('optionLink', option);
return node;
},
// clones an item with associated data
// didn't find a smarter away around this
_cloneWithData: function(clonee) {
var clone = clonee.clone(false,false);
clone.data('optionLink', clonee.data('optionLink'));
clone.data('idx', clonee.data('idx'));
return clone;
},
_setSelected: function(item, selected) {
item.data('optionLink')[ this.useProp ? 'prop' : 'attr' ]('selected', selected);
if (selected) {
var selectedItem = this._cloneWithData(item);
item[this.options.hide](this.options.animated, function() { $(this).remove(); });
selectedItem.appendTo(this.selectedList).hide()[this.options.show](this.options.animated);
this._applyItemState(selectedItem, true);
return selectedItem;
} else {
// look for successor based on initial option index
var items = this.availableList.find('li'), comparator = this.options.nodeComparator;
var succ = null, i = item.data('idx'), direction = comparator(item, $(items[i]));
// TODO: test needed for dynamic list populating
if ( direction ) {
while (i>=0 && i<items.length) {
direction > 0 ? i++ : i--;
if ( direction != comparator(item, $(items[i])) ) {
// going up, go back one item down, otherwise leave as is
succ = items[direction > 0 ? i : i+1];
break;
}
}
} else {
succ = items[i];
}
var availableItem = this._cloneWithData(item);
succ ? availableItem.insertBefore($(succ)) : availableItem.appendTo(this.availableList);
item[this.options.hide](this.options.animated, function() { $(this).remove(); });
availableItem.hide()[this.options.show](this.options.animated);
this._applyItemState(availableItem, false);
return availableItem;
}
},
_applyItemState: function(item, selected) {
if (selected) {
if (this.options.sortable)
item.children('span').addClass('ui-icon-arrowthick-2-n-s').removeClass('ui-helper-hidden').addClass('ui-icon');
else
item.children('span').removeClass('ui-icon-arrowthick-2-n-s').addClass('ui-helper-hidden').removeClass('ui-icon');
item.find('a.action span').addClass('ui-icon-minus').removeClass('ui-icon-plus');
this._registerRemoveEvents(item.find('a.action'));
} else {
item.children('span').removeClass('ui-icon-arrowthick-2-n-s').addClass('ui-helper-hidden').removeClass('ui-icon');
item.find('a.action span').addClass('ui-icon-plus').removeClass('ui-icon-minus');
this._registerAddEvents(item.find('a.action'));
}
this._registerDoubleClickEvents(item);
this._registerHoverEvents(item);
},
// taken from John Resig's liveUpdate script
_filter: function(list) {
var input = $(this);
var rows = list.children('li'),
cache = rows.map(function(){
return $(this).text().toLowerCase();
});
var term = $.trim(input.val().toLowerCase()), scores = [];
if (!term) {
rows.show();
} else {
rows.hide();
cache.each(function(i) {
if (this.indexOf(term)>-1) { scores.push(i); }
});
$.each(scores, function() {
$(rows[this]).show();
});
}
},
_registerDoubleClickEvents: function(elements) {
if (!this.options.doubleClickable) return;
elements.dblclick(function(ev) {
if ($(ev.target).closest('.action').length === 0) {
// This may be triggered with rapid clicks on actions as well. In that
// case don't trigger an additional click.
elements.find('a.action').click();
}
});
},
_registerHoverEvents: function(elements) {
elements.removeClass('ui-state-hover');
elements.mouseover(function() {
$(this).addClass('ui-state-hover');
});
elements.mouseout(function() {
$(this).removeClass('ui-state-hover');
});
},
_registerAddEvents: function(elements) {
var that = this;
elements.click(function() {
var item = that._setSelected($(this).parent(), true);
that.count += 1;
that._updateCount();
return false;
});
// make draggable
if (this.options.sortable) {
elements.each(function() {
$(this).parent().draggable({
connectToSortable: that.selectedList,
helper: function() {
var selectedItem = that._cloneWithData($(this)).width($(this).width() - 50);
selectedItem.width($(this).width());
return selectedItem;
},
appendTo: that.container,
containment: that.container,
revert: 'invalid'
});
});
}
},
_registerRemoveEvents: function(elements) {
var that = this;
elements.click(function() {
that._setSelected($(this).parent(), false);
that.count -= 1;
that._updateCount();
return false;
});
},
_registerSearchEvents: function(input) {
var that = this;
input.focus(function() {
$(this).addClass('ui-state-active');
})
.blur(function() {
$(this).removeClass('ui-state-active');
})
.keypress(function(e) {
if (e.keyCode == 13)
return false;
})
.keyup(function() {
that._filter.apply(this, [that.availableList]);
});
}
});
$.extend($.ui.multiselect, {
locale: {
addAll:'Add all',
removeAll:'Remove all',
itemsCount:'items selected'
}
});
})(jQuery);

View File

@ -0,0 +1,50 @@
#!/mod/bin/jimsh
######
# FlexView - Save flexview cofig in jqGrid JSON format
# Author: MymsMan, 2016
######
package require cgi
source /mod/webif/lib/setup
require system.class settings.class
# logging options
if {[lindex $argv 0] eq "-d"} {
set argv [lrange $argv 1 end]
set loglevel 2
} else {
# Use same logging option as auto log
set settings [settings]
set loglevel [$settings _nval_setting "autolog"]
}
proc log {msg {level 2}} {
if {$level > $::loglevel} return
system plog flexview $msg
}
httpheader
set start [clock milliseconds]
set name [cgi_get name "default"]
set fvconfig [cgi_get fvconfig "{}"]
set fname "/mod/etc/fvConfig_$name.json"
if {[catch {set fd [open $fname w]
puts $fd $fvconfig
close $fd
puts "Saved configuration- $name<br>"} msg opts] } {
log "Config file error caught: $msg $opts" 0
puts "<b>File save error $msg</b><br>"
}
source /mod/webif/plugin/flexview/configlist.jim
genConfigList
[settings new] _tval_setting "fvConfig" $name
set end [clock milliseconds]
set elapse $(double($end-$start)/1000.0)
log "Save | $elapse | $fname" 0

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,106 @@
.fvIcon {
vertical-align: center;
height: 21px;
}
#flcell {
vertical-align: top;
}
#dircell {
vertical-align: top;
}
.ui-resizable-helper {
border: 1px solid #999;
}
#resize {
background-color: #ccc;
}
#paste {
display: none
}
/* change grid z-index so it is lower than top menu pull downs */
.ui-jqgrid {
z-index: 20;
overflow: auto;
}
/* stop text bottom being cut off */
.ui-jqgrid .ui-jqgrid-sortable { height:24px !important; }
/* stop filter boxes overflowing */
.ui-search-table select, .ui-search-table input {
width: 90% !important;
}
.ui-jqgrid .ui-search-table .ui-search-oper {
width: 10px;
padding: 0;
}
/* Zebra stripe table in common with other webif displays */
/*
.ui-jqgrid tr.jqgrow:nth-child(even) {
background: #ffffcc;
}
.ui-jqgrid tr.jqgrow:nth-child(odd) {
background: #ccff99;
}
*/
#tabcontent
{
margin-top: 1em;
}
.spinner
{
width: 50%;
margin: 10px;
height: 20px
}
/* Minimal tabs */
/*
#tabbar
{
background: transparent;
border: none;
padding: 0 0 0 0;
}
#tabbar .ui-widget-header
{
background: transparent;
border: none;
border-bottom: 1px solid #c0c0c0;
-moz-border-radius: 0px;
-webkit-border-radius: 0px;
border-radius: 0px;
}
#tabbar .ui-tabs-nav .ui-state-default
{
background: transparent;
border: none;
}
#tabbar .ui-tabs-nav .ui-state-active
{
background: transparent url(img/uiTabsArrow.png) no-repeat bottom center;
border: none;
}
#tabbar .ui-tabs-nav .ui-state-default a
{
color: #c0c0c0;
}
#tabbar .ui-tabs-nav .ui-state-active a
{
color: #000000;
}
#tabbar .ui-tabs-panel
{
padding: 0 0 0 0;
}
*/

View File

@ -0,0 +1,4 @@
tb "/plugin/flexview/img/flexview.png" "Flex View" "/go/flexview"
#tb "/plugin/flexview/icon.png" "Flex View" "/plugin/flexview/index.jim"