recordselect_vho 3.0.205 → 3.0.206

Sign up to get free protection for your applications and to get access to all the features.
Binary file
Binary file
Binary file
@@ -0,0 +1,499 @@
1
+ if (typeof(Class) === 'undefined') {
2
+ /* Simple Inheritance
3
+ http://ejohn.org/blog/simple-javascript-inheritance/
4
+ */
5
+ (function(){
6
+ var initializing = false, fnTest = /xyz/.test(function(){xyz;}) ? /\b_super\b/ : /.*/;
7
+
8
+ // The base Class implementation (does nothing)
9
+ this.Class = function(){};
10
+
11
+ // Create a new Class that inherits from this class
12
+ Class.extend = function(prop) {
13
+ var _super = this.prototype;
14
+
15
+ // Instantiate a base class (but only create the instance,
16
+ // don't run the init constructor)
17
+ initializing = true;
18
+ var prototype = new this();
19
+ initializing = false;
20
+
21
+ // Copy the properties over onto the new prototype
22
+ for (var name in prop) {
23
+ // Check if we're overwriting an existing function
24
+ prototype[name] = typeof prop[name] == "function" &&
25
+ typeof _super[name] == "function" && fnTest.test(prop[name]) ?
26
+ (function(name, fn){
27
+ return function() {
28
+ var tmp = this._super;
29
+
30
+ // Add a new ._super() method that is the same method
31
+ // but on the super-class
32
+ this._super = _super[name];
33
+
34
+ // The method only need to be bound temporarily, so we
35
+ // remove it when we're done executing
36
+ var ret = fn.apply(this, arguments);
37
+ this._super = tmp;
38
+
39
+ return ret;
40
+ };
41
+ })(name, prop[name]) :
42
+ prop[name];
43
+ }
44
+
45
+ // The dummy class constructor
46
+ function Class() {
47
+ // All construction is actually done in the init method
48
+ if ( !initializing && this.init )
49
+ this.init.apply(this, arguments);
50
+ }
51
+
52
+ // Populate our constructed prototype object
53
+ Class.prototype = prototype;
54
+
55
+ // Enforce the constructor to be what we expect
56
+ Class.constructor = Class;
57
+
58
+ // And make this class extendable
59
+ Class.extend = arguments.callee;
60
+
61
+ return Class;
62
+ };
63
+ })();
64
+ };
65
+
66
+ /*
67
+ jQuery delayed observer
68
+ (c) 2007 - Maxime Haineault (max@centdessin.com)
69
+
70
+ Special thanks to Stephen Goguen & Tane Piper.
71
+
72
+ Slight modifications by Elliot Winkler
73
+ */
74
+
75
+ if (typeof(jQuery.fn.delayedObserver) === 'undefined') {
76
+ (function() {
77
+ var delayedObserverStack = [];
78
+ var observed;
79
+
80
+ function delayedObserverCallback(stackPos) {
81
+ observed = delayedObserverStack[stackPos];
82
+ if (observed.timer) return;
83
+
84
+ observed.timer = setTimeout(function(){
85
+ observed.timer = null;
86
+ observed.callback(observed.obj.val(), observed.obj);
87
+ }, observed.delay * 1000);
88
+
89
+ observed.oldVal = observed.obj.val();
90
+ }
91
+
92
+ // going by
93
+ // <http://www.cambiaresearch.com/c4/702b8cd1-e5b0-42e6-83ac-25f0306e3e25/Javascript-Char-Codes-Key-Codes.aspx>
94
+ // I think these codes only work when using keyup or keydown
95
+ function isNonPrintableKey(event) {
96
+ var code = event.keyCode;
97
+ return (
98
+ event.metaKey ||
99
+ (code >= 9 || code <= 16) || (code >= 27 && code <= 40) || (code >= 91 && code <= 93) || (code >= 112 && code <= 145)
100
+ );
101
+ }
102
+
103
+ jQuery.fn.extend({
104
+ delayedObserver:function(delay, callback){
105
+ $this = $(this);
106
+
107
+ delayedObserverStack.push({
108
+ obj: $this, timer: null, delay: delay,
109
+ oldVal: $this.val(), callback: callback
110
+ });
111
+
112
+ stackPos = delayedObserverStack.length-1;
113
+
114
+ $this.keyup(function(event) {
115
+ if (isNonPrintableKey(event)) return;
116
+ observed = delayedObserverStack[stackPos];
117
+ if (observed.obj.val() == observed.obj.oldVal) return;
118
+ else delayedObserverCallback(stackPos);
119
+ });
120
+ }
121
+ });
122
+ })();
123
+ };
124
+
125
+ $(document).ready(function() {
126
+ RecordSelect.document_loaded = true;
127
+ $(document).on('ajax:before', 'div.record-select * li.record a', function(event) {
128
+ var link = $(this);
129
+ if (link) {
130
+ if (RecordSelect.notify(link) == false) {
131
+ return false;
132
+ } else {
133
+ link.toggleClass("selected");
134
+ }
135
+ }
136
+ return true;
137
+ });
138
+ });
139
+
140
+ /**
141
+ Form.Element.AfterActivity = function(element, callback, delay) {
142
+ element = $(element);
143
+ if (!delay) delay = 0.25;
144
+ new Form.Element.Observer(element, delay, function(element, value) {
145
+ // TODO: display loading indicator
146
+ if (element.activity_timer) clearTimeout(element.activity_timer);
147
+ element.activity_timer = setTimeout(function() {
148
+ callback(element.value);
149
+ }, delay * 1000 + 50);
150
+ });
151
+ }
152
+ */
153
+
154
+ var RecordSelect = new Object();
155
+ RecordSelect.document_loaded = false;
156
+
157
+ RecordSelect.notify = function(item) {
158
+ var e = item.closest('.record-select-handler');
159
+ var onselect = e.get(0).onselect || e.attr('onselect');
160
+ if (typeof onselect != 'function') onselect = eval(onselect);
161
+ if (onselect)
162
+ {
163
+ try {
164
+ // .unescapeHTML() not implemented so far
165
+ var label = $.trim(item.find('label').first().html());
166
+ if (label.length == 0) {
167
+ label = item.html();
168
+ }
169
+ onselect(item.parent().attr('id').substr(2), label, e);
170
+ } catch(e) {
171
+ alert(e);
172
+ }
173
+ return false;
174
+ }
175
+ else return true;
176
+ }
177
+
178
+ RecordSelect.render_page = function(record_select_id, page) {
179
+ $('#' + record_select_id + ' ol').first().replaceWith(page);
180
+ };
181
+
182
+ RecordSelect.Abstract = Class.extend({
183
+ /**
184
+ * obj - the id or element that will anchor the recordselect to the page
185
+ * url - the url to run the recordselect
186
+ * options - ??? (check concrete classes)
187
+ */
188
+ init: function(obj, url, options) {
189
+ if (typeof(obj) == 'string') obj = '#' + obj;
190
+ this.obj = $(obj);
191
+ this.url = url;
192
+ this.options = options;
193
+ this.container;
194
+
195
+ if (RecordSelect.document_loaded) {
196
+ this.onload();
197
+ } else {
198
+ var _this = this; $(document).ready(function() { _this.onload(); })
199
+ }
200
+ },
201
+
202
+ /**
203
+ * Finish the setup - IE doesn't like doing certain things before the page loads
204
+ * --override--
205
+ */
206
+ onload: function() {},
207
+
208
+ /**
209
+ * the onselect event handler - when someone clicks on a record
210
+ * --override--
211
+ */
212
+ onselect: function(id, value) {
213
+ alert(id + ': ' + value);
214
+ },
215
+
216
+ /**
217
+ * opens the recordselect
218
+ */
219
+ open: function() {
220
+ if (this.is_open()) return;
221
+ var _this = this;
222
+ $.ajax({
223
+ url: this.url,
224
+ //type: "POST",
225
+ //data: options['params'],
226
+ //dataType: options.ajax_data_type,
227
+ success: function(data){
228
+ _this.container.html(data);
229
+ _this.show();
230
+ $(document.body).mousedown(jQuery.proxy(_this, "onbodyclick"));
231
+ }
232
+ });
233
+ },
234
+
235
+ /**
236
+ * positions and reveals the recordselect
237
+ */
238
+ show: function() {
239
+ var offset = this.obj.offset();
240
+ this.container.css('left', offset.left)
241
+ .css('top', (this.obj.height() + offset.top));
242
+
243
+ if (this._use_iframe_mask()) {
244
+ this.container.after('<iframe src="javascript:false;" class="record-select-mask" />');
245
+ var mask = this.container.next('iframe');
246
+ mask.css('left', this.container.css('left'))
247
+ .css('top', this.container.css('top'));
248
+ }
249
+
250
+ this.container.show();
251
+
252
+ if (this._use_iframe_mask()) {
253
+ var dimensions = this.container.children().first();
254
+ mask.css('width', dimensions.css('width'))
255
+ .css('height', dimensions.css('height'));
256
+ }
257
+ },
258
+
259
+ /**
260
+ * closes the recordselect by emptying the container
261
+ */
262
+ close: function() {
263
+ if (this._use_iframe_mask()) {
264
+ this.container.next('iframe').remove();
265
+ }
266
+
267
+ this.container.hide();
268
+ // hopefully by using remove() instead of innerHTML we won't leak memory
269
+ this.container.children().remove();
270
+ },
271
+
272
+ /**
273
+ * returns true/false for whether the recordselect is open
274
+ */
275
+ is_open: function() {
276
+ return (!($.trim(this.container.html()).length == 0))
277
+ },
278
+
279
+ /**
280
+ * when the user clicks outside the dropdown
281
+ */
282
+ onbodyclick: function(event) {
283
+ if (!this.is_open()) return;
284
+ if (this.container.has($(event.target)).length > 0) {
285
+ return;
286
+ } else {
287
+ this.close();
288
+ }
289
+ },
290
+
291
+ /**
292
+ * creates and initializes (and returns) the recordselect container
293
+ */
294
+ create_container: function() {
295
+ var e = $("<div />", {'class': "record-select-container record-select-handler"});
296
+ e.css('display', 'none')
297
+ $(document.body).append(e);
298
+ e.get(0).onselect = $.proxy(this, "onselect")
299
+ return e;
300
+ },
301
+
302
+ onkeyup: function(event) {
303
+ if (!this.is_open()) return;
304
+ this.container.find('.text-input').val(this.obj.val()).trigger(event);
305
+ },
306
+
307
+ /**
308
+ * all the behavior to respond to a text field as a search box
309
+ */
310
+ _respond_to_text_field: function(text_field) {
311
+ // attach the events to start this party
312
+ text_field.focus($.proxy(this, 'open'));
313
+
314
+ // the autosearch event - needs to happen slightly late (keyup is later than keypress)
315
+ text_field.keyup($.proxy(this, 'onkeyup'));
316
+
317
+ // keyboard navigation, if available
318
+ if (this.onkeypress) {
319
+ text_field.keypress($.proxy(this, "onkeypress"));
320
+ }
321
+ },
322
+
323
+ _use_iframe_mask: function() {
324
+ return this.container.insertAdjacentHTML ? true : false;
325
+ }
326
+ });
327
+
328
+
329
+
330
+ /**
331
+ * Adds keyboard navigation to RecordSelect objects
332
+ */
333
+ $.extend(RecordSelect.Abstract.prototype, {
334
+ current: null,
335
+
336
+ /**
337
+ * keyboard navigation - where to intercept the keys is up to the concrete class
338
+ */
339
+ onkeypress: function(ev) {
340
+ var elem;
341
+ switch (ev.keyCode) {
342
+ case 38: //Event.KEY_UP
343
+ if (this.current && this.current.closest('.record-select')) elem = this.current.prev();
344
+ if (!elem) elem = this.container.find('ol li.record').last();
345
+ this.highlight(elem);
346
+ break;
347
+ case 40: //Event.KEY_DOWN
348
+ if (this.current && this.current.closest('.record-select')) elem = this.current.next();
349
+ if (!elem) elem = this.container.find('ol li.record').first();
350
+ this.highlight(elem);
351
+ break;
352
+ case 13: // Event.KEY_RETURN
353
+ if (this.current) this.current.find('a').click();
354
+ break;
355
+ case 39: // Event.KEY_RIGHT
356
+ elem = this.container.find('li.pagination.next');
357
+ if (elem) elem.find('a').click();
358
+ break;
359
+ case 37: // Event.KEY_LEFT
360
+ elem = this.container.find('li.pagination.previous');
361
+ if (elem) elem.find('a').click();
362
+ break;
363
+ case 27: // Event.KEY_ESC
364
+ this.close();
365
+ break;
366
+ default:
367
+ return true;
368
+ }
369
+ ev.preventDefault(); // so "enter" doesn't submit the form, among other things(?)
370
+ },
371
+
372
+ /**
373
+ * moves the highlight to a new object
374
+ */
375
+ highlight: function(obj) {
376
+ if (this.current) this.current.removeClass('current');
377
+ this.current = $(obj);
378
+ obj.addClass('current');
379
+ }
380
+ });
381
+
382
+ /**
383
+ * Used by link_to_record_select
384
+ * The options hash should contain a onselect: key, with a javascript function as value
385
+ */
386
+ RecordSelect.Dialog = RecordSelect.Abstract.extend({
387
+ onload: function() {
388
+ this.container = this.create_container();
389
+ this.obj.click($.proxy(this, "toggle"));
390
+ if (this.onkeypress) this.obj.keypress($.proxy(this, 'onkeypress'));
391
+ },
392
+
393
+ onselect: function(id, value) {
394
+ if (this.options.onselect(id, value) != false) this.close();
395
+ },
396
+
397
+ toggle: function() {
398
+ if (this.is_open()) this.close();
399
+ else this.open();
400
+ }
401
+ });
402
+
403
+ /**
404
+ * Used by record_select_field helper
405
+ * The options hash may contain id: and label: keys, designating the current value
406
+ * The options hash may also include an onchange: key, where the value is a javascript function (or eval-able string) for an callback routine.
407
+ */
408
+ RecordSelect.Single = RecordSelect.Abstract.extend({
409
+ onload: function() {
410
+ // initialize the container
411
+ this.container = this.create_container();
412
+ this.container.addClass('record-select-autocomplete');
413
+
414
+ // create the hidden input
415
+ this.obj.after('<input type="hidden" name="" value="" />');
416
+ this.hidden_input = this.obj.next();
417
+
418
+ // transfer the input name from the text input to the hidden input
419
+ this.hidden_input.attr('name', this.obj.attr('name'));
420
+ this.obj.attr('name', '');
421
+
422
+ // initialize the values
423
+ this.set(this.options.id, this.options.label);
424
+
425
+ this._respond_to_text_field(this.obj);
426
+ if (this.obj.focused) this.open(); // if it was focused before we could attach observers
427
+ },
428
+
429
+ close: function() {
430
+ // if they close the dialog with the text field empty, then delete the id value
431
+ if (this.obj.val() == '') this.set('', '');
432
+
433
+ RecordSelect.Abstract.prototype.close.call(this);
434
+ },
435
+
436
+ onselect: function(id, value) {
437
+ if (this.options.onchange) this.options.onchange(id, value);
438
+ this.set(id, value);
439
+ this.close();
440
+ },
441
+
442
+ /**
443
+ * sets the id/label
444
+ */
445
+ set: function(id, label) {
446
+ // unescaped html missing for label
447
+ this.obj.val(label);
448
+ this.hidden_input.val(id);
449
+ }
450
+ });
451
+
452
+ /**
453
+ * Used by record_multi_select_field helper.
454
+ * Options:
455
+ * list - the id (or object) of the <ul> to contain the <li>s of selected entries
456
+ * current - an array of id:/label: keys designating the currently selected entries
457
+ */
458
+ RecordSelect.Multiple = RecordSelect.Abstract.extend({
459
+ onload: function() {
460
+ // initialize the container
461
+ this.container = this.create_container();
462
+ this.container.addClass('record-select-autocomplete');
463
+
464
+ // decide where the <li> entries should be placed
465
+ if (this.options.list) this.list_container = $(this.options.list);
466
+ else this.list_container = this.obj.next('ul');
467
+
468
+ // take the input name from the text input, and store it for this.add()
469
+ this.input_name = this.obj.attr('name');
470
+ this.obj.attr('name', '');
471
+
472
+ // initialize the list
473
+ for(var i = 0, length = this.options.current.length; i < length; i++) {
474
+ this.add(this.options.current[i].id, this.options.current[i].label);
475
+ }
476
+
477
+ this._respond_to_text_field(this.obj);
478
+ if (this.obj.focused) this.open(); // if it was focused before we could attach observers
479
+ },
480
+
481
+ onselect: function(id, value) {
482
+ this.add(id, value);
483
+ },
484
+
485
+ /**
486
+ * Adds a record to the selected list
487
+ */
488
+ add: function(id, label) {
489
+ // return silently if this value has already been selected
490
+ if (this.list_container.has('input[value=' + id + ']').length > 0) return;
491
+
492
+ var entry = '<li>'
493
+ + '<a href="#" onclick="$(this).parent().remove(); return false;" class="remove">remove</a>'
494
+ + '<input type="hidden" name="' + this.input_name + '" value="' + id + '" />'
495
+ + '<label>' + label + '</label>'
496
+ + '</li>';
497
+ this.list_container.prepend(entry)
498
+ }
499
+ });
@@ -0,0 +1,369 @@
1
+ document.observe("dom:loaded", function() {
2
+ RecordSelect.document_loaded = true;
3
+ document.on('ajax:before', 'div.record-select * li.record a', function(event) {
4
+ var link = event.findElement();
5
+ if (link) {
6
+ if (RecordSelect.notify(link) == false) {
7
+ event.stop();
8
+ } else {
9
+ link.toggleClassName("selected");
10
+ }
11
+ }
12
+ return true;
13
+ });
14
+ });
15
+
16
+ Form.Element.AfterActivity = function(element, callback, delay) {
17
+ element = $(element);
18
+ if (!delay) delay = 0.25;
19
+ new Form.Element.Observer(element, delay, function(element, value) {
20
+ // TODO: display loading indicator
21
+ if (element.activity_timer) clearTimeout(element.activity_timer);
22
+ element.activity_timer = setTimeout(function() {
23
+ callback(element.value);
24
+ }, delay * 1000 + 50);
25
+ });
26
+ }
27
+
28
+ var RecordSelect = new Object();
29
+ RecordSelect.document_loaded = false;
30
+
31
+ RecordSelect.notify = function(item) {
32
+ var e = Element.up(item, '.record-select-handler');
33
+ var onselect = e.onselect || e.getAttribute('onselect');
34
+ if (typeof onselect != 'function') onselect = eval(onselect);
35
+ if (onselect)
36
+ {
37
+ try {
38
+ onselect(item.parentNode.id.substr(2), (item.down('label') || item).innerHTML.unescapeHTML(), e);
39
+ } catch(e) {
40
+ alert(e);
41
+ }
42
+ return false;
43
+ }
44
+ else return true;
45
+ };
46
+
47
+ RecordSelect.render_page = function(record_select_id, page) {
48
+ var page_element = $$('#' + record_select_id + ' ol')[0];
49
+ if (page_element) Element.replace(page_element, page);
50
+ };
51
+
52
+ RecordSelect.Abstract = Class.create();
53
+ Object.extend(RecordSelect.Abstract.prototype, {
54
+ /**
55
+ * obj - the id or element that will anchor the recordselect to the page
56
+ * url - the url to run the recordselect
57
+ * options - ??? (check concrete classes)
58
+ */
59
+ initialize: function(obj, url, options) {
60
+ this.obj = $(obj);
61
+ this.url = url;
62
+ this.options = options;
63
+ this.container;
64
+
65
+ if (RecordSelect.document_loaded) this.onload();
66
+ else Event.observe(window, 'load', this.onload.bind(this));
67
+ },
68
+
69
+ /**
70
+ * Finish the setup - IE doesn't like doing certain things before the page loads
71
+ * --override--
72
+ */
73
+ onload: function() {},
74
+
75
+ /**
76
+ * the onselect event handler - when someone clicks on a record
77
+ * --override--
78
+ */
79
+ onselect: function(id, value) {
80
+ alert(id + ': ' + value);
81
+ },
82
+
83
+ /**
84
+ * opens the recordselect
85
+ */
86
+ open: function() {
87
+ if (this.is_open()) return;
88
+
89
+ new Ajax.Updater(this.container, this.url, {
90
+ method: 'get',
91
+ evalScripts: true,
92
+ asynchronous: true,
93
+ onComplete: function() {
94
+ this.show();
95
+ // needs to be mousedown so the event doesn't get canceled by other code (see issue #26)
96
+ Element.observe(document.body, 'mousedown', this.onbodyclick.bindAsEventListener(this));
97
+ }.bind(this)
98
+ });
99
+ },
100
+
101
+ /**
102
+ * positions and reveals the recordselect
103
+ */
104
+ show: function() {
105
+ var offset = Position.cumulativeOffset(this.obj);
106
+ this.container.style.left = offset[0] + 'px';
107
+ this.container.style.top = (Element.getHeight(this.obj) + offset[1]) + 'px';
108
+
109
+ if (this._use_iframe_mask()) {
110
+ this.container.insertAdjacentHTML('afterEnd', '<iframe src="javascript:false;" class="record-select-mask" />');
111
+ var mask = this.container.next('iframe');
112
+ mask.style.left = this.container.style.left;
113
+ mask.style.top = this.container.style.top;
114
+ }
115
+
116
+ this.container.show();
117
+
118
+ if (this._use_iframe_mask()) {
119
+ var dimensions = this.container.immediateDescendants().first().getDimensions();
120
+ mask.style.width = dimensions.width + 'px';
121
+ mask.style.height = dimensions.height + 'px';
122
+ }
123
+ },
124
+
125
+ /**
126
+ * closes the recordselect by emptying the container
127
+ */
128
+ close: function() {
129
+ if (this._use_iframe_mask()) {
130
+ this.container.next('iframe').remove();
131
+ }
132
+
133
+ this.container.hide();
134
+ // hopefully by using remove() instead of innerHTML we won't leak memory
135
+ this.container.immediateDescendants().invoke('remove');
136
+ },
137
+
138
+ /**
139
+ * returns true/false for whether the recordselect is open
140
+ */
141
+ is_open: function() {
142
+ return (!this.container.innerHTML.blank())
143
+ },
144
+
145
+ /**
146
+ * when the user clicks outside the dropdown
147
+ */
148
+ onbodyclick: function(ev) {
149
+ if (!this.is_open()) return;
150
+ var elem = $(Event.element(ev));
151
+ var ancestors = elem.ancestors();
152
+ ancestors.push(elem);
153
+ if (ancestors.include(this.container) || ancestors.include(this.obj)) return;
154
+ this.close();
155
+ },
156
+
157
+ /**
158
+ * creates and initializes (and returns) the recordselect container
159
+ */
160
+ create_container: function() {
161
+ new Insertion.Bottom(document.body, '<div class="record-select-container record-select-handler"></div>');
162
+ e = document.body.childNodes[document.body.childNodes.length - 1];
163
+ e.onselect = this.onselect.bind(this);
164
+ e.style.display = 'none';
165
+
166
+ return $(e);
167
+ },
168
+
169
+ /**
170
+ * all the behavior to respond to a text field as a search box
171
+ */
172
+ _respond_to_text_field: function(text_field) {
173
+ // attach the events to start this party
174
+ text_field.observe('focus', this.open.bind(this));
175
+
176
+ // the autosearch event - needs to happen slightly late (keyup is later than keypress)
177
+ text_field.observe('keyup', function() {
178
+ if (!this.is_open()) return;
179
+ this.container.down('.text-input').value = text_field.value;
180
+ }.bind(this));
181
+
182
+ // keyboard navigation, if available
183
+ if (this.onkeypress) {
184
+ text_field.observe('keypress', this.onkeypress.bind(this));
185
+ }
186
+ },
187
+
188
+ _use_iframe_mask: function() {
189
+ return this.container.insertAdjacentHTML ? true : false;
190
+ }
191
+ });
192
+
193
+ /**
194
+ * Adds keyboard navigation to RecordSelect objects
195
+ */
196
+ Object.extend(RecordSelect.Abstract.prototype, {
197
+ current: null,
198
+
199
+ /**
200
+ * keyboard navigation - where to intercept the keys is up to the concrete class
201
+ */
202
+ onkeypress: function(ev) {
203
+ var elem;
204
+ switch (ev.keyCode) {
205
+ case Event.KEY_UP:
206
+ if (this.current && this.current.up('.record-select')) elem = this.current.previous();
207
+ if (!elem) elem = this.container.getElementsBySelector('ol li.record').last();
208
+ this.highlight(elem);
209
+ break;
210
+ case Event.KEY_DOWN:
211
+ if (this.current && this.current.up('.record-select')) elem = this.current.next();
212
+ if (!elem) elem = this.container.getElementsBySelector('ol li.record').first();
213
+ this.highlight(elem);
214
+ break;
215
+ case Event.KEY_SPACE:
216
+ case Event.KEY_RETURN:
217
+ if (this.current) this.current.down('a').onclick();
218
+ break;
219
+ case Event.KEY_RIGHT:
220
+ elem = this.container.down('li.pagination.next');
221
+ if (elem) elem.down('a').onclick();
222
+ break;
223
+ case Event.KEY_LEFT:
224
+ elem = this.container.down('li.pagination.previous');
225
+ if (elem) elem.down('a').onclick();
226
+ break;
227
+ case Event.KEY_ESC:
228
+ this.close();
229
+ break;
230
+ default:
231
+ return;
232
+ }
233
+ Event.stop(ev); // so "enter" doesn't submit the form, among other things(?)
234
+ },
235
+
236
+ /**
237
+ * moves the highlight to a new object
238
+ */
239
+ highlight: function(obj) {
240
+ if (this.current) this.current.removeClassName('current');
241
+ this.current = $(obj);
242
+ obj.addClassName('current');
243
+ }
244
+ });
245
+
246
+ /**
247
+ * Used by link_to_record_select
248
+ * The options hash should contain a onselect: key, with a javascript function as value
249
+ */
250
+ RecordSelect.Dialog = Class.create();
251
+ RecordSelect.Dialog.prototype = Object.extend(new RecordSelect.Abstract(), {
252
+ onload: function() {
253
+ this.container = this.create_container();
254
+ this.obj.observe('click', this.toggle.bind(this));
255
+
256
+ if (this.onkeypress) this.obj.observe('keypress', this.onkeypress.bind(this));
257
+ },
258
+
259
+ onselect: function(id, value) {
260
+ if (this.options.onselect(id, value) != false) this.close();
261
+ },
262
+
263
+ toggle: function() {
264
+ if (this.is_open()) this.close();
265
+ else this.open();
266
+ }
267
+ });
268
+
269
+ /**
270
+ * Used by record_select_field helper
271
+ * The options hash may contain id: and label: keys, designating the current value
272
+ * The options hash may also include an onchange: key, where the value is a javascript function (or eval-able string) for an callback routine.
273
+ */
274
+ RecordSelect.Single = Class.create();
275
+ RecordSelect.Single.prototype = Object.extend(new RecordSelect.Abstract(), {
276
+ onload: function() {
277
+ // initialize the container
278
+ this.container = this.create_container();
279
+ this.container.addClassName('record-select-autocomplete');
280
+
281
+ // create the hidden input
282
+ new Insertion.After(this.obj, '<input type="hidden" name="" value="" />')
283
+ this.hidden_input = this.obj.next();
284
+
285
+ // transfer the input name from the text input to the hidden input
286
+ this.hidden_input.name = this.obj.name;
287
+ this.obj.name = '';
288
+
289
+ // initialize the values
290
+ this.set(this.options.id, this.options.label);
291
+
292
+ this._respond_to_text_field(this.obj);
293
+ if (this.obj.focused) this.open(); // if it was focused before we could attach observers
294
+ },
295
+
296
+ close: function() {
297
+ // if they close the dialog with the text field empty, then delete the id value
298
+ if (this.obj.value == '') this.set('', '');
299
+
300
+ RecordSelect.Abstract.prototype.close.call(this);
301
+ },
302
+
303
+ onselect: function(id, value) {
304
+ if (this.options.onchange) this.options.onchange(id, value);
305
+ this.set(id, value);
306
+ this.close();
307
+ },
308
+
309
+ /**
310
+ * sets the id/label
311
+ */
312
+ set: function(id, label) {
313
+ this.obj.value = label.unescapeHTML();
314
+ this.hidden_input.value = id;
315
+ }
316
+ });
317
+
318
+ /**
319
+ * Used by record_multi_select_field helper.
320
+ * Options:
321
+ * list - the id (or object) of the <ul> to contain the <li>s of selected entries
322
+ * current - an array of id:/label: keys designating the currently selected entries
323
+ */
324
+ RecordSelect.Multiple = Class.create();
325
+ RecordSelect.Multiple.prototype = Object.extend(new RecordSelect.Abstract(), {
326
+ onload: function() {
327
+ // initialize the container
328
+ this.container = this.create_container();
329
+ this.container.addClassName('record-select-autocomplete');
330
+
331
+ // decide where the <li> entries should be placed
332
+ if (this.options.list) this.list_container = $(this.options.list);
333
+ else this.list_container = this.obj.next('ul');
334
+
335
+ // take the input name from the text input, and store it for this.add()
336
+ this.input_name = this.obj.name;
337
+ this.obj.name = '';
338
+
339
+ // initialize the list
340
+ $A(this.options.current).each(function(c) {
341
+ this.add(c.id, c.label);
342
+ }.bind(this));
343
+
344
+ this._respond_to_text_field(this.obj);
345
+ if (this.obj.focused) this.open(); // if it was focused before we could attach observers
346
+ },
347
+
348
+ onselect: function(id, value) {
349
+ this.add(id, value);
350
+ },
351
+
352
+ /**
353
+ * Adds a record to the selected list
354
+ */
355
+ add: function(id, label) {
356
+ // return silently if this value has already been selected
357
+ var already_selected = this.list_container.getElementsBySelector('input').any(function(i) {
358
+ return i.value == id
359
+ });
360
+ if (already_selected) return;
361
+
362
+ var entry = '<li>'
363
+ + '<a href="#" onclick="$(this.parentNode).remove(); return false;" class="remove">remove</a>'
364
+ + '<input type="hidden" name="' + this.input_name + '" value="' + id + '" />'
365
+ + '<label>' + label + '</label>'
366
+ + '</li>';
367
+ new Insertion.Top(this.list_container, entry);
368
+ }
369
+ });
@@ -0,0 +1,133 @@
1
+ .record-select {
2
+ width: 300px;
3
+ border: 1px solid #afd0f5;
4
+ font-family: sans-serif;
5
+ background-color: #fff;
6
+ font-size: 11px;
7
+ }
8
+
9
+ .record-select img {
10
+ border-width: 0px;
11
+ }
12
+
13
+ .record-select form {
14
+ display: inline;
15
+ }
16
+
17
+ .record-select form .text-input {
18
+ width: 294px;
19
+ margin: 2px auto 1px auto;
20
+ display: block;
21
+ border: 1px solid #999;
22
+ }
23
+
24
+ .record-select form input.example {
25
+ color: #999;
26
+ text-align: center;
27
+ }
28
+
29
+ .record-select form .search_submit {
30
+ display: none;
31
+ }
32
+
33
+ .record-select ol,
34
+ .record-select li {
35
+ margin: 0px;
36
+ padding: 0px;
37
+ list-style: none;
38
+ clear: both;
39
+ }
40
+
41
+ .record-select a {
42
+ color: #0066cc;
43
+ text-decoration: none;
44
+ }
45
+
46
+ .record-select ol a {
47
+ display: block;
48
+ zoom: 1;
49
+ background-color: #e6f2ff;
50
+ padding: 2px 4px;
51
+ }
52
+
53
+ .record-select ol .even a {
54
+ background-color: #ffffff;
55
+ }
56
+
57
+ .record-select ol .pagination a {
58
+ background-color: #eee;
59
+ }
60
+
61
+ .record-select ol .previous a {
62
+ border-bottom: 1px solid #afd0f5;
63
+ }
64
+
65
+ .record-select ol .next a {
66
+ border-top: 1px solid #afd0f5;
67
+ }
68
+
69
+ .record-select ol .pagination a img {
70
+ vertical-align: middle;
71
+ }
72
+
73
+ .record-select ol .found {
74
+ text-align: center;
75
+ font-style: italic;
76
+ color: #999;
77
+ padding: 1px 4px;
78
+ border-bottom: 1px solid #afd0f5;
79
+ }
80
+
81
+ .record-select ol .current a,
82
+ .record-select ol a:hover {
83
+ background-color: #ffff88;
84
+ }
85
+
86
+ .record-select ol a.selected {
87
+ background-color: #666;
88
+ color: #fff;
89
+ }
90
+
91
+ .record-select-container {
92
+ position: absolute;
93
+ z-index: 100;
94
+ }
95
+
96
+ iframe.record-select-mask {
97
+ /* to mask windowed elements in IE6 */
98
+ position: absolute;
99
+ z-index: 99;
100
+ filter: progid:DXImageTransform.Microsoft.Alpha(style=0,opacity=0);
101
+ }
102
+
103
+ .record-select-autocomplete form .text-input {
104
+ display: none;
105
+ }
106
+
107
+ .record-select-list {
108
+ padding: 0px;
109
+ margin: 0px;
110
+ list-style: none;
111
+ }
112
+
113
+ .record-select-list li {
114
+ overflow: auto;
115
+ zoom: 1;
116
+ margin-left: 10px;
117
+ font-size: 80%;
118
+ }
119
+
120
+ .record-select-list label {
121
+ float: left;
122
+ }
123
+
124
+ .record-select-list a.remove {
125
+ display: block;
126
+ width: 0px;
127
+ height: 16px;
128
+ padding-left: 16px;
129
+ background: url('../../images/record_select/cross.gif') no-repeat 0 0;
130
+ overflow: hidden;
131
+ float: left;
132
+ margin-right: 5px;
133
+ }
@@ -2,7 +2,7 @@ module RecordSelect
2
2
  module Version
3
3
  MAJOR = 3
4
4
  MINOR = 0
5
- PATCH = 205
5
+ PATCH = 206
6
6
 
7
7
  STRING = [MAJOR, MINOR, PATCH].compact.join('.')
8
8
  end
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: recordselect_vho
3
3
  version: !ruby/object:Gem::Version
4
- hash: 413
4
+ hash: 411
5
5
  prerelease:
6
6
  segments:
7
7
  - 3
8
8
  - 0
9
- - 205
10
- version: 3.0.205
9
+ - 206
10
+ version: 3.0.206
11
11
  platform: ruby
12
12
  authors:
13
13
  - Sergio Cambra
@@ -105,6 +105,12 @@ files:
105
105
  - lib/record_select/form_builder.rb
106
106
  - lib/record_select_assets.rb
107
107
  - lib/record_select.rb
108
+ - assets/javascripts/prototype/record_select.js
109
+ - assets/javascripts/jquery/record_select.js
110
+ - assets/stylesheets/record_select.css
111
+ - assets/images/next.gif
112
+ - assets/images/previous.gif
113
+ - assets/images/cross.gif
108
114
  - MIT-LICENSE
109
115
  - CHANGELOG
110
116
  - README