Development

Changeset 7855

You must first sign up to be able to contribute.

Changeset 7855

Show
Ignore:
Timestamp:
03/13/08 10:12:37 (1 year ago)
Author:
FabianLange
Message:

Updated to Prototype 1.6.0.2, Scriptaculous 1.8.1 (fixes #2072)

- Added submit_image_to_remote method (fixes #2322)
- Improved Javascript type handling for Booleans and some strigns (fixes #2224)
- Allow pass through of InPlaceEditor? options (fixes #1276, #2176, #1753, #1540)
- Kept symfony option names for backwards compatibility
- removed "default options" in input_in_place_editor_tag, as they were not used in sf 1.0
- removed "tokens" option in InPlaceEditor? as it was only a c&p error from autocompleter. There is no such option.
- Updated documentation for InPlaceEditor?
- Replaced deprecated Insertion code in update_element_function

for the new InPlaceEditor? API visit: http://mir.aculo.us/2007/7/17/in-place-editing-the-summer-2007-rewrite/

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • branches/1.1/data/web/sf/prototype/js/builder.js

    r3316 r7855  
    1 // script.aculo.us builder.js v1.7.0, Fri Jan 19 19:16:36 CET 2007 
     1// script.aculo.us builder.js v1.8.1, Thu Jan 03 22:07:12 -0500 2008 
    22 
    3 // Copyright (c) 2005, 2006 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us) 
     3// Copyright (c) 2005-2007 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us) 
    44// 
    55// script.aculo.us is freely distributable under the terms of an MIT-style license. 
     
    4949    if(arguments[1]) 
    5050      if(this._isStringOrNumber(arguments[1]) || 
    51         (arguments[1] instanceof Array)) { 
     51        (arguments[1] instanceof Array) || 
     52        arguments[1].tagName) { 
    5253          this._children(element, arguments[1]); 
    5354        } else { 
     
    6768            if(element.tagName.toUpperCase() != elementName) 
    6869              element = parentElement.getElementsByTagName(elementName)[0]; 
    69            
     70         
    7071        }  
    7172 
     
    8990    for(attribute in attributes) 
    9091      attrs.push((attribute in this.ATTR_MAP ? this.ATTR_MAP[attribute] : attribute) + 
    91           '="' + attributes[attribute].toString().escapeHTML() + '"'); 
     92          '="' + attributes[attribute].toString().escapeHTML().gsub(/"/,'"') + '"'); 
    9293    return attrs.join(" "); 
    9394  }, 
    9495  _children: function(element, children) { 
     96    if(children.tagName) { 
     97      element.appendChild(children); 
     98      return; 
     99    } 
    95100    if(typeof children=='object') { // array can hold nodes and text 
    96101      children.flatten().each( function(e) { 
     
    102107      }); 
    103108    } else 
    104       if(Builder._isStringOrNumber(children))  
    105         element.appendChild(Builder._text(children)); 
     109      if(Builder._isStringOrNumber(children)) 
     110        element.appendChild(Builder._text(children)); 
    106111  }, 
    107112  _isStringOrNumber: function(param) { 
  • branches/1.1/data/web/sf/prototype/js/controls.js

    r3316 r7855  
    1 // script.aculo.us controls.js v1.7.0, Fri Jan 19 19:16:36 CET 2007 
    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) 
     1// script.aculo.us controls.js v1.8.1, Thu Jan 03 22:07:12 -0500 2008 
     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) 
    66// Contributors: 
    77//  Richard Livsey 
     
    4040  throw("controls.js requires including script.aculo.us' effects.js library"); 
    4141 
    42 var Autocompleter = {} 
    43 Autocompleter.Base = function() {}; 
    44 Autocompleter.Base.prototype = { 
     42var Autocompleter = { } 
     43Autocompleter.Base = Class.create({ 
    4544  baseInitialize: function(element, update, options) { 
    46     this.element     = $(element);  
     45    element          = $(element) 
     46    this.element     = element;  
    4747    this.update      = $(update);   
    4848    this.hasFocus    = false;  
     
    5151    this.index       = 0;      
    5252    this.entryCount  = 0; 
     53    this.oldElementValue = this.element.value; 
    5354 
    5455    if(this.setOptions) 
    5556      this.setOptions(options); 
    5657    else 
    57       this.options = options || {}; 
     58      this.options = options || { }; 
    5859 
    5960    this.options.paramName    = this.options.paramName || this.element.name; 
     
    7778    if(typeof(this.options.tokens) == 'string')  
    7879      this.options.tokens = new Array(this.options.tokens); 
     80    // Force carriage returns as token delimiters anyway 
     81    if (!this.options.tokens.include('\n')) 
     82      this.options.tokens.push('\n'); 
    7983 
    8084    this.observer = null; 
     
    8488    Element.hide(this.update); 
    8589 
    86     Event.observe(this.element, "blur", this.onBlur.bindAsEventListener(this)); 
    87     Event.observe(this.element, "keypress", this.onKeyPress.bindAsEventListener(this)); 
     90    Event.observe(this.element, 'blur', this.onBlur.bindAsEventListener(this)); 
     91    Event.observe(this.element, 'keydown', this.onKeyPress.bindAsEventListener(this)); 
    8892  }, 
    8993 
     
    9195    if(Element.getStyle(this.update, 'display')=='none') this.options.onShow(this.element, this.update); 
    9296    if(!this.iefix &&  
    93       (navigator.appVersion.indexOf('MSIE')>0) && 
    94       (navigator.userAgent.indexOf('Opera')<0) && 
     97      (Prototype.Browser.IE) && 
    9598      (Element.getStyle(this.update, 'position')=='absolute')) { 
    9699      new Insertion.After(this.update,  
     
    142145         this.markPrevious(); 
    143146         this.render(); 
    144          if(navigator.appVersion.indexOf('AppleWebKit')>0) Event.stop(event); 
     147         Event.stop(event); 
    145148         return; 
    146149       case Event.KEY_DOWN: 
    147150         this.markNext(); 
    148151         this.render(); 
    149          if(navigator.appVersion.indexOf('AppleWebKit')>0) Event.stop(event); 
     152         Event.stop(event); 
    150153         return; 
    151154      } 
    152155     else  
    153156       if(event.keyCode==Event.KEY_TAB || event.keyCode==Event.KEY_RETURN ||  
    154          (navigator.appVersion.indexOf('AppleWebKit') > 0 && event.keyCode == 0)) return; 
     157         (Prototype.Browser.WebKit > 0 && event.keyCode == 0)) return; 
    155158 
    156159    this.changed = true; 
     
    198201          Element.addClassName(this.getEntry(i),"selected") :  
    199202          Element.removeClassName(this.getEntry(i),"selected"); 
    200          
    201203      if(this.hasFocus) {  
    202204        this.show(); 
     
    241243    var value = ''; 
    242244    if (this.options.select) { 
    243       var nodes = document.getElementsByClassName(this.options.select, selectedElement) || []; 
     245      var nodes = $(selectedElement).select('.' + this.options.select) || []; 
    244246      if(nodes.length>0) value = Element.collectTextNodes(nodes[0], this.options.select); 
    245247    } else 
    246248      value = Element.collectTextNodesIgnoreClass(selectedElement, 'informal'); 
    247249     
    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+/); 
     250    var bounds = this.getTokenBounds(); 
     251    if (bounds[0] != -1) { 
     252      var newValue = this.element.value.substr(0, bounds[0]); 
     253      var whitespace = this.element.value.substr(bounds[0]).match(/^\s+/); 
    252254      if (whitespace) 
    253255        newValue += whitespace[0]; 
    254       this.element.value = newValue + value
     256      this.element.value = newValue + value + this.element.value.substr(bounds[1])
    255257    } else { 
    256258      this.element.value = value; 
    257259    } 
     260    this.oldElementValue = this.element.value; 
    258261    this.element.focus(); 
    259262     
     
    299302  onObserverEvent: function() { 
    300303    this.changed = false;    
     304    this.tokenBounds = null; 
    301305    if(this.getToken().length>=this.options.minChars) { 
    302       this.startIndicator(); 
    303306      this.getUpdatedChoices(); 
    304307    } else { 
     
    306309      this.hide(); 
    307310    } 
     311    this.oldElementValue = this.element.value; 
    308312  }, 
    309313 
    310314  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; 
     315    var bounds = this.getTokenBounds(); 
     316    return this.element.value.substring(bounds[0], bounds[1]).strip(); 
     317  }, 
     318 
     319  getTokenBounds: function() { 
     320    if (null != this.tokenBounds) return this.tokenBounds; 
     321    var value = this.element.value; 
     322    if (value.strip().empty()) return [-1, 0]; 
     323    var diff = arguments.callee.getFirstDifferencePos(value, this.oldElementValue); 
     324    var offset = (diff == this.oldElementValue.length ? 1 : 0); 
     325    var prevTokenPos = -1, nextTokenPos = value.length; 
     326    var tp; 
     327    for (var index = 0, l = this.options.tokens.length; index < l; ++index) { 
     328      tp = value.lastIndexOf(this.options.tokens[index], diff + offset - 1); 
     329      if (tp > prevTokenPos) prevTokenPos = tp; 
     330      tp = value.indexOf(this.options.tokens[index], diff + offset); 
     331      if (-1 != tp && tp < nextTokenPos) nextTokenPos = tp; 
     332    } 
     333    return (this.tokenBounds = [prevTokenPos + 1, nextTokenPos]); 
    329334  } 
    330 
    331  
    332 Ajax.Autocompleter = Class.create(); 
    333 Object.extend(Object.extend(Ajax.Autocompleter.prototype, Autocompleter.Base.prototype), { 
     335}); 
     336 
     337Autocompleter.Base.prototype.getTokenBounds.getFirstDifferencePos = function(newS, oldS) { 
     338  var boundary = Math.min(newS.length, oldS.length); 
     339  for (var index = 0; index < boundary; ++index) 
     340    if (newS[index] != oldS[index]) 
     341      return index; 
     342  return boundary; 
     343}; 
     344 
     345Ajax.Autocompleter = Class.create(Autocompleter.Base, { 
    334346  initialize: function(element, update, url, options) { 
    335347    this.baseInitialize(element, update, options); 
     
    341353 
    342354  getUpdatedChoices: function() { 
    343     entry = encodeURIComponent(this.options.paramName) + '=' +  
     355    this.startIndicator(); 
     356     
     357    var entry = encodeURIComponent(this.options.paramName) + '=' +  
    344358      encodeURIComponent(this.getToken()); 
    345359 
     
    349363    if(this.options.defaultParams)  
    350364      this.options.parameters += '&' + this.options.defaultParams; 
    351  
     365     
    352366    new Ajax.Request(this.url, this.options); 
    353367  }, 
     
    356370    this.updateChoices(request.responseText); 
    357371  } 
    358  
    359372}); 
    360373 
     
    394407// you support them. 
    395408 
    396 Autocompleter.Local = Class.create(); 
    397 Autocompleter.Local.prototype = Object.extend(new Autocompleter.Base(), { 
     409Autocompleter.Local = Class.create(Autocompleter.Base, { 
    398410  initialize: function(element, update, array, options) { 
    399411    this.baseInitialize(element, update, options); 
     
    451463        return "<ul>" + ret.join('') + "</ul>"; 
    452464      } 
    453     }, options || {}); 
     465    }, options || { }); 
    454466  } 
    455467}); 
    456468 
    457 // AJAX in-place editor 
    458 // 
    459 // see documentation on http://wiki.script.aculo.us/scriptaculous/show/Ajax.InPlaceEditor 
     469// AJAX in-place editor and collection editor 
     470// Full rewrite by Christophe Porteneuve <tdd@tddsworld.com> (April 2007). 
    460471 
    461472// Use this if you notice weird scrolling problems on some browsers, 
     
    468479} 
    469480 
    470 Ajax.InPlaceEditor = Class.create(); 
    471 Ajax.InPlaceEditor.defaultHighlightColor = "#FFFF99"; 
    472 Ajax.InPlaceEditor.prototype = { 
     481Ajax.InPlaceEditor = Class.create({ 
    473482  initialize: function(element, url, options) { 
    474483    this.url = url; 
    475     this.element = $(element); 
    476  
    477     this.options = Object.extend({ 
    478       paramName: "value", 
    479       okButton: true, 
    480       okText: "ok", 
    481       cancelLink: true, 
    482       cancelText: "cancel", 
    483       savingText: "Saving...", 
    484       clickToEditText: "Click to edit", 
    485       okText: "ok", 
    486       rows: 1, 
    487       onComplete: function(transport, element) { 
    488         new Effect.Highlight(element, {startcolor: this.options.highlightcolor}); 
    489       }, 
    490       onFailure: function(transport) { 
    491         alert("Error communicating with the server: " + transport.responseText.stripTags()); 
    492       }, 
    493       callback: function(form) { 
    494         return Form.serialize(form); 
    495       }, 
    496       handleLineBreaks: true, 
    497       loadingText: 'Loading...', 
    498       savingClassName: 'inplaceeditor-saving', 
    499       loadingClassName: 'inplaceeditor-loading', 
    500       formClassName: 'inplaceeditor-form', 
    501       highlightcolor: Ajax.InPlaceEditor.defaultHighlightColor, 
    502       highlightendcolor: "#FFFFFF", 
    503       externalControl: null, 
    504       submitOnBlur: false, 
    505       ajaxOptions: {}, 
    506       evalScripts: false 
    507     }, options || {}); 
    508  
    509     if(!this.options.formId && this.element.id) { 
    510       this.options.formId = this.element.id + "-inplaceeditor"; 
    511       if ($(this.options.formId)) { 
    512         // there's already a form with that name, don't specify an id 
    513         this.options.formId = null; 
    514       } 
    515     } 
    516      
    517     if (this.options.externalControl) { 
     484    this.element = element = $(element); 
     485    this.prepareOptions(); 
     486    this._controls = { }; 
     487    arguments.callee.dealWithDeprecatedOptions(options); // DEPRECATION LAYER!!! 
     488    Object.extend(this.options, options || { }); 
     489    if (!this.options.formId && this.element.id) { 
     490      this.options.formId = this.element.id + '-inplaceeditor'; 
     491      if ($(this.options.formId)) 
     492        this.options.formId = ''; 
     493    } 
     494    if (this.options.externalControl) 
    518495      this.options.externalControl = $(this.options.externalControl); 
    519     } 
    520      
    521     this.originalBackground = Element.getStyle(this.element, 'background-color'); 
    522     if (!this.originalBackground) { 
    523       this.originalBackground = "transparent"; 
    524     } 
    525      
     496    if (!this.options.externalControl) 
     497      this.options.externalControlOnly = false; 
     498    this._originalBackground = this.element.getStyle('background-color') || 'transparent'; 
    526499    this.element.title = this.options.clickToEditText; 
    527      
    528     this.onclickListener = this.enterEditMode.bindAsEventListener(this); 
    529     this.mouseoverListener = this.enterHover.bindAsEventListener(this); 
    530     this.mouseoutListener = this.leaveHover.bindAsEventListener(this); 
    531     Event.observe(this.element, 'click', this.onclickListener); 
    532     Event.observe(this.element, 'mouseover', this.mouseoverListener); 
    533     Event.observe(this.element, 'mouseout', this.mouseoutListener); 
    534     if (this.options.externalControl) { 
    535       Event.observe(this.options.externalControl, 'click', this.onclickListener); 
    536       Event.observe(this.options.externalControl, 'mouseover', this.mouseoverListener); 
    537       Event.observe(this.options.externalControl, 'mouseout', this.mouseoutListener); 
    538     } 
    539   }, 
    540   enterEditMode: function(evt) { 
    541     if (this.saving) return; 
    542     if (this.editing) return; 
    543     this.editing = true; 
    544     this.onEnterEditMode(); 
    545     if (this.options.externalControl) { 
    546       Element.hide(this.options.externalControl); 
    547     } 
    548     Element.hide(this.element); 
     500    this._boundCancelHandler = this.handleFormCancellation.bind(this); 
     501    this._boundComplete = (this.options.onComplete || Prototype.emptyFunction).bind(this); 
     502    this._boundFailureHandler = this.handleAJAXFailure.bind(this); 
     503    this._boundSubmitHandler = this.handleFormSubmission.bind(this); 
     504    this._boundWrapperHandler = this.wrapUp.bind(this); 
     505    this.registerListeners(); 
     506  }, 
     507  checkForEscapeOrReturn: function(e) { 
     508    if (!this._editing || e.ctrlKey || e.altKey || e.shiftKey) return; 
     509    if (Event.KEY_ESC == e.keyCode) 
     510      this.handleFormCancellation(e); 
     511    else if (Event.KEY_RETURN == e.keyCode) 
     512      this.handleFormSubmission(e); 
     513  }, 
     514  createControl: function(mode, handler, extraClasses) { 
     515    var control = this.options[mode + 'Control']; 
     516    var text = this.options[mode + 'Text']; 
     517    if ('button' == control) { 
     518      var btn = document.createElement('input'); 
     519      btn.type = 'submit'; 
     520      btn.value = text; 
     521      btn.className = 'editor_' + mode + '_button'; 
     522      if ('cancel' == mode) 
     523        btn.onclick = this._boundCancelHandler; 
     524      this._form.appendChild(btn); 
     525      this._controls[mode] = btn; 
     526    } else if ('link' == control) { 
     527      var link = document.createElement('a'); 
     528      link.href = '#'; 
     529      link.appendChild(document.createTextNode(text)); 
     530      link.onclick = 'cancel' == mode ? this._boundCancelHandler : this._boundSubmitHandler; 
     531      link.className = 'editor_' + mode + '_link'; 
     532      if (extraClasses) 
     533        link.className += ' ' + extraClasses; 
     534      this._form.appendChild(link); 
     535      this._controls[mode] = link; 
     536    } 
     537  }, 
     538  createEditField: function() { 
     539    var text = (this.options.loadTextURL ? this.options.loadingText : this.getText()); 
     540    var fld; 
     541    if (1 >= this.options.rows && !/\r|\n/.test(this.getText())) { 
     542      fld = document.createElement('input'); 
     543      fld.type = 'text'; 
     544      var size = this.options.size || this.options.cols || 0; 
     545      if (0 < size) fld.size = size; 
     546    } else { 
     547      fld = document.createElement('textarea'); 
     548      fld.rows = (1 >= this.options.rows ? this.options.autoRows : this.options.rows); 
     549      fld.cols = this.options.cols || 40; 
     550    } 
     551    fld.name = this.options.paramName; 
     552    fld.value = text; // No HTML breaks conversion anymore 
     553    fld.className = 'editor_field'; 
     554    if (this.options.submitOnBlur) 
     555      fld.onblur = this._boundSubmitHandler; 
     556    this._controls.editor = fld; 
     557    if (this.options.loadTextURL) 
     558      this.loadExternalText(); 
     559    this._form.appendChild(this._controls.editor); 
     560  }, 
     561  createForm: function() { 
     562    var ipe = this; 
     563    function addText(mode, condition) { 
     564      var text = ipe.options['text' + mode + 'Controls']; 
     565      if (!text || condition === false) return; 
     566      ipe._form.appendChild(document.createTextNode(text)); 
     567    }; 
     568    this._form = $(document.createElement('form')); 
     569    this._form.id = this.options.formId; 
     570    this._form.addClassName(this.options.formClassName); 
     571    this._form.onsubmit = this._boundSubmitHandler; 
     572    this.createEditField(); 
     573    if ('textarea' == this._controls.editor.tagName.toLowerCase()) 
     574      this._form.appendChild(document.createElement('br')); 
     575    if (this.options.onFormCustomization) 
     576      this.options.onFormCustomization(this, this._form); 
     577    addText('Before', this.options.okControl || this.options.cancelControl); 
     578    this.createControl('ok', this._boundSubmitHandler); 
     579    addText('Between', this.options.okControl && this.options.cancelControl); 
     580    this.createControl('cancel', this._boundCancelHandler, 'editor_cancel'); 
     581    addText('After', this.options.okControl || this.options.cancelControl); 
     582  }, 
     583  destroy: function() { 
     584    if (this._oldInnerHTML) 
     585      this.element.innerHTML = this._oldInnerHTML; 
     586    this.leaveEditMode(); 
     587    this.unregisterListeners(); 
     588  }, 
     589  enterEditMode: function(e) { 
     590    if (this._saving || this._editing) return; 
     591    this._editing = true; 
     592    this.triggerCallback('onEnterEditMode'); 
     593    if (this.options.externalControl) 
     594      this.options.externalControl.hide(); 
     595    this.element.hide(); 
    549596    this.createForm(); 
    550     this.element.parentNode.insertBefore(this.form, this.element); 
    551     if (!this.options.loadTextURL) Field.scrollFreeActivate(this.editField); 
    552     // stop the event to avoid a page refresh in Safari 
    553     if (evt) { 
    554       Event.stop(evt); 
    555     } 
    556     return false; 
    557   }, 
    558   createForm: function() { 
    559     this.form = document.createElement("form"); 
    560     this.form.id = this.options.formId; 
    561     Element.addClassName(this.form, this.options.formClassName) 
    562     this.form.onsubmit = this.onSubmit.bind(this); 
    563  
    564     this.createEditField(); 
    565  
    566     if (this.options.textarea) { 
    567       var br = document.createElement("br"); 
    568       this.form.appendChild(br); 
    569     } 
    570  
    571     if (this.options.okButton) { 
    572       okButton = document.createElement("input"); 
    573       okButton.type = "submit"; 
    574       okButton.value = this.options.okText; 
    575       okButton.className = 'editor_ok_button'; 
    576       this.form.appendChild(okButton); 
    577     } 
    578  
    579     if (this.options.cancelLink) { 
    580       cancelLink = document.createElement("a"); 
    581       cancelLink.href = "#"; 
    582       cancelLink.appendChild(document.createTextNode(this.options.cancelText)); 
    583       cancelLink.onclick = this.onclickCancel.bind(this); 
    584       cancelLink.className = 'editor_cancel';       
    585       this.form.appendChild(cancelLink); 
    586     } 
    587   }, 
    588   hasHTMLLineBreaks: function(string) { 
    589     if (!this.options.handleLineBreaks) return false; 
    590     return string.match(/<br/i) || string.match(/<p>/i); 
    591   }, 
    592   convertHTMLLineBreaks: function(string) { 
    593     return string.replace(/<br>/gi, "\n").replace(/<br\/>/gi, "\n").replace(/<\/p>/gi, "\n").replace(/<p>/gi, ""); 
    594   }, 
    595   createEditField: function() { 
    596     var text; 
    597     if(this.options.loadTextURL) { 
    598       text = this.options.loadingText; 
    599     } else { 
    600       text = this.getText(); 
    601     } 
    602  
    603     var obj = this; 
    604      
    605     if (this.options.rows == 1 && !this.hasHTMLLineBreaks(text)) { 
    606       this.options.textarea = false; 
    607       var textField = document.createElement("input"); 
    608       textField.obj = this; 
    609       textField.type = "text"; 
    610       textField.name = this.options.paramName; 
    611       textField.value = text; 
    612       textField.style.backgroundColor = this.options.highlightcolor; 
    613       textField.className = 'editor_field'; 
    614       var size = this.options.size || this.options.cols || 0; 
    615       if (size != 0) textField.size = size; 
    616       if (this.options.submitOnBlur) 
    617         textField.onblur = this.onSubmit.bind(this); 
    618       this.editField = textField; 
    619     } else { 
    620       this.options.textarea = true; 
    621       var textArea = document.createElement("textarea"); 
    622       textArea.obj = this; 
    623       textArea.name = this.options.paramName; 
    624       textArea.value = this.convertHTMLLineBreaks(text); 
    625       textArea.rows = this.options.rows; 
    626       textArea.cols = this.options.cols || 40; 
    627       textArea.className = 'editor_field';       
    628       if (this.options.submitOnBlur) 
    629         textArea.onblur = this.onSubmit.bind(this); 
    630       this.editField = textArea; 
    631     } 
    632      
    633     if(this.options.loadTextURL) { 
    634       this.loadExternalText(); 
    635     } 
    636     this.form.appendChild(this.editField); 
     597    this.element.parentNode.insertBefore(this._form, this.element); 
     598    if (!this.options.loadTextURL) 
     599      this.postProcessEditField(); 
     600    if (e) Event.stop(e); 
     601  }, 
     602  enterHover: function(e) { 
     603    if (this.options.hoverClassName) 
     604      this.element.addClassName(this.options.hoverClassName); 
     605    if (this._saving) return; 
     606    this.triggerCallback('onEnterHover'); 
    637607  }, 
    638608  getText: function() { 
    639609    return this.element.innerHTML; 
    640610  }, 
     611  handleAJAXFailure: function(transport) { 
     612    this.triggerCallback('onFailure', transport); 
     613    if (this._oldInnerHTML) { 
     614      this.element.innerHTML = this._oldInnerHTML; 
     615      this._oldInnerHTML = null; 
     616    } 
     617  }, 
     618  handleFormCancellation: function(e) { 
     619    this.wrapUp(); 
     620    if (e) Event.stop(e); 
     621  }, 
     622  handleFormSubmission: function(e) { 
     623    var form = this._form; 
     624    var value = $F(this._controls.editor); 
     625    this.prepareSubmission(); 
     626    var params = this.options.callback(form, value) || ''; 
     627    if (Object.isString(params)) 
     628      params = params.toQueryParams(); 
     629    params.editorId = this.element.id; 
     630    if (this.options.htmlResponse) { 
     631      var options = Object.extend({ evalScripts: true }, this.options.ajaxOptions); 
     632      Object.extend(options, { 
     633        parameters: params, 
     634        onComplete: this._boundWrapperHandler, 
     635        onFailure: this._boundFailureHandler 
     636      }); 
     637      new Ajax.Updater({ success: this.element }, this.url, options); 
     638    } else { 
     639      var options = Object.extend({ method: 'get' }, this.options.ajaxOptions); 
     640      Object.extend(options, { 
     641        parameters: params, 
     642        onComplete: this._boundWrapperHandler, 
     643        onFailure: this._boundFailureHandler 
     644      }); 
     645      new Ajax.Request(this.url, options); 
     646    } 
     647    if (e) Event.stop(e); 
     648  }, 
     649  leaveEditMode: function() { 
     650    this.element.removeClassName(this.options.savingClassName); 
     651    this.removeForm(); 
     652    this.leaveHover(); 
     653    this.element.style.backgroundColor = this._originalBackground; 
     654    this.element.show(); 
     655    if (this.options.externalControl) 
     656      this.options.externalControl.show(); 
     657    this._saving = false; 
     658    this._editing = false; 
     659    this._oldInnerHTML = null; 
     660    this.triggerCallback('onLeaveEditMode'); 
     661  }, 
     662  leaveHover: function(e) { 
     663    if (this.options.hoverClassName) 
     664      this.element.removeClassName(this.options.hoverClassName); 
     665    if (this._saving) return; 
     666    this.triggerCallback('onLeaveHover'); 
     667  }, 
    641668  loadExternalText: function() { 
    642     Element.addClassName(this.form, this.options.loadingClassName); 
    643     this.editField.disabled = true; 
    644     new Ajax.Request( 
    645       this.options.loadTextURL, 
    646       Object.extend({ 
    647         asynchronous: true, 
    648         onComplete: this.onLoadedExternalText.bind(this) 
    649       }, this.options.ajaxOptions) 
    650     ); 
    651   }, 
    652   onLoadedExternalText: function(transport) { 
    653     Element.removeClassName(this.form, this.options.loadingClassName); 
    654     this.editField.disabled = false; 
    655     this.editField.value = transport.responseText.stripTags(); 
    656     Field.scrollFreeActivate(this.editField); 
    657   }, 
    658   onclickCancel: function() { 
    659     this.onComplete(); 
    660     this.leaveEditMode(); 
    661     return false; 
    662   }, 
    663   onFailure: function(transport) { 
    664     this.options.onFailure(transport); 
    665     if (this.oldInnerHTML) { 
    666       this.element.innerHTML = this.oldInnerHTML; 
    667       this.oldInnerHTML = null; 
    668     } 
    669     return false; 
    670   }, 
    671   onSubmit: function() { 
    672     // onLoading resets these so we need to save them away for the Ajax call 
    673     var form = this.form; 
    674     var value = this.editField.value; 
    675      
    676     // do this first, sometimes the ajax call returns before we get a chance to switch on Saving... 
    677     // which means this will actually switch on Saving... *after* we've left edit mode causing Saving... 
    678     // to be displayed indefinitely 
    679     this.onLoading(); 
    680      
    681     if (this.options.evalScripts) { 
    682       new Ajax.Request( 
    683         this.url, Object.extend({ 
    684           parameters: this.options.callback(form, value), 
    685           onComplete: this.onComplete.bind(this), 
    686           onFailure: this.onFailure.bind(this), 
    687           asynchronous:true,  
    688           evalScripts:true 
    689         }, this.options.ajaxOptions)); 
    690     } else  { 
    691       new Ajax.Updater( 
    692         { success: this.element, 
    693           // don't update on failure (this could be an option) 
    694           failure: null },  
    695         this.url, Object.extend({ 
    696           parameters: this.options.callback(form, value), 
    697           onComplete: this.onComplete.bind(this), 
    698           onFailure: this.onFailure.bind(this) 
    699         }, this.options.ajaxOptions)); 
    700     } 
    701     // stop the event to avoid a page refresh in Safari 
    702     if (arguments.length > 1) { 
    703       Event.stop(arguments[0]); 
    704     } 
    705     return false; 
    706   }, 
    707   onLoading: function() { 
    708     this.saving = true; 
     669    this._form.addClassName(this.options.loadingClassName); 
     670    this._controls.editor.disabled = true; 
     671    var options = Object.extend({ method: 'get' }, this.options.ajaxOptions); 
     672    Object.extend(options, { 
     673      parameters: 'editorId=' + encodeURIComponent(this.element.id), 
     674      onComplete: Prototype.emptyFunction, 
     675      onSuccess: function(transport) { 
     676        this._form.removeClassName(this.options.loadingClassName); 
     677        var text = transport.responseText; 
     678        if (this.options.stripLoadedTextTags) 
     679          text = text.stripTags(); 
     680        this._controls.editor.value = text; 
     681        this._controls.editor.disabled = false; 
     682        this.postProcessEditField(); 
     683      }.bind(this), 
     684      onFailure: this._boundFailureHandler 
     685    }); 
     686    new Ajax.Request(this.options.loadTextURL, options); 
     687  }, 
     688  postProcessEditField: function() { 
     689    var fpc = this.options.fieldPostCreation; 
     690    if (fpc) 
     691      $(this._controls.editor)['focus' == fpc ? 'focus' : 'activate'](); 
     692  }, 
     693  prepareOptions: function() { 
     694    this.options = Object.clone(Ajax.InPlaceEditor.DefaultOptions); 
     695    Object.extend(this.options, Ajax.InPlaceEditor.DefaultCallbacks); 
     696    [this._extraDefaultOptions].flatten().compact().each(function(defs) { 
     697      Object.extend(this.options, defs); 
     698    }.bind(this)); 
     699  }, 
     700  prepareSubmission: function() { 
     701    this._saving = true; 
    709702    this.removeForm(); 
    710703    this.leaveHover(); 
    711704    this.showSaving(); 
    712705  }, 
     706  registerListeners: function() { 
     707    this._listeners = { }; 
     708    var listener; 
     709    $H(Ajax.InPlaceEditor.Listeners).each(function(pair) { 
     710      listener = this[pair.value].bind(this); 
     711      this._listeners[pair.key] = listener; 
     712      if (!this.options.externalControlOnly) 
     713        this.element.observe(pair.key, listener); 
     714      if (this.options.externalControl) 
     715        this.options.externalControl.observe(pair.key, listener); 
     716    }.bind(this)); 
     717  }, 
     718  removeForm: function() { 
     719    if (!this._form) return; 
     720    this._form.remove(); 
     721    this._form = null; 
     722    this._controls = { }; 
     723  }, 
    713724  showSaving: function() { 
    714     this.oldInnerHTML = this.element.innerHTML; 
     725    this._oldInnerHTML = this.element.innerHTML; 
    715726    this.element.innerHTML = this.options.savingText; 
    716     Element.addClassName(this.element, this.options.savingClassName); 
    717     this.element.style.backgroundColor = this.originalBackground; 
    718     Element.show(this.element); 
    719   }, 
    720   removeForm: function() { 
    721     if(this.form) { 
    722       if (this.form.parentNode) Element.remove(this.form); 
    723       this.form = null; 
    724     } 
    725   }, 
    726   enterHover: function() { 
    727     if (this.saving) return; 
    728     this.element.style.backgroundColor = this.options.highlightcolor; 
    729     if (this.effect) { 
    730       this.effect.cancel(); 
    731     } 
    732     Element.addClassName(this.element, this.options.hoverClassName) 
    733   }, 
    734   leaveHover: function() { 
    735     if (this.options.backgroundColor) { 
    736       this.element.style.backgroundColor = this.oldBackground; 
    737     } 
    738     Element.removeClassName(this.element, this.options.hoverClassName) 
    739     if (this.saving) return; 
    740     this.effect = new Effect.Highlight(this.element, { 
    741       startcolor: this.options.highlightcolor, 
    742       endcolor: this.options.highlightendcolor, 
    743       restorecolor: this.originalBackground 
    744     }); 
    745   }, 
    746   leaveEditMode: function() { 
    747     Element.removeClassName(this.element, this.options.savingClassName); 
    748     this.removeForm(); 
    749     this.leaveHover(); 
    750     this.element.style.backgroundColor = this.originalBackground; 
    751     Element.show(this.element); 
    752     if (this.options.externalControl) { 
    753       Element.show(this.options.externalControl); 
    754     } 
    755     this.editing = false; 
    756     this.saving = false; 
    757     this.oldInnerHTML = null; 
    758     this.onLeaveEditMode(); 
    759   }, 
    760   onComplete: function(transport) { 
     727    this.element.addClassName(this.options.savingClassName); 
     728    this.element.style.backgroundColor = this._originalBackground; 
     729    this.element.show(); 
     730  }, 
     731  triggerCallback: function(cbName, arg) { 
     732    if ('function' == typeof this.options[cbName]) { 
     733      this.options[cbName](this, arg); 
     734    } 
     735  }, 
     736  unregisterListeners: function() { 
     737    $H(this._listeners).each(function(pair) { 
     738      if (!this.options.externalControlOnly) 
     739        this.element.stopObserving(pair.key, pair.value); 
     740      if (this.options.externalControl) 
     741        this.options.externalControl.stopObserving(pair.key, pair.value); 
     742    }.bind(this)); 
     743  }, 
     744  wrapUp: function(transport) { 
    761745    this.leaveEditMode(); 
    762     this.options.onComplete.bind(this)(transport, this.element); 
    763   }, 
    764   onEnterEditMode: function() {}, 
    765   onLeaveEditMode: function() {}, 
    766   dispose: function() { 
    767     if (this.oldInnerHTML) { 
    768       this.element.innerHTML = this.oldInnerHTML; 
    769     } 
    770     this.leaveEditMode(); 
    771     Event.stopObserving(this.element, 'click', this.onclickListener); 
    772     Event.stopObserving(this.element, 'mouseover', this.mouseoverListener); 
    773     Event.stopObserving(this.element, 'mouseout', this.mouseoutListener); 
    774     if (this.options.externalControl) { 
    775       Event.stopObserving(this.options.externalControl, 'click', this.onclickListener); 
    776       Event.stopObserving(this.options.externalControl, 'mouseover', this.mouseoverListener); 
    777       Event.stopObserving(this.options.externalControl, 'mouseout', this.mouseoutListener); 
    778     } 
    779   } 
    780 }; 
    781  
    782 Ajax.InPlaceCollectionEditor = Class.create(); 
    783 Object.extend(Ajax.InPlaceCollectionEditor.prototype, Ajax.InPlaceEditor.prototype); 
    784 Object.extend(Ajax.InPlaceCollectionEditor.prototype, { 
    785   createEditField: function() { 
    786     if (!this.cached_selectTag) { 
    787       var selectTag = document.createElement("select"); 
    788       var collection = this.options.collection || []; 
    789       var optionTag; 
    790       collection.each(function(e,i) { 
    791         optionTag = document.createElement("option"); 
    792         optionTag.value = (e instanceof Array) ? e[0] : e; 
    793         if((typeof this.options.value == 'undefined') &&  
    794           ((e instanceof Array) ? this.element.innerHTML == e[1] : e == optionTag.value)) optionTag.selected = true; 
    795         if(this.options.value==optionTag.value) optionTag.selected = true; 
    796         optionTag.appendChild(document.createTextNode((e instanceof Array) ? e[1] : e)); 
    797         selectTag.appendChild(optionTag); 
    798       }.bind(this)); 
    799       this.cached_selectTag = selectTag; 
    800     } 
    801  
    802     this.editField = this.cached_selectTag; 
    803     if(this.options.loadTextURL) this.loadExternalText(); 
    804     this.form.appendChild(this.editField); 
    805     this.options.callback = function(form, value) { 
    806       return "value=" + encodeURIComponent(value); 
    807     } 
     746    // Can't use triggerCallback due to backward compatibility: requires 
     747    // binding + direct element 
     748    this._boundComplete(transport, this.element); 
    808749  } 
    809750}); 
     751 
     752Object.extend(Ajax.InPlaceEditor.prototype, { 
     753  dispose: Ajax.InPlaceEditor.prototype.destroy 
     754}); 
     755 
     756Ajax.InPlaceCollectionEditor = Class.create(Ajax.InPlaceEditor, { 
     757  initialize: function($super, element, url, options) { 
     758    this._extraDefaultOptions = Ajax.InPlaceCollectionEditor.DefaultOptions; 
     759    $super(element, url, options); 
     760  }, 
     761 
     762  createEditField: function() { 
     763    var list = document.createElement('select'); 
     764    list.name = this.options.paramName; 
     765    list.size = 1; 
     766    this._controls.editor = list; 
     767    this._collection = this.options.collection || []; 
     768    if (this.options.loadCollectionURL) 
     769      this.loadCollection(); 
     770    else 
     771      this.checkForExternalText(); 
     772    this._form.appendChild(this._controls.editor); 
     773  }, 
     774 
     775  loadCollection: function() { 
     776    this._form.addClassName(this.options.loadingClassName); 
     777    this.showLoadingText(this.options.loadingCollectionText); 
     778    var options = Object.extend({ method: 'get' }, this.options.ajaxOptions); 
     779    Object.extend(options, { 
     780      parameters: 'editorId=' + encodeURIComponent(this.element.id), 
     781      onComplete: Prototype.emptyFunction, 
     782      onSuccess: function(transport) { 
     783        var js = transport.responseText.strip(); 
     784        if (!/^\[.*\]$/.test(js)) // TODO: improve sanity check 
     785          throw 'Server returned an invalid collection representation.'; 
     786        this._collection = eval(js); 
     787        this.checkForExternalText(); 
     788      }.bind(this), 
     789      onFailure: this.onFailure 
     790    }); 
     791    new Ajax.Request(this.options.loadCollectionURL, options); 
     792  }, 
     793 
     794  showLoadingText: function(text) { 
     795    this._controls.editor.disabled = true; 
     796    var tempOption = this._controls.editor.firstChild; 
     797    if (!tempOption) { 
     798      tempOption = document.createElement('option'); 
     799      tempOption.value = ''; 
     800      this._controls.editor.appendChild(tempOption); 
     801      tempOption.selected = true; 
     802    } 
     803    tempOption.update((text || '').stripScripts().stripTags()); 
     804  }, 
     805 
     806  checkForExternalText: function() { 
     807    this._text = this.getText(); 
     808    if (this.options.loadTextURL) 
     809      this.loadExternalText(); 
     810    else 
     811      this.buildOptionList(); 
     812  }, 
     813 
     814  loadExternalText: function() { 
     815    this.showLoadingText(this.options.loadingText); 
     816    var options = Object.extend({ method: 'get' }, this.options.ajaxOptions); 
     817    Object.extend(options, { 
     818      parameters: 'editorId=' + encodeURIComponent(this.element.id), 
     819      onComplete: Prototype.emptyFunction, 
     820      onSuccess: function(transport) { 
     821        this._text = transport.responseText.strip(); 
     822        this.buildOptionList(); 
     823      }.bind(this), 
     824      onFailure: this.onFailure 
     825    }); 
     826    new Ajax.Request(this.options.loadTextURL, options); 
     827  }, 
     828 
     829  buildOptionList: function() { 
     830    this._form.removeClassName(this.options.loadingClassName); 
     831    this._collection = this._collection.map(function(entry) { 
     832      return 2 === entry.length ? entry : [entry, entry].flatten(); 
     833    }); 
     834    var marker = ('value' in this.options) ? this.options.value : this._text; 
     835    var textFound = this._collection.any(function(entry) { 
     836      return entry[0] == marker; 
     837    }.bind(this)); 
     838    this._controls.editor.update(''); 
     839    var option; 
     840    this._collection.each(function(entry, index) { 
     841      option = document.createElement('option'); 
     842      option.value = entry[0]; 
     843      option.selected = textFound ? entry[0] == marker : 0 == index; 
     844      option.appendChild(document.createTextNode(entry[1])); 
     845      this._controls.editor.appendChild(option); 
     846    }.bind(this)); 
     847    this._controls.editor.disabled = false; 
     848    Field.scrollFreeActivate(this._controls.editor); 
     849  } 
     850}); 
     851 
     852//**** DEPRECATION LAYER FOR InPlace[Collection]Editor! **** 
     853//**** This only  exists for a while,  in order to  let **** 
     854//**** users adapt to  the new API.  Read up on the new **** 
     855//**** API and convert your code to it ASAP!            **** 
     856 
     857Ajax.InPlaceEditor.prototype.initialize.dealWithDeprecatedOptions = function(options) { 
     858  if (!options) return; 
     859  function fallback(name, expr) { 
     860    if (name in options || expr === undefined) return; 
     861    options[name] = expr; 
     862  }; 
     863  fallback('cancelControl', (options.cancelLink ? 'link' : (options.cancelButton ? 'button' : 
     864    options.cancelLink == options.cancelButton == false ? false : undefined))); 
     865  fallback('okControl', (options.okLink ? 'link' : (options.okButton ? 'button' : 
     866    options.okLink == options.okButton == false ? false : undefined))); 
     867  fallback('highlightColor', options.highlightcolor); 
     868  fallback('highlightEndColor', options.highlightendcolor); 
     869}; 
     870 
     871Object.extend(Ajax.InPlaceEditor, { 
     872  DefaultOptions: { 
     873    ajaxOptions: { }, 
     874    autoRows: 3,                                // Use when multi-line w/ rows == 1 
     875    cancelControl: 'link',                      // 'link'|'button'|false 
     876    cancelText: 'cancel', 
     877    clickToEditText: 'Click to edit', 
     878    externalControl: null,                      // id|elt 
     879    externalControlOnly: false, 
     880    fieldPostCreation: 'activate',              // 'activate'|'focus'|false 
     881    formClassName: 'inplaceeditor-form', 
     882    formId: null,                               // id|elt 
     883    highlightColor: '#ffff99', 
     884    highlightEndColor: '#ffffff', 
     885    hoverClassName: '', 
     886    htmlResponse: true, 
     887    loadingClassName: 'inplaceeditor-loading', 
     888    loadingText: 'Loading...', 
     889    okControl: 'button',                        // 'link'|'button'|false 
     890    okText: 'ok', 
     891    paramName: 'value', 
     892    rows: 1,                                    // If 1 and multi-line, uses autoRows 
     893    savingClassName: 'inplaceeditor-saving', 
     894    savingText: 'Saving...', 
     895    size: 0, 
     896    stripLoadedTextTags: false, 
     897    submitOnBlur: false, 
     898    textAfterControls: '', 
     899    textBeforeControls: '', 
     900    textBetweenControls: '' 
     901  }, 
     902  DefaultCallbacks: { 
     903    callback: function(form) { 
     904      return Form.serialize(form); 
     905    }, 
     906    onComplete: function(transport, element) { 
     907      // For backward compatibility, this one is bound to the IPE, and passes 
     908      // the element directly.  It was too often customized, so we don't break it. 
     909      new Effect.Highlight(element, { 
     910        startcolor: this.options.highlightColor, keepBackgroundImage: true }); 
     911    }, 
     912    onEnterEditMode: null, 
     913    onEnterHover: function(ipe) { 
     914      ipe.element.style.backgroundColor = ipe.options.highlightColor; 
     915      if (ipe._effect) 
     916        ipe._effect.cancel(); 
     917    }, 
     918    onFailure: function(transport, ipe) { 
     919      alert('Error communication with the server: ' + transport.responseText.stripTags()); 
     920    }, 
     921    onFormCustomization: null, // Takes the IPE and its generated form, after editor, before controls. 
     922    onLeaveEditMode: null, 
     923    onLeaveHover: function(ipe) { 
     924      ipe._effect = new Effect.Highlight(ipe.element, { 
     925        startcolor: ipe.options.highlightColor, endcolor: ipe.options.highlightEndColor, 
     926        restorecolor: ipe._originalBackground, keepBackgroundImage: true 
     927      }); 
     928    } 
     929  }, 
     930  Listeners: { 
     931    click: 'enterEditMode', 
     932    keydown: 'checkForEscapeOrReturn', 
     933    mouseover: 'enterHover', 
     934    mouseout: 'leaveHover' 
     935  } 
     936}); 
     937 
     938Ajax.InPlaceCollectionEditor.DefaultOptions = { 
     939  loadingCollectionText: 'Loading options...' 
     940}; 
    810941 
    811942// Delayed observer, like Form.Element.Observer,  
     
    813944// Ideal for live-search fields 
    814945 
    815 Form.Element.DelayedObserver = Class.create(); 
    816 Form.Element.DelayedObserver.prototype = { 
     946Form.Element.DelayedObserver = Class.create({ 
    817947  initialize: function(element, delay, callback) { 
    818948    this.delay     = delay || 0.5; 
     
    833963    this.callback(this.element, $F(this.element)); 
    834964  } 
    835 }
     965})
  • branches/1.1/data/web/sf/prototype/js/dragdrop.js

    r3316 r7855  
    1 // script.aculo.us dragdrop.js v1.7.0, Fri Jan 19 19:16:36 CET 2007 
    2  
    3 // Copyright (c) 2005, 2006 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us) 
    4 //           (c) 2005, 2006 Sammi Williams (http://www.oriontransfer.co.nz, sammi@oriontransfer.co.nz) 
     1// script.aculo.us dragdrop.js v1.8.1, Thu Jan 03 22:07:12 -0500 2008 
     2 
     3// Copyright (c) 2005-2007 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us) 
     4//           (c) 2005-2007 Sammi Williams (http://www.oriontransfer.co.nz, sammi@oriontransfer.co.nz) 
    55//  
    66// script.aculo.us is freely distributable under the terms of an MIT-style license. 
    77// For details, see the script.aculo.us web site: http://script.aculo.us/ 
    88 
    9 if(typeof Effect == 'undefined'
     9if(Object.isUndefined(Effect)
    1010  throw("dragdrop.js requires including script.aculo.us' effects.js library"); 
    1111 
     
    2323      hoverclass: null, 
    2424      tree:       false 
    25     }, arguments[1] || {}); 
     25    }, arguments[1] || { }); 
    2626 
    2727    // cache containers 
     
    2929      options._containers = []; 
    3030      var containment = options.containment; 
    31       if((typeof containment == 'object') &&  
    32         (containment.constructor == Array)) { 
     31      if(Object.isArray(containment)) { 
    3332        containment.each( function(c) { options._containers.push($(c)) }); 
    3433      } else { 
     
    9089  show: function(point, element) { 
    9190    if(!this.drops.length) return; 
    92     var affected = []; 
    93      
    94     if(this.last_active) this.deactivate(this.last_active); 
     91    var drop, affected = []; 
     92     
    9593    this.drops.each( function(drop) { 
    9694      if(Droppables.isAffected(point, element, drop)) 
     
    9896    }); 
    9997         
    100     if(affected.length>0) { 
     98    if(affected.length>0) 
    10199      drop = Droppables.findDeepestChild(affected); 
     100 
     101    if(this.last_active && this.last_active != drop) this.deactivate(this.last_active); 
     102    if (drop) { 
    102103      Position.within(drop.element, point[0], point[1]); 
    103104      if(drop.onHover) 
    104105        drop.onHover(element, drop.element, Position.overlap(drop.overlap, drop.element)); 
    105106       
    106       Droppables.activate(drop); 
     107      if (drop != this.last_active) Droppables.activate(drop); 
    107108    } 
    108109  }, 
     
    113114 
    114115    if (this.isAffected([Event.pointerX(event), Event.pointerY(event)], element, this.last_active)) 
    115       if (this.last_active.onDrop)  
    116         this.last_active.onDrop(element, this.last_active.element, event); 
     116      if (this.last_active.onDrop) { 
     117        this.last_active.onDrop(element, this.last_active.element, event);  
     118        return true;  
     119      } 
    117120  }, 
    118121 
     
    222225/*--------------------------------------------------------------------------*/ 
    223226 
    224 var Draggable = Class.create(); 
    225 Draggable._dragging    = {}; 
    226  
    227 Draggable.prototype = { 
     227var Draggable = Class.create({ 
    228228  initialize: function(element) { 
    229229    var defaults = { 
     
    236236      }, 
    237237      endeffect: function(element) { 
    238         var toOpacity = typeof element._opacity == 'number' ? element._opacity : 1.0; 
     238        var toOpacity = Object.isNumber(element._opacity) ? element._opacity : 1.0; 
    239239        new Effect.Opacity(element, {duration:0.2, from:0.7, to:toOpacity,  
    240240          queue: {scope:'_draggable', position:'end'}, 
     
    246246      zindex: 1000, 
    247247      revert: false, 
     248      quiet: false, 
    248249      scroll: false, 
    249250      scrollSensitivity: 20, 
     
    253254    }; 
    254255     
    255     if(!arguments[1] || typeof arguments[1].endeffect == 'undefined'
     256    if(!arguments[1] || Object.isUndefined(arguments[1].endeffect)
    256257      Object.extend(defaults, { 
    257258        starteffect: function(element) { 
     
    262263      }); 
    263264     
    264     var options = Object.extend(defaults, arguments[1] || {}); 
     265    var options = Object.extend(defaults, arguments[1] || { }); 
    265266 
    266267    this.element = $(element); 
    267268     
    268     if(options.handle && (typeof options.handle == 'string')) 
     269    if(options.handle && Object.isString(options.handle)) 
    269270      this.handle = this.element.down('.'+options.handle, 0); 
    270271     
     
    279280    Element.makePositioned(this.element); // fix IE     
    280281 
    281     this.delta    = this.currentDelta(); 
    282282    this.options  = options; 
    283283    this.dragging = false;    
     
    301301   
    302302  initDrag: function(event) { 
    303     if(typeof Draggable._dragging[this.element] != 'undefined' && 
     303    if(!Object.isUndefined(Draggable._dragging[this.element]) && 
    304304      Draggable._dragging[this.element]) return; 
    305305    if(Event.isLeftClick(event)) {     
     
    324324  startDrag: function(event) { 
    325325    this.dragging = true; 
     326    if(!this.delta) 
     327      this.delta = this.currentDelta(); 
    326328     
    327329    if(this.options.zindex) { 
     
    332334    if(this.options.ghosting) { 
    333335      this._clone = this.element.cloneNode(true); 
    334       Position.absolutize(this.element); 
     336      this.element._originallyAbsolute = (this.element.getStyle('position') == 'absolute'); 
     337      if (!this.element._originallyAbsolute) 
     338        Position.absolutize(this.element); 
    335339      this.element.parentNode.insertBefore(this._clone, this.element); 
    336340    } 
     
    354358  updateDrag: function(event, pointer) { 
    355359    if(!this.dragging) this.startDrag(event); 
    356     Position.prepare(); 
    357     Droppables.show(pointer, this.element); 
     360     
     361    if(!this.options.quiet){ 
     362      Position.prepare(); 
     363      Droppables.show(pointer, this.element); 
     364    } 
     365     
    358366    Draggables.notify('onDrag', this, event); 
    359367     
     
    383391     
    384392    // fix AppleWebKit rendering 
    385     if(navigator.appVersion.indexOf('AppleWebKit')>0) window.scrollBy(0,0); 
     393    if(Prototype.Browser.WebKit) window.scrollBy(0,0); 
    386394     
    387395    Event.stop(event); 
     
    390398  finishDrag: function(event, success) { 
    391399    this.dragging = false; 
     400     
     401    if(this.options.quiet){ 
     402      Position.prepare(); 
     403      var pointer = [Event.pointerX(event), Event.pointerY(event)]; 
     404      Droppables.show(pointer, this.element); 
     405    } 
    392406 
    393407    if(this.options.ghosting) { 
    394       Position.relativize(this.element); 
     408      if (!this.element._originallyAbsolute) 
     409        Position.relativize(this.element); 
     410      delete this.element._originallyAbsolute; 
    395411      Element.remove(this._clone); 
    396412      this._clone = null; 
    397413    } 
    398414 
    399     if(success) Droppables.fire(event, this.element); 
     415    var dropped = false;  
     416    if(success) {  
     417      dropped = Droppables.fire(event, this.element);  
     418      if (!dropped) dropped = false;  
     419    } 
     420    if(dropped && this.options.onDropped) this.options.onDropped(this.element); 
    400421    Draggables.notify('onEnd', this, event); 
    401422 
    402423    var revert = this.options.revert; 
    403     if(revert && typeof revert == 'function') revert = revert(this.element); 
     424    if(revert && Object.isFunction(revert)) revert = revert(this.element); 
    404425     
    405426    var d = this.currentDelta(); 
    406427    if(revert && this.options.reverteffect) { 
    407       this.options.reverteffect(this.element,  
    408         d[1]-this.delta[1], d[0]-this.delta[0]); 
     428      if (dropped == 0 || revert != 'failure') 
     429        this.options.reverteffect(this.element, 
     430          d[1]-this.delta[1], d[0]-this.delta[0]); 
    409431    } else { 
    410432      this.delta = d; 
     
    454476     
    455477    if(this.options.snap) { 
    456       if(typeof this.options.snap == 'function') { 
     478      if(Object.isFunction(this.options.snap)) { 
    457479        p = this.options.snap(p[0],p[1],this); 
    458480      } else { 
    459       if(this.options.snap instanceof Array) { 
     481      if(Object.isArray(this.options.snap)) { 
    460482        p = p.map( function(v, i) { 
    461           return Math.round(v/this.options.snap[i])*this.options.snap[i] }.bind(this)) 
     483          return (v/this.options.snap[i]).round()*this.options.snap[i] }.bind(this)) 
    462484      } else { 
    463485        p = p.map( function(v) { 
    464           return Math.round(v/this.options.snap)*this.options.snap }.bind(this)) 
     486          return (v/this.options.snap).round()*this.options.snap }.bind(this)) 
    465487      } 
    466488    }} 
     
    546568    return { top: T, left: L, width: W, height: H }; 
    547569  } 
    548 
     570}); 
     571 
     572Draggable._dragging = { }; 
    549573 
    550574/*--------------------------------------------------------------------------*/ 
    551575 
    552 var SortableObserver = Class.create(); 
    553 SortableObserver.prototype = { 
     576var SortableObserver = Class.create({ 
    554577  initialize: function(element, observer) { 
    555578    this.element   = $(element); 
     
    567590      this.observer(this.element) 
    568591  } 
    569 } 
     592}); 
    570593 
    571594var Sortable = { 
    572595  SERIALIZE_RULE: /^[^_\-](?:[A-Za-z0-9\-\_]*)[_](.*)$/, 
    573596   
    574   sortables: {}, 
     597  sortables: { }, 
    575598   
    576599  _findRootElement: function(element) { 
     
    615638      hoverclass:  null, 
    616639      ghosting:    false, 
     640      quiet:       false,  
    617641      scroll:      false, 
    618642      scrollSensitivity: 20, 
    619643      scrollSpeed: 15, 
    620644      format:      this.SERIALIZE_RULE, 
     645       
     646      // these take arrays of elements or ids and can be  
     647      // used for better initialization performance 
     648      elements:    false, 
     649      handles:     false, 
     650       
    621651      onChange:    Prototype.emptyFunction, 
    622652      onUpdate:    Prototype.emptyFunction 
    623     }, arguments[1] || {}); 
     653    }, arguments[1] || { }); 
    624654 
    625655    // clear any old sortable with same element 
     
    629659    var options_for_draggable = { 
    630660      revert:      true, 
     661      quiet:       options.quiet, 
    631662      scroll:      options.scroll, 
    632663      scrollSpeed: options.scrollSpeed, 
     
    682713    } 
    683714 
    684     (this.findElements(element, options) || []).each( function(e) { 
    685       // handles are per-draggable 
    686       var handle = options.handle ?  
    687         $(e).down('.'+options.handle,0) : e;     
     715    (options.elements || this.findElements(element, options) || []).each( function(e,i) { 
     716      var handle = options.handles ? $(options.handles[i]) : 
     717        (options.handle ? $(e).select('.' + options.handle)[0] : e);  
    688718      options.draggables.push( 
    689719        new Draggable(e, Object.extend(options_for_draggable, { handle: handle }))); 
     
    845875      name: element.id, 
    846876      format: sortableOptions.format 
    847     }, arguments[1] || {}); 
     877    }, arguments[1] || { }); 
    848878     
    849879    var root = { 
     
    869899  sequence: function(element) { 
    870900    element = $(element); 
    871     var options = Object.extend(this.options(element), arguments[1] || {}); 
     901    var options = Object.extend(this.options(element), arguments[1] || { }); 
    872902     
    873903    return $(this.findElements(element, options) || []).map( function(item) { 
     
    878908  setSequence: function(element, new_sequence) { 
    879909    element = $(element); 
    880     var options = Object.extend(this.options(element), arguments[2] || {}); 
    881      
    882     var nodeMap = {}; 
     910    var options = Object.extend(this.options(element), arguments[2] || { }); 
     911     
     912    var nodeMap = { }; 
    883913    this.findElements(element, options).each( function(n) { 
    884914        if (n.id.match(options.format)) 
     
    898928  serialize: function(element) { 
    899929    element = $(element); 
    900     var options = Object.extend(Sortable.options(element), arguments[1] || {}); 
     930    var options = Object.extend(Sortable.options(element), arguments[1] || { }); 
    901931    var name = encodeURIComponent( 
    902932      (arguments[1] && arguments[1].name) ? arguments[1].name : element.id); 
     
    922952} 
    923953 
    924 Element.findChildren = function(element, only, recursive, tagName) {     
     954Element.findChildren = function(element, only, recursive, tagName) {    
    925955  if(!element.hasChildNodes()) return null; 
    926956  tagName = tagName.toUpperCase(); 
  • branches/1.1/data/web/sf/prototype/js/effects.js

    r3316 r7855  
    1 // script.aculo.us effects.js v1.7.0, Fri Jan 19 19:16:36 CET 2007 
    2  
    3 // Copyright (c) 2005, 2006 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us) 
     1// script.aculo.us effects.js v1.8.1, Thu Jan 03 22:07:12 -0500 2008 
     2 
     3// Copyright (c) 2005-2007 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us) 
    44// Contributors: 
    55//  Justin Palmer (http://encytemedia.com/) 
     
    1414String.prototype.parseColor = function() {   
    1515  var color = '#'; 
    16   if(this.slice(0,4) == 'rgb(') {   
     16  if (this.slice(0,4) == 'rgb(') {   
    1717    var cols = this.slice(4,this.length-1).split(',');   
    1818    var i=0; do { color += parseInt(cols[i]).toColorPart() } while (++i<3);   
    1919  } else {   
    20     if(this.slice(0,1) == '#') {   
    21       if(this.length==4) for(var i=1;i<4;i++) color += (this.charAt(i) + this.charAt(i)).toLowerCase();   
    22       if(this.length==7) color = this.toLowerCase();   
     20    if (this.slice(0,1) == '#') {   
     21      if (this.length==4) for(var i=1;i<4;i++) color += (this.charAt(i) + this.charAt(i)).toLowerCase();   
     22      if (this.length==7) color = this.toLowerCase();   
    2323    }   
    2424  }   
    25   return(color.length==7 ? color : (arguments[0] || this));   
    26 } 
     25  return (color.length==7 ? color : (arguments[0] || this));   
     26}; 
    2727 
    2828/*--------------------------------------------------------------------------*/ 
     
    3333      (node.hasChildNodes() ? Element.collectTextNodes(node) : '')); 
    3434  }).flatten().join(''); 
    35 } 
     35}; 
    3636 
    3737Element.collectTextNodesIgnoreClass = function(element, className) {   
     
    4141        Element.collectTextNodesIgnoreClass(node, className) : '')); 
    4242  }).flatten().join(''); 
    43 } 
     43}; 
    4444 
    4545Element.setContentZoom = function(element, percent) { 
    4646  element = $(element);   
    4747  element.setStyle({fontSize: (percent/100) + 'em'});    
    48   if(navigator.appVersion.indexOf('AppleWebKit')>0) window.scrollBy(0,0); 
     48  if (Prototype.Browser.WebKit) window.scrollBy(0,0); 
    4949  return element; 
    50 
    51  
    52 Element.getOpacity = function(element){ 
    53   return $(element).getStyle('opacity'); 
    54 
    55  
    56 Element.setOpacity = function(element, value){ 
    57   return $(element).setStyle({opacity:value}); 
    58 
     50}; 
    5951 
    6052Element.getInlineOpacity = function(element){ 
    6153  return $(element).style.opacity || ''; 
    62 } 
     54}; 
    6355 
    6456Element.forceRerendering = function(element) { 
     
    7365/*--------------------------------------------------------------------------*/ 
    7466 
    75 Array.prototype.call = function() { 
    76   var args = arguments; 
    77   this.each(function(f){ f.apply(this, args) }); 
    78 } 
    79  
    80 /*--------------------------------------------------------------------------*/ 
    81  
    8267var Effect = { 
    8368  _elementDoesNotExistError: { 
     
    8570    message: 'The specified DOM element does not exist, but is required for this effect to operate' 
    8671  }, 
     72  Transitions: { 
     73    linear: Prototype.K, 
     74    sinoidal: function(pos) { 
     75      return (-Math.cos(pos*Math.PI)/2) + 0.5; 
     76    }, 
     77    reverse: function(pos) { 
     78      return 1-pos; 
     79    }, 
     80    flicker: function(pos) { 
     81      var pos = ((-Math.cos(pos*Math.PI)/4) + 0.75) + Math.random()/4; 
     82      return pos > 1 ? 1 : pos; 
     83    }, 
     84    wobble: function(pos) { 
     85      return (-Math.cos(pos*Math.PI*(9*pos))/2) + 0.5; 
     86    }, 
     87    pulse: function(pos, pulses) {  
     88      pulses = pulses || 5;  
     89      return ( 
     90        ((pos % (1/pulses)) * pulses).round() == 0 ?  
     91              ((pos * pulses * 2) - (pos * pulses * 2).floor()) :  
     92          1 - ((pos * pulses * 2) - (pos * pulses * 2).floor()) 
     93        ); 
     94    }, 
     95    spring: function(pos) {  
     96      return 1 - (Math.cos(pos * 4.5 * Math.PI) * Math.exp(-pos * 6));  
     97    }, 
     98    none: function(pos) { 
     99      return 0; 
     100    }, 
     101    full: function(pos) { 
     102      return 1; 
     103    } 
     104  }, 
     105  DefaultOptions: { 
     106    duration:   1.0,   // seconds 
     107    fps:        100,   // 100= assume 66fps max. 
     108    sync:       false, // true for combining 
     109    from:       0.0, 
     110    to:         1.0, 
     111    delay:      0.0, 
     112    queue:      'parallel' 
     113  }, 
    87114  tagifyText: function(element) { 
    88     if(typeof Builder == 'undefined') 
    89       throw("Effect.tagifyText requires including script.aculo.us' builder.js library"); 
    90        
    91115    var tagifyStyle = 'position:relative'; 
    92     if(/MSIE/.test(navigator.userAgent) && !window.opera) tagifyStyle += ';zoom:1'; 
     116    if (Prototype.Browser.IE) tagifyStyle += ';zoom:1'; 
    93117     
    94118    element = $(element); 
    95119    $A(element.childNodes).each( function(child) { 
    96       if(child.nodeType==3) { 
     120      if (child.nodeType==3) { 
    97121        child.nodeValue.toArray().each( function(character) { 
    98122          element.insertBefore( 
    99             Builder.node('span',{style: tagifyStyle}, 
     123            new Element('span', {style: tagifyStyle}).update( 
    100124              character == ' ' ? String.fromCharCode(160) : character),  
    101125              child); 
     
    107131  multiple: function(element, effect) { 
    108132    var elements; 
    109     if(((typeof element == 'object') ||  
    110         (typeof element == 'function')) &&  
     133    if (((typeof element == 'object') ||  
     134        Object.isFunction(element)) &&  
    111135       (element.length)) 
    112136      elements = element; 
     
    117141      speed: 0.1, 
    118142      delay: 0.0 
    119     }, arguments[2] || {}); 
     143    }, arguments[2] || { }); 
    120144    var masterDelay = options.delay; 
    121145 
     
    134158    var options = Object.extend({ 
    135159      queue: { position:'end', scope:(element.id || 'global'), limit: 1 } 
    136     }, arguments[2] || {}); 
     160    }, arguments[2] || { }); 
    137161    Effect[element.visible() ?  
    138162      Effect.PAIRS[effect][1] : Effect.PAIRS[effect][0]](element, options); 
     
    140164}; 
    141165 
    142 var Effect2 = Effect; // deprecated 
    143  
    144 /* ------------- transitions ------------- */ 
    145  
    146 Effect.Transitions = { 
    147   linear: Prototype.K, 
    148   sinoidal: function(pos) { 
    149     return (-Math.cos(pos*Math.PI)/2) + 0.5; 
    150   }, 
    151   reverse: function(pos) { 
    152     return 1-pos; 
    153   }, 
    154   flicker: function(pos) { 
    155     return ((-Math.cos(pos*Math.PI)/4) + 0.75) + Math.random()/4; 
    156   }, 
    157   wobble: function(pos) { 
    158     return (-Math.cos(pos*Math.PI*(9*pos))/2) + 0.5; 
    159   }, 
    160   pulse: function(pos, pulses) {  
    161     pulses = pulses || 5;  
    162     return ( 
    163       Math.round((pos % (1/pulses)) * pulses) == 0 ?  
    164             ((pos * pulses * 2) - Math.floor(pos * pulses * 2)) :  
    165         1 - ((pos * pulses * 2) - Math.floor(pos * pulses * 2)) 
    166       ); 
    167   }, 
    168   none: function(pos) { 
    169     return 0; 
    170   }, 
    171   full: function(pos) { 
    172     return 1; 
    173   } 
    174 }; 
     166Effect.DefaultOptions.transition = Effect.Transitions.sinoidal; 
    175167 
    176168/* ------------- core effects ------------- */ 
    177169 
    178 Effect.ScopedQueue = Class.create(); 
    179 Object.extend(Object.extend(Effect.ScopedQueue.prototype, Enumerable), { 
     170Effect.ScopedQueue = Class.create(Enumerable, { 
    180171  initialize: function() { 
    181172    this.effects  = []; 
    182     this.interval = null; 
     173    this.interval = null;     
    183174  }, 
    184175  _each: function(iterator) { 
     
    188179    var timestamp = new Date().getTime(); 
    189180     
    190     var position = (typeof effect.options.queue == 'string') ?  
     181    var position = Object.isString(effect.options.queue) ?  
    191182      effect.options.queue : effect.options.queue.position; 
    192183     
     
    211202    effect.finishOn += timestamp; 
    212203 
    213     if(!effect.options.queue.limit || (this.effects.length < effect.options.queue.limit)) 
     204    if (!effect.options.queue.limit || (this.effects.length < effect.options.queue.limit)) 
    214205      this.effects.push(effect); 
    215206     
    216     if(!this.interval)  
     207    if (!this.interval) 
    217208      this.interval = setInterval(this.loop.bind(this), 15); 
    218209  }, 
    219210  remove: function(effect) { 
    220211    this.effects = this.effects.reject(function(e) { return e==effect }); 
    221     if(this.effects.length == 0) { 
     212    if (this.effects.length == 0) { 
    222213      clearInterval(this.interval); 
    223214      this.interval = null; 
     
    227218    var timePos = new Date().getTime(); 
    228219    for(var i=0, len=this.effects.length;i<len;i++)  
    229       if(this.effects[i]) this.effects[i].loop(timePos); 
     220      this.effects[i] && this.effects[i].loop(timePos); 
    230221  } 
    231222}); 
     
    234225  instances: $H(), 
    235226  get: function(queueName) { 
    236     if(typeof queueName != 'string') return queueName; 
    237      
    238     if(!this.instances[queueName]) 
    239       this.instances[queueName] = new Effect.ScopedQueue(); 
    240        
    241     return this.instances[queueName]; 
    242   } 
    243 
     227    if (!Object.isString(queueName)) return queueName; 
     228     
     229    return this.instances.get(queueName) || 
     230      this.instances.set(queueName, new Effect.ScopedQueue()); 
     231  } 
     232}; 
    244233Effect.Queue = Effect.Queues.get('global'); 
    245234 
    246 Effect.DefaultOptions = { 
    247   transition: Effect.Transitions.sinoidal, 
    248   duration:   1.0,   // seconds 
    249   fps:        60.0,  // max. 60fps due to Effect.Queue implementation 
    250   sync:       false, // true for combining 
    251   from:       0.0, 
    252   to:         1.0, 
    253   delay:      0.0, 
    254   queue:      'parallel' 
    255 
    256  
    257 Effect.Base = function() {}; 
    258 Effect.Base.prototype = { 
     235Effect.Base = Class.create({ 
    259236  position: null, 
    260237  start: function(options) { 
    261     this.options      = Object.extend(Object.extend({},Effect.DefaultOptions), options || {}); 
     238    function codeForEvent(options,eventName){ 
     239      return ( 
     240        (options[eventName+'Internal'] ? 'this.options.'+eventName+'Internal(this);' : '') + 
     241        (options[eventName] ? 'this.options.'+eventName+'(this);' : '') 
     242      ); 
     243    } 
     244    if (options && options.transition === false) options.transition = Effect.Transitions.linear; 
     245    this.options      = Object.extend(Object.extend({ },Effect.DefaultOptions), options || { }); 
    262246    this.currentFrame = 0; 
    263247    this.state        = 'idle'; 
    264248    this.startOn      = this.options.delay*1000; 
    265     this.finishOn     = this.startOn + (this.options.duration*1000); 
     249    this.finishOn     = this.startOn+(this.options.duration*1000); 
     250    this.fromToDelta  = this.options.to-this.options.from; 
     251    this.totalTime    = this.finishOn-this.startOn; 
     252    this.totalFrames  = this.options.fps*this.options.duration; 
     253     
     254    eval('this.render = function(pos){ '+ 
     255      'if (this.state=="idle"){this.state="running";'+ 
     256      codeForEvent(this.options,'beforeSetup')+ 
     257      (this.setup ? 'this.setup();':'')+  
     258      codeForEvent(this.options,'afterSetup')+ 
     259      '};if (this.state=="running"){'+ 
     260      'pos=this.options.transition(pos)*'+this.fromToDelta+'+'+this.options.from+';'+ 
     261      'this.position=pos;'+ 
     262      codeForEvent(this.options,'beforeUpdate')+ 
     263      (this.update ? 'this.update(pos);':'')+ 
     264      codeForEvent(this.options,'afterUpdate')+ 
     265      '}}'); 
     266     
    266267    this.event('beforeStart'); 
    267     if(!this.options.sync) 
    268       Effect.Queues.get(typeof this.options.queue == 'string' ?  
     268    if (!this.options.sync) 
     269      Effect.Queues.get(Object.isString(this.options.queue) ?  
    269270        'global' : this.options.queue.scope).add(this); 
    270271  }, 
    271272  loop: function(timePos) { 
    272     if(timePos >= this.startOn) { 
    273       if(timePos >= this.finishOn) { 
     273    if (timePos >= this.startOn) { 
     274      if (timePos >= this.finishOn) { 
    274275        this.render(1.0); 
    275276        this.cancel(); 
    276277        this.event('beforeFinish'); 
    277         if(this.finish) this.finish();  
     278        if (this.finish) this.finish();  
    278279        this.event('afterFinish'); 
    279280        return;   
    280281      } 
    281       var pos   = (timePos - this.startOn) / (this.finishOn - this.startOn); 
    282       var frame = Math.round(pos * this.options.fps * this.options.duration); 
    283       if(frame > this.currentFrame) { 
     282      var pos   = (timePos - this.startOn) / this.totalTime, 
     283          frame = (pos * this.totalFrames).round(); 
     284      if (frame > this.currentFrame) { 
    284285        this.render(pos); 
    285286        this.currentFrame = frame; 
     
    287288    } 
    288289  }, 
    289   render: function(pos) { 
    290     if(this.state == 'idle') { 
    291       this.state = 'running'; 
    292       this.event('beforeSetup'); 
    293       if(this.setup) this.setup(); 
    294       this.event('afterSetup'); 
    295     } 
    296     if(this.state == 'running') { 
    297       if(this.options.transition) pos = this.options.transition(pos); 
    298       pos *= (this.options.to-this.options.from); 
    299       pos += this.options.from; 
    300       this.position = pos; 
    301       this.event('beforeUpdate'); 
    302       if(this.update) this.update(pos); 
    303       this.event('afterUpdate'); 
    304     } 
    305   }, 
    306290  cancel: function() { 
    307     if(!this.options.sync) 
    308       Effect.Queues.get(typeof this.options.queue == 'string' ?  
     291    if (!this.options.sync) 
     292      Effect.Queues.get(Object.isString(this.options.queue) ?  
    309293        'global' : this.options.queue.scope).remove(this); 
    310294    this.state = 'finished'; 
    311295  }, 
    312296  event: function(eventName) { 
    313     if(this.options[eventName + 'Internal']) this.options[eventName + 'Internal'](this); 
    314     if(this.options[eventName]) this.options[eventName](this); 
     297    if (this.options[eventName + 'Internal']) this.options[eventName + 'Internal'](this); 
     298    if (this.options[eventName]) this.options[eventName](this); 
    315299  }, 
    316300  inspect: function() { 
    317301    var data = $H(); 
    318302    for(property in this) 
    319       if(typeof this[property] != 'function') data[property] = this[property]
     303      if (!Object.isFunction(this[property])) data.set(property, this[property])
    320304    return '#<Effect:' + data.inspect() + ',options:' + $H(this.options).inspect() + '>'; 
    321305  } 
    322 
    323  
    324 Effect.Parallel = Class.create(); 
    325 Object.extend(Object.extend(Effect.Parallel.prototype, Effect.Base.prototype), { 
     306}); 
     307 
     308Effect.Parallel = Class.create(Effect.Base, { 
    326309  initialize: function(effects) { 
    327310    this.effects = effects || []; 
     
    336319      effect.cancel(); 
    337320      effect.event('beforeFinish'); 
    338       if(effect.finish) effect.finish(position); 
     321      if (effect.finish) effect.finish(position); 
    339322      effect.event('afterFinish'); 
    340323    }); 
     
    342325}); 
    343326 
    344 Effect.Event = Class.create(); 
    345 Object.extend(Object.extend(Effect.Event.prototype, Effect.Base.prototype), { 
     327Effect.Tween = Class.create(Effect.Base, { 
     328  initialize: function(object, from, to) { 
     329    object = Object.isString(object) ? $(object) : object; 
     330    var args = $A(arguments), method = args.last(),  
     331      options = args.length == 5 ? args[3] : null; 
     332    this.method = Object.isFunction(method) ? method.bind(object) : 
     333      Object.isFunction(object[method]) ? object[method].bind(object) :  
     334      function(value) { object[method] = value }; 
     335    this.start(Object.extend({ from: from, to: to }, options || { })); 
     336  }, 
     337  update: function(position) { 
     338    this.method(position); 
     339  } 
     340}); 
     341 
     342Effect.Event = Class.create(Effect.Base, { 
    346343  initialize: function() { 
    347     var options = Object.extend({ 
    348       duration: 0 
    349     }, arguments[0] || {}); 
    350     this.start(options); 
     344    this.start(Object.extend({ duration: 0 }, arguments[0] || { })); 
    351345  }, 
    352346  update: Prototype.emptyFunction 
    353347}); 
    354348 
    355 Effect.Opacity = Class.create(); 
    356 Object.extend(Object.extend(Effect.Opacity.prototype, Effect.Base.prototype), { 
     349Effect.Opacity = Class.create(Effect.Base, { 
    357350  initialize: function(element) { 
    358351    this.element = $(element); 
    359     if(!this.element) throw(Effect._elementDoesNotExistError); 
     352    if (!this.element) throw(Effect._elementDoesNotExistError); 
    360353    // make this work on IE on elements without 'layout' 
    361     if(/MSIE/.test(navigator.userAgent) && !window.opera && (!this.element.currentStyle.hasLayout)) 
     354    if (Prototype.Browser.IE && (!this.element.currentStyle.hasLayout)) 
    362355      this.element.setStyle({zoom: 1}); 
    363356    var options = Object.extend({ 
    364357      from: this.element.getOpacity() || 0.0, 
    365358      to:   1.0 
    366     }, arguments[1] || {}); 
     359    }, arguments[1] || { }); 
    367360    this.start(options); 
    368361  }, 
     
    372365}); 
    373366 
    374 Effect.Move = Class.create(); 
    375 Object.extend(Object.extend(Effect.Move.prototype, Effect.Base.prototype), { 
     367Effect.Move = Class.create(Effect.Base, { 
    376368  initialize: function(element) { 
    377369    this.element = $(element); 
    378     if(!this.element) throw(Effect._elementDoesNotExistError); 
     370    if (!this.element) throw(Effect._elementDoesNotExistError); 
    379371    var options = Object.extend({ 
    380372      x:    0, 
    381373      y:    0, 
    382374      mode: 'relative' 
    383     }, arguments[1] || {}); 
     375    }, arguments[1] || { }); 
    384376    this.start(options); 
    385377  }, 
    386378  setup: function() { 
    387     // Bug in Opera: Opera returns the "real" position of a static element or 
    388     // relative element that does not have top/left explicitly set. 
    389     // ==> Always set top and left for position relative elements in your stylesheets  
    390     // (to 0 if you do not need them)  
    391379    this.element.makePositioned(); 
    392380    this.originalLeft = parseFloat(this.element.getStyle('left') || '0'); 
    393381    this.originalTop  = parseFloat(this.element.getStyle('top')  || '0'); 
    394     if(this.options.mode == 'absolute') { 
    395       // absolute movement, so we need to calc deltaX and deltaY 
     382    if (this.options.mode == 'absolute') { 
    396383      this.options.x = this.options.x - this.originalLeft; 
    397384      this.options.y = this.options.y - this.originalTop; 
     
    400387  update: function(position) { 
    401388    this.element.setStyle({ 
    402       left: Math.round(this.options.x  * position + this.originalLeft) + 'px', 
    403       top:  Math.round(this.options.y  * position + this.originalTop)  + 'px' 
     389      left: (this.options.x  * position + this.originalLeft).round() + 'px', 
     390      top:  (this.options.y  * position + this.originalTop).round()  + 'px' 
    404391    }); 
    405392  } 
     
    409396Effect.MoveBy = function(element, toTop, toLeft) { 
    410397  return new Effect.Move(element,  
    411     Object.extend({ x: toLeft, y: toTop }, arguments[3] || {})); 
    412 }; 
    413  
    414 Effect.Scale = Class.create(); 
    415 Object.extend(Object.extend(Effect.Scale.prototype, Effect.Base.prototype), { 
     398    Object.extend({ x: toLeft, y: toTop }, arguments[3] || { })); 
     399}; 
     400 
     401Effect.Scale = Class.create(Effect.Base, { 
    416402  initialize: function(element, percent) { 
    417403    this.element = $(element); 
    418     if(!this.element) throw(Effect._elementDoesNotExistError); 
     404    if (!this.element) throw(Effect._elementDoesNotExistError); 
    419405    var options = Object.extend({ 
    420406      scaleX: true, 
     
    422408      scaleContent: true, 
    423409      scaleFromCenter: false, 
    424       scaleMode: 'box',        // 'box' or 'contents' or {} with provided values 
     410      scaleMode: 'box',        // 'box' or 'contents' or { } with provided values 
    425411      scaleFrom: 100.0, 
    426412      scaleTo:   percent 
    427     }, arguments[2] || {}); 
     413    }, arguments[2] || { }); 
    428414    this.start(options); 
    429415  }, 
     
    432418    this.elementPositioning = this.element.getStyle('position'); 
    433419     
    434     this.originalStyle = {}; 
     420    this.originalStyle = { }; 
    435421    ['top','left','width','height','fontSize'].each( function(k) { 
    436422      this.originalStyle[k] = this.element.style[k]; 
     
    442428    var fontSize = this.element.getStyle('font-size') || '100%'; 
    443429    ['em','px','%','pt'].each( function(fontSizeType) { 
    444       if(fontSize.indexOf(fontSizeType)>0) { 
     430      if (fontSize.indexOf(fontSizeType)>0) { 
    445431        this.fontSize     = parseFloat(fontSize); 
    446432        this.fontSizeType = fontSizeType; 
     
    451437     
    452438    this.dims = null; 
    453     if(this.options.scaleMode=='box') 
     439    if (this.options.scaleMode=='box') 
    454440      this.dims = [this.element.offsetHeight, this.element.offsetWidth]; 
    455     if(/^content/.test(this.options.scaleMode)) 
     441    if (/^content/.test(this.options.scaleMode)) 
    456442      this.dims = [this.element.scrollHeight, this.element.scrollWidth]; 
    457     if(!this.dims) 
     443    if (!this.dims) 
    458444      this.dims = [this.options.scaleMode.originalHeight, 
    459445                   this.options.scaleMode.originalWidth]; 
     
    461447  update: function(position) { 
    462448    var currentScale = (this.options.scaleFrom/100.0) + (this.factor * position); 
    463     if(this.options.scaleContent && this.fontSize) 
     449    if (this.options.scaleContent && this.fontSize) 
    464450      this.element.setStyle({fontSize: this.fontSize * currentScale + this.fontSizeType }); 
    465451    this.setDimensions(this.dims[0] * currentScale, this.dims[1] * currentScale); 
    466452  }, 
    467453  finish: function(position) { 
    468     if(this.restoreAfterFinish) this.element.setStyle(this.originalStyle); 
     454    if (this.restoreAfterFinish) this.element.setStyle(this.originalStyle); 
    469455  }, 
    470456  setDimensions: function(height, width) { 
    471     var d = {}; 
    472     if(this.options.scaleX) d.width = Math.round(width) + 'px'; 
    473     if(this.options.scaleY) d.height = Math.round(height) + 'px'; 
    474     if(this.options.scaleFromCenter) { 
     457    var d = { }; 
     458    if (this.options.scaleX) d.width = width.round() + 'px'; 
     459    if (this.options.scaleY) d.height = height.round() + 'px'; 
     460    if (this.options.scaleFromCenter) { 
    475461      var topd  = (height - this.dims[0])/2; 
    476462      var leftd = (width  - this.dims[1])/2; 
    477       if(this.elementPositioning == 'absolute') { 
    478         if(this.options.scaleY) d.top = this.originalTop-topd + 'px'; 
    479         if(this.options.scaleX) d.left = this.originalLeft-leftd + 'px'; 
     463      if (this.elementPositioning == 'absolute') { 
     464        if (this.options.scaleY) d.top = this.originalTop-topd + 'px'; 
     465        if (this.options.scaleX) d.left = this.originalLeft-leftd + 'px'; 
    480466      } else { 
    481         if(this.options.scaleY) d.top = -topd + 'px'; 
    482         if(this.options.scaleX) d.left = -leftd + 'px'; 
     467        if (this.options.scaleY) d.top = -topd + 'px'; 
     468        if (this.options.scaleX) d.left = -leftd + 'px'; 
    483469      } 
    484470    } 
     
    487473}); 
    488474 
    489 Effect.Highlight = Class.create(); 
    490 Object.extend(Object.extend(Effect.Highlight.prototype, Effect.Base.prototype), { 
     475Effect.Highlight = Class.create(Effect.Base, { 
    491476  initialize: function(element) { 
    492477    this.element = $(element); 
    493     if(!this.element) throw(Effect._elementDoesNotExistError); 
    494     var options = Object.extend({ startcolor: '#ffff99' }, arguments[1] || {}); 
     478    if (!this.element) throw(Effect._elementDoesNotExistError); 
     479    var options = Object.extend({ startcolor: '#ffff99' }, arguments[1] || { }); 
    495480    this.start(options); 
    496481  }, 
    497482  setup: function() { 
    498483    // Prevent executing on elements not in the layout flow 
    499     if(this.element.getStyle('display')=='none') { this.cancel(); return; } 
     484    if (this.element.getStyle('display')=='none') { this.cancel(); return; } 
    500485    // Disable background image during the effect 
    501     this.oldStyle = {}; 
     486    this.oldStyle = { }; 
    502487    if (!this.options.keepBackgroundImage) { 
    503488      this.oldStyle.backgroundImage = this.element.getStyle('background-image'); 
    504489      this.element.setStyle({backgroundImage: 'none'}); 
    505490    } 
    506     if(!this.options.endcolor) 
     491    if (!this.options.endcolor) 
    507492      this.options.endcolor = this.element.getStyle('background-color').parseColor('#ffffff'); 
    508     if(!this.options.restorecolor) 
     493    if (!this.options.restorecolor) 
    509494      this.options.restorecolor = this.element.getStyle('background-color'); 
    510495    // init color calculations 
     
    514499  update: function(position) { 
    515500    this.element.setStyle({backgroundColor: $R(0,2).inject('#',function(m,v,i){ 
    516       return m+(Math.round(this._base[i]+(this._delta[i]*position)).toColorPart()); }.bind(this)) }); 
     501      return m+((this._base[i]+(this._delta[i]*position)).round().toColorPart()); }.bind(this)) }); 
    517502  }, 
    518503  finish: function() { 
     
    523508}); 
    524509 
    525 Effect.ScrollTo = Class.create(); 
    526 Object.extend(Object.extend(Effect.ScrollTo.prototype, Effect.Base.prototype), { 
    527   initialize: function(element) { 
    528     this.element = $(element); 
    529     this.start(arguments[1] || {}); 
    530   }, 
    531   setup: function() { 
    532     Position.prepare(); 
    533     var offsets = Position.cumulativeOffset(this.element); 
    534     if(this.options.offset) offsets[1] += this.options.offset; 
    535     var max = window.innerHeight ?  
    536       window.height - window.innerHeight : 
    537       document.body.scrollHeight -  
    538         (document.documentElement.clientHeight ?  
    539           document.documentElement.clientHeight : document.body.clientHeight); 
    540     this.scrollStart = Position.deltaY; 
    541     this.delta = (offsets[1] > max ? max : offsets[1]) - this.scrollStart; 
    542   }, 
    543   update: function(position) { 
    544     Position.prepare(); 
    545     window.scrollTo(Position.deltaX,  
    546       this.scrollStart + (position*this.delta)); 
    547   } 
    548 }); 
     510Effect.ScrollTo = function(element) { 
     511  var options = arguments[1] || { }, 
     512    scrollOffsets = document.viewport.getScrollOffsets(), 
     513    elementOffsets = $(element).cumulativeOffset(), 
     514    max = (window.height || document.body.scrollHeight) - document.viewport.getHeight();   
     515 
     516  if (options.offset) elementOffsets[1] += options.offset; 
     517 
     518  return new Effect.Tween(null, 
     519    scrollOffsets.top, 
     520    elementOffsets[1] > max ? max : elementOffsets[1], 
     521    options, 
     522    function(p){ scrollTo(scrollOffsets.left, p.round()) } 
     523  ); 
     524}; 
    549525 
    550526/* ------------- combination effects ------------- */ 
     
    554530  var oldOpacity = element.getInlineOpacity(); 
    555531  var options = Object.extend({ 
    556   from: element.getOpacity() || 1.0, 
    557   to:   0.0, 
    558   afterFinishInternal: function(effect) {  
    559     if(effect.options.to!=0) return; 
    560     effect.element.hide().setStyle({opacity: oldOpacity});  
    561   }}, arguments[1] || {}); 
     532    from: element.getOpacity() || 1.0, 
     533    to:   0.0, 
     534    afterFinishInternal: function(effect) {  
     535      if (effect.options.to!=0) return; 
     536      effect.element.hide().setStyle({opacity: oldOpacity});  
     537    } 
     538  }, arguments[1] || { }); 
    562539  return new Effect.Opacity(element,options); 
    563 } 
     540}; 
    564541 
    565542Effect.Appear = function(element) { 
     
    574551  beforeSetup: function(effect) { 
    575552    effect.element.setOpacity(effect.options.from).show();  
    576   }}, arguments[1] || {}); 
     553  }}, arguments[1] || { }); 
    577554  return new Effect.Opacity(element,options); 
    578 } 
     555}; 
    579556 
    580557Effect.Puff = function(element) { 
     
    598575      afterFinishInternal: function(effect) { 
    599576         effect.effects[0].element.hide().setStyle(oldStyle); } 
    600      }, arguments[1] || {}) 
     577     }, arguments[1] || { }) 
    601578   ); 
    602 } 
     579}; 
    603580 
    604581Effect.BlindUp = function(element) { 
     
    612589        effect.element.hide().undoClipping(); 
    613590      }  
    614     }, arguments[1] || {}) 
     591    }, arguments[1] || { }) 
    615592  ); 
    616 } 
     593}; 
    617594 
    618595Effect.BlindDown = function(element) { 
     
    631608      effect.element.undoClipping(); 
    632609    } 
    633   }, arguments[1] || {})); 
    634 } 
     610  }, arguments[1] || { })); 
     611}; 
    635612 
    636613Effect.SwitchOff = function(element) { 
     
    653630      }) 
    654631    } 
    655   }, arguments[1] || {})); 
    656 } 
     632  }, arguments[1] || { })); 
     633}; 
    657634 
    658635Effect.DropOut = function(element) { 
     
    673650          effect.effects[0].element.hide().undoPositioned().setStyle(oldStyle); 
    674651        }  
    675       }, arguments[1] || {})); 
    676 } 
     652      }, arguments[1] || { })); 
     653}; 
    677654 
    678655Effect.Shake = function(element) { 
    679656  element = $(element); 
     657  var options = Object.extend({ 
     658    distance: 20, 
     659    duration: 0.5 
     660  }, arguments[1] || {}); 
     661  var distance = parseFloat(options.distance); 
     662  var split = parseFloat(options.duration) / 10.0; 
    680663  var oldStyle = { 
    681664    top: element.getStyle('top'), 
    682665    left: element.getStyle('left') }; 
    683     return new Effect.Move(element,  
    684       { x:  20, y: 0, duration: 0.05, afterFinishInternal: function(effect) { 
     666    return new Effect.Move(element, 
     667      { x:  distance, y: 0, duration: split, afterFinishInternal: function(effect) { 
    685668    new Effect.Move(effect.element, 
    686       { x: -40, y: 0, duration: 0.1,  afterFinishInternal: function(effect) { 
     669      { x: -distance*2, y: 0, duration: split*2,  afterFinishInternal: function(effect) { 
    687670    new Effect.Move(effect.element, 
    688       { x:  40, y: 0, duration: 0.1,  afterFinishInternal: function(effect) { 
     671      { x:  distance*2, y: 0, duration: split*2,  afterFinishInternal: function(effect) { 
    689672    new Effect.Move(effect.element, 
    690       { x: -40, y: 0, duration: 0.1,  afterFinishInternal: function(effect) { 
     673      { x: -distance*2, y: 0, duration: split*2,  afterFinishInternal: function(effect) { 
    691674    new Effect.Move(effect.element, 
    692       { x:  40, y: 0, duration: 0.1,  afterFinishInternal: function(effect) { 
     675      { x:  distance*2, y: 0, duration: split*2,  afterFinishInternal: function(effect) { 
    693676    new Effect.Move(effect.element, 
    694       { x: -20, y: 0, duration: 0.05, afterFinishInternal: function(effect) { 
     677      { x: -distance, y: 0, duration: split, afterFinishInternal: function(effect) { 
    695678        effect.element.undoPositioned().setStyle(oldStyle); 
    696679  }}) }}) }}) }}) }}) }}); 
    697 } 
     680}; 
    698681 
    699682Effect.SlideDown = function(element) { 
     
    711694      effect.element.makePositioned(); 
    712695      effect.element.down().makePositioned(); 
    713       if(window.opera) effect.element.setStyle({top: ''}); 
     696      if (window.opera) effect.element.setStyle({top: ''}); 
    714697      effect.element.makeClipping().setStyle({height: '0px'}).show();  
    715698    }, 
     
    721704      effect.element.undoClipping().undoPositioned(); 
    722705      effect.element.down().undoPositioned().setStyle({bottom: oldInnerBottom}); } 
    723     }, arguments[1] || {}) 
     706    }, arguments[1] || { }) 
    724707  ); 
    725 } 
     708}; 
    726709 
    727710Effect.SlideUp = function(element) { 
    728711  element = $(element).cleanWhitespace(); 
    729712  var oldInnerBottom = element.down().getStyle('bottom'); 
     713  var elementDimensions = element.getDimensions(); 
    730714  return new Effect.Scale(element, window.opera ? 0 : 1, 
    731715   Object.extend({ scaleContent: false,  
     
    733717    scaleMode: 'box', 
    734718    scaleFrom: 100, 
     719    scaleMode: {originalHeight: elementDimensions.height, originalWidth: elementDimensions.width}, 
    735720    restoreAfterFinish: true, 
    736     beforeStartInternal: function(effect) { 
     721    afterSetup: function(effect) { 
    737722      effect.element.makePositioned(); 
    738723      effect.element.down().makePositioned(); 
    739       if(window.opera) effect.element.setStyle({top: ''}); 
     724      if (window.opera) effect.element.setStyle({top: ''}); 
    740725      effect.element.makeClipping().show(); 
    741726    },   
     
    745730    }, 
    746731    afterFinishInternal: function(effect) { 
    747       effect.element.hide().undoClipping().undoPositioned().setStyle({bottom: oldInnerBottom})
    748       effect.element.down().undoPositioned()
    749     } 
    750    }, arguments[1] || {}) 
     732      effect.element.hide().undoClipping().undoPositioned()
     733      effect.element.down().undoPositioned().setStyle({bottom: oldInnerBottom})
     734    } 
     735   }, arguments[1] || { }) 
    751736  ); 
    752 } 
     737}; 
    753738 
    754739// Bug in opera makes the TD containing this element expand for a instance after finish  
     
    763748    } 
    764749  }); 
    765 } 
     750}; 
    766751 
    767752Effect.Grow = function(element) { 
     
    772757    scaleTransition: Effect.Transitions.sinoidal, 
    773758    opacityTransition: Effect.Transitions.full 
    774   }, arguments[1] || {}); 
     759  }, arguments[1] || { }); 
    775760  var oldStyle = { 
    776761    top: element.style.top, 
     
    837822    } 
    838823  }); 
    839 } 
     824}; 
    840825 
    841826Effect.Shrink = function(element) { 
     
    846831    scaleTransition: Effect.Transitions.sinoidal, 
    847832    opacityTransition: Effect.Transitions.none 
    848   }, arguments[1] || {}); 
     833  }, arguments[1] || { }); 
    849834  var oldStyle = { 
    850835    top: element.style.top, 
     
    891876       }, options) 
    892877  ); 
    893 } 
     878}; 
    894879 
    895880Effect.Pulsate = function(element) { 
    896881  element = $(element); 
    897   var options    = arguments[1] || {}; 
     882  var options    = arguments[1] || { }; 
    898883  var oldOpacity = element.getInlineOpacity(); 
    899884  var transition = options.transition || Effect.Transitions.sinoidal; 
     
    904889      afterFinishInternal: function(effect) { effect.element.setStyle({opacity: oldOpacity}); } 
    905890    }, options), {transition: reverser})); 
    906 } 
     891}; 
    907892 
    908893Effect.Fold = function(element) { 
     
    924909        effect.element.hide().undoClipping().setStyle(oldStyle); 
    925910      } }); 
    926   }}, arguments[1] || {})); 
    927 }; 
    928  
    929 Effect.Morph = Class.create(); 
    930 Object.extend(Object.extend(Effect.Morph.prototype, Effect.Base.prototype), { 
     911  }}, arguments[1] || { })); 
     912}; 
     913 
     914Effect.Morph = Class.create(Effect.Base, { 
    931915  initialize: function(element) { 
    932916    this.element = $(element); 
    933     if(!this.element) throw(Effect._elementDoesNotExistError); 
     917    if (!this.element) throw(Effect._elementDoesNotExistError); 
    934918    var options = Object.extend({ 
    935       style: {} 
    936     }, arguments[1] || {}); 
    937     if (typeof options.style == 'string') { 
    938       if(options.style.indexOf(':') == -1) { 
    939         var cssText = '', selector = '.' + options.style; 
    940         $A(document.styleSheets).reverse().each(function(styleSheet) { 
    941           if (styleSheet.cssRules) cssRules = styleSheet.cssRules; 
    942           else if (styleSheet.rules) cssRules = styleSheet.rules; 
    943           $A(cssRules).reverse().each(function(rule) { 
    944             if (selector == rule.selectorText) { 
    945               cssText = rule.style.cssText; 
    946               throw $break; 
    947             } 
    948           }); 
    949           if (cssText) throw $break; 
     919      style: { } 
     920    }, arguments[1] || { }); 
     921     
     922    if (!Object.isString(options.style)) this.style = $H(options.style); 
     923    else { 
     924      if (options.style.include(':')) 
     925        this.style = options.style.parseStyle(); 
     926      else { 
     927        this.element.addClassName(options.style); 
     928        this.style = $H(this.element.getStyles()); 
     929        this.element.removeClassName(options.style); 
     930        var css = this.element.getStyles(); 
     931        this.style = this.style.reject(function(style) { 
     932          return style.value == css[style.key]; 
    950933        }); 
    951         this.style = cssText.parseStyle(); 
    952         options.afterFinishInternal = function(effect){ 
     934        options.afterFinishInternal = function(effect) { 
    953935          effect.element.addClassName(effect.options.style); 
    954936          effect.transforms.each(function(transform) { 
    955             if(transform.style != 'opacity') 
    956               effect.element.style[transform.style.camelize()] = ''; 
     937            effect.element.style[transform.style] = ''; 
    957938          }); 
    958939        } 
    959       } else this.style = options.style.parseStyle(); 
    960     } else this.style = $H(options.style) 
     940      } 
     941    } 
    961942    this.start(options); 
    962943  }, 
     944   
    963945  setup: function(){ 
    964946    function parseColor(color){ 
    965       if(!color || ['rgba(0, 0, 0, 0)','transparent'].include(color)) color = '#ffffff'; 
     947      if (!color || ['rgba(0, 0, 0, 0)','transparent'].include(color)) color = '#ffffff'; 
    966948      color = color.parseColor(); 
    967949      return $R(0,2).map(function(i){ 
     
    970952    } 
    971953    this.transforms = this.style.map(function(pair){ 
    972       var property = pair[0].underscore().dasherize(), value = pair[1], unit = null; 
    973  
    974       if(value.parseColor('#zzzzzz') != '#zzzzzz') { 
     954      var property = pair[0], value = pair[1], unit = null; 
     955 
     956      if (value.parseColor('#zzzzzz') != '#zzzzzz') { 
    975957        value = value.parseColor(); 
    976958        unit  = 'color'; 
    977       } else if(property == 'opacity') { 
     959      } else if (property == 'opacity') { 
    978960        value = parseFloat(value); 
    979         if(/MSIE/.test(navigator.userAgent) && !window.opera && (!this.element.currentStyle.hasLayout)) 
     961        if (Prototype.Browser.IE && (!this.element.currentStyle.hasLayout)) 
    980962          this.element.setStyle({zoom: 1}); 
    981       } else if(Element.CSS_LENGTH.test(value))  
    982         var components = value.match(/^([\+\-]?[0-9\.]+)(.*)$/), 
    983           value = parseFloat(components[1]), unit = (components.length == 3) ? components[2] : null; 
     963      } else if (Element.CSS_LENGTH.test(value)) { 
     964          var components = value.match(/^([\+\-]?[0-9\.]+)(.*)$/); 
     965          value = parseFloat(components[1]); 
     966          unit = (components.length == 3) ? components[2] : null; 
     967      } 
    984968 
    985969      var originalValue = this.element.getStyle(property); 
    986       return $H({  
    987         style: property,  
     970      return {  
     971        style: property.camelize(),  
    988972        originalValue: unit=='color' ? parseColor(originalValue) : parseFloat(originalValue || 0),  
    989973        targetValue: unit=='color' ? parseColor(value) : value, 
    990974        unit: unit 
    991       })
     975      }
    992976    }.bind(this)).reject(function(transform){ 
    993977      return ( 
     
    1001985  }, 
    1002986  update: function(position) { 
    1003     var style = $H(), value = null; 
    1004     this.transforms.each(function(transform){ 
    1005       value = transform.unit=='color' ? 
    1006         $R(0,2).inject('#',function(m,v,i){ 
    1007           return m+(Math.round(transform.originalValue[i]+ 
    1008             (transform.targetValue[i] - transform.originalValue[i])*position)).toColorPart() }) :  
    1009         transform.originalValue + Math.round( 
    1010           ((transform.targetValue - transform.originalValue) * position) * 1000)/1000 + transform.unit; 
    1011       style[transform.style] = value; 
    1012     }); 
    1013     this.element.setStyle(style); 
     987    var style = { }, transform, i = this.transforms.length; 
     988    while(i--) 
     989      style[(transform = this.transforms[i]).style] =  
     990        transform.unit=='color' ? '#'+ 
     991          (Math.round(transform.originalValue[0]+ 
     992            (transform.targetValue[0]-transform.originalValue[0])*position)).toColorPart() + 
     993          (Math.round(transform.originalValue[1]+ 
     994            (transform.targetValue[1]-transform.originalValue[1])*position)).toColorPart() + 
     995          (Math.round(transform.originalValue[2]+ 
     996            (transform.targetValue[2]-transform.originalValue[2])*position)).toColorPart() : 
     997        (transform.originalValue + 
     998          (transform.targetValue - transform.originalValue) * position).toFixed(3) +  
     999            (transform.unit === null ? '' : transform.unit); 
     1000    this.element.setStyle(style, true); 
    10141001  } 
    10151002}); 
    10161003 
    1017 Effect.Transform = Class.create(); 
    1018 Object.extend(Effect.Transform.prototype, { 
     1004Effect.Transform = Class.create({ 
    10191005  initialize: function(tracks){ 
    10201006    this.tracks  = []; 
    1021     this.options = arguments[1] || {}; 
     1007    this.options = arguments[1] || { }; 
    10221008    this.addTracks(tracks); 
    10231009  }, 
    10241010  addTracks: function(tracks){ 
    10251011    tracks.each(function(track){ 
    1026       var data = $H(track).values().first(); 
     1012      track = $H(track); 
     1013      var data = track.values().first(); 
    10271014      this.tracks.push($H({ 
    1028         ids:     $H(track).keys().first(), 
     1015        ids:     track.keys().first(), 
    10291016        effect:  Effect.Morph, 
    10301017        options: { style: data } 
     
    10361023    return new Effect.Parallel( 
    10371024      this.tracks.map(function(track){ 
    1038         var elements = [$(track.ids) || $$(track.ids)].flatten(); 
    1039         return elements.map(function(e){ return new track.effect(e, Object.extend({ sync:true }, track.options)) }); 
     1025        var ids = track.get('ids'), effect = track.get('effect'), options = track.get('options'); 
     1026        var elements = [$(ids) || $$(ids)].flatten(); 
     1027        return elements.map(function(e){ return new effect(e, Object.extend({ sync:true }, options)) }); 
    10401028      }).flatten(), 
    10411029      this.options 
     
    10571045Element.CSS_LENGTH = /^(([\+\-]?[0-9\.]+)(em|ex|px|in|cm|mm|pt|pc|\%))|0$/; 
    10581046 
     1047String.__parseStyleElement = document.createElement('div'); 
    10591048String.prototype.parseStyle = function(){ 
    1060   var element = Element.extend(document.createElement('div')); 
    1061   element.innerHTML = '<div style="' + this + '"></div>'; 
    1062   var style = element.down().style, styleRules = $H(); 
     1049  var style, styleRules = $H(); 
     1050  if (Prototype.Browser.WebKit) 
     1051    style = new Element('div',{style:this}).style; 
     1052  else { 
     1053    String.__parseStyleElement.innerHTML = '<div style="' + this + '"></div>'; 
     1054    style = String.__parseStyleElement.childNodes[0].style; 
     1055  } 
    10631056   
    10641057  Element.CSS_PROPERTIES.each(function(property){ 
    1065     if(style[property]) styleRules[property] = style[property];  
     1058    if (style[property]) styleRules.set(property, style[property]);  
    10661059  }); 
    1067   if(/MSIE/.test(navigator.userAgent) && !window.opera && this.indexOf('opacity') > -1) { 
    1068     styleRules.opacity = this.match(/opacity:\s*((?:0|1)?(?:\.\d*)?)/)[1]; 
    1069   } 
     1060   
     1061  if (Prototype.Browser.IE && this.include('opacity')) 
     1062    styleRules.set('opacity', this.match(/opacity:\s*((?:0|1)?(?:\.\d*)?)/)[1]); 
     1063 
    10701064  return styleRules; 
    10711065}; 
    10721066 
    1073 Element.morph = function(element, style) { 
    1074   new Effect.Morph(element, Object.extend({ style: style }, arguments[2] || {})); 
    1075   return element; 
    1076 }; 
    1077  
    1078 ['setOpacity','getOpacity','getInlineOpacity','forceRerendering','setContentZoom', 
    1079  'collectTextNodes','collectTextNodesIgnoreClass','morph'].each(  
    1080   function(f) { Element.Methods[f] = Element[f]; } 
     1067if (document.defaultView && document.defaultView.getComputedStyle) { 
     1068  Element.getStyles = function(element) { 
     1069    var css = document.defaultView.getComputedStyle($(element), null); 
     1070    return Element.CSS_PROPERTIES.inject({ }, function(styles, property) { 
     1071      styles[property] = css[property]; 
     1072      return styles; 
     1073    }); 
     1074  }; 
     1075} else { 
     1076  Element.getStyles = function(element) { 
     1077    element = $(element); 
     1078    var css = element.currentStyle, styles; 
     1079    styles = Element.CSS_PROPERTIES.inject({ }, function(results, property) { 
     1080      results[property] = css[property]; 
     1081      return results; 
     1082    }); 
     1083    if (!styles.opacity) styles.opacity = element.getOpacity(); 
     1084    return styles; 
     1085  }; 
     1086}; 
     1087 
     1088Effect.Methods = { 
     1089  morph: function(element, style) { 
     1090    element = $(element); 
     1091    new Effect.Morph(element, Object.extend({ style: style }, arguments[2] || { })); 
     1092    return element; 
     1093  }, 
     1094  visualEffect: function(element, effect, options) { 
     1095    element = $(element) 
     1096    var s = effect.dasherize().camelize(), klass = s.charAt(0).toUpperCase() + s.substring(1); 
     1097    new Effect[klass](element, options); 
     1098    return element; 
     1099  }, 
     1100  highlight: function(element, options) { 
     1101    element = $(element); 
     1102    new Effect.Highlight(element, options); 
     1103    return element; 
     1104  } 
     1105}; 
     1106 
     1107$w('fade appear grow shrink fold blindUp blindDown slideUp slideDown '+ 
     1108  'pulsate shake puff squish switchOff dropOut').each( 
     1109  function(effect) {  
     1110    Effect.Methods[effect] = function(element, options){ 
     1111      element = $(element); 
     1112      Effect[effect.charAt(0).toUpperCase() + effect.substring(1)](element, options); 
     1113      return element; 
     1114    } 
     1115  } 
    10811116); 
    10821117 
    1083 Element.Methods.visualEffect = function(element, effect, options) { 
    1084   s = effect.gsub(/_/, '-').camelize(); 
    1085   effect_class = s.charAt(0).toUpperCase() + s.substring(1); 
    1086   new Effect[effect_class](element, options); 
    1087   return $(element); 
    1088 }; 
    1089  
    1090 Element.addMethods(); 
     1118$w('getInlineOpacity forceRerendering setContentZoom collectTextNodes collectTextNodesIgnoreClass getStyles').each(  
     1119  function(f) { Effect.Methods[f] = Element[f]; } 
     1120); 
     1121 
     1122Element.addMethods(Effect.Methods); 
  • branches/1.1/data/web/sf/prototype/js/prototype.js

    r3316 r7855  
    1 /*  Prototype JavaScript framework, version 1.5.0 
    2  *  (c) 2005-2007 Sam Stephenson 
     1/*  Prototype JavaScript framework, version 1.6.0.2 
     2 *  (c) 2005-2008 Sam Stephenson 
    33 * 
    44 *  Prototype is freely distributable under the terms of an MIT-style license. 
    5  *  For details, see the Prototype web site: http://prototype.conio.net
     5 *  For details, see the Prototype web site: http://www.prototypejs.org
    66 * 
    7 /*--------------------------------------------------------------------------*/ 
     7 *--------------------------------------------------------------------------*/ 
    88 
    99var Prototype = { 
    10   Version: '1.5.0', 
     10  Version: '1.6.0.2', 
     11 
     12  Browser: { 
     13    IE:     !!(window.attachEvent && !window.opera), 
     14    Opera:  !!window.opera, 
     15    WebKit: navigator.userAgent.indexOf('AppleWebKit/') > -1, 
     16    Gecko:  navigator.userAgent.indexOf('Gecko') > -1 && navigator.userAgent.indexOf('KHTML') == -1, 
     17    MobileSafari: !!navigator.userAgent.match(/Apple.*Mobile.*Safari/) 
     18  }, 
     19 
    1120  BrowserFeatures: { 
    12     XPath: !!document.evaluate 
    13   }, 
    14  
    15   ScriptFragment: '(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)', 
    16   emptyFunction: function() {}, 
     21    XPath: !!document.evaluate, 
     22    ElementExtensions: !!window.HTMLElement, 
     23    SpecificElementExtensions: 
     24      document.createElement('div').__proto__ && 
     25      document.createElement('div').__proto__ !== 
     26        document.createElement('form').__proto__ 
     27  }, 
     28 
     29  ScriptFragment: '<script[^>]*>([\\S\\s]*?)<\/script>', 
     30  JSONFilter: /^\/\*-secure-([\s\S]*)\*\/\s*$/, 
     31 
     32  emptyFunction: function() { }, 
    1733  K: function(x) { return x } 
    18 
    19  
     34}; 
     35 
     36if (Prototype.Browser.MobileSafari) 
     37  Prototype.BrowserFeatures.SpecificElementExtensions = false; 
     38 
     39 
     40/* Based on Alex Arnell's inheritance implementation. */ 
    2041var Class = { 
    2142  create: function() { 
    22     return function() { 
     43    var parent = null, properties = $A(arguments); 
     44    if (Object.isFunction(properties[0])) 
     45      parent = properties.shift(); 
     46 
     47    function klass() { 
    2348      this.initialize.apply(this, arguments); 
    2449    } 
    25   } 
    26 
    27  
    28 var Abstract = new Object(); 
     50 
     51    Object.extend(klass, Class.Methods); 
     52    klass.superclass = parent; 
     53    klass.subclasses = []; 
     54 
     55    if (parent) { 
     56      var subclass = function() { }; 
     57      subclass.prototype = parent.prototype; 
     58      klass.prototype = new subclass; 
     59      parent.subclasses.push(klass); 
     60    } 
     61 
     62    for (var i = 0; i < properties.length; i++) 
     63      klass.addMethods(properties[i]); 
     64 
     65    if (!klass.prototype.initialize) 
     66      klass.prototype.initialize = Prototype.emptyFunction; 
     67 
     68    klass.prototype.constructor = klass; 
     69 
     70    return klass; 
     71  } 
     72}; 
     73 
     74Class.Methods = { 
     75  addMethods: function(source) { 
     76    var ancestor   = this.superclass && this.superclass.prototype; 
     77    var properties = Object.keys(source); 
     78 
     79    if (!Object.keys({ toString: true }).length) 
     80      properties.push("toString", "valueOf"); 
     81 
     82    for (var i = 0, length = properties.length; i < length; i++) { 
     83      var property = properties[i], value = source[property]; 
     84      if (ancestor && Object.isFunction(value) && 
     85          value.argumentNames().first() == "$super") { 
     86        var method = value, value = Object.extend((function(m) { 
     87          return function() { return ancestor[m].apply(this, arguments) }; 
     88        })(property).wrap(method), { 
     89          valueOf:  function() { return method }, 
     90          toString: function() { return method.toString() } 
     91        }); 
     92      } 
     93      this.prototype[property] = value; 
     94    } 
     95 
     96    return this; 
     97  } 
     98}; 
     99 
     100var Abstract = { }; 
    29101 
    30102Object.extend = function(destination, source) { 
    31   for (var property in source) { 
     103  for (var property in source) 
    32104    destination[property] = source[property]; 
    33   } 
    34105  return destination; 
    35 } 
     106}; 
    36107 
    37108Object.extend(Object, { 
    38109  inspect: function(object) { 
    39110    try { 
    40       if (object === undefined) return 'undefined'; 
     111      if (Object.isUndefined(object)) return 'undefined'; 
    41112      if (object === null) return 'null'; 
    42       return object.inspect ? object.inspect() : object.toString(); 
     113      return object.inspect ? object.inspect() : String(object); 
    43114    } catch (e) { 
    44115      if (e instanceof RangeError) return '...'; 
    45116      throw e; 
    46117    } 
     118  }, 
     119 
     120  toJSON: function(object) { 
     121    var type = typeof object; 
     122    switch (type) { 
     123      case 'undefined': 
     124      case 'function': 
     125      case 'unknown': return; 
     126      case 'boolean': return object.toString(); 
     127    } 
     128 
     129    if (object === null) return 'null'; 
     130    if (object.toJSON) return object.toJSON(); 
     131    if (Object.isElement(object)) return; 
     132 
     133    var results = []; 
     134    for (var property in object) { 
     135      var value = Object.toJSON(object[property]); 
     136      if (!Object.isUndefined(value)) 
     137        results.push(property.toJSON() + ': ' + value); 
     138    } 
     139 
     140    return '{' + results.join(', ') + '}'; 
     141  }, 
     142 
     143  toQueryString: function(object) { 
     144    return $H(object).toQueryString(); 
     145  }, 
     146 
     147  toHTML: function(object) { 
     148    return object && object.toHTML ? object.toHTML() : String.interpret(object); 
    47149  }, 
    48150 
     
    62164 
    63165  clone: function(object) { 
    64     return Object.extend({}, object); 
     166    return Object.extend({ }, object); 
     167  }, 
     168 
     169  isElement: function(object) { 
     170    return object && object.nodeType == 1; 
     171  }, 
     172 
     173  isArray: function(object) { 
     174    return object != null && typeof object == "object" && 
     175      'splice' in object && 'join' in object; 
     176  }, 
     177 
     178  isHash: function(object) { 
     179    return object instanceof Hash; 
     180  }, 
     181 
     182  isFunction: function(object) { 
     183    return typeof object == "function"; 
     184  }, 
     185 
     186  isString: function(object) { 
     187    return typeof object == "string"; 
     188  }, 
     189 
     190  isNumber: function(object) { 
     191    return typeof object == "number"; 
     192  }, 
     193 
     194  isUndefined: function(object) { 
     195    return typeof object == "undefined"; 
    65196  } 
    66197}); 
    67198 
    68 Function.prototype.bind = function() { 
    69   var __method = this, args = $A(arguments), object = args.shift(); 
    70   return function() { 
    71     return __method.apply(object, args.concat($A(arguments))); 
    72   } 
    73 
    74  
    75 Function.prototype.bindAsEventListener = function(object) { 
    76   var __method = this, args = $A(arguments), object = args.shift(); 
    77   return function(event) { 
    78     return __method.apply(object, [( event || window.event)].concat(args).concat($A(arguments))); 
    79   } 
    80 
    81  
    82 Object.extend(Number.prototype, { 
    83   toColorPart: function() { 
    84     var digits = this.toString(16); 
    85     if (this < 16) return '0' + digits; 
    86     return digits; 
    87   }, 
    88  
    89   succ: function() { 
    90     return this + 1; 
    91   }, 
    92  
    93   times: function(iterator) { 
    94     $R(0, this, true).each(iterator); 
    95     return this; 
     199Object.extend(Function.prototype, { 
     200  argumentNames: function() { 
     201    var names = this.toString().match(/^[\s\(]*function[^(]*\((.*?)\)/)[1].split(",").invoke("strip"); 
     202    return names.length == 1 && !names[0] ? [] : names; 
     203  }, 
     204 
     205  bind: function() { 
     206    if (arguments.length < 2 && Object.isUndefined(arguments[0])) return this; 
     207    var __method = this, args = $A(arguments), object = args.shift(); 
     208    return function() { 
     209      return __method.apply(object, args.concat($A(arguments))); 
     210    } 
     211  }, 
     212 
     213  bindAsEventListener: function() { 
     214    var __method = this, args = $A(arguments), object = args.shift(); 
     215    return function(event) { 
     216      return __method.apply(object, [event || window.event].concat(args)); 
     217    } 
     218  }, 
     219 
     220  curry: function() { 
     221    if (!arguments.length) return this; 
     222    var __method = this, args = $A(arguments); 
     223    return function() { 
     224      return __method.apply(this, args.concat($A(arguments))); 
     225    } 
     226  }, 
     227 
     228  delay: function() { 
     229    var __method = this, args = $A(arguments), timeout = args.shift() * 1000; 
     230    return window.setTimeout(function() { 
     231      return __method.apply(__method, args); 
     232    }, timeout); 
     233  }, 
     234 
     235  wrap: function(wrapper) { 
     236    var __method = this; 
     237    return function() { 
     238      return wrapper.apply(this, [__method.bind(this)].concat($A(arguments))); 
     239    } 
     240  }, 
     241 
     242  methodize: function() { 
     243    if (this._methodized) return this._methodized; 
     244    var __method = this; 
     245    return this._methodized = function() { 
     246      return __method.apply(null, [this].concat($A(arguments))); 
     247    }; 
    96248  } 
    97249}); 
     250 
     251Function.prototype.defer = Function.prototype.delay.curry(0.01); 
     252 
     253Date.prototype.toJSON = function() { 
     254  return '"' + this.getUTCFullYear() + '-' + 
     255    (this.getUTCMonth() + 1).toPaddedString(2) + '-' + 
     256    this.getUTCDate().toPaddedString(2) + 'T' + 
     257    this.getUTCHours().toPaddedString(2) + ':' + 
     258    this.getUTCMinutes().toPaddedString(2) + ':' + 
     259    this.getUTCSeconds().toPaddedString(2) + 'Z"'; 
     260}; 
    98261 
    99262var Try = { 
     
    106269        returnValue = lambda(); 
    107270        break; 
    108       } catch (e) {
     271      } catch (e) {
    109272    } 
    110273 
    111274    return returnValue; 
    112275  } 
    113 
     276}; 
     277 
     278RegExp.prototype.match = RegExp.prototype.test; 
     279 
     280RegExp.escape = function(str) { 
     281  return String(str).replace(/([.*+?^=!:${}()|[\]\/\\])/g, '\\$1'); 
     282}; 
    114283 
    115284/*--------------------------------------------------------------------------*/ 
    116285 
    117 var PeriodicalExecuter = Class.create(); 
    118 PeriodicalExecuter.prototype = { 
     286var PeriodicalExecuter = Class.create({ 
    119287  initialize: function(callback, frequency) { 
    120288    this.callback = callback; 
     
    129297  }, 
    130298 
     299  execute: function() { 
     300    this.callback(this); 
     301  }, 
     302 
    131303  stop: function() { 
    132304    if (!this.timer) return; 
     
    139311      try { 
    140312        this.currentlyExecuting = true; 
    141         this.callback(this); 
     313        this.execute(); 
    142314      } finally { 
    143315        this.currentlyExecuting = false; 
     
    145317    } 
    146318  } 
    147 
    148 String.interpret = function(value){ 
    149   return value == null ? '' : String(value); 
    150 
     319}); 
     320Object.extend(String, { 
     321  interpret: function(value) { 
     322    return value == null ? '' : String(value); 
     323  }, 
     324  specialChar: { 
     325    '\b': '\\b', 
     326    '\t': '\\t', 
     327    '\n': '\\n', 
     328    '\f': '\\f', 
     329    '\r': '\\r', 
     330    '\\': '\\\\' 
     331  } 
     332}); 
    151333 
    152334Object.extend(String.prototype, { 
     
    169351  sub: function(pattern, replacement, count) { 
    170352    replacement = this.gsub.prepareReplacement(replacement); 
    171     count = count === undefined ? 1 : count; 
     353    count = Object.isUndefined(count) ? 1 : count; 
    172354 
    173355    return this.gsub(pattern, function(match) { 
     
    179361  scan: function(pattern, iterator) { 
    180362    this.gsub(pattern, iterator); 
    181     return this
     363    return String(this)
    182364  }, 
    183365 
    184366  truncate: function(length, truncation) { 
    185367    length = length || 30; 
    186     truncation = truncation === undefined ? '...' : truncation; 
     368    truncation = Object.isUndefined(truncation) ? '...' : truncation; 
    187369    return this.length > length ? 
    188       this.slice(0, length - truncation.length) + truncation : this
     370      this.slice(0, length - truncation.length) + truncation : String(this)
    189371  }, 
    190372 
     
    214396 
    215397  escapeHTML: function() { 
    216     var div = document.createElement('div'); 
    217     var text = document.createTextNode(this); 
    218     div.appendChild(text); 
    219     return div.innerHTML; 
     398    var self = arguments.callee; 
     399    self.text.data = this; 
     400    return self.div.innerHTML; 
    220401  }, 
    221402 
    222403  unescapeHTML: function() { 
    223     var div = document.createElement('div'); 
     404    var div = new Element('div'); 
    224405    div.innerHTML = this.stripTags(); 
    225406    return div.childNodes[0] ? (div.childNodes.length > 1 ? 
    226       $A(div.childNodes).inject('',function(memo,node){ return memo+node.nodeValue }) : 
     407      $A(div.childNodes).inject('', function(memo, node) { return memo+node.nodeValue }) : 
    227408      div.childNodes[0].nodeValue) : ''; 
    228409  }, 
     
    230411  toQueryParams: function(separator) { 
    231412    var match = this.strip().match(/([^?#]*)(#.*)?$/); 
    232     if (!match) return {}; 
    233  
    234     return match[1].split(separator || '&').inject({}, function(hash, pair) { 
     413    if (!match) return { }; 
     414 
     415    return match[1].split(separator || '&').inject({ }, function(hash, pair) { 
    235416      if ((pair = pair.split('='))[0]) { 
    236         var name = decodeURIComponent(pair[0]); 
    237         var value = pair[1] ? decodeURIComponent(pair[1]) : undefined
    238  
    239         if (hash[name] !== undefined) { 
    240           if (hash[name].constructor != Array) 
    241             hash[name] = [hash[name]]; 
    242           if (value) hash[name].push(value); 
     417        var key = decodeURIComponent(pair.shift()); 
     418        var value = pair.length > 1 ? pair.join('=') : pair[0]
     419        if (value != undefined) value = decodeURIComponent(value); 
     420 
     421        if (key in hash) { 
     422          if (!Object.isArray(hash[key])) hash[key] = [hash[key]]; 
     423          hash[key].push(value); 
    243424        } 
    244         else hash[name] = value; 
     425        else hash[key] = value; 
    245426      } 
    246427      return hash; 
     
    257438  }, 
    258439 
     440  times: function(count) { 
     441    return count < 1 ? '' : new Array(count + 1).join(this); 
     442  }, 
     443 
    259444  camelize: function() { 
    260445    var parts = this.split('-'), len = parts.length; 
     
    271456  }, 
    272457 
    273   capitalize: function()
     458  capitalize: function()
    274459    return this.charAt(0).toUpperCase() + this.substring(1).toLowerCase(); 
    275460  }, 
     
    284469 
    285470  inspect: function(useDoubleQuotes) { 
    286     var escapedString = this.replace(/\\/g, '\\\\'); 
    287     if (useDoubleQuotes) 
    288       return '"' + escapedString.replace(/"/g, '\\"') + '"'; 
    289     else 
    290       return "'" + escapedString.replace(/'/g, '\\\'') + "'"; 
     471    var escapedString = this.gsub(/[\x00-\x1f\\]/, function(match) { 
     472      var character = String.specialChar[match[0]]; 
     473      return character ? character : '\\u00' + match[0].charCodeAt().toPaddedString(2, 16); 
     474    }); 
     475    if (useDoubleQuotes) return '"' + escapedString.replace(/"/g, '\\"') + '"'; 
     476    return "'" + escapedString.replace(/'/g, '\\\'') + "'"; 
     477  }, 
     478 
     479  toJSON: function() { 
     480    return this.inspect(true); 
     481  }, 
     482 
     483  unfilterJSON: function(filter) { 
     484    return this.sub(filter || Prototype.JSONFilter, '#{1}'); 
     485  }, 
     486 
     487  isJSON: function() { 
     488    var str = this; 
     489    if (str.blank()) return false; 
     490    str = this.replace(/\\./g, '@').replace(/"[^"\\\n\r]*"/g, ''); 
     491    return (/^[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t]*$/).test(str); 
     492  }, 
     493 
     494  evalJSON: function(sanitize) { 
     495    var json = this.unfilterJSON(); 
     496    try { 
     497      if (!sanitize || json.isJSON()) return eval('(' + json + ')'); 
     498    } catch (e) { } 
     499    throw new SyntaxError('Badly formed JSON string: ' + this.inspect()); 
     500  }, 
     501 
     502  include: function(pattern) { 
     503    return this.indexOf(pattern) > -1; 
     504  }, 
     505 
     506  startsWith: function(pattern) { 
     507    return this.indexOf(pattern) === 0; 
     508  }, 
     509 
     510  endsWith: function(pattern) { 
     511    var d = this.length - pattern.length; 
     512    return d >= 0 && this.lastIndexOf(pattern) === d; 
     513  }, 
     514 
     515  empty: function() { 
     516    return this == ''; 
     517  }, 
     518 
     519  blank: function() { 
     520    return /^\s*$/.test(this); 
     521  }, 
     522 
     523  interpolate: function(object, pattern) { 
     524    return new Template(this, pattern).evaluate(object); 
    291525  } 
    292526}); 
    293527 
     528if (Prototype.Browser.WebKit || Prototype.Browser.IE) Object.extend(String.prototype, { 
     529  escapeHTML: function() { 
     530    return this.replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;'); 
     531  }, 
     532  unescapeHTML: function() { 
     533    return this.replace(/&amp;/g,'&').replace(/&lt;/g,'<').replace(/&gt;/g,'>'); 
     534  } 
     535}); 
     536 
    294537String.prototype.gsub.prepareReplacement = function(replacement) { 
    295   if (typeof replacement == 'function') return replacement; 
     538  if (Object.isFunction(replacement)) return replacement; 
    296539  var template = new Template(replacement); 
    297540  return function(match) { return template.evaluate(match) }; 
    298 } 
     541}; 
    299542 
    300543String.prototype.parseQuery = String.prototype.toQueryParams; 
    301544 
    302 var Template = Class.create(); 
    303 Template.Pattern = /(^|.|\r|\n)(#\{(.*?)\})/; 
    304 Template.prototype = { 
     545Object.extend(String.prototype.escapeHTML, { 
     546  div:  document.createElement('div'), 
     547  text: document.createTextNode('') 
     548}); 
     549 
     550with (String.prototype.escapeHTML) div.appendChild(text); 
     551 
     552var Template = Class.create({ 
    305553  initialize: function(template, pattern) { 
    306554    this.template = template.toString(); 
    307     this.pattern = pattern || Template.Pattern; 
     555    this.pattern = pattern || Template.Pattern; 
    308556  }, 
    309557 
    310558  evaluate: function(object) { 
     559    if (Object.isFunction(object.toTemplateReplacements)) 
     560      object = object.toTemplateReplacements(); 
     561 
    311562    return this.template.gsub(this.pattern, function(match) { 
    312       var before = match[1]; 
     563      if (object == null) return ''; 
     564 
     565      var before = match[1] || ''; 
    313566      if (before == '\\') return match[2]; 
    314       return before + String.interpret(object[match[3]]); 
     567 
     568      var ctx = object, expr = match[3]; 
     569      var pattern = /^([^.[]+|\[((?:.*?[^\\])?)\])(\.|\[|$)/; 
     570      match = pattern.exec(expr); 
     571      if (match == null) return before; 
     572 
     573      while (match != null) { 
     574        var comp = match[1].startsWith('[') ? match[2].gsub('\\\\]', ']') : match[1]; 
     575        ctx = ctx[comp]; 
     576        if (null == ctx || '' == match[3]) break; 
     577        expr = expr.substring('[' == match[3] ? match[1].length : match[0].length); 
     578        match = pattern.exec(expr); 
     579      } 
     580 
     581      return before + String.interpret(ctx); 
    315582    }); 
    316583  } 
    317 } 
    318  
    319 var $break    = new Object(); 
    320 var $continue = new Object()
     584}); 
     585Template.Pattern = /(^|.|\r|\n)(#\{(.*?)\})/; 
     586 
     587var $break = { }
    321588 
    322589var Enumerable = { 
    323   each: function(iterator) { 
     590  each: function(iterator, context) { 
    324591    var index = 0; 
     592    iterator = iterator.bind(context); 
    325593    try { 
    326594      this._each(function(value) { 
    327         try { 
    328           iterator(value, index++); 
    329         } catch (e) { 
    330           if (e != $continue) throw e; 
    331         } 
     595        iterator(value, index++); 
    332596      }); 
    333597    } catch (e) { 
     
    337601  }, 
    338602 
    339   eachSlice: function(number, iterator) { 
     603  eachSlice: function(number, iterator, context) { 
     604    iterator = iterator ? iterator.bind(context) : Prototype.K; 
    340605    var index = -number, slices = [], array = this.toArray(); 
    341606    while ((index += number) < array.length) 
    342607      slices.push(array.slice(index, index+number)); 
    343     return slices.map(iterator); 
    344   }, 
    345  
    346   all: function(iterator) { 
     608    return slices.collect(iterator, context); 
     609  }, 
     610 
     611  all: function(iterator, context) { 
     612    iterator = iterator ? iterator.bind(context) : Prototype.K; 
    347613    var result = true; 
    348614    this.each(function(value, index) { 
    349       result = result && !!(iterator || Prototype.K)(value, index); 
     615      result = result && !!iterator(value, index); 
    350616      if (!result) throw $break; 
    351617    }); 
     
    353619  }, 
    354620 
    355   any: function(iterator) { 
     621  any: function(iterator, context) { 
     622    iterator = iterator ? iterator.bind(context) : Prototype.K; 
    356623    var result = false; 
    357624    this.each(function(value, index) { 
    358       if (result = !!(iterator || Prototype.K)(value, index)) 
     625      if (result = !!iterator(value, index)) 
    359626        throw $break; 
    360627    }); 
     
    362629  }, 
    363630 
    364   collect: function(iterator) { 
     631  collect: function(iterator, context) { 
     632    iterator = iterator ? iterator.bind(context) : Prototype.K; 
    365633    var results = []; 
    366634    this.each(function(value, index) { 
    367       results.push((iterator || Prototype.K)(value, index)); 
     635      results.push(iterator(value, index)); 
    368636    }); 
    369637    return results; 
    370638  }, 
    371639 
    372   detect: function(iterator) { 
     640  detect: function(iterator, context) { 
     641    iterator = iterator.bind(context); 
    373642    var result; 
    374643    this.each(function(value, index) { 
     
    381650  }, 
    382651 
    383   findAll: function(iterator) { 
     652  findAll: function(iterator, context) { 
     653    iterator = iterator.bind(context); 
    384654    var results = []; 
    385655    this.each(function(value, index) { 
     
    390660  }, 
    391661 
    392   grep: function(pattern, iterator) { 
     662  grep: function(filter, iterator, context) { 
     663    iterator = iterator ? iterator.bind(context) : Prototype.K; 
    393664    var results = []; 
     665 
     666    if (Object.isString(filter)) 
     667      filter = new RegExp(filter); 
     668 
    394669    this.each(function(value, index) { 
    395       var stringValue = value.toString(); 
    396       if (stringValue.match(pattern)) 
    397         results.push((iterator || Prototype.K)(value, index)); 
    398     }) 
     670      if (filter.match(value)) 
     671        results.push(iterator(value, index)); 
     672    }); 
    399673    return results; 
    400674  }, 
    401675 
    402676  include: function(object) { 
     677    if (Object.isFunction(this.indexOf)) 
     678      if (this.indexOf(object) != -1) return true; 
     679 
    403680    var found = false; 
    404681    this.each(function(value) { 
     
    412689 
    413690  inGroupsOf: function(number, fillWith) { 
    414     fillWith = fillWith === undefined ? null : fillWith; 
     691    fillWith = Object.isUndefined(fillWith) ? null : fillWith; 
    415692    return this.eachSlice(number, function(slice) { 
    416693      while(slice.length < number) slice.push(fillWith); 
     
    419696  }, 
    420697 
    421   inject: function(memo, iterator) { 
     698  inject: function(memo, iterator, context) { 
     699    iterator = iterator.bind(context); 
    422700    this.each(function(value, index) { 
    423701      memo = iterator(memo, value, index); 
     
    433711  }, 
    434712 
    435   max: function(iterator) { 
     713  max: function(iterator, context) { 
     714    iterator = iterator ? iterator.bind(context) : Prototype.K; 
    436715    var result; 
    437716    this.each(function(value, index) { 
    438       value = (iterator || Prototype.K)(value, index); 
    439       if (result == undefined || value >= result) 
     717      value = iterator(value, index); 
     718      if (result == null || value >= result) 
    440719        result = value; 
    441720    }); 
     
    443722  }, 
    444723 
    445   min: function(iterator) { 
     724  min: function(iterator, context) { 
     725    iterator = iterator ? iterator.bind(context) : Prototype.K; 
    446726    var result; 
    447727    this.each(function(value, index) { 
    448       value = (iterator || Prototype.K)(value, index); 
    449       if (result == undefined || value < result) 
     728      value = iterator(value, index); 
     729      if (result == null || value < result) 
    450730        result = value; 
    451731    }); 
     
    453733  }, 
    454734 
    455   partition: function(iterator) { 
     735  partition: function(iterator, context) { 
     736    iterator = iterator ? iterator.bind(context) : Prototype.K; 
    456737    var trues = [], falses = []; 
    457738    this.each(function(value, index) { 
    458       ((iterator || Prototype.K)(value, index) ? 
     739      (iterator(value, index) ? 
    459740        trues : falses).push(value); 
    460741    }); 
     
    464745  pluck: function(property) { 
    465746    var results = []; 
    466     this.each(function(value, index) { 
     747    this.each(function(value) { 
    467748      results.push(value[property]); 
    468749    }); 
     
    470751  }, 
    471752 
    472   reject: function(iterator) { 
     753  reject: function(iterator, context) { 
     754    iterator = iterator.bind(context); 
    473755    var results = []; 
    474756    this.each(function(value, index) { 
     
    479761  }, 
    480762 
    481   sortBy: function(iterator) { 
     763  sortBy: function(iterator, context) { 
     764    iterator = iterator.bind(context); 
    482765    return this.map(function(value, index) { 
    483766      return {value: value, criteria: iterator(value, index)}; 
     
    494777  zip: function() { 
    495778    var iterator = Prototype.K, args = $A(arguments); 
    496     if (typeof args.last() == 'function'
     779    if (Object.isFunction(args.last())
    497780      iterator = args.pop(); 
    498781 
     
    510793    return '#<Enumerable:' + this.toArray().inspect() + '>'; 
    511794  } 
    512 } 
     795}; 
    513796 
    514797Object.extend(Enumerable, { 
     
    516799  find:    Enumerable.detect, 
    517800  select:  Enumerable.findAll, 
     801  filter:  Enumerable.findAll, 
    518802  member:  Enumerable.include, 
    519   entries: Enumerable.toArray 
     803  entries: Enumerable.toArray, 
     804  every:   Enumerable.all, 
     805  some:    Enumerable.any 
    520806}); 
    521 var $A = Array.from = function(iterable) { 
     807function $A(iterable) { 
    522808  if (!iterable) return []; 
    523   if (iterable.toArray) { 
    524     return iterable.toArray(); 
    525   } else { 
    526     var results = []; 
    527     for (var i = 0, length = iterable.length; i < length; i++) 
    528       results.push(iterable[i]); 
     809  if (iterable.toArray) return iterable.toArray(); 
     810  var length = iterable.length || 0, results = new Array(length); 
     811  while (length--) results[length] = iterable[length]; 
     812  return results; 
     813
     814 
     815if (Prototype.Browser.WebKit) { 
     816  $A = function(iterable) { 
     817    if (!iterable) return []; 
     818    if (!(Object.isFunction(iterable) && iterable == '[object NodeList]') && 
     819        iterable.toArray) return iterable.toArray(); 
     820    var length = iterable.length || 0, results = new Array(length); 
     821    while (length--) results[length] = iterable[length]; 
    529822    return results; 
    530   } 
     823  }; 
    531824} 
    532825 
     826Array.from = $A; 
     827 
    533828Object.extend(Array.prototype, Enumerable); 
    534829 
    535 if (!Array.prototype._reverse) 
    536   Array.prototype._reverse = Array.prototype.reverse; 
     830if (!Array.prototype._reverse) Array.prototype._reverse = Array.prototype.reverse; 
    537831 
    538832Object.extend(Array.prototype, { 
     
    563857  flatten: function() { 
    564858    return this.inject([], function(array, value) { 
    565       return array.concat(value && value.constructor == Array
     859      return array.concat(Object.isArray(value)
    566860        value.flatten() : [value]); 
    567861    }); 
     
    575869  }, 
    576870 
    577   indexOf: function(object) { 
    578     for (var i = 0, length = this.length; i < length; i++) 
    579       if (this[i] == object) return i; 
    580     return -1; 
    581   }, 
    582  
    583871  reverse: function(inline) { 
    584872    return (inline !== false ? this : this.toArray())._reverse(); 
     
    589877  }, 
    590878 
    591   uniq: function() { 
    592     return this.inject([], function(array, value) { 
    593       return array.include(value) ? array : array.concat([value]); 
     879  uniq: function(sorted) { 
     880    return this.inject([], function(array, value, index) { 
     881      if (0 == index || (sorted ? array.last() != value : !array.include(value))) 
     882        array.push(value); 
     883      return array; 
     884    }); 
     885  }, 
     886 
     887  intersect: function(array) { 
     888    return this.uniq().findAll(function(item) { 
     889      return array.detect(function(value) { return item === value }); 
    594890    }); 
    595891  }, 
     
    605901  inspect: function() { 
    606902    return '[' + this.map(Object.inspect).join(', ') + ']'; 
     903  }, 
     904 
     905  toJSON: function() { 
     906    var results = []; 
     907    this.each(function(object) { 
     908      var value = Object.toJSON(object); 
     909      if (!Object.isUndefined(value)) results.push(value); 
     910    }); 
     911    return '[' + results.join(', ') + ']'; 
    607912  } 
    608913}); 
    609914 
     915// use native browser JS 1.6 implementation if available 
     916if (Object.isFunction(Array.prototype.forEach)) 
     917  Array.prototype._each = Array.prototype.forEach; 
     918 
     919if (!Array.prototype.indexOf) Array.prototype.indexOf = function(item, i) { 
     920  i || (i = 0); 
     921  var length = this.length; 
     922  if (i < 0) i = length + i; 
     923  for (; i < length; i++) 
     924    if (this[i] === item) return i; 
     925  return -1; 
     926}; 
     927 
     928if (!Array.prototype.lastIndexOf) Array.prototype.lastIndexOf = function(item, i) { 
     929  i = isNaN(i) ? this.length : (i < 0 ? this.length + i : i) + 1; 
     930  var n = this.slice(0, i).reverse().indexOf(item); 
     931  return (n < 0) ? n : i - n - 1; 
     932}; 
     933 
    610934Array.prototype.toArray = Array.prototype.clone; 
    611935 
    612 function $w(string){ 
     936function $w(string) { 
     937  if (!Object.isString(string)) return []; 
    613938  string = string.strip(); 
    614939  return string ? string.split(/\s+/) : []; 
    615940} 
    616941 
    617 if(window.opera){ 
    618   Array.prototype.concat = function()
     942if (Prototype.Browser.Opera){ 
     943  Array.prototype.concat = function()
    619944    var array = []; 
    620     for(var i = 0, length = this.length; i < length; i++) array.push(this[i]); 
    621     for(var i = 0, length = arguments.length; i < length; i++) { 
    622       if(arguments[i].constructor == Array) { 
    623         for(var j = 0, arrayLength = arguments[i].length; j < arrayLength; j++) 
     945    for (var i = 0, length = this.length; i < length; i++) array.push(this[i]); 
     946    for (var i = 0, length = arguments.length; i < length; i++) { 
     947      if (Object.isArray(arguments[i])) { 
     948        for (var j = 0, arrayLength = arguments[i].length; j < arrayLength; j++) 
    624949          array.push(arguments[i][j]); 
    625950      } else { 
     
    628953    } 
    629954    return array; 
    630   } 
     955  }; 
    631956} 
    632 var Hash = function(obj) { 
    633   Object.extend(this, obj || {}); 
    634 }; 
    635  
    636 Object.extend(Hash, { 
    637   toQueryString: function(obj) { 
    638     var parts = []; 
    639  
    640     this.prototype._each.call(obj, function(pair) { 
    641       if (!pair.key) return; 
    642  
    643       if (pair.value && pair.value.constructor == Array) { 
    644         var values = pair.value.compact(); 
    645         if (values.length < 2) pair.value = values.reduce(); 
    646         else { 
    647           key = encodeURIComponent(pair.key); 
    648           values.each(function(value) { 
    649             value = value != undefined ? encodeURIComponent(value) : ''; 
    650             parts.push(key + '=' + encodeURIComponent(value)); 
    651           }); 
    652           return; 
    653         } 
    654       } 
    655       if (pair.value == undefined) pair[1] = ''; 
    656       parts.push(pair.map(encodeURIComponent).join('=')); 
    657     }); 
    658  
    659     return parts.join('&'); 
     957Object.extend(Number.prototype, { 
     958  toColorPart: function() { 
     959    return this.toPaddedString(2, 16); 
     960  }, 
     961 
     962  succ: function() { 
     963    return this + 1; 
     964  }, 
     965 
     966  times: function(iterator) { 
     967    $R(0, this, true).each(iterator); 
     968    return this; 
     969  }, 
     970 
     971  toPaddedString: function(length, radix) { 
     972    var string = this.toString(radix || 10); 
     973    return '0'.times(length - string.length) + string; 
     974  }, 
     975 
     976  toJSON: function() { 
     977    return isFinite(this) ? this.toString() : 'null'; 
    660978  } 
    661979}); 
    662980 
    663 Object.extend(Hash.prototype, Enumerable); 
    664 Object.extend(Hash.prototype, { 
    665   _each: function(iterator) { 
    666     for (var key in this) { 
    667       var value = this[key]; 
    668       if (value && value == Hash.prototype[key]) continue; 
    669  
    670       var pair = [key, value]; 
    671       pair.key = key; 
    672       pair.value = value; 
    673       iterator(pair); 
    674     } 
    675   }, 
    676  
    677   keys: function() { 
    678     return this.pluck('key'); 
    679   }, 
    680  
    681   values: function() { 
    682     return this.pluck('value'); 
    683   }, 
    684  
    685   merge: function(hash) { 
    686     return $H(hash).inject(this, function(mergedHash, pair) { 
    687       mergedHash[pair.key] = pair.value; 
    688       return mergedHash; 
    689     }); 
    690   }, 
    691  
    692   remove: function() { 
    693     var result; 
    694     for(var i = 0, length = arguments.length; i < length; i++) { 
    695       var value = this[arguments[i]]; 
    696       if (value !== undefined){ 
    697         if (result === undefined) result = value; 
    698         else { 
    699           if (result.constructor != Array) result = [result]; 
    700           result.push(value) 
    701         } 
    702       } 
    703       delete this[arguments[i]]; 
    704     } 
    705     return result; 
    706   }, 
    707  
    708   toQueryString: function() { 
    709     return Hash.toQueryString(this); 
    710   }, 
    711  
    712   inspect: function() { 
    713     return '#<Hash:{' + this.map(function(pair) { 
    714       return pair.map(Object.inspect).join(': '); 
    715     }).join(', ') + '}>'; 
    716   } 
     981$w('abs round ceil floor').each(function(method){ 
     982  Number.prototype[method] = Math[method].methodize(); 
    717983}); 
    718  
    719984function $H(object) { 
    720   if (object && object.constructor == Hash) return object; 
    721985  return new Hash(object); 
    722986}; 
    723 ObjectRange = Class.create(); 
    724 Object.extend(ObjectRange.prototype, Enumerable); 
    725 Object.extend(ObjectRange.prototype, { 
     987 
     988var Hash = Class.create(Enumerable, (function() { 
     989 
     990  function toQueryPair(key, value) { 
     991    if (Object.isUndefined(value)) return key; 
     992    return key + '=' + encodeURIComponent(String.interpret(value)); 
     993  } 
     994 
     995  return { 
     996    initialize: function(object) { 
     997      this._object = Object.isHash(object) ? object.toObject() : Object.clone(object); 
     998    }, 
     999 
     1000    _each: function(iterator) { 
     1001      for (var key in this._object) { 
     1002        var value = this._object[key], pair = [key, value]; 
     1003        pair.key = key; 
     1004        pair.value = value; 
     1005        iterator(pair); 
     1006      } 
     1007    }, 
     1008 
     1009    set: function(key, value) { 
     1010      return this._object[key] = value; 
     1011    }, 
     1012 
     1013    get: function(key) { 
     1014      return this._object[key]; 
     1015    }, 
     1016 
     1017    unset: function(key) { 
     1018      var value = this._object[key]; 
     1019      delete this._object[key]; 
     1020      return value; 
     1021    }, 
     1022 
     1023    toObject: function() { 
     1024      return Object.clone(this._object); 
     1025    }, 
     1026 
     1027    keys: function() { 
     1028      return this.pluck('key'); 
     1029    }, 
     1030 
     1031    values: function() { 
     1032      return this.pluck('value'); 
     1033    }, 
     1034 
     1035    index: function(value) { 
     1036      var match = this.detect(function(pair) { 
     1037        return pair.value === value; 
     1038      }); 
     1039      return match && match.key; 
     1040    }, 
     1041 
     1042    merge: function(object) { 
     1043      return this.clone().update(object); 
     1044    }, 
     1045 
     1046    update: function(object) { 
     1047      return new Hash(object).inject(this, function(result, pair) { 
     1048        result.set(pair.key, pair.value); 
     1049        return result; 
     1050      }); 
     1051    }, 
     1052 
     1053    toQueryString: function() { 
     1054      return this.map(function(pair) { 
     1055        var key = encodeURIComponent(pair.key), values = pair.value; 
     1056 
     1057        if (values && typeof values == 'object') { 
     1058          if (Object.isArray(values)) 
     1059            return values.map(toQueryPair.curry(key)).join('&'); 
     1060        } 
     1061        return toQueryPair(key, values); 
     1062      }).join('&'); 
     1063    }, 
     1064 
     1065    inspect: function() { 
     1066      return '#<Hash:{' + this.map(function(pair) { 
     1067        return pair.map(Object.inspect).join(': '); 
     1068      }).join(', ') + '}>'; 
     1069    }, 
     1070 
     1071    toJSON: function() { 
     1072      return Object.toJSON(this.toObject()); 
     1073    }, 
     1074 
     1075    clone: function() { 
     1076      return new Hash(this); 
     1077    } 
     1078  } 
     1079})()); 
     1080 
     1081Hash.prototype.toTemplateReplacements = Hash.prototype.toObject; 
     1082Hash.from = $H; 
     1083var ObjectRange = Class.create(Enumerable, { 
    7261084  initialize: function(start, end, exclusive) { 
    7271085    this.start = start; 
     
    7491107var $R = function(start, end, exclusive) { 
    7501108  return new ObjectRange(start, end, exclusive); 
    751 } 
     1109}; 
    7521110 
    7531111var Ajax = { 
     
    7611119 
    7621120  activeRequestCount: 0 
    763 } 
     1121}; 
    7641122 
    7651123Ajax.Responders = { 
     
    7811139  dispatch: function(callback, request, transport, json) { 
    7821140    this.each(function(responder) { 
    783       if (typeof responder[callback] == 'function') { 
     1141      if (Object.isFunction(responder[callback])) { 
    7841142        try { 
    7851143          responder[callback].apply(responder, [request, transport, json]); 
    786         } catch (e) {
     1144        } catch (e) {
    7871145      } 
    7881146    }); 
     
    7931151 
    7941152Ajax.Responders.register({ 
    795   onCreate: function() { 
    796     Ajax.activeRequestCount++; 
    797   }, 
    798   onComplete: function() { 
    799     Ajax.activeRequestCount--; 
    800   } 
     1153  onCreate:   function() { Ajax.activeRequestCount++ }, 
     1154  onComplete: function() { Ajax.activeRequestCount-- } 
    8011155}); 
    8021156 
    803 Ajax.Base = function() {}; 
    804 Ajax.Base.prototype = { 
    805   setOptions: function(options) { 
     1157Ajax.Base = Class.create({ 
     1158  initialize: function(options) { 
    8061159    this.options = { 
    8071160      method:       'post', 
     
    8091162      contentType:  'application/x-www-form-urlencoded', 
    8101163      encoding:     'UTF-8', 
    811       parameters:   '' 
    812     } 
    813     Object.extend(this.options, options || {}); 
     1164      parameters:   '', 
     1165      evalJSON:     true, 
     1166      evalJS:       true 
     1167    }; 
     1168    Object.extend(this.options, options || { }); 
    8141169 
    8151170    this.options.method = this.options.method.toLowerCase(); 
    816     if (typeof this.options.parameters == 'string') 
     1171 
     1172    if (Object.isString(this.options.parameters)) 
    8171173      this.options.parameters = this.options.parameters.toQueryParams(); 
    818   } 
    819 
    820  
    821 Ajax.Request = Class.create(); 
    822 Ajax.Request.Events = 
    823   ['Uninitialized', 'Loading', 'Loaded', 'Interactive', 'Complete']; 
    824  
    825 Ajax.Request.prototype = Object.extend(new Ajax.Base(), { 
     1174    else if (Object.isHash(this.options.parameters)) 
     1175      this.options.parameters = this.options.parameters.toObject(); 
     1176  } 
     1177}); 
     1178 
     1179Ajax.Request = Class.create(Ajax.Base, { 
    8261180  _complete: false, 
    8271181 
    828   initialize: function(url, options) { 
     1182  initialize: function($super, url, options) { 
     1183    $super(options); 
    8291184    this.transport = Ajax.getTransport(); 
    830     this.setOptions(options); 
    8311185    this.request(url); 
    8321186  }, 
     
    8351189    this.url = url; 
    8361190    this.method = this.options.method; 
    837     var params = this.options.parameters
     1191    var params = Object.clone(this.options.parameters)
    8381192 
    8391193    if (!['get', 'post'].include(this.method)) { 
     
    8431197    } 
    8441198 
    845     params = Hash.toQueryString(params); 
    846     if (params && /Konqueror|Safari|KHTML/.test(navigator.userAgent)) params += '&_=' 
    847  
    848     // when GET, append parameters to URL 
    849     if (this.method == 'get' && params) 
    850       this.url += (this.url.indexOf('?') > -1 ? '&' : '?') + params; 
     1199    this.parameters = params; 
     1200 
     1201    if (params = Object.toQueryString(params)) { 
     1202      // when GET, append parameters to URL 
     1203      if (this.method == 'get') 
     1204        this.url += (this.url.include('?') ? '&' : '?') + params; 
     1205      else if (/Konqueror|Safari|KHTML/.test(navigator.userAgent)) 
     1206        params += '&_='; 
     1207    } 
    8511208 
    8521209    try { 
    853       Ajax.Responders.dispatch('onCreate', this, this.transport); 
     1210      var response = new Ajax.Response(this); 
     1211      if (this.options.onCreate) this.options.onCreate(response); 
     1212      Ajax.Responders.dispatch('onCreate', this, response); 
    8541213 
    8551214      this.transport.open(this.method.toUpperCase(), this.url, 
    8561215        this.options.asynchronous); 
    8571216 
    858       if (this.options.asynchronous) 
    859         setTimeout(function() { this.respondToReadyState(1) }.bind(this), 10); 
     1217      if (this.options.asynchronous) this.respondToReadyState.bind(this).defer(1); 
    8601218 
    8611219      this.transport.onreadystatechange = this.onStateChange.bind(this); 
    8621220      this.setRequestHeaders(); 
    8631221 
    864       var body = this.method == 'post' ? (this.options.postBody || params) : null; 
    865  
    866       this.transport.send(body); 
     1222      this.body = this.method == 'post' ? (this.options.postBody || params) : null; 
     1223      this.transport.send(this.body); 
    8671224 
    8681225      /* Force Firefox to handle ready state 4 for synchronous requests */ 
     
    9061263      var extras = this.options.requestHeaders; 
    9071264 
    908       if (typeof extras.push == 'function'
     1265      if (Object.isFunction(extras.push)
    9091266        for (var i = 0, length = extras.length; i < length; i += 2) 
    9101267          headers[extras[i]] = extras[i+1]; 
     
    9181275 
    9191276  success: function() { 
    920     return !this.transport.status 
    921         || (this.transport.status >= 200 && this.transport.status < 300); 
     1277    var status = this.getStatus(); 
     1278    return !status || (status >= 200 && status < 300); 
     1279  }, 
     1280 
     1281  getStatus: function() { 
     1282    try { 
     1283      return this.transport.status || 0; 
     1284    } catch (e) { return 0 } 
    9221285  }, 
    9231286 
    9241287  respondToReadyState: function(readyState) { 
    925     var state = Ajax.Request.Events[readyState]; 
    926     var transport = this.transport, json = this.evalJSON(); 
     1288    var state = Ajax.Request.Events[readyState], response = new Ajax.Response(this); 
    9271289 
    9281290    if (state == 'Complete') { 
    9291291      try { 
    9301292        this._complete = true; 
    931         (this.options['on' + this.transport.status] 
     1293        (this.options['on' + response.status] 
    9321294         || this.options['on' + (this.success() ? 'Success' : 'Failure')] 
    933          || Prototype.emptyFunction)(transport, json); 
     1295         || Prototype.emptyFunction)(response, response.headerJSON); 
    9341296      } catch (e) { 
    9351297        this.dispatchException(e); 
    9361298      } 
    9371299 
    938       if ((this.getHeader('Content-type') || 'text/javascript').strip(). 
    939         match(/^(text|application)\/(x-)?(java|ecma)script(;.*)?$/i)) 
    940           this.evalResponse(); 
     1300      var contentType = response.getHeader('Content-type'); 
     1301      if (this.options.evalJS == 'force' 
     1302          || (this.options.evalJS && this.isSameOrigin() && contentType 
     1303          && contentType.match(/^\s*(text|application)\/(x-)?(java|ecma)script(;.*)?\s*$/i))) 
     1304        this.evalResponse(); 
    9411305    } 
    9421306 
    9431307    try { 
    944       (this.options['on' + state] || Prototype.emptyFunction)(transport, json); 
    945       Ajax.Responders.dispatch('on' + state, this, transport, json); 
     1308      (this.options['on' + state] || Prototype.emptyFunction)(response, response.headerJSON); 
     1309      Ajax.Responders.dispatch('on' + state, this, response, response.headerJSON); 
    9461310    } catch (e) { 
    9471311      this.dispatchException(e); 
     
    9541318  }, 
    9551319 
     1320  isSameOrigin: function() { 
     1321    var m = this.url.match(/^\s*https?:\/\/[^\/]*/); 
     1322    return !m || (m[0] == '#{protocol}//#{domain}#{port}'.interpolate({ 
     1323      protocol: location.protocol, 
     1324      domain: document.domain, 
     1325      port: location.port ? ':' + location.port : '' 
     1326    })); 
     1327  }, 
     1328 
    9561329  getHeader: function(name) { 
    9571330    try { 
    958       return this.transport.getResponseHeader(name); 
    959     } catch (e) { return null } 
    960   }, 
    961  
    962   evalJSON: function() { 
    963     try { 
    964       var json = this.getHeader('X-JSON'); 
    965       return json ? eval('(' + json + ')') : null; 
     1331      return this.transport.getResponseHeader(name) || null; 
    9661332    } catch (e) { return null } 
    9671333  }, 
     
    9691335  evalResponse: function() { 
    9701336    try { 
    971       return eval(this.transport.responseText); 
     1337      return eval((this.transport.responseText || '').unfilterJSON()); 
    9721338    } catch (e) { 
    9731339      this.dispatchException(e); 
     
    9811347}); 
    9821348 
    983 Ajax.Updater = Class.create(); 
    984  
    985 Object.extend(Object.extend(Ajax.Updater.prototype, Ajax.Request.prototype), { 
    986   initialize: function(container, url, options) { 
     1349Ajax.Request.Events = 
     1350  ['Uninitialized', 'Loading', 'Loaded', 'Interactive', 'Complete']; 
     1351 
     1352Ajax.Response = Class.create({ 
     1353  initialize: function(request){ 
     1354    this.request = request; 
     1355    var transport  = this.transport  = request.transport, 
     1356        readyState = this.readyState = transport.readyState; 
     1357 
     1358    if((readyState > 2 && !Prototype.Browser.IE) || readyState == 4) { 
     1359      this.status       = this.getStatus(); 
     1360      this.statusText   = this.getStatusText(); 
     1361      this.responseText = String.interpret(transport.responseText); 
     1362      this.headerJSON   = this._getHeaderJSON(); 
     1363    } 
     1364 
     1365    if(readyState == 4) { 
     1366      var xml = transport.responseXML; 
     1367      this.responseXML  = Object.isUndefined(xml) ? null : xml; 
     1368      this.responseJSON = this._getResponseJSON(); 
     1369    } 
     1370  }, 
     1371 
     1372  status:      0, 
     1373  statusText: '', 
     1374 
     1375  getStatus: Ajax.Request.prototype.getStatus, 
     1376 
     1377  getStatusText: function() { 
     1378    try { 
     1379      return this.transport.statusText || ''; 
     1380    } catch (e) { return '' } 
     1381  }, 
     1382 
     1383  getHeader: Ajax.Request.prototype.getHeader, 
     1384 
     1385  getAllHeaders: function() { 
     1386    try { 
     1387      return this.getAllResponseHeaders(); 
     1388    } catch (e) { return null } 
     1389  }, 
     1390 
     1391  getResponseHeader: function(name) { 
     1392    return this.transport.getResponseHeader(name); 
     1393  }, 
     1394 
     1395  getAllResponseHeaders: function() { 
     1396    return this.transport.getAllResponseHeaders(); 
     1397  }, 
     1398 
     1399  _getHeaderJSON: function() { 
     1400    var json = this.getHeader('X-JSON'); 
     1401    if (!json) return null; 
     1402    json = decodeURIComponent(escape(json)); 
     1403    try { 
     1404      return json.evalJSON(this.request.options.sanitizeJSON || 
     1405        !this.request.isSameOrigin()); 
     1406    } catch (e) { 
     1407      this.request.dispatchException(e); 
     1408    } 
     1409  }, 
     1410 
     1411  _getResponseJSON: function() { 
     1412    var options = this.request.options; 
     1413    if (!options.evalJSON || (options.evalJSON != 'force' && 
     1414      !(this.getHeader('Content-type') || '').include('application/json')) || 
     1415        this.responseText.blank()) 
     1416          return null; 
     1417    try { 
     1418      return this.responseText.evalJSON(options.sanitizeJSON || 
     1419        !this.request.isSameOrigin()); 
     1420    } catch (e) { 
     1421      this.request.dispatchException(e); 
     1422    } 
     1423  } 
     1424}); 
     1425 
     1426Ajax.Updater = Class.create(Ajax.Request, { 
     1427  initialize: function($super, container, url, options) { 
    9871428    this.container = { 
    9881429      success: (container.success || container), 
    9891430      failure: (container.failure || (container.success ? null : container)) 
    990     } 
    991  
    992     this.transport = Ajax.getTransport(); 
    993     this.setOptions(options); 
    994  
    995     var onComplete = this.options.onComplete || Prototype.emptyFunction; 
    996     this.options.onComplete = (function(transport, param) { 
    997       this.updateContent(); 
    998       onComplete(transport, param); 
     1431    }; 
     1432 
     1433    options = Object.clone(options); 
     1434    var onComplete = options.onComplete; 
     1435    options.onComplete = (function(response, json) { 
     1436      this.updateContent(response.responseText); 
     1437      if (Object.isFunction(onComplete)) onComplete(response, json); 
    9991438    }).bind(this); 
    10001439 
    1001     this.request(url); 
    1002   }, 
    1003  
    1004   updateContent: function() { 
    1005     var receiver = this.container[this.success() ? 'success' : 'failure']; 
    1006     var response = this.transport.responseText
    1007  
    1008     if (!this.options.evalScripts) response = response.stripScripts(); 
     1440    $super(url, options); 
     1441  }, 
     1442 
     1443  updateContent: function(responseText) { 
     1444    var receiver = this.container[this.success() ? 'success' : 'failure'], 
     1445        options = this.options
     1446 
     1447    if (!options.evalScripts) responseText = responseText.stripScripts(); 
    10091448 
    10101449    if (receiver = $(receiver)) { 
    1011       if (this.options.insertion) 
    1012         new this.options.insertion(receiver, response); 
    1013       else 
    1014         receiver.update(response); 
    1015     } 
    1016  
    1017     if (this.success()) { 
    1018       if (this.onComplete) 
    1019         setTimeout(this.onComplete.bind(this), 10); 
     1450      if (options.insertion) { 
     1451        if (Object.isString(options.insertion)) { 
     1452          var insertion = { }; insertion[options.insertion] = responseText; 
     1453          receiver.insert(insertion); 
     1454        } 
     1455        else options.insertion(receiver, responseText); 
     1456      } 
     1457      else receiver.update(responseText); 
    10201458    } 
    10211459  } 
    10221460}); 
    10231461 
    1024 Ajax.PeriodicalUpdater = Class.create(); 
    1025 Ajax.PeriodicalUpdater.prototype = Object.extend(new Ajax.Base(), { 
    1026   initialize: function(container, url, options) { 
    1027     this.setOptions(options); 
     1462Ajax.PeriodicalUpdater = Class.create(Ajax.Base, { 
     1463  initialize: function($super, container, url, options) { 
     1464    $super(options); 
    10281465    this.onComplete = this.options.onComplete; 
    10291466 
     
    10311468    this.decay = (this.options.decay || 1); 
    10321469 
    1033     this.updater = {}; 
     1470    this.updater = { }; 
    10341471    this.container = container; 
    10351472    this.url = url; 
     
    10491486  }, 
    10501487 
    1051   updateComplete: function(request) { 
     1488  updateComplete: function(response) { 
    10521489    if (this.options.decay) { 
    1053       this.decay = (request.responseText == this.lastText ? 
     1490      this.decay = (response.responseText == this.lastText ? 
    10541491        this.decay * this.options.decay : 1); 
    10551492 
    1056       this.lastText = request.responseText; 
    1057     } 
    1058     this.timer = setTimeout(this.onTimerEvent.bind(this), 
    1059       this.decay * this.frequency * 1000); 
     1493      this.lastText = response.responseText; 
     1494    } 
     1495    this.timer = this.onTimerEvent.bind(this).delay(this.decay * this.frequency); 
    10601496  }, 
    10611497 
     
    10701506    return elements; 
    10711507  } 
    1072   if (typeof element == 'string'
     1508  if (Object.isString(element)
    10731509    element = document.getElementById(element); 
    10741510  return Element.extend(element); 
     
    10811517      null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null); 
    10821518    for (var i = 0, length = query.snapshotLength; i < length; i++) 
    1083       results.push(query.snapshotItem(i)); 
     1519      results.push(Element.extend(query.snapshotItem(i))); 
    10841520    return results; 
    10851521  }; 
    10861522} 
    10871523 
    1088 document.getElementsByClassName = function(className, parentElement) { 
    1089   if (Prototype.BrowserFeatures.XPath) { 
    1090     var q = ".//*[contains(concat(' ', @class, ' '), ' " + className + " ')]"; 
    1091     return document._getElementsByXPath(q, parentElement); 
    1092   } else { 
    1093     var children = ($(parentElement) || document.body).getElementsByTagName('*'); 
    1094     var elements = [], child; 
    1095     for (var i = 0, length = children.length; i < length; i++) { 
    1096       child = children[i]; 
    1097       if (Element.hasClassName(child, className)) 
    1098         elements.push(Element.extend(child)); 
    1099     } 
    1100     return elements; 
    1101   } 
    1102 }; 
    1103  
    11041524/*--------------------------------------------------------------------------*/ 
    11051525 
    1106 if (!window.Element) 
    1107   var Element = new Object(); 
    1108  
    1109 Element.extend = function(element) { 
    1110   if (!element || _nativeExtensions || element.nodeType == 3) return element; 
    1111  
    1112   if (!element._extended && element.tagName && element != window) { 
    1113     var methods = Object.clone(Element.Methods), cache = Element.extend.cache; 
    1114  
    1115     if (element.tagName == 'FORM') 
    1116       Object.extend(methods, Form.Methods); 
    1117     if (['INPUT', 'TEXTAREA', 'SELECT'].include(element.tagName)) 
    1118       Object.extend(methods, Form.Element.Methods); 
    1119  
    1120     Object.extend(methods, Element.Methods.Simulated); 
    1121  
    1122     for (var property in methods) { 
    1123       var value = methods[property]; 
    1124       if (typeof value == 'function' && !(property in element)) 
    1125         element[property] = cache.findOrStore(value); 
    1126     } 
    1127   } 
    1128  
    1129   element._extended = true; 
    1130   return element; 
    1131 }; 
    1132  
    1133 Element.extend.cache = { 
    1134   findOrStore: function(value) { 
    1135     return this[value] = this[value] || function() { 
    1136       return value.apply(null, [this].concat($A(arguments))); 
    1137     } 
    1138   } 
    1139 }; 
     1526if (!window.Node) var Node = { }; 
     1527 
     1528if (!Node.ELEMENT_NODE) { 
     1529  // DOM level 2 ECMAScript Language Binding 
     1530  Object.extend(Node, { 
     1531    ELEMENT_NODE: 1, 
     1532    ATTRIBUTE_NODE: 2, 
     1533    TEXT_NODE: 3, 
     1534    CDATA_SECTION_NODE: 4, 
     1535    ENTITY_REFERENCE_NODE: 5, 
     1536    ENTITY_NODE: 6, 
     1537    PROCESSING_INSTRUCTION_NODE: 7, 
     1538    COMMENT_NODE: 8, 
     1539    DOCUMENT_NODE: 9, 
     1540    DOCUMENT_TYPE_NODE: 10, 
     1541    DOCUMENT_FRAGMENT_NODE: 11, 
     1542    NOTATION_NODE: 12 
     1543  }); 
     1544
     1545 
     1546(function() { 
     1547  var element = this.Element; 
     1548  this.Element = function(tagName, attributes) { 
     1549    attributes = attributes || { }; 
     1550    tagName = tagName.toLowerCase(); 
     1551    var cache = Element.cache; 
     1552    if (Prototype.Browser.IE && attributes.name) { 
     1553      tagName = '<' + tagName + ' name="' + attributes.name + '">'; 
     1554      delete attributes.name; 
     1555      return Element.writeAttribute(document.createElement(tagName), attributes); 
     1556    } 
     1557    if (!cache[tagName]) cache[tagName] = Element.extend(document.createElement(tagName)); 
     1558    return Element.writeAttribute(cache[tagName].cloneNode(false), attributes); 
     1559  }; 
     1560  Object.extend(this.Element, element || { }); 
     1561}).call(window); 
     1562 
     1563Element.cache = { }; 
    11401564 
    11411565Element.Methods = { 
     
    11661590  }, 
    11671591 
    1168   update: function(element, html) { 
    1169     html = typeof html == 'undefined' ? '' : html.toString(); 
    1170     $(element).innerHTML = html.stripScripts(); 
    1171     setTimeout(function() {html.evalScripts()}, 10); 
     1592  update: function(element, content) { 
     1593    element = $(element); 
     1594    if (content && content.toElement) content = content.toElement(); 
     1595    if (Object.isElement(content)) return element.update().insert(content); 
     1596    content = Object.toHTML(content); 
     1597    element.innerHTML = content.stripScripts(); 
     1598    content.evalScripts.bind(content).defer(); 
    11721599    return element; 
    11731600  }, 
    11741601 
    1175   replace: function(element, html) { 
     1602  replace: function(element, content) { 
    11761603    element = $(element); 
    1177     html = typeof html == 'undefined' ? '' : html.toString(); 
    1178     if (element.outerHTML) { 
    1179       element.outerHTML = html.stripScripts(); 
    1180     } else { 
     1604    if (content && content.toElement) content = content.toElement(); 
     1605    else if (!Object.isElement(content)) { 
     1606      content = Object.toHTML(content); 
    11811607      var range = element.ownerDocument.createRange(); 
    1182       range.selectNodeContents(element); 
    1183       element.parentNode.replaceChild( 
    1184         range.createContextualFragment(html.stripScripts()), element); 
    1185     } 
    1186     setTimeout(function() {html.evalScripts()}, 10); 
     1608      range.selectNode(element); 
     1609      content.evalScripts.bind(content).defer(); 
     1610      content = range.createContextualFragment(content.stripScripts()); 
     1611    } 
     1612    element.parentNode.replaceChild(content, element); 
    11871613    return element; 
     1614  }, 
     1615 
     1616  insert: function(element, insertions) { 
     1617    element = $(element); 
     1618 
     1619    if (Object.isString(insertions) || Object.isNumber(insertions) || 
     1620        Object.isElement(insertions) || (insertions && (insertions.toElement || insertions.toHTML))) 
     1621          insertions = {bottom:insertions}; 
     1622 
     1623    var content, insert, tagName, childNodes; 
     1624 
     1625    for (var position in insertions) { 
     1626      content  = insertions[position]; 
     1627      position = position.toLowerCase(); 
     1628      insert = Element._insertionTranslations[position]; 
     1629 
     1630      if (content && content.toElement) content = content.toElement(); 
     1631      if (Object.isElement(content)) { 
     1632        insert(element, content); 
     1633        continue; 
     1634      } 
     1635 
     1636      content = Object.toHTML(content); 
     1637 
     1638      tagName = ((position == 'before' || position == 'after') 
     1639        ? element.parentNode : element).tagName.toUpperCase(); 
     1640 
     1641      childNodes = Element._getContentFromAnonymousElement(tagName, content.stripScripts()); 
     1642 
     1643      if (position == 'top' || position == 'after') childNodes.reverse(); 
     1644      childNodes.each(insert.curry(element)); 
     1645 
     1646      content.evalScripts.bind(content).defer(); 
     1647    } 
     1648 
     1649    return element; 
     1650  }, 
     1651 
     1652  wrap: function(element, wrapper, attributes) { 
     1653    element = $(element); 
     1654    if (Object.isElement(wrapper)) 
     1655      $(wrapper).writeAttribute(attributes || { }); 
     1656    else if (Object.isString(wrapper)) wrapper = new Element(wrapper, attributes); 
     1657    else wrapper = new Element('div', wrapper); 
     1658    if (element.parentNode) 
     1659      element.parentNode.replaceChild(wrapper, element); 
     1660    wrapper.appendChild(element); 
     1661    return wrapper; 
    11881662  }, 
    11891663 
     
    12131687 
    12141688  descendants: function(element) { 
    1215     return $A($(element).getElementsByTagName('*')); 
     1689    return $(element).select("*"); 
     1690  }, 
     1691 
     1692  firstDescendant: function(element) { 
     1693    element = $(element).firstChild; 
     1694    while (element && element.nodeType != 1) element = element.nextSibling; 
     1695    return $(element); 
    12161696  }, 
    12171697 
     
    12371717 
    12381718  match: function(element, selector) { 
    1239     if (typeof selector == 'string'
     1719    if (Object.isString(selector)
    12401720      selector = new Selector(selector); 
    12411721    return selector.match($(element)); 
     
    12431723 
    12441724  up: function(element, expression, index) { 
    1245     return Selector.findElement($(element).ancestors(), expression, index); 
     1725    element = $(element); 
     1726    if (arguments.length == 1) return $(element.parentNode); 
     1727    var ancestors = element.ancestors(); 
     1728    return Object.isNumber(expression) ? ancestors[expression] : 
     1729      Selector.findElement(ancestors, expression, index); 
    12461730  }, 
    12471731 
    12481732  down: function(element, expression, index) { 
    1249     return Selector.findElement($(element).descendants(), expression, index); 
     1733    element = $(element); 
     1734    if (arguments.length == 1) return element.firstDescendant(); 
     1735    return Object.isNumber(expression) ? element.descendants()[expression] : 
     1736      element.select(expression)[index || 0]; 
    12501737  }, 
    12511738 
    12521739  previous: function(element, expression, index) { 
    1253     return Selector.findElement($(element).previousSiblings(), expression, index); 
     1740    element = $(element); 
     1741    if (arguments.length == 1) return $(Selector.handlers.previousElementSibling(element)); 
     1742    var previousSiblings = element.previousSiblings(); 
     1743    return Object.isNumber(expression) ? previousSiblings[expression] : 
     1744      Selector.findElement(previousSiblings, expression, index); 
    12541745  }, 
    12551746 
    12561747  next: function(element, expression, index) { 
    1257     return Selector.findElement($(element).nextSiblings(), expression, index); 
    1258   }, 
    1259  
    1260   getElementsBySelector: function() { 
     1748    element = $(element); 
     1749    if (arguments.length == 1) return $(Selector.handlers.nextElementSibling(element)); 
     1750    var nextSiblings = element.nextSiblings(); 
     1751    return Object.isNumber(expression) ? nextSiblings[expression] : 
     1752      Selector.findElement(nextSiblings, expression, index); 
     1753  }, 
     1754 
     1755  select: function() { 
    12611756    var args = $A(arguments), element = $(args.shift()); 
    12621757    return Selector.findChildElements(element, args); 
    12631758  }, 
    12641759 
    1265   getElementsByClassName: function(element, className) { 
    1266     return document.getElementsByClassName(className, element); 
     1760  adjacent: function() { 
     1761    var args = $A(arguments), element = $(args.shift()); 
     1762    return Selector.findChildElements(element.parentNode, args).without(element); 
     1763  }, 
     1764 
     1765  identify: function(element) { 
     1766    element = $(element); 
     1767    var id = element.readAttribute('id'), self = arguments.callee; 
     1768    if (id) return id; 
     1769    do { id = 'anonymous_element_' + self.counter++ } while ($(id)); 
     1770    element.writeAttribute('id', id); 
     1771    return id; 
    12671772  }, 
    12681773 
    12691774  readAttribute: function(element, name) { 
    12701775    element = $(element); 
    1271     if (document.all && !window.opera) { 
    1272       var t = Element._attributeTranslations
     1776    if (Prototype.Browser.IE) { 
     1777      var t = Element._attributeTranslations.read
    12731778      if (t.values[name]) return t.values[name](element, name); 
    1274       if (t.names[name])  name = t.names[name]; 
    1275       var attribute = element.attributes[name]; 
    1276       if(attribute) return attribute.nodeValue; 
     1779      if (t.names[name]) name = t.names[name]; 
     1780      if (name.include(':')) { 
     1781        return (!element.attributes || !element.attributes[name]) ? null : 
     1782         element.attributes[name].value; 
     1783      } 
    12771784    } 
    12781785    return element.getAttribute(name); 
     1786  }, 
     1787 
     1788  writeAttribute: function(element, name, value) { 
     1789    element = $(element); 
     1790    var attributes = { }, t = Element._attributeTranslations.write; 
     1791 
     1792    if (typeof name == 'object') attributes = name; 
     1793    else attributes[name] = Obje