PHP Cross Reference of WordPress Subversion HEAD

[ Index ]     [ Classes ]     [ Functions ]     [ Variables ]     [ Constants ]

title

Body

[close]

/wp-includes/js/scriptaculous/ -> controls.js (source)

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


Generated Thu Dec 6 06:47:08 2007 for RedAlt XRefs Cross-referenced by PHPXref 0.6 and RedAlt