zfben_rails_assets 0.0.5 → 0.0.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/example/Gemfile +1 -1
- data/lib/zfben_rails_assets/assets/javascripts/zff/lib/jquery/colorbox.js +872 -0
- data/lib/zfben_rails_assets/assets/javascripts/zff/lib/jquery/contextmenu.js +144 -0
- data/lib/zfben_rails_assets/assets/javascripts/zff/lib/jquery/grid/addons.js +704 -0
- data/lib/zfben_rails_assets/assets/javascripts/zff/lib/jquery/grid/core.js +10913 -0
- data/lib/zfben_rails_assets/assets/javascripts/zff/lib/jquery/grid/i18n/cn.js +132 -0
- data/lib/zfben_rails_assets/assets/javascripts/zff/lib/jquery/grid/i18n/en.js +128 -0
- data/lib/zfben_rails_assets/assets/javascripts/zff/lib/jquery/grid/postext.js +64 -0
- data/lib/zfben_rails_assets/assets/javascripts/zff/lib/jquery/grid/setcolumns.js +126 -0
- data/lib/zfben_rails_assets/assets/javascripts/zff/lib/jquery/grid.js +2 -467
- data/lib/zfben_rails_assets/assets/javascripts/zff/lib/jquery/searchFilter.js +716 -0
- data/lib/zfben_rails_assets/assets/javascripts/zff/lib/jquery/tablednd.js +383 -0
- data/lib/zfben_rails_assets/assets/javascripts/zff/lib/jquery/ui/multiselect.js +314 -0
- data/lib/zfben_rails_assets/assets/stylesheets/zff/lib/jquery/grid/grid.css +140 -0
- data/lib/zfben_rails_assets/assets/stylesheets/zff/lib/{qunit.css → jquery/qunit/qunit.css} +0 -0
- data/lib/zfben_rails_assets/assets/stylesheets/zff/lib/jquery/ui/multiselect.css +30 -0
- data/lib/zfben_rails_assets/tasks.rb +2 -4
- data/lib/zfben_rails_assets/version.rb +1 -1
- metadata +16 -3
@@ -0,0 +1,716 @@
|
|
1
|
+
/* Plugin: searchFilter v1.2.9
|
2
|
+
* Author: Kasey Speakman (kasey@cornerspeed.com)
|
3
|
+
* License: Dual Licensed, MIT and GPL v2 (http://www.gnu.org/licenses/gpl-2.0.html)
|
4
|
+
*
|
5
|
+
* REQUIREMENTS:
|
6
|
+
* jQuery 1.3+ (http://jquery.com/)
|
7
|
+
* A Themeroller Theme (http://jqueryui.com/themeroller/)
|
8
|
+
*
|
9
|
+
* SECURITY WARNING
|
10
|
+
* You should always implement server-side checking to ensure that
|
11
|
+
* the query will fail when forged/invalid data is received.
|
12
|
+
* Clever users can send any value they want through JavaScript and HTTP POST/GET.
|
13
|
+
*
|
14
|
+
* THEMES
|
15
|
+
* Simply include the CSS file for your Themeroller theme.
|
16
|
+
*
|
17
|
+
* DESCRIPTION
|
18
|
+
* This plugin creates a new searchFilter object in the specified container
|
19
|
+
*
|
20
|
+
* INPUT TYPE
|
21
|
+
* fields: an array of field objects. each object has the following properties:
|
22
|
+
* text: a string containing the display name of the field (e.g. "Field 1")
|
23
|
+
* itemval: a string containing the actual field name (e.g. "field1")
|
24
|
+
* optional properties:
|
25
|
+
* ops: an array of operators in the same format as jQuery.fn.searchFilter.defaults.operators
|
26
|
+
* that is: [ { op: 'gt', text: 'greater than'}, { op:'lt', text: 'less than'}, ... ]
|
27
|
+
* if not specified, the passed-in options used, and failting that, jQuery.fn.searchFilter.defaults.operators will be used
|
28
|
+
* *** NOTE ***
|
29
|
+
* Specifying a dataUrl or dataValues property means that a <select ...> (drop-down-list) will be generated
|
30
|
+
* instead of a text input <input type='text'.../> where the user would normally type in their search data
|
31
|
+
* ************
|
32
|
+
* dataUrl: a url that will return the html select for this field, this url will only be called once for this field
|
33
|
+
* dataValues: the possible values for this field in the form [ { text: 'Data Display Text', value: 'data_actual_value' }, { ... } ]
|
34
|
+
* dataInit: a function that you can use to initialize the data field. this function is passed the jQuery-fied data element
|
35
|
+
* dataEvents: list of events to apply to the data element. uses $("#id").bind(type, [data], fn) to bind events to data element
|
36
|
+
* *** JSON of this object could look like this: ***
|
37
|
+
* var fields = [
|
38
|
+
* {
|
39
|
+
* text: 'Field Display Name',
|
40
|
+
* itemval: 'field_actual_name',
|
41
|
+
* // below this are optional values
|
42
|
+
* ops: [ // this format is the same as jQuery.fn.searchFilter.defaults.operators
|
43
|
+
* { op: 'gt', text: 'greater than' },
|
44
|
+
* { op: 'lt', text: 'less than' }
|
45
|
+
* ],
|
46
|
+
* dataUrl: 'http://server/path/script.php?propName=propValue', // using this creates a select for the data input instead of an input type='text'
|
47
|
+
* dataValues: [ // using this creates a select for the data input instead of an input type='text'
|
48
|
+
* { text: 'Data Value Display Name', value: 'data_actual_value' },
|
49
|
+
* { ... }
|
50
|
+
* ],
|
51
|
+
* dataInit: function(jElem) { jElem.datepicker(options); },
|
52
|
+
* dataEvents: [ // these are the same options that you pass to $("#id").bind(type, [data], fn)
|
53
|
+
* { type: 'click', data: { i: 7 }, fn: function(e) { console.log(e.data.i); } },
|
54
|
+
* { type: 'keypress', fn: function(e) { console.log('keypress'); } }
|
55
|
+
* ]
|
56
|
+
* },
|
57
|
+
* { ... }
|
58
|
+
* ]
|
59
|
+
* options: name:value properties containing various creation options
|
60
|
+
* see jQuery.fn.searchFilter.defaults for the overridable options
|
61
|
+
*
|
62
|
+
* RETURN TYPE: This plugin returns a SearchFilter object, which has additional SearchFilter methods:
|
63
|
+
* Methods
|
64
|
+
* add: Adds a filter. added to the end of the list unless a jQuery event object or valid row number is passed.
|
65
|
+
* del: Removes a filter. removed from the end of the list unless a jQuery event object or valid row number is passed.
|
66
|
+
* reset: resets filters back to original state (only one blank filter), and calls onReset
|
67
|
+
* search: puts the search rules into an object and calls onSearch with it
|
68
|
+
* close: calls the onClose event handler
|
69
|
+
*
|
70
|
+
* USAGE
|
71
|
+
* HTML
|
72
|
+
* <head>
|
73
|
+
* ...
|
74
|
+
* <script src="path/to/jquery.min.js" type="text/javascript"></script>
|
75
|
+
* <link href="path/to/themeroller.css" rel="Stylesheet" type="text/css" />
|
76
|
+
* <script src="path/to/jquery.searchFilter.js" type="text/javascript"></script>
|
77
|
+
* <link href="path/to/jquery.searchFilter.css" rel="Stylesheet" type="text/css" />
|
78
|
+
* ...
|
79
|
+
* </head>
|
80
|
+
* <body>
|
81
|
+
* ...
|
82
|
+
* <div id='mySearch'></div>
|
83
|
+
* ...
|
84
|
+
* </body>
|
85
|
+
* JQUERY
|
86
|
+
* Methods
|
87
|
+
* initializing: $("#mySearch").searchFilter([{text: "Field 1", value: "field1"},{text: "Field 2", value: "field2"}], {onSearch: myFilterRuleReceiverFn, onReset: myFilterResetFn });
|
88
|
+
* Manual Methods (there's no need to call these methods unless you are trying to manipulate searchFilter with script)
|
89
|
+
* add: $("#mySearch").searchFilter().add(); // appends a blank filter
|
90
|
+
* $("#mySearch").searchFilter().add(0); // copies the first filter as second
|
91
|
+
* del: $("#mySearch").searchFilter().del(); // removes the bottom filter
|
92
|
+
* $("#mySearch").searchFilter().del(1); // removes the second filter
|
93
|
+
* search: $("#mySearch").searchFilter().search(); // invokes onSearch, passing it a ruleGroup object
|
94
|
+
* reset: $("#mySearch").searchFilter().reset(); // resets rules and invokes onReset
|
95
|
+
* close: $("#mySearch").searchFilter().close(); // without an onClose handler, equivalent to $("#mySearch").hide();
|
96
|
+
*
|
97
|
+
* NOTE: You can get the jQuery object back from the SearchFilter object by chaining .$
|
98
|
+
* Example
|
99
|
+
* $("#mySearch").searchFilter().add().add().reset().$.hide();
|
100
|
+
* Verbose Example
|
101
|
+
* $("#mySearch") // gets jQuery object for the HTML element with id="mySearch"
|
102
|
+
* .searchFilter() // gets the SearchFilter object for an existing search filter
|
103
|
+
* .add() // adds a new filter to the end of the list
|
104
|
+
* .add() // adds another new filter to the end of the list
|
105
|
+
* .reset() // resets filters back to original state, triggers onReset
|
106
|
+
* .$ // returns jQuery object for $("#mySearch")
|
107
|
+
* .hide(); // equivalent to $("#mySearch").hide();
|
108
|
+
*/
|
109
|
+
|
110
|
+
jQuery.fn.searchFilter = function(fields, options) {
|
111
|
+
|
112
|
+
function SearchFilter(jQ, fields, options) {
|
113
|
+
|
114
|
+
|
115
|
+
//---------------------------------------------------------------
|
116
|
+
// PUBLIC VARS
|
117
|
+
//---------------------------------------------------------------
|
118
|
+
|
119
|
+
this.$ = jQ; // makes the jQuery object available as .$ from the return value
|
120
|
+
|
121
|
+
|
122
|
+
//---------------------------------------------------------------
|
123
|
+
// PUBLIC FUNCTIONS
|
124
|
+
//---------------------------------------------------------------
|
125
|
+
|
126
|
+
this.add = function(i) {
|
127
|
+
if (i == null) jQ.find(".ui-add-last").click();
|
128
|
+
else jQ.find(".sf:eq(" + i + ") .ui-add").click();
|
129
|
+
return this;
|
130
|
+
};
|
131
|
+
|
132
|
+
this.del = function(i) {
|
133
|
+
if (i == null) jQ.find(".sf:last .ui-del").click();
|
134
|
+
else jQ.find(".sf:eq(" + i + ") .ui-del").click();
|
135
|
+
return this;
|
136
|
+
};
|
137
|
+
|
138
|
+
this.search = function(e) {
|
139
|
+
jQ.find(".ui-search").click();
|
140
|
+
return this;
|
141
|
+
};
|
142
|
+
|
143
|
+
this.reset = function(o) {
|
144
|
+
if(o===undefined) o = false;
|
145
|
+
jQ.find(".ui-reset").trigger('click',[o]);
|
146
|
+
return this;
|
147
|
+
};
|
148
|
+
|
149
|
+
this.close = function() {
|
150
|
+
jQ.find(".ui-closer").click();
|
151
|
+
return this;
|
152
|
+
};
|
153
|
+
|
154
|
+
|
155
|
+
|
156
|
+
//---------------------------------------------------------------
|
157
|
+
// "CONSTRUCTOR" (in air quotes)
|
158
|
+
//---------------------------------------------------------------
|
159
|
+
|
160
|
+
if (fields != null) { // type coercion matches undefined as well as null
|
161
|
+
|
162
|
+
|
163
|
+
//---------------------------------------------------------------
|
164
|
+
// UTILITY FUNCTIONS
|
165
|
+
//---------------------------------------------------------------
|
166
|
+
|
167
|
+
function hover() {
|
168
|
+
jQuery(this).toggleClass("ui-state-hover");
|
169
|
+
return false;
|
170
|
+
}
|
171
|
+
|
172
|
+
function active(e) {
|
173
|
+
jQuery(this).toggleClass("ui-state-active", (e.type == "mousedown"));
|
174
|
+
return false;
|
175
|
+
}
|
176
|
+
|
177
|
+
function buildOpt(value, text) {
|
178
|
+
return "<option value='" + value + "'>" + text + "</option>";
|
179
|
+
}
|
180
|
+
|
181
|
+
function buildSel(className, options, isHidden) {
|
182
|
+
return "<select class='" + className + "'" + (isHidden ? " style='display:none;'" : "") + ">" + options + "</select>";
|
183
|
+
}
|
184
|
+
|
185
|
+
function initData(selector, fn) {
|
186
|
+
var jElem = jQ.find("tr.sf td.data " + selector);
|
187
|
+
if (jElem[0] != null)
|
188
|
+
fn(jElem);
|
189
|
+
}
|
190
|
+
|
191
|
+
function bindDataEvents(selector, events) {
|
192
|
+
var jElem = jQ.find("tr.sf td.data " + selector);
|
193
|
+
if (jElem[0] != null) {
|
194
|
+
jQuery.each(events, function() {
|
195
|
+
if (this.data != null)
|
196
|
+
jElem.bind(this.type, this.data, this.fn);
|
197
|
+
else
|
198
|
+
jElem.bind(this.type, this.fn);
|
199
|
+
});
|
200
|
+
}
|
201
|
+
}
|
202
|
+
|
203
|
+
|
204
|
+
//---------------------------------------------------------------
|
205
|
+
// SUPER IMPORTANT PRIVATE VARS
|
206
|
+
//---------------------------------------------------------------
|
207
|
+
|
208
|
+
// copies jQuery.fn.searchFilter.defaults.options properties onto an empty object, then options onto that
|
209
|
+
var opts = jQuery.extend({}, jQuery.fn.searchFilter.defaults, options);
|
210
|
+
|
211
|
+
// this is keeps track of the last asynchronous setup
|
212
|
+
var highest_late_setup = -1;
|
213
|
+
|
214
|
+
|
215
|
+
//---------------------------------------------------------------
|
216
|
+
// CREATION PROCESS STARTS
|
217
|
+
//---------------------------------------------------------------
|
218
|
+
|
219
|
+
// generate the global ops
|
220
|
+
var gOps_html = "";
|
221
|
+
jQuery.each(opts.groupOps, function() { gOps_html += buildOpt(this.op, this.text); });
|
222
|
+
gOps_html = "<select name='groupOp'>" + gOps_html + "</select>";
|
223
|
+
|
224
|
+
/* original content - doesn't minify very well
|
225
|
+
jQ
|
226
|
+
.html("") // clear any old content
|
227
|
+
.addClass("ui-searchFilter") // add classes
|
228
|
+
.append( // add content
|
229
|
+
"\
|
230
|
+
<div class='ui-widget-overlay' style='z-index: -1'> </div>\
|
231
|
+
<table class='ui-widget-content ui-corner-all'>\
|
232
|
+
<thead>\
|
233
|
+
<tr>\
|
234
|
+
<td colspan='5' class='ui-widget-header ui-corner-all' style='line-height: 18px;'>\
|
235
|
+
<div class='ui-closer ui-state-default ui-corner-all ui-helper-clearfix' style='float: right;'>\
|
236
|
+
<span class='ui-icon ui-icon-close'></span>\
|
237
|
+
</div>\
|
238
|
+
" + opts.windowTitle + "\
|
239
|
+
</td>\
|
240
|
+
</tr>\
|
241
|
+
</thead>\
|
242
|
+
<tbody>\
|
243
|
+
<tr class='sf'>\
|
244
|
+
<td class='fields'></td>\
|
245
|
+
<td class='ops'></td>\
|
246
|
+
<td class='data'></td>\
|
247
|
+
<td><div class='ui-del ui-state-default ui-corner-all'><span class='ui-icon ui-icon-minus'></span></div></td>\
|
248
|
+
<td><div class='ui-add ui-state-default ui-corner-all'><span class='ui-icon ui-icon-plus'></span></div></td>\
|
249
|
+
</tr>\
|
250
|
+
<tr>\
|
251
|
+
<td colspan='5' class='divider'><div> </div></td>\
|
252
|
+
</tr>\
|
253
|
+
</tbody>\
|
254
|
+
<tfoot>\
|
255
|
+
<tr>\
|
256
|
+
<td colspan='3'>\
|
257
|
+
<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>\
|
258
|
+
<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>\
|
259
|
+
<span class='matchText'>" + opts.matchText + "</span> \
|
260
|
+
" + gOps_html + " \
|
261
|
+
<span class='rulesText'>" + opts.rulesText + "</span>\
|
262
|
+
</td>\
|
263
|
+
<td> </td>\
|
264
|
+
<td><div class='ui-add-last ui-state-default ui-corner-all'><span class='ui-icon ui-icon-plusthick'></span></div></td>\
|
265
|
+
</tr>\
|
266
|
+
</tfoot>\
|
267
|
+
</table>\
|
268
|
+
");
|
269
|
+
/* end hard-to-minify code */
|
270
|
+
/* begin easier to minify code */
|
271
|
+
jQ.html("").addClass("ui-searchFilter").append("<div class='ui-widget-overlay' style='z-index: -1'> </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> </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>");
|
272
|
+
/* end easier-to-minify code */
|
273
|
+
|
274
|
+
var jRow = jQ.find("tr.sf");
|
275
|
+
var jFields = jRow.find("td.fields");
|
276
|
+
var jOps = jRow.find("td.ops");
|
277
|
+
var jData = jRow.find("td.data");
|
278
|
+
|
279
|
+
// generate the defaults
|
280
|
+
var default_ops_html = "";
|
281
|
+
jQuery.each(opts.operators, function() { default_ops_html += buildOpt(this.op, this.text); });
|
282
|
+
default_ops_html = buildSel("default", default_ops_html, true);
|
283
|
+
jOps.append(default_ops_html);
|
284
|
+
var default_data_html = "<input type='text' class='default' style='display:none;' />";
|
285
|
+
jData.append(default_data_html);
|
286
|
+
|
287
|
+
// generate the field list as a string
|
288
|
+
var fields_html = "";
|
289
|
+
var has_custom_ops = false;
|
290
|
+
var has_custom_data = false;
|
291
|
+
jQuery.each(fields, function(i) {
|
292
|
+
var field_num = i;
|
293
|
+
fields_html += buildOpt(this.itemval, this.text);
|
294
|
+
// add custom ops if they exist
|
295
|
+
if (this.ops != null) {
|
296
|
+
has_custom_ops = true;
|
297
|
+
var custom_ops = "";
|
298
|
+
jQuery.each(this.ops, function() { custom_ops += buildOpt(this.op, this.text); });
|
299
|
+
custom_ops = buildSel("field" + field_num, custom_ops, true);
|
300
|
+
jOps.append(custom_ops);
|
301
|
+
}
|
302
|
+
// add custom data if it is given
|
303
|
+
if (this.dataUrl != null) {
|
304
|
+
if (i > highest_late_setup) highest_late_setup = i;
|
305
|
+
has_custom_data = true;
|
306
|
+
var dEvents = this.dataEvents;
|
307
|
+
var iEvent = this.dataInit;
|
308
|
+
var bs = this.buildSelect;
|
309
|
+
jQuery.ajax(jQuery.extend({
|
310
|
+
url : this.dataUrl,
|
311
|
+
complete: function(data) {
|
312
|
+
var $d;
|
313
|
+
if(bs != null) $d =jQuery("<div />").append(bs(data));
|
314
|
+
else $d = jQuery("<div />").append(data.responseText);
|
315
|
+
$d.find("select").addClass("field" + field_num).hide();
|
316
|
+
jData.append($d.html());
|
317
|
+
if (iEvent) initData(".field" + i, iEvent);
|
318
|
+
if (dEvents) bindDataEvents(".field" + i, dEvents);
|
319
|
+
if (i == highest_late_setup) { // change should get called no more than twice when this searchFilter is constructed
|
320
|
+
jQ.find("tr.sf td.fields select[name='field']").change();
|
321
|
+
}
|
322
|
+
}
|
323
|
+
},opts.ajaxSelectOptions));
|
324
|
+
} else if (this.dataValues != null) {
|
325
|
+
has_custom_data = true;
|
326
|
+
var custom_data = "";
|
327
|
+
jQuery.each(this.dataValues, function() { custom_data += buildOpt(this.value, this.text); });
|
328
|
+
custom_data = buildSel("field" + field_num, custom_data, true);
|
329
|
+
jData.append(custom_data);
|
330
|
+
} else if (this.dataEvents != null || this.dataInit != null) {
|
331
|
+
has_custom_data = true;
|
332
|
+
var custom_data = "<input type='text' class='field" + field_num + "' />";
|
333
|
+
jData.append(custom_data);
|
334
|
+
}
|
335
|
+
// attach events to data if they exist
|
336
|
+
if (this.dataInit != null && i != highest_late_setup)
|
337
|
+
initData(".field" + i, this.dataInit);
|
338
|
+
if (this.dataEvents != null && i != highest_late_setup)
|
339
|
+
bindDataEvents(".field" + i, this.dataEvents);
|
340
|
+
});
|
341
|
+
fields_html = "<select name='field'>" + fields_html + "</select>";
|
342
|
+
jFields.append(fields_html);
|
343
|
+
|
344
|
+
// setup the field select with an on-change event if there are custom ops or data
|
345
|
+
var jFSelect = jFields.find("select[name='field']");
|
346
|
+
if (has_custom_ops) jFSelect.change(function(e) {
|
347
|
+
var index = e.target.selectedIndex;
|
348
|
+
var td = jQuery(e.target).parents("tr.sf").find("td.ops");
|
349
|
+
td.find("select").removeAttr("name").hide(); // disown and hide all elements
|
350
|
+
var jElem = td.find(".field" + index);
|
351
|
+
if (jElem[0] == null) jElem = td.find(".default"); // if there's not an element for that field, use the default one
|
352
|
+
jElem.attr("name", "op").show();
|
353
|
+
return false;
|
354
|
+
});
|
355
|
+
else jOps.find(".default").attr("name", "op").show();
|
356
|
+
if (has_custom_data) jFSelect.change(function(e) {
|
357
|
+
var index = e.target.selectedIndex;
|
358
|
+
var td = jQuery(e.target).parents("tr.sf").find("td.data");
|
359
|
+
td.find("select,input").removeClass("vdata").hide(); // disown and hide all elements
|
360
|
+
var jElem = td.find(".field" + index);
|
361
|
+
if (jElem[0] == null) jElem = td.find(".default"); // if there's not an element for that field, use the default one
|
362
|
+
jElem.show().addClass("vdata");
|
363
|
+
return false;
|
364
|
+
});
|
365
|
+
else jData.find(".default").show().addClass("vdata");
|
366
|
+
// go ahead and call the change event and setup the ops and data values
|
367
|
+
if (has_custom_ops || has_custom_data) jFSelect.change();
|
368
|
+
|
369
|
+
// bind events
|
370
|
+
jQ.find(".ui-state-default").hover(hover, hover).mousedown(active).mouseup(active); // add hover/active effects to all buttons
|
371
|
+
jQ.find(".ui-closer").click(function(e) {
|
372
|
+
opts.onClose(jQuery(jQ.selector));
|
373
|
+
return false;
|
374
|
+
});
|
375
|
+
jQ.find(".ui-del").click(function(e) {
|
376
|
+
var row = jQuery(e.target).parents(".sf");
|
377
|
+
if (row.siblings(".sf").length > 0) { // doesn't remove if there's only one filter left
|
378
|
+
if (opts.datepickerFix === true && jQuery.fn.datepicker !== undefined)
|
379
|
+
row.find(".hasDatepicker").datepicker("destroy"); // clean up datepicker's $.data mess
|
380
|
+
row.remove(); // also unbinds
|
381
|
+
} else { // resets the filter if it's the last one
|
382
|
+
row.find("select[name='field']")[0].selectedIndex = 0;
|
383
|
+
row.find("select[name='op']")[0].selectedIndex = 0;
|
384
|
+
row.find(".data input").val(""); // blank all input values
|
385
|
+
row.find(".data select").each(function() { this.selectedIndex = 0; }); // select first option on all selects
|
386
|
+
row.find("select[name='field']").change(function(event){event.stopPropagation();}); // trigger any change events
|
387
|
+
}
|
388
|
+
return false;
|
389
|
+
});
|
390
|
+
jQ.find(".ui-add").click(function(e) {
|
391
|
+
var row = jQuery(e.target).parents(".sf");
|
392
|
+
var newRow = row.clone(true).insertAfter(row);
|
393
|
+
newRow.find(".ui-state-default").removeClass("ui-state-hover ui-state-active");
|
394
|
+
if (opts.clone) {
|
395
|
+
newRow.find("select[name='field']")[0].selectedIndex = row.find("select[name='field']")[0].selectedIndex;
|
396
|
+
var stupid_browser = (newRow.find("select[name='op']")[0] == null); // true for IE6
|
397
|
+
if (!stupid_browser)
|
398
|
+
newRow.find("select[name='op']").focus()[0].selectedIndex = row.find("select[name='op']")[0].selectedIndex;
|
399
|
+
var jElem = newRow.find("select.vdata");
|
400
|
+
if (jElem[0] != null) // select doesn't copy it's selected index when cloned
|
401
|
+
jElem[0].selectedIndex = row.find("select.vdata")[0].selectedIndex;
|
402
|
+
} else {
|
403
|
+
newRow.find(".data input").val(""); // blank all input values
|
404
|
+
newRow.find("select[name='field']").focus();
|
405
|
+
}
|
406
|
+
if (opts.datepickerFix === true && jQuery.fn.datepicker !== undefined) { // using $.data to associate data with document elements is Not Good
|
407
|
+
row.find(".hasDatepicker").each(function() {
|
408
|
+
var settings = jQuery.data(this, "datepicker").settings;
|
409
|
+
newRow.find("#" + this.id).unbind().removeAttr("id").removeClass("hasDatepicker").datepicker(settings);
|
410
|
+
});
|
411
|
+
}
|
412
|
+
newRow.find("select[name='field']").change(function(event){event.stopPropagation();} );
|
413
|
+
return false;
|
414
|
+
});
|
415
|
+
jQ.find(".ui-search").click(function(e) {
|
416
|
+
var ui = jQuery(jQ.selector); // pointer to search box wrapper element
|
417
|
+
var ruleGroup;
|
418
|
+
var group_op = ui.find("select[name='groupOp'] :selected").val(); // puls "AND" or "OR"
|
419
|
+
if (!opts.stringResult) {
|
420
|
+
ruleGroup = {
|
421
|
+
groupOp: group_op,
|
422
|
+
rules: []
|
423
|
+
};
|
424
|
+
} else {
|
425
|
+
ruleGroup = "{\"groupOp\":\"" + group_op + "\",\"rules\":[";
|
426
|
+
}
|
427
|
+
ui.find(".sf").each(function(i) {
|
428
|
+
var tField = jQuery(this).find("select[name='field'] :selected").val();
|
429
|
+
var tOp = jQuery(this).find("select[name='op'] :selected").val();
|
430
|
+
var tData = jQuery(this).find("input.vdata,select.vdata :selected").val();
|
431
|
+
tData += "";
|
432
|
+
if (!opts.stringResult) {
|
433
|
+
ruleGroup.rules.push({
|
434
|
+
field: tField,
|
435
|
+
op: tOp,
|
436
|
+
data: tData
|
437
|
+
});
|
438
|
+
} else {
|
439
|
+
tData = tData.replace(/\\/g,'\\\\').replace(/\"/g,'\\"');
|
440
|
+
if (i > 0) ruleGroup += ",";
|
441
|
+
ruleGroup += "{\"field\":\"" + tField + "\",";
|
442
|
+
ruleGroup += "\"op\":\"" + tOp + "\",";
|
443
|
+
ruleGroup += "\"data\":\"" + tData + "\"}";
|
444
|
+
}
|
445
|
+
});
|
446
|
+
if (opts.stringResult) ruleGroup += "]}";
|
447
|
+
opts.onSearch(ruleGroup);
|
448
|
+
return false;
|
449
|
+
});
|
450
|
+
jQ.find(".ui-reset").click(function(e,op) {
|
451
|
+
var ui = jQuery(jQ.selector);
|
452
|
+
ui.find(".ui-del").click(); // removes all filters, resets the last one
|
453
|
+
ui.find("select[name='groupOp']")[0].selectedIndex = 0; // changes the op back to the default one
|
454
|
+
opts.onReset(op);
|
455
|
+
return false;
|
456
|
+
});
|
457
|
+
jQ.find(".ui-add-last").click(function() {
|
458
|
+
var row = jQuery(jQ.selector + " .sf:last");
|
459
|
+
var newRow = row.clone(true).insertAfter(row);
|
460
|
+
newRow.find(".ui-state-default").removeClass("ui-state-hover ui-state-active");
|
461
|
+
newRow.find(".data input").val(""); // blank all input values
|
462
|
+
newRow.find("select[name='field']").focus();
|
463
|
+
if (opts.datepickerFix === true && jQuery.fn.datepicker !== undefined) { // using $.data to associate data with document elements is Not Good
|
464
|
+
row.find(".hasDatepicker").each(function() {
|
465
|
+
var settings = jQuery.data(this, "datepicker").settings;
|
466
|
+
newRow.find("#" + this.id).unbind().removeAttr("id").removeClass("hasDatepicker").datepicker(settings);
|
467
|
+
});
|
468
|
+
}
|
469
|
+
newRow.find("select[name='field']").change(function(event){event.stopPropagation();});
|
470
|
+
return false;
|
471
|
+
});
|
472
|
+
|
473
|
+
this.setGroupOp = function(setting) {
|
474
|
+
/* a "setter" for groupping argument.
|
475
|
+
* ("AND" or "OR")
|
476
|
+
*
|
477
|
+
* Inputs:
|
478
|
+
* setting - a string
|
479
|
+
*
|
480
|
+
* Returns:
|
481
|
+
* Does not return anything. May add success / failure reporting in future versions.
|
482
|
+
*
|
483
|
+
* author: Daniel Dotsenko (dotsa@hotmail.com)
|
484
|
+
*/
|
485
|
+
selDOMobj = jQ.find("select[name='groupOp']")[0];
|
486
|
+
var indexmap = {}, l = selDOMobj.options.length, i;
|
487
|
+
for (i=0; i<l; i++) {
|
488
|
+
indexmap[selDOMobj.options[i].value] = i;
|
489
|
+
}
|
490
|
+
selDOMobj.selectedIndex = indexmap[setting];
|
491
|
+
jQuery(selDOMobj).change(function(event){event.stopPropagation();});
|
492
|
+
};
|
493
|
+
|
494
|
+
this.setFilter = function(settings) {
|
495
|
+
/* a "setter" for an arbitrary SearchFilter's filter line.
|
496
|
+
* designed to abstract the DOM manipulations required to infer
|
497
|
+
* a particular filter is a fit to the search box.
|
498
|
+
*
|
499
|
+
* Inputs:
|
500
|
+
* settings - an "object" (dictionary)
|
501
|
+
* index (optional*) (to be implemented in the future) : signed integer index (from top to bottom per DOM) of the filter line to fill.
|
502
|
+
* Negative integers (rooted in -1 and lower) denote position of the line from the bottom.
|
503
|
+
* sfref (optional*) : DOM object referencing individual '.sf' (normally a TR element) to be populated. (optional)
|
504
|
+
* filter (mandatory) : object (dictionary) of form {'field':'field_value','op':'op_value','data':'data value'}
|
505
|
+
*
|
506
|
+
* * It is mandatory to have either index or sfref defined.
|
507
|
+
*
|
508
|
+
* Returns:
|
509
|
+
* Does not return anything. May add success / failure reporting in future versions.
|
510
|
+
*
|
511
|
+
* author: Daniel Dotsenko (dotsa@hotmail.com)
|
512
|
+
*/
|
513
|
+
|
514
|
+
var o = settings['sfref'], filter = settings['filter'];
|
515
|
+
|
516
|
+
// setting up valueindexmap that we will need to manipulate SELECT elements.
|
517
|
+
var fields = [], i, j , l, lj, li,
|
518
|
+
valueindexmap = {};
|
519
|
+
// example of valueindexmap:
|
520
|
+
// {'field1':{'index':0,'ops':{'eq':0,'ne':1}},'fieldX':{'index':1,'ops':{'eq':0,'ne':1},'data':{'true':0,'false':1}}},
|
521
|
+
// if data is undefined it's a INPUT field. If defined, it's SELECT
|
522
|
+
selDOMobj = o.find("select[name='field']")[0];
|
523
|
+
for (i=0, l=selDOMobj.options.length; i<l; i++) {
|
524
|
+
valueindexmap[selDOMobj.options[i].value] = {'index':i,'ops':{}};
|
525
|
+
fields.push(selDOMobj.options[i].value);
|
526
|
+
}
|
527
|
+
for (i=0, li=fields.length; i < li; i++) {
|
528
|
+
selDOMobj = o.find(".ops > select[class='field"+i+"']")[0];
|
529
|
+
if (selDOMobj) {
|
530
|
+
for (j=0, lj=selDOMobj.options.length; j<lj; j++) {
|
531
|
+
valueindexmap[fields[i]]['ops'][selDOMobj.options[j].value] = j;
|
532
|
+
}
|
533
|
+
}
|
534
|
+
selDOMobj = o.find(".data > select[class='field"+i+"']")[0];
|
535
|
+
if (selDOMobj) {
|
536
|
+
valueindexmap[fields[i]]['data'] = {}; // this setting is the flag that 'data' is contained in a SELECT
|
537
|
+
for (j=0, lj=selDOMobj.options.length; j<lj; j++) {
|
538
|
+
valueindexmap[fields[i]]['data'][selDOMobj.options[j].value] = j;
|
539
|
+
}
|
540
|
+
}
|
541
|
+
} // done populating valueindexmap
|
542
|
+
|
543
|
+
// preparsing the index values for SELECT elements.
|
544
|
+
var fieldvalue, fieldindex, opindex, datavalue, dataindex;
|
545
|
+
fieldvalue = filter['field'];
|
546
|
+
if (valueindexmap[fieldvalue]) {
|
547
|
+
fieldindex = valueindexmap[fieldvalue]['index'];
|
548
|
+
}
|
549
|
+
if (fieldindex != null) {
|
550
|
+
opindex = valueindexmap[fieldvalue]['ops'][filter['op']];
|
551
|
+
if(opindex === undefined) {
|
552
|
+
for(i=0,li=options.operators.length; i<li;i++) {
|
553
|
+
if(options.operators[i].op == filter.op ){
|
554
|
+
opindex = i;
|
555
|
+
break;
|
556
|
+
}
|
557
|
+
}
|
558
|
+
}
|
559
|
+
datavalue = filter['data'];
|
560
|
+
if (valueindexmap[fieldvalue]['data'] == null) {
|
561
|
+
dataindex = -1; // 'data' is not SELECT, Making the var 'defined'
|
562
|
+
} else {
|
563
|
+
dataindex = valueindexmap[fieldvalue]['data'][datavalue]; // 'undefined' may come from here.
|
564
|
+
}
|
565
|
+
}
|
566
|
+
// only if values for 'field' and 'op' and 'data' are 'found' in mapping...
|
567
|
+
if (fieldindex != null && opindex != null && dataindex != null) {
|
568
|
+
o.find("select[name='field']")[0].selectedIndex = fieldindex;
|
569
|
+
o.find("select[name='field']").change();
|
570
|
+
o.find("select[name='op']")[0].selectedIndex = opindex;
|
571
|
+
o.find("input.vdata").val(datavalue); // if jquery does not find any INPUT, it does not set any. This means we deal with SELECT
|
572
|
+
o = o.find("select.vdata")[0];
|
573
|
+
if (o) {
|
574
|
+
o.selectedIndex = dataindex;
|
575
|
+
}
|
576
|
+
return true
|
577
|
+
} else {
|
578
|
+
return false
|
579
|
+
}
|
580
|
+
}; // end of this.setFilter fn
|
581
|
+
} // end of if fields != null
|
582
|
+
}
|
583
|
+
return new SearchFilter(this, fields, options);
|
584
|
+
};
|
585
|
+
|
586
|
+
jQuery.fn.searchFilter.version = '1.2.9';
|
587
|
+
|
588
|
+
/* This property contains the default options */
|
589
|
+
jQuery.fn.searchFilter.defaults = {
|
590
|
+
|
591
|
+
/*
|
592
|
+
* PROPERTY
|
593
|
+
* TYPE: boolean
|
594
|
+
* DESCRIPTION: clone a row if it is added from an existing row
|
595
|
+
* when false, any new added rows will be blank.
|
596
|
+
*/
|
597
|
+
clone: true,
|
598
|
+
|
599
|
+
/*
|
600
|
+
* PROPERTY
|
601
|
+
* TYPE: boolean
|
602
|
+
* DESCRIPTION: current version of datepicker uses a data store,
|
603
|
+
* which is incompatible with $().clone(true)
|
604
|
+
*/
|
605
|
+
datepickerFix: true,
|
606
|
+
|
607
|
+
/*
|
608
|
+
* FUNCTION
|
609
|
+
* DESCRIPTION: the function that will be called when the user clicks Reset
|
610
|
+
* INPUT TYPE: JS object if stringResult is false, otherwise is JSON string
|
611
|
+
*/
|
612
|
+
onReset: function(data) { alert("Reset Clicked. Data Returned: " + data) },
|
613
|
+
|
614
|
+
/*
|
615
|
+
* FUNCTION
|
616
|
+
* DESCRIPTION: the function that will be called when the user clicks Search
|
617
|
+
* INPUT TYPE: JS object if stringResult is false, otherwise is JSON string
|
618
|
+
*/
|
619
|
+
onSearch: function(data) { alert("Search Clicked. Data Returned: " + data) },
|
620
|
+
|
621
|
+
/*
|
622
|
+
* FUNCTION
|
623
|
+
* DESCRIPTION: the function that will be called when the user clicks the Closer icon
|
624
|
+
* or the close() function is called
|
625
|
+
* if left null, it simply does a .hide() on the searchFilter
|
626
|
+
* INPUT TYPE: a jQuery object for the searchFilter
|
627
|
+
*/
|
628
|
+
onClose: function(jElem) { jElem.hide(); },
|
629
|
+
|
630
|
+
/*
|
631
|
+
* PROPERTY
|
632
|
+
* TYPE: array of objects, each object has the properties op and text
|
633
|
+
* DESCRIPTION: the selectable operators that are applied between rules
|
634
|
+
* e.g. for {op:"AND", text:"all"}
|
635
|
+
* the search filter box will say: match all rules
|
636
|
+
* the server should interpret this as putting the AND op between each rule:
|
637
|
+
* rule1 AND rule2 AND rule3
|
638
|
+
* text will be the option text, and op will be the option value
|
639
|
+
*/
|
640
|
+
groupOps: [
|
641
|
+
{ op: "AND", text: "all" },
|
642
|
+
{ op: "OR", text: "any" }
|
643
|
+
],
|
644
|
+
|
645
|
+
|
646
|
+
/*
|
647
|
+
* PROPERTY
|
648
|
+
* TYPE: array of objects, each object has the properties op and text
|
649
|
+
* DESCRIPTION: the operators that will appear as drop-down options
|
650
|
+
* text will be the option text, and op will be the option value
|
651
|
+
*/
|
652
|
+
operators: [
|
653
|
+
{ op: "eq", text: "is equal to" },
|
654
|
+
{ op: "ne", text: "is not equal to" },
|
655
|
+
{ op: "lt", text: "is less than" },
|
656
|
+
{ op: "le", text: "is less or equal to" },
|
657
|
+
{ op: "gt", text: "is greater than" },
|
658
|
+
{ op: "ge", text: "is greater or equal to" },
|
659
|
+
{ op: "in", text: "is in" },
|
660
|
+
{ op: "ni", text: "is not in" },
|
661
|
+
{ op: "bw", text: "begins with" },
|
662
|
+
{ op: "bn", text: "does not begin with" },
|
663
|
+
{ op: "ew", text: "ends with" },
|
664
|
+
{ op: "en", text: "does not end with" },
|
665
|
+
{ op: "cn", text: "contains" },
|
666
|
+
{ op: "nc", text: "does not contain" }
|
667
|
+
],
|
668
|
+
|
669
|
+
/*
|
670
|
+
* PROPERTY
|
671
|
+
* TYPE: string
|
672
|
+
* DESCRIPTION: part of the phrase: _match_ ANY/ALL rules
|
673
|
+
*/
|
674
|
+
matchText: "match",
|
675
|
+
|
676
|
+
/*
|
677
|
+
* PROPERTY
|
678
|
+
* TYPE: string
|
679
|
+
* DESCRIPTION: part of the phrase: match ANY/ALL _rules_
|
680
|
+
*/
|
681
|
+
rulesText: "rules",
|
682
|
+
|
683
|
+
/*
|
684
|
+
* PROPERTY
|
685
|
+
* TYPE: string
|
686
|
+
* DESCRIPTION: the text that will be displayed in the reset button
|
687
|
+
*/
|
688
|
+
resetText: "Reset",
|
689
|
+
|
690
|
+
/*
|
691
|
+
* PROPERTY
|
692
|
+
* TYPE: string
|
693
|
+
* DESCRIPTION: the text that will be displayed in the search button
|
694
|
+
*/
|
695
|
+
searchText: "Search",
|
696
|
+
|
697
|
+
/*
|
698
|
+
* PROPERTY
|
699
|
+
* TYPE: boolean
|
700
|
+
* DESCRIPTION: a flag that, when set, will make the onSearch and onReset return strings instead of objects
|
701
|
+
*/
|
702
|
+
stringResult: true,
|
703
|
+
|
704
|
+
/*
|
705
|
+
* PROPERTY
|
706
|
+
* TYPE: string
|
707
|
+
* DESCRIPTION: the title of the searchFilter window
|
708
|
+
*/
|
709
|
+
windowTitle: "Search Rules",
|
710
|
+
/*
|
711
|
+
* PROPERTY
|
712
|
+
* TYPE: object
|
713
|
+
* DESCRIPTION: options to extend the ajax request
|
714
|
+
*/
|
715
|
+
ajaxSelectOptions : {}
|
716
|
+
}; /* end of searchFilter */
|