zfben_rails_assets 0.0.5 → 0.0.6

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,383 @@
1
+ /**
2
+ * TableDnD plug-in for JQuery, allows you to drag and drop table rows
3
+ * You can set up various options to control how the system will work
4
+ * Copyright (c) Denis Howlett <denish@isocra.com>
5
+ * Licensed like jQuery, see http://docs.jquery.com/License.
6
+ *
7
+ * Configuration options:
8
+ *
9
+ * onDragStyle
10
+ * This is the style that is assigned to the row during drag. There are limitations to the styles that can be
11
+ * associated with a row (such as you can't assign a border--well you can, but it won't be
12
+ * displayed). (So instead consider using onDragClass.) The CSS style to apply is specified as
13
+ * a map (as used in the jQuery css(...) function).
14
+ * onDropStyle
15
+ * This is the style that is assigned to the row when it is dropped. As for onDragStyle, there are limitations
16
+ * to what you can do. Also this replaces the original style, so again consider using onDragClass which
17
+ * is simply added and then removed on drop.
18
+ * onDragClass
19
+ * This class is added for the duration of the drag and then removed when the row is dropped. It is more
20
+ * flexible than using onDragStyle since it can be inherited by the row cells and other content. The default
21
+ * is class is tDnD_whileDrag. So to use the default, simply customise this CSS class in your
22
+ * stylesheet.
23
+ * onDrop
24
+ * Pass a function that will be called when the row is dropped. The function takes 2 parameters: the table
25
+ * and the row that was dropped. You can work out the new order of the rows by using
26
+ * table.rows.
27
+ * onDragStart
28
+ * Pass a function that will be called when the user starts dragging. The function takes 2 parameters: the
29
+ * table and the row which the user has started to drag.
30
+ * onAllowDrop
31
+ * Pass a function that will be called as a row is over another row. If the function returns true, allow
32
+ * dropping on that row, otherwise not. The function takes 2 parameters: the dragged row and the row under
33
+ * the cursor. It returns a boolean: true allows the drop, false doesn't allow it.
34
+ * scrollAmount
35
+ * This is the number of pixels to scroll if the user moves the mouse cursor to the top or bottom of the
36
+ * window. The page should automatically scroll up or down as appropriate (tested in IE6, IE7, Safari, FF2,
37
+ * FF3 beta
38
+ * dragHandle
39
+ * This is the name of a class that you assign to one or more cells in each row that is draggable. If you
40
+ * specify this class, then you are responsible for setting cursor: move in the CSS and only these cells
41
+ * will have the drag behaviour. If you do not specify a dragHandle, then you get the old behaviour where
42
+ * the whole row is draggable.
43
+ *
44
+ * Other ways to control behaviour:
45
+ *
46
+ * Add class="nodrop" to any rows for which you don't want to allow dropping, and class="nodrag" to any rows
47
+ * that you don't want to be draggable.
48
+ *
49
+ * Inside the onDrop method you can also call $.tableDnD.serialize() this returns a string of the form
50
+ * <tableID>[]=<rowID1>&<tableID>[]=<rowID2> so that you can send this back to the server. The table must have
51
+ * an ID as must all the rows.
52
+ *
53
+ * Other methods:
54
+ *
55
+ * $("...").tableDnDUpdate()
56
+ * Will update all the matching tables, that is it will reapply the mousedown method to the rows (or handle cells).
57
+ * This is useful if you have updated the table rows using Ajax and you want to make the table draggable again.
58
+ * The table maintains the original configuration (so you don't have to specify it again).
59
+ *
60
+ * $("...").tableDnDSerialize()
61
+ * Will serialize and return the serialized string as above, but for each of the matching tables--so it can be
62
+ * called from anywhere and isn't dependent on the currentTable being set up correctly before calling
63
+ *
64
+ * Known problems:
65
+ * - Auto-scoll has some problems with IE7 (it scrolls even when it shouldn't), work-around: set scrollAmount to 0
66
+ *
67
+ * Version 0.2: 2008-02-20 First public version
68
+ * Version 0.3: 2008-02-07 Added onDragStart option
69
+ * Made the scroll amount configurable (default is 5 as before)
70
+ * Version 0.4: 2008-03-15 Changed the noDrag/noDrop attributes to nodrag/nodrop classes
71
+ * Added onAllowDrop to control dropping
72
+ * Fixed a bug which meant that you couldn't set the scroll amount in both directions
73
+ * Added serialize method
74
+ * Version 0.5: 2008-05-16 Changed so that if you specify a dragHandle class it doesn't make the whole row
75
+ * draggable
76
+ * Improved the serialize method to use a default (and settable) regular expression.
77
+ * Added tableDnDupate() and tableDnDSerialize() to be called when you are outside the table
78
+ */
79
+ jQuery.tableDnD = {
80
+ /** Keep hold of the current table being dragged */
81
+ currentTable : null,
82
+ /** Keep hold of the current drag object if any */
83
+ dragObject: null,
84
+ /** The current mouse offset */
85
+ mouseOffset: null,
86
+ /** Remember the old value of Y so that we don't do too much processing */
87
+ oldY: 0,
88
+
89
+ /** Actually build the structure */
90
+ build: function(options) {
91
+ // Set up the defaults if any
92
+
93
+ this.each(function() {
94
+ // This is bound to each matching table, set up the defaults and override with user options
95
+ this.tableDnDConfig = jQuery.extend({
96
+ onDragStyle: null,
97
+ onDropStyle: null,
98
+ // Add in the default class for whileDragging
99
+ onDragClass: "tDnD_whileDrag",
100
+ onDrop: null,
101
+ onDragStart: null,
102
+ scrollAmount: 5,
103
+ serializeRegexp: /[^\-]*$/, // The regular expression to use to trim row IDs
104
+ serializeParamName: null, // If you want to specify another parameter name instead of the table ID
105
+ dragHandle: null // If you give the name of a class here, then only Cells with this class will be draggable
106
+ }, options || {});
107
+ // Now make the rows draggable
108
+ jQuery.tableDnD.makeDraggable(this);
109
+ });
110
+
111
+ // Now we need to capture the mouse up and mouse move event
112
+ // We can use bind so that we don't interfere with other event handlers
113
+ jQuery(document)
114
+ .bind('mousemove', jQuery.tableDnD.mousemove)
115
+ .bind('mouseup', jQuery.tableDnD.mouseup);
116
+
117
+ // Don't break the chain
118
+ return this;
119
+ },
120
+
121
+ /** This function makes all the rows on the table draggable apart from those marked as "NoDrag" */
122
+ makeDraggable: function(table) {
123
+ var config = table.tableDnDConfig;
124
+ if (table.tableDnDConfig.dragHandle) {
125
+ // We only need to add the event to the specified cells
126
+ var cells = jQuery("td."+table.tableDnDConfig.dragHandle, table);
127
+ cells.each(function() {
128
+ // The cell is bound to "this"
129
+ jQuery(this).mousedown(function(ev) {
130
+ jQuery.tableDnD.dragObject = this.parentNode;
131
+ jQuery.tableDnD.currentTable = table;
132
+ jQuery.tableDnD.mouseOffset = jQuery.tableDnD.getMouseOffset(this, ev);
133
+ if (config.onDragStart) {
134
+ // Call the onDrop method if there is one
135
+ config.onDragStart(table, this);
136
+ }
137
+ return false;
138
+ });
139
+ })
140
+ } else {
141
+ // For backwards compatibility, we add the event to the whole row
142
+ var rows = jQuery("tr", table); // get all the rows as a wrapped set
143
+ rows.each(function() {
144
+ // Iterate through each row, the row is bound to "this"
145
+ var row = jQuery(this);
146
+ if (! row.hasClass("nodrag")) {
147
+ row.mousedown(function(ev) {
148
+ if (ev.target.tagName == "TD") {
149
+ jQuery.tableDnD.dragObject = this;
150
+ jQuery.tableDnD.currentTable = table;
151
+ jQuery.tableDnD.mouseOffset = jQuery.tableDnD.getMouseOffset(this, ev);
152
+ if (config.onDragStart) {
153
+ // Call the onDrop method if there is one
154
+ config.onDragStart(table, this);
155
+ }
156
+ return false;
157
+ }
158
+ }).css("cursor", "move"); // Store the tableDnD object
159
+ }
160
+ });
161
+ }
162
+ },
163
+
164
+ updateTables: function() {
165
+ this.each(function() {
166
+ // this is now bound to each matching table
167
+ if (this.tableDnDConfig) {
168
+ jQuery.tableDnD.makeDraggable(this);
169
+ }
170
+ })
171
+ },
172
+
173
+ /** Get the mouse coordinates from the event (allowing for browser differences) */
174
+ mouseCoords: function(ev){
175
+ if(ev.pageX || ev.pageY){
176
+ return {x:ev.pageX, y:ev.pageY};
177
+ }
178
+ return {
179
+ x:ev.clientX + document.body.scrollLeft - document.body.clientLeft,
180
+ y:ev.clientY + document.body.scrollTop - document.body.clientTop
181
+ };
182
+ },
183
+
184
+ /** Given a target element and a mouse event, get the mouse offset from that element.
185
+ To do this we need the element's position and the mouse position */
186
+ getMouseOffset: function(target, ev) {
187
+ ev = ev || window.event;
188
+
189
+ var docPos = this.getPosition(target);
190
+ var mousePos = this.mouseCoords(ev);
191
+ return {x:mousePos.x - docPos.x, y:mousePos.y - docPos.y};
192
+ },
193
+
194
+ /** Get the position of an element by going up the DOM tree and adding up all the offsets */
195
+ getPosition: function(e){
196
+ var left = 0;
197
+ var top = 0;
198
+ /** Safari fix -- thanks to Luis Chato for this! */
199
+ if (e.offsetHeight == 0) {
200
+ /** Safari 2 doesn't correctly grab the offsetTop of a table row
201
+ this is detailed here:
202
+ http://jacob.peargrove.com/blog/2006/technical/table-row-offsettop-bug-in-safari/
203
+ the solution is likewise noted there, grab the offset of a table cell in the row - the firstChild.
204
+ note that firefox will return a text node as a first child, so designing a more thorough
205
+ solution may need to take that into account, for now this seems to work in firefox, safari, ie */
206
+ e = e.firstChild; // a table cell
207
+ }
208
+ if (e && e.offsetParent) {
209
+ while (e.offsetParent){
210
+ left += e.offsetLeft;
211
+ top += e.offsetTop;
212
+ e = e.offsetParent;
213
+ }
214
+
215
+ left += e.offsetLeft;
216
+ top += e.offsetTop;
217
+ }
218
+
219
+ return {x:left, y:top};
220
+ },
221
+
222
+ mousemove: function(ev) {
223
+ if (jQuery.tableDnD.dragObject == null) {
224
+ return;
225
+ }
226
+
227
+ var dragObj = jQuery(jQuery.tableDnD.dragObject);
228
+ var config = jQuery.tableDnD.currentTable.tableDnDConfig;
229
+ var mousePos = jQuery.tableDnD.mouseCoords(ev);
230
+ var y = mousePos.y - jQuery.tableDnD.mouseOffset.y;
231
+ //auto scroll the window
232
+ var yOffset = window.pageYOffset;
233
+ if (document.all) {
234
+ // Windows version
235
+ //yOffset=document.body.scrollTop;
236
+ if (typeof document.compatMode != 'undefined' &&
237
+ document.compatMode != 'BackCompat') {
238
+ yOffset = document.documentElement.scrollTop;
239
+ }
240
+ else if (typeof document.body != 'undefined') {
241
+ yOffset=document.body.scrollTop;
242
+ }
243
+
244
+ }
245
+
246
+ if (mousePos.y-yOffset < config.scrollAmount) {
247
+ window.scrollBy(0, -config.scrollAmount);
248
+ } else {
249
+ var windowHeight = window.innerHeight ? window.innerHeight
250
+ : document.documentElement.clientHeight ? document.documentElement.clientHeight : document.body.clientHeight;
251
+ if (windowHeight-(mousePos.y-yOffset) < config.scrollAmount) {
252
+ window.scrollBy(0, config.scrollAmount);
253
+ }
254
+ }
255
+
256
+
257
+ if (y != jQuery.tableDnD.oldY) {
258
+ // work out if we're going up or down...
259
+ var movingDown = y > jQuery.tableDnD.oldY;
260
+ // update the old value
261
+ jQuery.tableDnD.oldY = y;
262
+ // update the style to show we're dragging
263
+ if (config.onDragClass) {
264
+ dragObj.addClass(config.onDragClass);
265
+ } else {
266
+ dragObj.css(config.onDragStyle);
267
+ }
268
+ // If we're over a row then move the dragged row to there so that the user sees the
269
+ // effect dynamically
270
+ var currentRow = jQuery.tableDnD.findDropTargetRow(dragObj, y);
271
+ if (currentRow) {
272
+ // TODO worry about what happens when there are multiple TBODIES
273
+ if (movingDown && jQuery.tableDnD.dragObject != currentRow) {
274
+ jQuery.tableDnD.dragObject.parentNode.insertBefore(jQuery.tableDnD.dragObject, currentRow.nextSibling);
275
+ } else if (! movingDown && jQuery.tableDnD.dragObject != currentRow) {
276
+ jQuery.tableDnD.dragObject.parentNode.insertBefore(jQuery.tableDnD.dragObject, currentRow);
277
+ }
278
+ }
279
+ }
280
+
281
+ return false;
282
+ },
283
+
284
+ /** We're only worried about the y position really, because we can only move rows up and down */
285
+ findDropTargetRow: function(draggedRow, y) {
286
+ var rows = jQuery.tableDnD.currentTable.rows;
287
+ for (var i=0; i<rows.length; i++) {
288
+ var row = rows[i];
289
+ var rowY = this.getPosition(row).y;
290
+ var rowHeight = parseInt(row.offsetHeight)/2;
291
+ if (row.offsetHeight == 0) {
292
+ rowY = this.getPosition(row.firstChild).y;
293
+ rowHeight = parseInt(row.firstChild.offsetHeight)/2;
294
+ }
295
+ // Because we always have to insert before, we need to offset the height a bit
296
+ if ((y > rowY - rowHeight) && (y < (rowY + rowHeight))) {
297
+ // that's the row we're over
298
+ // If it's the same as the current row, ignore it
299
+ if (row == draggedRow) {return null;}
300
+ var config = jQuery.tableDnD.currentTable.tableDnDConfig;
301
+ if (config.onAllowDrop) {
302
+ if (config.onAllowDrop(draggedRow, row)) {
303
+ return row;
304
+ } else {
305
+ return null;
306
+ }
307
+ } else {
308
+ // If a row has nodrop class, then don't allow dropping (inspired by John Tarr and Famic)
309
+ var nodrop = jQuery(row).hasClass("nodrop");
310
+ if (! nodrop) {
311
+ return row;
312
+ } else {
313
+ return null;
314
+ }
315
+ }
316
+ return row;
317
+ }
318
+ }
319
+ return null;
320
+ },
321
+
322
+ mouseup: function(e) {
323
+ if (jQuery.tableDnD.currentTable && jQuery.tableDnD.dragObject) {
324
+ var droppedRow = jQuery.tableDnD.dragObject;
325
+ var config = jQuery.tableDnD.currentTable.tableDnDConfig;
326
+ // If we have a dragObject, then we need to release it,
327
+ // The row will already have been moved to the right place so we just reset stuff
328
+ if (config.onDragClass) {
329
+ jQuery(droppedRow).removeClass(config.onDragClass);
330
+ } else {
331
+ jQuery(droppedRow).css(config.onDropStyle);
332
+ }
333
+ jQuery.tableDnD.dragObject = null;
334
+ if (config.onDrop) {
335
+ // Call the onDrop method if there is one
336
+ config.onDrop(jQuery.tableDnD.currentTable, droppedRow);
337
+ }
338
+ jQuery.tableDnD.currentTable = null; // let go of the table too
339
+ }
340
+ },
341
+
342
+ serialize: function() {
343
+ if (jQuery.tableDnD.currentTable) {
344
+ return jQuery.tableDnD.serializeTable(jQuery.tableDnD.currentTable);
345
+ } else {
346
+ return "Error: No Table id set, you need to set an id on your table and every row";
347
+ }
348
+ },
349
+
350
+ serializeTable: function(table) {
351
+ var result = "";
352
+ var tableId = table.id;
353
+ var rows = table.rows;
354
+ for (var i=0; i<rows.length; i++) {
355
+ if (result.length > 0) result += "&";
356
+ var rowId = rows[i].id;
357
+ if (rowId && rowId && table.tableDnDConfig && table.tableDnDConfig.serializeRegexp) {
358
+ rowId = rowId.match(table.tableDnDConfig.serializeRegexp)[0];
359
+ }
360
+
361
+ result += tableId + '[]=' + rowId;
362
+ }
363
+ return result;
364
+ },
365
+
366
+ serializeTables: function() {
367
+ var result = "";
368
+ this.each(function() {
369
+ // this is now bound to each matching table
370
+ result += jQuery.tableDnD.serializeTable(this);
371
+ });
372
+ return result;
373
+ }
374
+
375
+ }
376
+
377
+ jQuery.fn.extend(
378
+ {
379
+ tableDnD : jQuery.tableDnD.build,
380
+ tableDnDUpdate : jQuery.tableDnD.updateTables,
381
+ tableDnDSerialize: jQuery.tableDnD.serializeTables
382
+ }
383
+ );
@@ -0,0 +1,314 @@
1
+ /*
2
+ * jQuery UI Multiselect
3
+ *
4
+ * Authors:
5
+ * Michael Aufreiter (quasipartikel.at)
6
+ * Yanick Rochon (yanick.rochon[at]gmail[dot]com)
7
+ *
8
+ * Dual licensed under the MIT (MIT-LICENSE.txt)
9
+ * and GPL (GPL-LICENSE.txt) licenses.
10
+ *
11
+ * http://www.quasipartikel.at/multiselect/
12
+ *
13
+ *
14
+ * Depends:
15
+ * ui.core.js
16
+ * ui.sortable.js
17
+ *
18
+ * Optional:
19
+ * localization (http://plugins.jquery.com/project/localisation)
20
+ * scrollTo (http://plugins.jquery.com/project/ScrollTo)
21
+ *
22
+ * Todo:
23
+ * Make batch actions faster
24
+ * Implement dynamic insertion through remote calls
25
+ */
26
+
27
+
28
+ (function($) {
29
+
30
+ $.widget("ui.multiselect", {
31
+ _init: function() {
32
+ this.element.hide();
33
+ this.id = this.element.attr("id");
34
+ this.container = $('<div class="ui-multiselect ui-helper-clearfix ui-widget"></div>').insertAfter(this.element);
35
+ this.count = 0; // number of currently selected options
36
+ this.selectedContainer = $('<div class="selected"></div>').appendTo(this.container);
37
+ this.availableContainer = $('<div class="available"></div>').appendTo(this.container);
38
+ 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);
39
+ 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);
40
+ this.selectedList = $('<ul class="selected connected-list"><li class="ui-helper-hidden-accessible"></li></ul>').bind('selectstart', function(){return false;}).appendTo(this.selectedContainer);
41
+ this.availableList = $('<ul class="available connected-list"><li class="ui-helper-hidden-accessible"></li></ul>').bind('selectstart', function(){return false;}).appendTo(this.availableContainer);
42
+
43
+ var that = this;
44
+
45
+ // set dimensions
46
+ this.container.width(this.element.width()+1);
47
+ this.selectedContainer.width(Math.floor(this.element.width()*this.options.dividerLocation));
48
+ this.availableContainer.width(Math.floor(this.element.width()*(1-this.options.dividerLocation)));
49
+
50
+ // fix list height to match <option> depending on their individual header's heights
51
+ this.selectedList.height(Math.max(this.element.height()-this.selectedActions.height(),1));
52
+ this.availableList.height(Math.max(this.element.height()-this.availableActions.height(),1));
53
+
54
+ if ( !this.options.animated ) {
55
+ this.options.show = 'show';
56
+ this.options.hide = 'hide';
57
+ }
58
+
59
+ // init lists
60
+ this._populateLists(this.element.find('option'));
61
+
62
+ // make selection sortable
63
+ if (this.options.sortable) {
64
+ $("ul.selected").sortable({
65
+ placeholder: 'ui-state-highlight',
66
+ axis: 'y',
67
+ update: function(event, ui) {
68
+ // apply the new sort order to the original selectbox
69
+ that.selectedList.find('li').each(function() {
70
+ if ($(this).data('optionLink'))
71
+ $(this).data('optionLink').remove().appendTo(that.element);
72
+ });
73
+ },
74
+ receive: function(event, ui) {
75
+ ui.item.data('optionLink').attr('selected', true);
76
+ // increment count
77
+ that.count += 1;
78
+ that._updateCount();
79
+ // workaround, because there's no way to reference
80
+ // the new element, see http://dev.jqueryui.com/ticket/4303
81
+ that.selectedList.children('.ui-draggable').each(function() {
82
+ $(this).removeClass('ui-draggable');
83
+ $(this).data('optionLink', ui.item.data('optionLink'));
84
+ $(this).data('idx', ui.item.data('idx'));
85
+ that._applyItemState($(this), true);
86
+ });
87
+
88
+ // workaround according to http://dev.jqueryui.com/ticket/4088
89
+ setTimeout(function() { ui.item.remove(); }, 1);
90
+ }
91
+ });
92
+ }
93
+
94
+ // set up livesearch
95
+ if (this.options.searchable) {
96
+ this._registerSearchEvents(this.availableContainer.find('input.search'));
97
+ } else {
98
+ $('.search').hide();
99
+ }
100
+
101
+ // batch actions
102
+ $(".remove-all").click(function() {
103
+ that._populateLists(that.element.find('option').removeAttr('selected'));
104
+ return false;
105
+ });
106
+ $(".add-all").click(function() {
107
+ that._populateLists(that.element.find('option').attr('selected', 'selected'));
108
+ return false;
109
+ });
110
+ },
111
+ destroy: function() {
112
+ this.element.show();
113
+ this.container.remove();
114
+
115
+ $.widget.prototype.destroy.apply(this, arguments);
116
+ },
117
+ _populateLists: function(options) {
118
+ this.selectedList.children('.ui-element').remove();
119
+ this.availableList.children('.ui-element').remove();
120
+ this.count = 0;
121
+
122
+ var that = this;
123
+ var items = $(options.map(function(i) {
124
+ var item = that._getOptionNode(this).appendTo(this.selected ? that.selectedList : that.availableList).show();
125
+
126
+ if (this.selected) that.count += 1;
127
+ that._applyItemState(item, this.selected);
128
+ item.data('idx', i);
129
+ return item[0];
130
+ }));
131
+
132
+ // update count
133
+ this._updateCount();
134
+ },
135
+ _updateCount: function() {
136
+ this.selectedContainer.find('span.count').text(this.count+" "+$.ui.multiselect.locale.itemsCount);
137
+ },
138
+ _getOptionNode: function(option) {
139
+ option = $(option);
140
+ 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();
141
+ node.data('optionLink', option);
142
+ return node;
143
+ },
144
+ // clones an item with associated data
145
+ // didn't find a smarter away around this
146
+ _cloneWithData: function(clonee) {
147
+ var clone = clonee.clone();
148
+ clone.data('optionLink', clonee.data('optionLink'));
149
+ clone.data('idx', clonee.data('idx'));
150
+ return clone;
151
+ },
152
+ _setSelected: function(item, selected) {
153
+ item.data('optionLink').attr('selected', selected);
154
+
155
+ if (selected) {
156
+ var selectedItem = this._cloneWithData(item);
157
+ item[this.options.hide](this.options.animated, function() { $(this).remove(); });
158
+ selectedItem.appendTo(this.selectedList).hide()[this.options.show](this.options.animated);
159
+
160
+ this._applyItemState(selectedItem, true);
161
+ return selectedItem;
162
+ } else {
163
+
164
+ // look for successor based on initial option index
165
+ var items = this.availableList.find('li'), comparator = this.options.nodeComparator;
166
+ var succ = null, i = item.data('idx'), direction = comparator(item, $(items[i]));
167
+
168
+ // TODO: test needed for dynamic list populating
169
+ if ( direction ) {
170
+ while (i>=0 && i<items.length) {
171
+ direction > 0 ? i++ : i--;
172
+ if ( direction != comparator(item, $(items[i])) ) {
173
+ // going up, go back one item down, otherwise leave as is
174
+ succ = items[direction > 0 ? i : i+1];
175
+ break;
176
+ }
177
+ }
178
+ } else {
179
+ succ = items[i];
180
+ }
181
+
182
+ var availableItem = this._cloneWithData(item);
183
+ succ ? availableItem.insertBefore($(succ)) : availableItem.appendTo(this.availableList);
184
+ item[this.options.hide](this.options.animated, function() { $(this).remove(); });
185
+ availableItem.hide()[this.options.show](this.options.animated);
186
+
187
+ this._applyItemState(availableItem, false);
188
+ return availableItem;
189
+ }
190
+ },
191
+ _applyItemState: function(item, selected) {
192
+ if (selected) {
193
+ if (this.options.sortable)
194
+ item.children('span').addClass('ui-icon-arrowthick-2-n-s').removeClass('ui-helper-hidden').addClass('ui-icon');
195
+ else
196
+ item.children('span').removeClass('ui-icon-arrowthick-2-n-s').addClass('ui-helper-hidden').removeClass('ui-icon');
197
+ item.find('a.action span').addClass('ui-icon-minus').removeClass('ui-icon-plus');
198
+ this._registerRemoveEvents(item.find('a.action'));
199
+
200
+ } else {
201
+ item.children('span').removeClass('ui-icon-arrowthick-2-n-s').addClass('ui-helper-hidden').removeClass('ui-icon');
202
+ item.find('a.action span').addClass('ui-icon-plus').removeClass('ui-icon-minus');
203
+ this._registerAddEvents(item.find('a.action'));
204
+ }
205
+
206
+ this._registerHoverEvents(item);
207
+ },
208
+ // taken from John Resig's liveUpdate script
209
+ _filter: function(list) {
210
+ var input = $(this);
211
+ var rows = list.children('li'),
212
+ cache = rows.map(function(){
213
+
214
+ return $(this).text().toLowerCase();
215
+ });
216
+
217
+ var term = $.trim(input.val().toLowerCase()), scores = [];
218
+
219
+ if (!term) {
220
+ rows.show();
221
+ } else {
222
+ rows.hide();
223
+
224
+ cache.each(function(i) {
225
+ if (this.indexOf(term)>-1) { scores.push(i); }
226
+ });
227
+
228
+ $.each(scores, function() {
229
+ $(rows[this]).show();
230
+ });
231
+ }
232
+ },
233
+ _registerHoverEvents: function(elements) {
234
+ elements.removeClass('ui-state-hover');
235
+ elements.mouseover(function() {
236
+ $(this).addClass('ui-state-hover');
237
+ });
238
+ elements.mouseout(function() {
239
+ $(this).removeClass('ui-state-hover');
240
+ });
241
+ },
242
+ _registerAddEvents: function(elements) {
243
+ var that = this;
244
+ elements.click(function() {
245
+ var item = that._setSelected($(this).parent(), true);
246
+ that.count += 1;
247
+ that._updateCount();
248
+ return false;
249
+ })
250
+ // make draggable
251
+ .each(function() {
252
+ $(this).parent().draggable({
253
+ connectToSortable: 'ul.selected',
254
+ helper: function() {
255
+ var selectedItem = that._cloneWithData($(this)).width($(this).width() - 50);
256
+ selectedItem.width($(this).width());
257
+ return selectedItem;
258
+ },
259
+ appendTo: '.ui-multiselect',
260
+ containment: '.ui-multiselect',
261
+ revert: 'invalid'
262
+ });
263
+ });
264
+ },
265
+ _registerRemoveEvents: function(elements) {
266
+ var that = this;
267
+ elements.click(function() {
268
+ that._setSelected($(this).parent(), false);
269
+ that.count -= 1;
270
+ that._updateCount();
271
+ return false;
272
+ });
273
+ },
274
+ _registerSearchEvents: function(input) {
275
+ var that = this;
276
+
277
+ input.focus(function() {
278
+ $(this).addClass('ui-state-active');
279
+ })
280
+ .blur(function() {
281
+ $(this).removeClass('ui-state-active');
282
+ })
283
+ .keypress(function(e) {
284
+ if (e.keyCode == 13)
285
+ return false;
286
+ })
287
+ .keyup(function() {
288
+ that._filter.apply(this, [that.availableList]);
289
+ });
290
+ }
291
+ });
292
+
293
+ $.extend($.ui.multiselect, {
294
+ defaults: {
295
+ sortable: true,
296
+ searchable: true,
297
+ animated: 'fast',
298
+ show: 'slideDown',
299
+ hide: 'slideUp',
300
+ dividerLocation: 0.6,
301
+ nodeComparator: function(node1,node2) {
302
+ var text1 = node1.text(),
303
+ text2 = node2.text();
304
+ return text1 == text2 ? 0 : (text1 < text2 ? -1 : 1);
305
+ }
306
+ },
307
+ locale: {
308
+ addAll:'Add all',
309
+ removeAll:'Remove all',
310
+ itemsCount:'items selected'
311
+ }
312
+ });
313
+
314
+ })(jQuery);