Actualización

This commit is contained in:
Xes
2025-04-10 12:49:05 +02:00
parent 4aff98e77b
commit 1cdd00920f
9151 changed files with 1800913 additions and 0 deletions

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,314 @@
/*
* 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", {
_init: 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>').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';
}
// init lists
this._populateLists(this.element.find('option'));
// make selection sortable
if (this.options.sortable) {
$("ul.selected").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').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
$(".remove-all").click(function() {
that._populateLists(that.element.find('option').removeAttr('selected'));
return false;
});
$(".add-all").click(function() {
that._populateLists(that.element.find('option').attr('selected', 'selected'));
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();
},
_updateCount: function() {
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();
clone.data('optionLink', clonee.data('optionLink'));
clone.data('idx', clonee.data('idx'));
return clone;
},
_setSelected: function(item, selected) {
item.data('optionLink').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._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();
});
}
},
_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
.each(function() {
$(this).parent().draggable({
connectToSortable: 'ul.selected',
helper: function() {
var selectedItem = that._cloneWithData($(this)).width($(this).width() - 50);
selectedItem.width($(this).width());
return selectedItem;
},
appendTo: '.ui-multiselect',
containment: '.ui-multiselect',
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, {
defaults: {
sortable: true,
searchable: true,
animated: 'fast',
show: 'slideDown',
hide: 'slideUp',
dividerLocation: 0.6,
nodeComparator: function(node1,node2) {
var text1 = node1.text(),
text2 = node2.text();
return text1 == text2 ? 0 : (text1 < text2 ? -1 : 1);
}
},
locale: {
addAll:'Add all',
removeAll:'Remove all',
itemsCount:'items selected'
}
});
})(jQuery);