source: branches/1.2/workflow/js/scriptaculous/controls.js @ 1349

Revision 1349, 28.3 KB checked in by niltonneto, 15 years ago (diff)

Ticket #561 - Inclusão do módulo Workflow faltante nessa versão.

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