/[webpac2]/Webpacus/root/js/controls.js
This is repository of my old source code which isn't updated any more. Go to git.rot13.org for current projects!
ViewVC logotype

Contents of /Webpacus/root/js/controls.js

Parent Directory Parent Directory | Revision Log Revision Log


Revision 203 - (show annotations)
Fri Dec 2 23:01:25 2005 UTC (18 years, 5 months ago) by dpavlin
File MIME type: text/cpp
File size: 25574 byte(s)
 r11427@llin:  dpavlin | 2005-12-02 23:58:03 +0100
 huge update: new upstream HTML::Prototype and handling of Ajax.Autocompleter mode: enter
 now *ALWAYS* trigger search, while accepting suggestions is made using tab.
 Also, list doesn't wrap. [0.12]
 

1 // Copyright (c) 2005 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
2 // (c) 2005 Ivan Krstic (http://blogs.law.harvard.edu/ivan)
3 // (c) 2005 Jon Tirsen (http://www.tirsen.com)
4 // Contributors:
5 // Richard Livsey
6 // Rahul Bhargava
7 // Rob Wills
8 //
9 // See scriptaculous.js for full license.
10
11 // Autocompleter.Base handles all the autocompletion functionality
12 // that's independent of the data source for autocompletion. This
13 // includes drawing the autocompletion menu, observing keyboard
14 // and mouse events, and similar.
15 //
16 // Specific autocompleters need to provide, at the very least,
17 // a getUpdatedChoices function that will be invoked every time
18 // the text inside the monitored textbox changes. This method
19 // should get the text for which to provide autocompletion by
20 // invoking this.getToken(), NOT by directly accessing
21 // this.element.value. This is to allow incremental tokenized
22 // autocompletion. Specific auto-completion logic (AJAX, etc)
23 // belongs in getUpdatedChoices.
24 //
25 // Tokenized incremental autocompletion is enabled automatically
26 // when an autocompleter is instantiated with the 'tokens' option
27 // in the options parameter, e.g.:
28 // new Ajax.Autocompleter('id','upd', '/url/', { tokens: ',' });
29 // will incrementally autocomplete with a comma as the token.
30 // Additionally, ',' in the above example can be replaced with
31 // a token array, e.g. { tokens: [',', '\n'] } which
32 // enables autocompletion on multiple tokens. This is most
33 // useful when one of the tokens is \n (a newline), as it
34 // allows smart autocompletion after linebreaks.
35
36 var Autocompleter = {}
37 Autocompleter.Base = function() {};
38 Autocompleter.Base.prototype = {
39 baseInitialize: function(element, update, options) {
40 this.element = $(element);
41 this.update = $(update);
42 this.hasFocus = false;
43 this.changed = false;
44 this.active = false;
45 this.index = 0;
46 this.entryCount = 0;
47
48 if (this.setOptions)
49 this.setOptions(options);
50 else
51 this.options = options || {};
52
53 this.options.paramName = this.options.paramName || this.element.name;
54 this.options.tokens = this.options.tokens || [];
55 this.options.frequency = this.options.frequency || 0.4;
56 this.options.minChars = this.options.minChars || 1;
57 this.options.onShow = this.options.onShow ||
58 function(element, update){
59 if(!update.style.position || update.style.position=='absolute') {
60 update.style.position = 'absolute';
61 Position.clone(element, update, {setHeight: false, offsetTop: element.offsetHeight});
62 }
63 Effect.Appear(update,{duration:0.15});
64 };
65 this.options.onHide = this.options.onHide ||
66 function(element, update){ new Effect.Fade(update,{duration:0.15}) };
67
68 if (typeof(this.options.tokens) == 'string')
69 this.options.tokens = new Array(this.options.tokens);
70
71 this.observer = null;
72
73 this.element.setAttribute('autocomplete','off');
74
75 Element.hide(this.update);
76
77 Event.observe(this.element, "blur", this.onBlur.bindAsEventListener(this));
78 Event.observe(this.element, "keypress", this.onKeyPress.bindAsEventListener(this));
79 },
80
81 show: function() {
82 if(Element.getStyle(this.update, 'display')=='none') this.options.onShow(this.element, this.update);
83 if(!this.iefix &&
84 (navigator.appVersion.indexOf('MSIE')>0) &&
85 (navigator.userAgent.indexOf('Opera')<0) &&
86 (Element.getStyle(this.update, 'position')=='absolute')) {
87 new Insertion.After(this.update,
88 '<iframe id="' + this.update.id + '_iefix" '+
89 'style="display:none;position:absolute;filter:progid:DXImageTransform.Microsoft.Alpha(opacity=0);" ' +
90 'src="javascript:false;" frameborder="0" scrolling="no"></iframe>');
91 this.iefix = $(this.update.id+'_iefix');
92 }
93 if(this.iefix) setTimeout(this.fixIEOverlapping.bind(this), 50);
94 },
95
96 fixIEOverlapping: function() {
97 Position.clone(this.update, this.iefix);
98 this.iefix.style.zIndex = 1;
99 this.update.style.zIndex = 2;
100 Element.show(this.iefix);
101 },
102
103 hide: function() {
104 this.stopIndicator();
105 if(Element.getStyle(this.update, 'display')!='none') this.options.onHide(this.element, this.update);
106 if(this.iefix) Element.hide(this.iefix);
107 },
108
109 startIndicator: function() {
110 if(this.options.indicator) Element.show(this.options.indicator);
111 },
112
113 stopIndicator: function() {
114 if(this.options.indicator) Element.hide(this.options.indicator);
115 },
116
117 onKeyPress: function(event) {
118 if(this.active)
119 switch(event.keyCode) {
120 case Event.KEY_TAB:
121 this.selectEntry();
122 Event.stop(event);
123 case Event.KEY_RETURN:
124 if (this.ignoreReturn) {
125 this.hide();
126 this.active = false;
127 return;
128 } else {
129 this.selectEntry();
130 Event.stop(event);
131 }
132 case Event.KEY_ESC:
133 this.hide();
134 this.active = false;
135 Event.stop(event);
136 return;
137 case Event.KEY_LEFT:
138 case Event.KEY_RIGHT:
139 return;
140 case Event.KEY_UP:
141 this.markPrevious();
142 this.render();
143 if(navigator.appVersion.indexOf('AppleWebKit')>0) Event.stop(event);
144 return;
145 case Event.KEY_DOWN:
146 this.markNext();
147 this.render();
148 if(navigator.appVersion.indexOf('AppleWebKit')>0) Event.stop(event);
149 return;
150 }
151 else
152 if(event.keyCode==Event.KEY_TAB || event.keyCode==Event.KEY_RETURN)
153 return;
154
155 this.changed = true;
156 this.hasFocus = true;
157
158 if(this.observer) clearTimeout(this.observer);
159 this.observer =
160 setTimeout(this.onObserverEvent.bind(this), this.options.frequency*1000);
161 },
162
163 onHover: function(event) {
164 var element = Event.findElement(event, 'LI');
165 if(this.index != element.autocompleteIndex)
166 {
167 this.index = element.autocompleteIndex;
168 this.render();
169 }
170 Event.stop(event);
171 },
172
173 onClick: function(event) {
174 var element = Event.findElement(event, 'LI');
175 this.index = element.autocompleteIndex;
176 this.selectEntry();
177 this.hide();
178 },
179
180 onBlur: function(event) {
181 // needed to make click events working
182 setTimeout(this.hide.bind(this), 250);
183 this.hasFocus = false;
184 this.active = false;
185 },
186
187 render: function() {
188 if(this.entryCount > 0) {
189 for (var i = 0; i < this.entryCount; i++)
190 this.index==i ?
191 Element.addClassName(this.getEntry(i),"selected") :
192 Element.removeClassName(this.getEntry(i),"selected");
193
194 if(this.hasFocus) {
195 this.show();
196 this.active = true;
197 }
198 } else {
199 this.active = false;
200 this.hide();
201 }
202 },
203
204 markPrevious: function() {
205 if(this.index > 0) this.index--
206 else this.index = this.entryCount-1;
207 },
208
209 markNext: function() {
210 if(this.index < this.entryCount-1) this.index++
211 else this.index = 0;
212 },
213
214 getEntry: function(index) {
215 return this.update.firstChild.childNodes[index];
216 },
217
218 getCurrentEntry: function() {
219 return this.getEntry(this.index);
220 },
221
222 selectEntry: function() {
223 this.active = false;
224 this.updateElement(this.getCurrentEntry());
225 },
226
227 updateElement: function(selectedElement) {
228 if (this.options.updateElement) {
229 this.options.updateElement(selectedElement);
230 return;
231 }
232
233 var value = Element.collectTextNodesIgnoreClass(selectedElement, 'informal');
234 var lastTokenPos = this.findLastToken();
235 if (lastTokenPos != -1) {
236 var newValue = this.element.value.substr(0, lastTokenPos + 1);
237 var whitespace = this.element.value.substr(lastTokenPos + 1).match(/^\s+/);
238 if (whitespace)
239 newValue += whitespace[0];
240 this.element.value = newValue + value;
241 } else {
242 this.element.value = value;
243 }
244 this.element.focus();
245
246 if (this.options.afterUpdateElement)
247 this.options.afterUpdateElement(this.element, selectedElement);
248 },
249
250 updateChoices: function(choices) {
251 if(!this.changed && this.hasFocus) {
252 this.update.innerHTML = choices;
253 Element.cleanWhitespace(this.update);
254 Element.cleanWhitespace(this.update.firstChild);
255
256 if(this.update.firstChild && this.update.firstChild.childNodes) {
257 this.entryCount =
258 this.update.firstChild.childNodes.length;
259 for (var i = 0; i < this.entryCount; i++) {
260 var entry = this.getEntry(i);
261 entry.autocompleteIndex = i;
262 this.addObservers(entry);
263 }
264 } else {
265 this.entryCount = 0;
266 }
267
268 this.stopIndicator();
269
270 this.index = 0;
271 this.render();
272 }
273 },
274
275 addObservers: function(element) {
276 Event.observe(element, "mouseover", this.onHover.bindAsEventListener(this));
277 Event.observe(element, "click", this.onClick.bindAsEventListener(this));
278 },
279
280 onObserverEvent: function() {
281 this.changed = false;
282 if(this.getToken().length>=this.options.minChars) {
283 this.startIndicator();
284 this.getUpdatedChoices();
285 } else {
286 this.active = false;
287 this.hide();
288 }
289 },
290
291 getToken: function() {
292 var tokenPos = this.findLastToken();
293 if (tokenPos != -1)
294 var ret = this.element.value.substr(tokenPos + 1).replace(/^\s+/,'').replace(/\s+$/,'');
295 else
296 var ret = this.element.value;
297
298 return /\n/.test(ret) ? '' : ret;
299 },
300
301 findLastToken: function() {
302 var lastTokenPos = -1;
303
304 for (var i=0; i<this.options.tokens.length; i++) {
305 var thisTokenPos = this.element.value.lastIndexOf(this.options.tokens[i]);
306 if (thisTokenPos > lastTokenPos)
307 lastTokenPos = thisTokenPos;
308 }
309 return lastTokenPos;
310 }
311 }
312
313 Ajax.Autocompleter = Class.create();
314 Object.extend(Object.extend(Ajax.Autocompleter.prototype, Autocompleter.Base.prototype), {
315 initialize: function(element, update, url, options) {
316 this.baseInitialize(element, update, options);
317 this.options.asynchronous = true;
318 this.options.onComplete = this.onComplete.bind(this);
319 this.options.defaultParams = this.options.parameters || null;
320 this.url = url;
321 },
322
323 getUpdatedChoices: function() {
324 entry = encodeURIComponent(this.options.paramName) + '=' +
325 encodeURIComponent(this.getToken());
326
327 this.options.parameters = this.options.callback ?
328 this.options.callback(this.element, entry) : entry;
329
330 if(this.options.defaultParams)
331 this.options.parameters += '&' + this.options.defaultParams;
332
333 new Ajax.Request(this.url, this.options);
334 },
335
336 onComplete: function(request) {
337 this.updateChoices(request.responseText);
338 }
339
340 });
341
342 // The local array autocompleter. Used when you'd prefer to
343 // inject an array of autocompletion options into the page, rather
344 // than sending out Ajax queries, which can be quite slow sometimes.
345 //
346 // The constructor takes four parameters. The first two are, as usual,
347 // the id of the monitored textbox, and id of the autocompletion menu.
348 // The third is the array you want to autocomplete from, and the fourth
349 // is the options block.
350 //
351 // Extra local autocompletion options:
352 // - choices - How many autocompletion choices to offer
353 //
354 // - partialSearch - If false, the autocompleter will match entered
355 // text only at the beginning of strings in the
356 // autocomplete array. Defaults to true, which will
357 // match text at the beginning of any *word* in the
358 // strings in the autocomplete array. If you want to
359 // search anywhere in the string, additionally set
360 // the option fullSearch to true (default: off).
361 //
362 // - fullSsearch - Search anywhere in autocomplete array strings.
363 //
364 // - partialChars - How many characters to enter before triggering
365 // a partial match (unlike minChars, which defines
366 // how many characters are required to do any match
367 // at all). Defaults to 2.
368 //
369 // - ignoreCase - Whether to ignore case when autocompleting.
370 // Defaults to true.
371 //
372 // It's possible to pass in a custom function as the 'selector'
373 // option, if you prefer to write your own autocompletion logic.
374 // In that case, the other options above will not apply unless
375 // you support them.
376
377 Autocompleter.Local = Class.create();
378 Autocompleter.Local.prototype = Object.extend(new Autocompleter.Base(), {
379 initialize: function(element, update, array, options) {
380 this.baseInitialize(element, update, options);
381 this.options.array = array;
382 },
383
384 getUpdatedChoices: function() {
385 this.updateChoices(this.options.selector(this));
386 },
387
388 setOptions: function(options) {
389 this.options = Object.extend({
390 choices: 10,
391 partialSearch: true,
392 partialChars: 2,
393 ignoreCase: true,
394 fullSearch: false,
395 selector: function(instance) {
396 var ret = []; // Beginning matches
397 var partial = []; // Inside matches
398 var entry = instance.getToken();
399 var count = 0;
400
401 for (var i = 0; i < instance.options.array.length &&
402 ret.length < instance.options.choices ; i++) {
403
404 var elem = instance.options.array[i];
405 var foundPos = instance.options.ignoreCase ?
406 elem.toLowerCase().indexOf(entry.toLowerCase()) :
407 elem.indexOf(entry);
408
409 while (foundPos != -1) {
410 if (foundPos == 0 && elem.length != entry.length) {
411 ret.push("<li><strong>" + elem.substr(0, entry.length) + "</strong>" +
412 elem.substr(entry.length) + "</li>");
413 break;
414 } else if (entry.length >= instance.options.partialChars &&
415 instance.options.partialSearch && foundPos != -1) {
416 if (instance.options.fullSearch || /\s/.test(elem.substr(foundPos-1,1))) {
417 partial.push("<li>" + elem.substr(0, foundPos) + "<strong>" +
418 elem.substr(foundPos, entry.length) + "</strong>" + elem.substr(
419 foundPos + entry.length) + "</li>");
420 break;
421 }
422 }
423
424 foundPos = instance.options.ignoreCase ?
425 elem.toLowerCase().indexOf(entry.toLowerCase(), foundPos + 1) :
426 elem.indexOf(entry, foundPos + 1);
427
428 }
429 }
430 if (partial.length)
431 ret = ret.concat(partial.slice(0, instance.options.choices - ret.length))
432 return "<ul>" + ret.join('') + "</ul>";
433 }
434 }, options || {});
435 }
436 });
437
438 // AJAX in-place editor
439 //
440 // see documentation on http://wiki.script.aculo.us/scriptaculous/show/Ajax.InPlaceEditor
441
442 // Use this if you notice weird scrolling problems on some browsers,
443 // the DOM might be a bit confused when this gets called so do this
444 // waits 1 ms (with setTimeout) until it does the activation
445 Field.scrollFreeActivate = function(field) {
446 setTimeout(function() {
447 Field.activate(field);
448 }, 1);
449 }
450
451 Ajax.InPlaceEditor = Class.create();
452 Ajax.InPlaceEditor.defaultHighlightColor = "#FFFF99";
453 Ajax.InPlaceEditor.prototype = {
454 initialize: function(element, url, options) {
455 this.url = url;
456 this.element = $(element);
457
458 this.options = Object.extend({
459 okText: "ok",
460 cancelText: "cancel",
461 savingText: "Saving...",
462 clickToEditText: "Click to edit",
463 okText: "ok",
464 rows: 1,
465 onComplete: function(transport, element) {
466 new Effect.Highlight(element, {startcolor: this.options.highlightcolor});
467 },
468 onFailure: function(transport) {
469 alert("Error communicating with the server: " + transport.responseText.stripTags());
470 },
471 callback: function(form) {
472 return Form.serialize(form);
473 },
474 handleLineBreaks: true,
475 loadingText: 'Loading...',
476 savingClassName: 'inplaceeditor-saving',
477 loadingClassName: 'inplaceeditor-loading',
478 formClassName: 'inplaceeditor-form',
479 highlightcolor: Ajax.InPlaceEditor.defaultHighlightColor,
480 highlightendcolor: "#FFFFFF",
481 externalControl: null,
482 ajaxOptions: {}
483 }, options || {});
484
485 if(!this.options.formId && this.element.id) {
486 this.options.formId = this.element.id + "-inplaceeditor";
487 if ($(this.options.formId)) {
488 // there's already a form with that name, don't specify an id
489 this.options.formId = null;
490 }
491 }
492
493 if (this.options.externalControl) {
494 this.options.externalControl = $(this.options.externalControl);
495 }
496
497 this.originalBackground = Element.getStyle(this.element, 'background-color');
498 if (!this.originalBackground) {
499 this.originalBackground = "transparent";
500 }
501
502 this.element.title = this.options.clickToEditText;
503
504 this.onclickListener = this.enterEditMode.bindAsEventListener(this);
505 this.mouseoverListener = this.enterHover.bindAsEventListener(this);
506 this.mouseoutListener = this.leaveHover.bindAsEventListener(this);
507 Event.observe(this.element, 'click', this.onclickListener);
508 Event.observe(this.element, 'mouseover', this.mouseoverListener);
509 Event.observe(this.element, 'mouseout', this.mouseoutListener);
510 if (this.options.externalControl) {
511 Event.observe(this.options.externalControl, 'click', this.onclickListener);
512 Event.observe(this.options.externalControl, 'mouseover', this.mouseoverListener);
513 Event.observe(this.options.externalControl, 'mouseout', this.mouseoutListener);
514 }
515 },
516 enterEditMode: function(evt) {
517 if (this.saving) return;
518 if (this.editing) return;
519 this.editing = true;
520 this.onEnterEditMode();
521 if (this.options.externalControl) {
522 Element.hide(this.options.externalControl);
523 }
524 Element.hide(this.element);
525 this.createForm();
526 this.element.parentNode.insertBefore(this.form, this.element);
527 Field.scrollFreeActivate(this.editField);
528 // stop the event to avoid a page refresh in Safari
529 if (evt) {
530 Event.stop(evt);
531 }
532 return false;
533 },
534 createForm: function() {
535 this.form = document.createElement("form");
536 this.form.id = this.options.formId;
537 Element.addClassName(this.form, this.options.formClassName)
538 this.form.onsubmit = this.onSubmit.bind(this);
539
540 this.createEditField();
541
542 if (this.options.textarea) {
543 var br = document.createElement("br");
544 this.form.appendChild(br);
545 }
546
547 okButton = document.createElement("input");
548 okButton.type = "submit";
549 okButton.value = this.options.okText;
550 this.form.appendChild(okButton);
551
552 cancelLink = document.createElement("a");
553 cancelLink.href = "#";
554 cancelLink.appendChild(document.createTextNode(this.options.cancelText));
555 cancelLink.onclick = this.onclickCancel.bind(this);
556 this.form.appendChild(cancelLink);
557 },
558 hasHTMLLineBreaks: function(string) {
559 if (!this.options.handleLineBreaks) return false;
560 return string.match(/<br/i) || string.match(/<p>/i);
561 },
562 convertHTMLLineBreaks: function(string) {
563 return string.replace(/<br>/gi, "\n").replace(/<br\/>/gi, "\n").replace(/<\/p>/gi, "\n").replace(/<p>/gi, "");
564 },
565 createEditField: function() {
566 var text;
567 if(this.options.loadTextURL) {
568 text = this.options.loadingText;
569 } else {
570 text = this.getText();
571 }
572
573 if (this.options.rows == 1 && !this.hasHTMLLineBreaks(text)) {
574 this.options.textarea = false;
575 var textField = document.createElement("input");
576 textField.type = "text";
577 textField.name = "value";
578 textField.value = text;
579 textField.style.backgroundColor = this.options.highlightcolor;
580 var size = this.options.size || this.options.cols || 0;
581 if (size != 0) textField.size = size;
582 this.editField = textField;
583 } else {
584 this.options.textarea = true;
585 var textArea = document.createElement("textarea");
586 textArea.name = "value";
587 textArea.value = this.convertHTMLLineBreaks(text);
588 textArea.rows = this.options.rows;
589 textArea.cols = this.options.cols || 40;
590 this.editField = textArea;
591 }
592
593 if(this.options.loadTextURL) {
594 this.loadExternalText();
595 }
596 this.form.appendChild(this.editField);
597 },
598 getText: function() {
599 return this.element.innerHTML;
600 },
601 loadExternalText: function() {
602 Element.addClassName(this.form, this.options.loadingClassName);
603 this.editField.disabled = true;
604 new Ajax.Request(
605 this.options.loadTextURL,
606 Object.extend({
607 asynchronous: true,
608 onComplete: this.onLoadedExternalText.bind(this)
609 }, this.options.ajaxOptions)
610 );
611 },
612 onLoadedExternalText: function(transport) {
613 Element.removeClassName(this.form, this.options.loadingClassName);
614 this.editField.disabled = false;
615 this.editField.value = transport.responseText.stripTags();
616 },
617 onclickCancel: function() {
618 this.onComplete();
619 this.leaveEditMode();
620 return false;
621 },
622 onFailure: function(transport) {
623 this.options.onFailure(transport);
624 if (this.oldInnerHTML) {
625 this.element.innerHTML = this.oldInnerHTML;
626 this.oldInnerHTML = null;
627 }
628 return false;
629 },
630 onSubmit: function() {
631 // onLoading resets these so we need to save them away for the Ajax call
632 var form = this.form;
633 var value = this.editField.value;
634
635 // do this first, sometimes the ajax call returns before we get a chance to switch on Saving...
636 // which means this will actually switch on Saving... *after* we've left edit mode causing Saving...
637 // to be displayed indefinitely
638 this.onLoading();
639
640 new Ajax.Updater(
641 {
642 success: this.element,
643 // don't update on failure (this could be an option)
644 failure: null
645 },
646 this.url,
647 Object.extend({
648 parameters: this.options.callback(form, value),
649 onComplete: this.onComplete.bind(this),
650 onFailure: this.onFailure.bind(this)
651 }, this.options.ajaxOptions)
652 );
653 // stop the event to avoid a page refresh in Safari
654 if (arguments.length > 1) {
655 Event.stop(arguments[0]);
656 }
657 return false;
658 },
659 onLoading: function() {
660 this.saving = true;
661 this.removeForm();
662 this.leaveHover();
663 this.showSaving();
664 },
665 showSaving: function() {
666 this.oldInnerHTML = this.element.innerHTML;
667 this.element.innerHTML = this.options.savingText;
668 Element.addClassName(this.element, this.options.savingClassName);
669 this.element.style.backgroundColor = this.originalBackground;
670 Element.show(this.element);
671 },
672 removeForm: function() {
673 if(this.form) {
674 if (this.form.parentNode) Element.remove(this.form);
675 this.form = null;
676 }
677 },
678 enterHover: function() {
679 if (this.saving) return;
680 this.element.style.backgroundColor = this.options.highlightcolor;
681 if (this.effect) {
682 this.effect.cancel();
683 }
684 Element.addClassName(this.element, this.options.hoverClassName)
685 },
686 leaveHover: function() {
687 if (this.options.backgroundColor) {
688 this.element.style.backgroundColor = this.oldBackground;
689 }
690 Element.removeClassName(this.element, this.options.hoverClassName)
691 if (this.saving) return;
692 this.effect = new Effect.Highlight(this.element, {
693 startcolor: this.options.highlightcolor,
694 endcolor: this.options.highlightendcolor,
695 restorecolor: this.originalBackground
696 });
697 },
698 leaveEditMode: function() {
699 Element.removeClassName(this.element, this.options.savingClassName);
700 this.removeForm();
701 this.leaveHover();
702 this.element.style.backgroundColor = this.originalBackground;
703 Element.show(this.element);
704 if (this.options.externalControl) {
705 Element.show(this.options.externalControl);
706 }
707 this.editing = false;
708 this.saving = false;
709 this.oldInnerHTML = null;
710 this.onLeaveEditMode();
711 },
712 onComplete: function(transport) {
713 this.leaveEditMode();
714 this.options.onComplete.bind(this)(transport, this.element);
715 },
716 onEnterEditMode: function() {},
717 onLeaveEditMode: function() {},
718 dispose: function() {
719 if (this.oldInnerHTML) {
720 this.element.innerHTML = this.oldInnerHTML;
721 }
722 this.leaveEditMode();
723 Event.stopObserving(this.element, 'click', this.onclickListener);
724 Event.stopObserving(this.element, 'mouseover', this.mouseoverListener);
725 Event.stopObserving(this.element, 'mouseout', this.mouseoutListener);
726 if (this.options.externalControl) {
727 Event.stopObserving(this.options.externalControl, 'click', this.onclickListener);
728 Event.stopObserving(this.options.externalControl, 'mouseover', this.mouseoverListener);
729 Event.stopObserving(this.options.externalControl, 'mouseout', this.mouseoutListener);
730 }
731 }
732 };
733
734 // Delayed observer, like Form.Element.Observer,
735 // but waits for delay after last key input
736 // Ideal for live-search fields
737
738 Form.Element.DelayedObserver = Class.create();
739 Form.Element.DelayedObserver.prototype = {
740 initialize: function(element, delay, callback) {
741 this.delay = delay || 0.5;
742 this.element = $(element);
743 this.callback = callback;
744 this.timer = null;
745 this.lastValue = $F(this.element);
746 Event.observe(this.element,'keyup',this.delayedListener.bindAsEventListener(this));
747 },
748 delayedListener: function(event) {
749 if(this.lastValue == $F(this.element)) return;
750 if(this.timer) clearTimeout(this.timer);
751 this.timer = setTimeout(this.onTimerEvent.bind(this), this.delay * 1000);
752 this.lastValue = $F(this.element);
753 },
754 onTimerEvent: function() {
755 this.timer = null;
756 this.callback(this.element, $F(this.element));
757 }
758 };

Properties

Name Value
svn:mime-type text/cpp

  ViewVC Help
Powered by ViewVC 1.1.26