diff options
| author | wei <> | 2007-03-27 02:23:20 +0000 | 
|---|---|---|
| committer | wei <> | 2007-03-27 02:23:20 +0000 | 
| commit | 1b9b64637fe08848f610bbd19b80c031cb7dba63 (patch) | |
| tree | 52f7486e911f60c9a91904c91e3944f4f4f27bca /framework/Web/Javascripts/js/debug | |
| parent | c5d0cd2824e3c3bb2f6f3834177a71c1d7b78519 (diff) | |
set prado trunk to use prototype.js v1.5.1rc2, script.aculo.us to v1.7.1beta
Diffstat (limited to 'framework/Web/Javascripts/js/debug')
| -rw-r--r-- | framework/Web/Javascripts/js/debug/ajax.js | 2474 | ||||
| -rw-r--r-- | framework/Web/Javascripts/js/debug/clientscripts.php | 2 | ||||
| -rw-r--r-- | framework/Web/Javascripts/js/debug/effects.js | 126 | ||||
| -rw-r--r-- | framework/Web/Javascripts/js/debug/prado.js | 280 | ||||
| -rw-r--r-- | framework/Web/Javascripts/js/debug/validator.js | 158 | 
5 files changed, 2884 insertions, 156 deletions
diff --git a/framework/Web/Javascripts/js/debug/ajax.js b/framework/Web/Javascripts/js/debug/ajax.js new file mode 100644 index 00000000..60956194 --- /dev/null +++ b/framework/Web/Javascripts/js/debug/ajax.js @@ -0,0 +1,2474 @@ +// script.aculo.us controls.js v1.7.1_beta1, Mon Mar 12 14:40:50 +0100 2007 + +// Copyright (c) 2005-2007 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us) +//           (c) 2005-2007 Ivan Krstic (http://blogs.law.harvard.edu/ivan) +//           (c) 2005-2007 Jon Tirsen (http://www.tirsen.com) +// Contributors: +//  Richard Livsey +//  Rahul Bhargava +//  Rob Wills +//  +// script.aculo.us is freely distributable under the terms of an MIT-style license. +// For details, see the script.aculo.us web site: http://script.aculo.us/ + +// Autocompleter.Base handles all the autocompletion functionality  +// that's independent of the data source for autocompletion. This +// includes drawing the autocompletion menu, observing keyboard +// and mouse events, and similar. +// +// Specific autocompleters need to provide, at the very least,  +// a getUpdatedChoices function that will be invoked every time +// the text inside the monitored textbox changes. This method  +// should get the text for which to provide autocompletion by +// invoking this.getToken(), NOT by directly accessing +// this.element.value. This is to allow incremental tokenized +// autocompletion. Specific auto-completion logic (AJAX, etc) +// belongs in getUpdatedChoices. +// +// Tokenized incremental autocompletion is enabled automatically +// when an autocompleter is instantiated with the 'tokens' option +// in the options parameter, e.g.: +// new Ajax.Autocompleter('id','upd', '/url/', { tokens: ',' }); +// will incrementally autocomplete with a comma as the token. +// Additionally, ',' in the above example can be replaced with +// a token array, e.g. { tokens: [',', '\n'] } which +// enables autocompletion on multiple tokens. This is most  +// useful when one of the tokens is \n (a newline), as it  +// allows smart autocompletion after linebreaks. + +if(typeof Effect == 'undefined') +  throw("controls.js requires including script.aculo.us' effects.js library"); + +var Autocompleter = {} +Autocompleter.Base = function() {}; +Autocompleter.Base.prototype = { +  baseInitialize: function(element, update, options) { +    this.element     = $(element);  +    this.update      = $(update);   +    this.hasFocus    = false;  +    this.changed     = false;  +    this.active      = false;  +    this.index       = 0;      +    this.entryCount  = 0; + +    if(this.setOptions) +      this.setOptions(options); +    else +      this.options = options || {}; + +    this.options.paramName    = this.options.paramName || this.element.name; +    this.options.tokens       = this.options.tokens || []; +    this.options.frequency    = this.options.frequency || 0.4; +    this.options.minChars     = this.options.minChars || 1; +    this.options.onShow       = this.options.onShow ||  +      function(element, update){  +        if(!update.style.position || update.style.position=='absolute') { +          update.style.position = 'absolute'; +          Position.clone(element, update, { +            setHeight: false,  +            offsetTop: element.offsetHeight +          }); +        } +        Effect.Appear(update,{duration:0.15}); +      }; +    this.options.onHide = this.options.onHide ||  +      function(element, update){ new Effect.Fade(update,{duration:0.15}) }; + +    if(typeof(this.options.tokens) == 'string')  +      this.options.tokens = new Array(this.options.tokens); + +    this.observer = null; +     +    this.element.setAttribute('autocomplete','off'); + +    Element.hide(this.update); + +    Event.observe(this.element, "blur", this.onBlur.bindAsEventListener(this)); +    Event.observe(this.element, "keypress", this.onKeyPress.bindAsEventListener(this)); +  }, + +  show: function() { +    if(Element.getStyle(this.update, 'display')=='none') this.options.onShow(this.element, this.update); +    if(!this.iefix &&  +      (Prototype.Browser.IE) && +      (Element.getStyle(this.update, 'position')=='absolute')) { +      new Insertion.After(this.update,  +       '<iframe id="' + this.update.id + '_iefix" '+ +       'style="display:none;position:absolute;filter:progid:DXImageTransform.Microsoft.Alpha(opacity=0);" ' + +       'src="javascript:false;" frameborder="0" scrolling="no"></iframe>'); +      this.iefix = $(this.update.id+'_iefix'); +    } +    if(this.iefix) setTimeout(this.fixIEOverlapping.bind(this), 50); +  }, +   +  fixIEOverlapping: function() { +    Position.clone(this.update, this.iefix, {setTop:(!this.update.style.height)}); +    this.iefix.style.zIndex = 1; +    this.update.style.zIndex = 2; +    Element.show(this.iefix); +  }, + +  hide: function() { +    this.stopIndicator(); +    if(Element.getStyle(this.update, 'display')!='none') this.options.onHide(this.element, this.update); +    if(this.iefix) Element.hide(this.iefix); +  }, + +  startIndicator: function() { +    if(this.options.indicator) Element.show(this.options.indicator); +  }, + +  stopIndicator: function() { +    if(this.options.indicator) Element.hide(this.options.indicator); +  }, + +  onKeyPress: function(event) { +    if(this.active) +      switch(event.keyCode) { +       case Event.KEY_TAB: +       case Event.KEY_RETURN: +         this.selectEntry(); +         Event.stop(event); +       case Event.KEY_ESC: +         this.hide(); +         this.active = false; +         Event.stop(event); +         return; +       case Event.KEY_LEFT: +       case Event.KEY_RIGHT: +         return; +       case Event.KEY_UP: +         this.markPrevious(); +         this.render(); +         if(Prototype.Browser.WebKit) Event.stop(event); +         return; +       case Event.KEY_DOWN: +         this.markNext(); +         this.render(); +         if(Prototype.Browser.WebKit) Event.stop(event); +         return; +      } +     else  +       if(event.keyCode==Event.KEY_TAB || event.keyCode==Event.KEY_RETURN ||  +         (Prototype.Browser.WebKit > 0 && event.keyCode == 0)) return; + +    this.changed = true; +    this.hasFocus = true; + +    if(this.observer) clearTimeout(this.observer); +      this.observer =  +        setTimeout(this.onObserverEvent.bind(this), this.options.frequency*1000); +  }, + +  activate: function() { +    this.changed = false; +    this.hasFocus = true; +    this.getUpdatedChoices(); +  }, + +  onHover: function(event) { +    var element = Event.findElement(event, 'LI'); +    if(this.index != element.autocompleteIndex)  +    { +        this.index = element.autocompleteIndex; +        this.render(); +    } +    Event.stop(event); +  }, +   +  onClick: function(event) { +    var element = Event.findElement(event, 'LI'); +    this.index = element.autocompleteIndex; +    this.selectEntry(); +    this.hide(); +  }, +   +  onBlur: function(event) { +    // needed to make click events working +    setTimeout(this.hide.bind(this), 250); +    this.hasFocus = false; +    this.active = false;      +  },  +   +  render: function() { +    if(this.entryCount > 0) { +      for (var i = 0; i < this.entryCount; i++) +        this.index==i ?  +          Element.addClassName(this.getEntry(i),"selected") :  +          Element.removeClassName(this.getEntry(i),"selected"); +         +      if(this.hasFocus) {  +        this.show(); +        this.active = true; +      } +    } else { +      this.active = false; +      this.hide(); +    } +  }, +   +  markPrevious: function() { +    if(this.index > 0) this.index-- +      else this.index = this.entryCount-1; +    this.getEntry(this.index).scrollIntoView(true); +  }, +   +  markNext: function() { +    if(this.index < this.entryCount-1) this.index++ +      else this.index = 0; +    this.getEntry(this.index).scrollIntoView(false); +  }, +   +  getEntry: function(index) { +    return this.update.firstChild.childNodes[index]; +  }, +   +  getCurrentEntry: function() { +    return this.getEntry(this.index); +  }, +   +  selectEntry: function() { +    this.active = false; +    this.updateElement(this.getCurrentEntry()); +  }, + +  updateElement: function(selectedElement) { +    if (this.options.updateElement) { +      this.options.updateElement(selectedElement); +      return; +    } +    var value = ''; +    if (this.options.select) { +      var nodes = document.getElementsByClassName(this.options.select, selectedElement) || []; +      if(nodes.length>0) value = Element.collectTextNodes(nodes[0], this.options.select); +    } else +      value = Element.collectTextNodesIgnoreClass(selectedElement, 'informal'); +     +    var lastTokenPos = this.findLastToken(); +    if (lastTokenPos != -1) { +      var newValue = this.element.value.substr(0, lastTokenPos + 1); +      var whitespace = this.element.value.substr(lastTokenPos + 1).match(/^\s+/); +      if (whitespace) +        newValue += whitespace[0]; +      this.element.value = newValue + value; +    } else { +      this.element.value = value; +    } +    this.element.focus(); +     +    if (this.options.afterUpdateElement) +      this.options.afterUpdateElement(this.element, selectedElement); +  }, + +  updateChoices: function(choices) { +    if(!this.changed && this.hasFocus) { +      this.update.innerHTML = choices; +      Element.cleanWhitespace(this.update); +      Element.cleanWhitespace(this.update.down()); + +      if(this.update.firstChild && this.update.down().childNodes) { +        this.entryCount =  +          this.update.down().childNodes.length; +        for (var i = 0; i < this.entryCount; i++) { +          var entry = this.getEntry(i); +          entry.autocompleteIndex = i; +          this.addObservers(entry); +        } +      } else {  +        this.entryCount = 0; +      } + +      this.stopIndicator(); +      this.index = 0; +       +      if(this.entryCount==1 && this.options.autoSelect) { +        this.selectEntry(); +        this.hide(); +      } else { +        this.render(); +      } +    } +  }, + +  addObservers: function(element) { +    Event.observe(element, "mouseover", this.onHover.bindAsEventListener(this)); +    Event.observe(element, "click", this.onClick.bindAsEventListener(this)); +  }, + +  onObserverEvent: function() { +    this.changed = false;    +    if(this.getToken().length>=this.options.minChars) { +      this.startIndicator(); +      this.getUpdatedChoices(); +    } else { +      this.active = false; +      this.hide(); +    } +  }, + +  getToken: function() { +    var tokenPos = this.findLastToken(); +    if (tokenPos != -1) +      var ret = this.element.value.substr(tokenPos + 1).replace(/^\s+/,'').replace(/\s+$/,''); +    else +      var ret = this.element.value; + +    return /\n/.test(ret) ? '' : ret; +  }, + +  findLastToken: function() { +    var lastTokenPos = -1; + +    for (var i=0; i<this.options.tokens.length; i++) { +      var thisTokenPos = this.element.value.lastIndexOf(this.options.tokens[i]); +      if (thisTokenPos > lastTokenPos) +        lastTokenPos = thisTokenPos; +    } +    return lastTokenPos; +  } +} + +Ajax.Autocompleter = Class.create(); +Object.extend(Object.extend(Ajax.Autocompleter.prototype, Autocompleter.Base.prototype), { +  initialize: function(element, update, url, options) { +    this.baseInitialize(element, update, options); +    this.options.asynchronous  = true; +    this.options.onComplete    = this.onComplete.bind(this); +    this.options.defaultParams = this.options.parameters || null; +    this.url                   = url; +  }, + +  getUpdatedChoices: function() { +    entry = encodeURIComponent(this.options.paramName) + '=' +  +      encodeURIComponent(this.getToken()); + +    this.options.parameters = this.options.callback ? +      this.options.callback(this.element, entry) : entry; + +    if(this.options.defaultParams)  +      this.options.parameters += '&' + this.options.defaultParams; + +    new Ajax.Request(this.url, this.options); +  }, + +  onComplete: function(request) { +    this.updateChoices(request.responseText); +  } + +}); + +// The local array autocompleter. Used when you'd prefer to +// inject an array of autocompletion options into the page, rather +// than sending out Ajax queries, which can be quite slow sometimes. +// +// The constructor takes four parameters. The first two are, as usual, +// the id of the monitored textbox, and id of the autocompletion menu. +// The third is the array you want to autocomplete from, and the fourth +// is the options block. +// +// Extra local autocompletion options: +// - choices - How many autocompletion choices to offer +// +// - partialSearch - If false, the autocompleter will match entered +//                    text only at the beginning of strings in the  +//                    autocomplete array. Defaults to true, which will +//                    match text at the beginning of any *word* in the +//                    strings in the autocomplete array. If you want to +//                    search anywhere in the string, additionally set +//                    the option fullSearch to true (default: off). +// +// - fullSsearch - Search anywhere in autocomplete array strings. +// +// - partialChars - How many characters to enter before triggering +//                   a partial match (unlike minChars, which defines +//                   how many characters are required to do any match +//                   at all). Defaults to 2. +// +// - ignoreCase - Whether to ignore case when autocompleting. +//                 Defaults to true. +// +// It's possible to pass in a custom function as the 'selector'  +// option, if you prefer to write your own autocompletion logic. +// In that case, the other options above will not apply unless +// you support them. + +Autocompleter.Local = Class.create(); +Autocompleter.Local.prototype = Object.extend(new Autocompleter.Base(), { +  initialize: function(element, update, array, options) { +    this.baseInitialize(element, update, options); +    this.options.array = array; +  }, + +  getUpdatedChoices: function() { +    this.updateChoices(this.options.selector(this)); +  }, + +  setOptions: function(options) { +    this.options = Object.extend({ +      choices: 10, +      partialSearch: true, +      partialChars: 2, +      ignoreCase: true, +      fullSearch: false, +      selector: function(instance) { +        var ret       = []; // Beginning matches +        var partial   = []; // Inside matches +        var entry     = instance.getToken(); +        var count     = 0; + +        for (var i = 0; i < instance.options.array.length &&   +          ret.length < instance.options.choices ; i++) {  + +          var elem = instance.options.array[i]; +          var foundPos = instance.options.ignoreCase ?  +            elem.toLowerCase().indexOf(entry.toLowerCase()) :  +            elem.indexOf(entry); + +          while (foundPos != -1) { +            if (foundPos == 0 && elem.length != entry.length) {  +              ret.push("<li><strong>" + elem.substr(0, entry.length) + "</strong>" +  +                elem.substr(entry.length) + "</li>"); +              break; +            } else if (entry.length >= instance.options.partialChars &&  +              instance.options.partialSearch && foundPos != -1) { +              if (instance.options.fullSearch || /\s/.test(elem.substr(foundPos-1,1))) { +                partial.push("<li>" + elem.substr(0, foundPos) + "<strong>" + +                  elem.substr(foundPos, entry.length) + "</strong>" + elem.substr( +                  foundPos + entry.length) + "</li>"); +                break; +              } +            } + +            foundPos = instance.options.ignoreCase ?  +              elem.toLowerCase().indexOf(entry.toLowerCase(), foundPos + 1) :  +              elem.indexOf(entry, foundPos + 1); + +          } +        } +        if (partial.length) +          ret = ret.concat(partial.slice(0, instance.options.choices - ret.length)) +        return "<ul>" + ret.join('') + "</ul>"; +      } +    }, options || {}); +  } +}); + +// AJAX in-place editor +// +// see documentation on http://wiki.script.aculo.us/scriptaculous/show/Ajax.InPlaceEditor + +// Use this if you notice weird scrolling problems on some browsers, +// the DOM might be a bit confused when this gets called so do this +// waits 1 ms (with setTimeout) until it does the activation +Field.scrollFreeActivate = function(field) { +  setTimeout(function() { +    Field.activate(field); +  }, 1); +} + +Ajax.InPlaceEditor = Class.create(); +Ajax.InPlaceEditor.defaultHighlightColor = "#FFFF99"; +Ajax.InPlaceEditor.prototype = { +  initialize: function(element, url, options) { +    this.url = url; +    this.element = $(element); + +    this.options = Object.extend({ +      paramName: "value", +      okButton: true, +      okLink: false, +      okText: "ok", +      cancelButton: false, +      cancelLink: true, +      cancelText: "cancel", +      textBeforeControls: '', +      textBetweenControls: '', +      textAfterControls: '', +      savingText: "Saving...", +      clickToEditText: "Click to edit", +      okText: "ok", +      rows: 1, +      onComplete: function(transport, element) { +        new Effect.Highlight(element, {startcolor: this.options.highlightcolor}); +      }, +      onFailure: function(transport) { +        alert("Error communicating with the server: " + transport.responseText.stripTags()); +      }, +      callback: function(form) { +        return Form.serialize(form); +      }, +      handleLineBreaks: true, +      loadingText: 'Loading...', +      savingClassName: 'inplaceeditor-saving', +      loadingClassName: 'inplaceeditor-loading', +      formClassName: 'inplaceeditor-form', +      highlightcolor: Ajax.InPlaceEditor.defaultHighlightColor, +      highlightendcolor: "#FFFFFF", +      externalControl: null, +      submitOnBlur: false, +      ajaxOptions: {}, +      evalScripts: false +    }, options || {}); + +    if(!this.options.formId && this.element.id) { +      this.options.formId = this.element.id + "-inplaceeditor"; +      if ($(this.options.formId)) { +        // there's already a form with that name, don't specify an id +        this.options.formId = null; +      } +    } +     +    if (this.options.externalControl) { +      this.options.externalControl = $(this.options.externalControl); +    } +     +    this.originalBackground = Element.getStyle(this.element, 'background-color'); +    if (!this.originalBackground) { +      this.originalBackground = "transparent"; +    } +     +    this.element.title = this.options.clickToEditText; +     +    this.onclickListener = this.enterEditMode.bindAsEventListener(this); +    this.mouseoverListener = this.enterHover.bindAsEventListener(this); +    this.mouseoutListener = this.leaveHover.bindAsEventListener(this); +    Event.observe(this.element, 'click', this.onclickListener); +    Event.observe(this.element, 'mouseover', this.mouseoverListener); +    Event.observe(this.element, 'mouseout', this.mouseoutListener); +    if (this.options.externalControl) { +      Event.observe(this.options.externalControl, 'click', this.onclickListener); +      Event.observe(this.options.externalControl, 'mouseover', this.mouseoverListener); +      Event.observe(this.options.externalControl, 'mouseout', this.mouseoutListener); +    } +  }, +  enterEditMode: function(evt) { +    if (this.saving) return; +    if (this.editing) return; +    this.editing = true; +    this.onEnterEditMode(); +    if (this.options.externalControl) { +      Element.hide(this.options.externalControl); +    } +    Element.hide(this.element); +    this.createForm(); +    this.element.parentNode.insertBefore(this.form, this.element); +    if (!this.options.loadTextURL) Field.scrollFreeActivate(this.editField); +    // stop the event to avoid a page refresh in Safari +    if (evt) { +      Event.stop(evt); +    } +    return false; +  }, +  createForm: function() { +    this.form = document.createElement("form"); +    this.form.id = this.options.formId; +    Element.addClassName(this.form, this.options.formClassName) +    this.form.onsubmit = this.onSubmit.bind(this); + +    this.createEditField(); + +    if (this.options.textarea) { +      var br = document.createElement("br"); +      this.form.appendChild(br); +    } +     +    if (this.options.textBeforeControls) +      this.form.appendChild(document.createTextNode(this.options.textBeforeControls)); + +    if (this.options.okButton) { +      var okButton = document.createElement("input"); +      okButton.type = "submit"; +      okButton.value = this.options.okText; +      okButton.className = 'editor_ok_button'; +      this.form.appendChild(okButton); +    } +     +    if (this.options.okLink) { +      var okLink = document.createElement("a"); +      okLink.href = "#"; +      okLink.appendChild(document.createTextNode(this.options.okText)); +      okLink.onclick = this.onSubmit.bind(this); +      okLink.className = 'editor_ok_link'; +      this.form.appendChild(okLink); +    } +     +    if (this.options.textBetweenControls &&  +      (this.options.okLink || this.options.okButton) &&  +      (this.options.cancelLink || this.options.cancelButton)) +      this.form.appendChild(document.createTextNode(this.options.textBetweenControls)); +       +    if (this.options.cancelButton) { +      var cancelButton = document.createElement("input"); +      cancelButton.type = "submit"; +      cancelButton.value = this.options.cancelText; +      cancelButton.onclick = this.onclickCancel.bind(this); +      cancelButton.className = 'editor_cancel_button'; +      this.form.appendChild(cancelButton); +    } + +    if (this.options.cancelLink) { +      var cancelLink = document.createElement("a"); +      cancelLink.href = "#"; +      cancelLink.appendChild(document.createTextNode(this.options.cancelText)); +      cancelLink.onclick = this.onclickCancel.bind(this); +      cancelLink.className = 'editor_cancel editor_cancel_link';       +      this.form.appendChild(cancelLink); +    } +     +    if (this.options.textAfterControls) +      this.form.appendChild(document.createTextNode(this.options.textAfterControls)); +  }, +  hasHTMLLineBreaks: function(string) { +    if (!this.options.handleLineBreaks) return false; +    return string.match(/<br/i) || string.match(/<p>/i); +  }, +  convertHTMLLineBreaks: function(string) { +    return string.replace(/<br>/gi, "\n").replace(/<br\/>/gi, "\n").replace(/<\/p>/gi, "\n").replace(/<p>/gi, ""); +  }, +  createEditField: function() { +    var text; +    if(this.options.loadTextURL) { +      text = this.options.loadingText; +    } else { +      text = this.getText(); +    } + +    var obj = this; +     +    if (this.options.rows == 1 && !this.hasHTMLLineBreaks(text)) { +      this.options.textarea = false; +      var textField = document.createElement("input"); +      textField.obj = this; +      textField.type = "text"; +      textField.name = this.options.paramName; +      textField.value = text; +      textField.style.backgroundColor = this.options.highlightcolor; +      textField.className = 'editor_field'; +      var size = this.options.size || this.options.cols || 0; +      if (size != 0) textField.size = size; +      if (this.options.submitOnBlur) +        textField.onblur = this.onSubmit.bind(this); +      this.editField = textField; +    } else { +      this.options.textarea = true; +      var textArea = document.createElement("textarea"); +      textArea.obj = this; +      textArea.name = this.options.paramName; +      textArea.value = this.convertHTMLLineBreaks(text); +      textArea.rows = this.options.rows; +      textArea.cols = this.options.cols || 40; +      textArea.className = 'editor_field';       +      if (this.options.submitOnBlur) +        textArea.onblur = this.onSubmit.bind(this); +      this.editField = textArea; +    } +     +    if(this.options.loadTextURL) { +      this.loadExternalText(); +    } +    this.form.appendChild(this.editField); +  }, +  getText: function() { +    return this.element.innerHTML; +  }, +  loadExternalText: function() { +    Element.addClassName(this.form, this.options.loadingClassName); +    this.editField.disabled = true; +    new Ajax.Request( +      this.options.loadTextURL, +      Object.extend({ +        asynchronous: true, +        onComplete: this.onLoadedExternalText.bind(this) +      }, this.options.ajaxOptions) +    ); +  }, +  onLoadedExternalText: function(transport) { +    Element.removeClassName(this.form, this.options.loadingClassName); +    this.editField.disabled = false; +    this.editField.value = transport.responseText.stripTags(); +    Field.scrollFreeActivate(this.editField); +  }, +  onclickCancel: function() { +    this.onComplete(); +    this.leaveEditMode(); +    return false; +  }, +  onFailure: function(transport) { +    this.options.onFailure(transport); +    if (this.oldInnerHTML) { +      this.element.innerHTML = this.oldInnerHTML; +      this.oldInnerHTML = null; +    } +    return false; +  }, +  onSubmit: function() { +    // onLoading resets these so we need to save them away for the Ajax call +    var form = this.form; +    var value = this.editField.value; +     +    // do this first, sometimes the ajax call returns before we get a chance to switch on Saving... +    // which means this will actually switch on Saving... *after* we've left edit mode causing Saving... +    // to be displayed indefinitely +    this.onLoading(); +     +    if (this.options.evalScripts) { +      new Ajax.Request( +        this.url, Object.extend({ +          parameters: this.options.callback(form, value), +          onComplete: this.onComplete.bind(this), +          onFailure: this.onFailure.bind(this), +          asynchronous:true,  +          evalScripts:true +        }, this.options.ajaxOptions)); +    } else  { +      new Ajax.Updater( +        { success: this.element, +          // don't update on failure (this could be an option) +          failure: null },  +        this.url, Object.extend({ +          parameters: this.options.callback(form, value), +          onComplete: this.onComplete.bind(this), +          onFailure: this.onFailure.bind(this) +        }, this.options.ajaxOptions)); +    } +    // stop the event to avoid a page refresh in Safari +    if (arguments.length > 1) { +      Event.stop(arguments[0]); +    } +    return false; +  }, +  onLoading: function() { +    this.saving = true; +    this.removeForm(); +    this.leaveHover(); +    this.showSaving(); +  }, +  showSaving: function() { +    this.oldInnerHTML = this.element.innerHTML; +    this.element.innerHTML = this.options.savingText; +    Element.addClassName(this.element, this.options.savingClassName); +    this.element.style.backgroundColor = this.originalBackground; +    Element.show(this.element); +  }, +  removeForm: function() { +    if(this.form) { +      if (this.form.parentNode) Element.remove(this.form); +      this.form = null; +    } +  }, +  enterHover: function() { +    if (this.saving) return; +    this.element.style.backgroundColor = this.options.highlightcolor; +    if (this.effect) { +      this.effect.cancel(); +    } +    Element.addClassName(this.element, this.options.hoverClassName) +  }, +  leaveHover: function() { +    if (this.options.backgroundColor) { +      this.element.style.backgroundColor = this.oldBackground; +    } +    Element.removeClassName(this.element, this.options.hoverClassName) +    if (this.saving) return; +    this.effect = new Effect.Highlight(this.element, { +      startcolor: this.options.highlightcolor, +      endcolor: this.options.highlightendcolor, +      restorecolor: this.originalBackground +    }); +  }, +  leaveEditMode: function() { +    Element.removeClassName(this.element, this.options.savingClassName); +    this.removeForm(); +    this.leaveHover(); +    this.element.style.backgroundColor = this.originalBackground; +    Element.show(this.element); +    if (this.options.externalControl) { +      Element.show(this.options.externalControl); +    } +    this.editing = false; +    this.saving = false; +    this.oldInnerHTML = null; +    this.onLeaveEditMode(); +  }, +  onComplete: function(transport) { +    this.leaveEditMode(); +    this.options.onComplete.bind(this)(transport, this.element); +  }, +  onEnterEditMode: function() {}, +  onLeaveEditMode: function() {}, +  dispose: function() { +    if (this.oldInnerHTML) { +      this.element.innerHTML = this.oldInnerHTML; +    } +    this.leaveEditMode(); +    Event.stopObserving(this.element, 'click', this.onclickListener); +    Event.stopObserving(this.element, 'mouseover', this.mouseoverListener); +    Event.stopObserving(this.element, 'mouseout', this.mouseoutListener); +    if (this.options.externalControl) { +      Event.stopObserving(this.options.externalControl, 'click', this.onclickListener); +      Event.stopObserving(this.options.externalControl, 'mouseover', this.mouseoverListener); +      Event.stopObserving(this.options.externalControl, 'mouseout', this.mouseoutListener); +    } +  } +}; + +Ajax.InPlaceCollectionEditor = Class.create(); +Object.extend(Ajax.InPlaceCollectionEditor.prototype, Ajax.InPlaceEditor.prototype); +Object.extend(Ajax.InPlaceCollectionEditor.prototype, { +  createEditField: function() { +    if (!this.cached_selectTag) { +      var selectTag = document.createElement("select"); +      var collection = this.options.collection || []; +      var optionTag; +      collection.each(function(e,i) { +        optionTag = document.createElement("option"); +        optionTag.value = (e instanceof Array) ? e[0] : e; +        if((typeof this.options.value == 'undefined') &&  +          ((e instanceof Array) ? this.element.innerHTML == e[1] : e == optionTag.value)) optionTag.selected = true; +        if(this.options.value==optionTag.value) optionTag.selected = true; +        optionTag.appendChild(document.createTextNode((e instanceof Array) ? e[1] : e)); +        selectTag.appendChild(optionTag); +      }.bind(this)); +      this.cached_selectTag = selectTag; +    } + +    this.editField = this.cached_selectTag; +    if(this.options.loadTextURL) this.loadExternalText(); +    this.form.appendChild(this.editField); +    this.options.callback = function(form, value) { +      return "value=" + encodeURIComponent(value); +    } +  } +}); + +// Delayed observer, like Form.Element.Observer,  +// but waits for delay after last key input +// Ideal for live-search fields + +Form.Element.DelayedObserver = Class.create(); +Form.Element.DelayedObserver.prototype = { +  initialize: function(element, delay, callback) { +    this.delay     = delay || 0.5; +    this.element   = $(element); +    this.callback  = callback; +    this.timer     = null; +    this.lastValue = $F(this.element);  +    Event.observe(this.element,'keyup',this.delayedListener.bindAsEventListener(this)); +  }, +  delayedListener: function(event) { +    if(this.lastValue == $F(this.element)) return; +    if(this.timer) clearTimeout(this.timer); +    this.timer = setTimeout(this.onTimerEvent.bind(this), this.delay * 1000); +    this.lastValue = $F(this.element); +  }, +  onTimerEvent: function() { +    this.timer = null; +    this.callback(this.element, $F(this.element)); +  } +}; + + +/*
 +Copyright (c) 2005 JSON.org
 +
 +Permission is hereby granted, free of charge, to any person obtaining a copy
 +of this software and associated documentation files (the "Software"), to deal
 +in the Software without restriction, including without limitation the rights
 +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 +copies of the Software, and to permit persons to whom the Software is
 +furnished to do so, subject to the following conditions:
 +
 +The Software shall be used for Good, not Evil.
 +
 +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 +SOFTWARE.
 +*/
 +
 +Array.prototype.______array = '______array';
 +
 +Prado.JSON = {
 +    org: 'http://www.JSON.org',
 +    copyright: '(c)2005 JSON.org',
 +    license: 'http://www.crockford.com/JSON/license.html',
 +
 +    stringify: function (arg) {
 +        var c, i, l, s = '', v;
 +
 +        switch (typeof arg) {
 +        case 'object':
 +            if (arg) {
 +                if (arg.______array == '______array') {
 +                    for (i = 0; i < arg.length; ++i) {
 +                        v = this.stringify(arg[i]);
 +                        if (s) {
 +                            s += ',';
 +                        }
 +                        s += v;
 +                    }
 +                    return '[' + s + ']';
 +                } else if (typeof arg.toString != 'undefined') {
 +                    for (i in arg) {
 +                        v = arg[i];
 +                        if (typeof v != 'undefined' && typeof v != 'function') {
 +                            v = this.stringify(v);
 +                            if (s) {
 +                                s += ',';
 +                            }
 +                            s += this.stringify(i) + ':' + v;
 +                        }
 +                    }
 +                    return '{' + s + '}';
 +                }
 +            }
 +            return 'null';
 +        case 'number':
 +            return isFinite(arg) ? String(arg) : 'null';
 +        case 'string':
 +            l = arg.length;
 +            s = '"';
 +            for (i = 0; i < l; i += 1) {
 +                c = arg.charAt(i);
 +                if (c >= ' ') {
 +                    if (c == '\\' || c == '"') {
 +                        s += '\\';
 +                    }
 +                    s += c;
 +                } else {
 +                    switch (c) {
 +                        case '\b':
 +                            s += '\\b';
 +                            break;
 +                        case '\f':
 +                            s += '\\f';
 +                            break;
 +                        case '\n':
 +                            s += '\\n';
 +                            break;
 +                        case '\r':
 +                            s += '\\r';
 +                            break;
 +                        case '\t':
 +                            s += '\\t';
 +                            break;
 +                        default:
 +                            c = c.charCodeAt();
 +                            s += '\\u00' + Math.floor(c / 16).toString(16) +
 +                                (c % 16).toString(16);
 +                    }
 +                }
 +            }
 +            return s + '"';
 +        case 'boolean':
 +            return String(arg);
 +        default:
 +            return 'null';
 +        }
 +    },
 +    parse: function (text) {
 +        var at = 0;
 +        var ch = ' ';
 +
 +        function error(m) {
 +            throw {
 +                name: 'JSONError',
 +                message: m,
 +                at: at - 1,
 +                text: text
 +            };
 +        }
 +
 +        function next() {
 +            ch = text.charAt(at);
 +            at += 1;
 +            return ch;
 +        }
 +
 +        function white() {
 +            while (ch) {
 +                if (ch <= ' ') {
 +                    next();
 +                } else if (ch == '/') {
 +                    switch (next()) {
 +                        case '/':
 +                            while (next() && ch != '\n' && ch != '\r') {}
 +                            break;
 +                        case '*':
 +                            next();
 +                            for (;;) {
 +                                if (ch) {
 +                                    if (ch == '*') {
 +                                        if (next() == '/') {
 +                                            next();
 +                                            break;
 +                                        }
 +                                    } else {
 +                                        next();
 +                                    }
 +                                } else {
 +                                    error("Unterminated comment");
 +                                }
 +                            }
 +                            break;
 +                        default:
 +                            error("Syntax error");
 +                    }
 +                } else {
 +                    break;
 +                }
 +            }
 +        }
 +
 +        function string() {
 +            var i, s = '', t, u;
 +
 +            if (ch == '"') {
 +outer:          while (next()) {
 +                    if (ch == '"') {
 +                        next();
 +                        return s;
 +                    } else if (ch == '\\') {
 +                        switch (next()) {
 +                        case 'b':
 +                            s += '\b';
 +                            break;
 +                        case 'f':
 +                            s += '\f';
 +                            break;
 +                        case 'n':
 +                            s += '\n';
 +                            break;
 +                        case 'r':
 +                            s += '\r';
 +                            break;
 +                        case 't':
 +                            s += '\t';
 +                            break;
 +                        case 'u':
 +                            u = 0;
 +                            for (i = 0; i < 4; i += 1) {
 +                                t = parseInt(next(), 16);
 +                                if (!isFinite(t)) {
 +                                    break outer;
 +                                }
 +                                u = u * 16 + t;
 +                            }
 +                            s += String.fromCharCode(u);
 +                            break;
 +                        default:
 +                            s += ch;
 +                        }
 +                    } else {
 +                        s += ch;
 +                    }
 +                }
 +            }
 +            error("Bad string");
 +        }
 +
 +        function array() {
 +            var a = [];
 +
 +            if (ch == '[') {
 +                next();
 +                white();
 +                if (ch == ']') {
 +                    next();
 +                    return a;
 +                }
 +                while (ch) {
 +                    a.push(value());
 +                    white();
 +                    if (ch == ']') {
 +                        next();
 +                        return a;
 +                    } else if (ch != ',') {
 +                        break;
 +                    }
 +                    next();
 +                    white();
 +                }
 +            }
 +            error("Bad array");
 +        }
 +
 +        function object() {
 +            var k, o = {};
 +
 +            if (ch == '{') {
 +                next();
 +                white();
 +                if (ch == '}') {
 +                    next();
 +                    return o;
 +                }
 +                while (ch) {
 +                    k = string();
 +                    white();
 +                    if (ch != ':') {
 +                        break;
 +                    }
 +                    next();
 +                    o[k] = value();
 +                    white();
 +                    if (ch == '}') {
 +                        next();
 +                        return o;
 +                    } else if (ch != ',') {
 +                        break;
 +                    }
 +                    next();
 +                    white();
 +                }
 +            }
 +            error("Bad object");
 +        }
 +
 +        function number() {
 +            var n = '', v;
 +            if (ch == '-') {
 +                n = '-';
 +                next();
 +            }
 +            while (ch >= '0' && ch <= '9') {
 +                n += ch;
 +                next();
 +            }
 +            if (ch == '.') {
 +                n += '.';
 +                while (next() && ch >= '0' && ch <= '9') {
 +                    n += ch;
 +                }
 +            }
 +            if (ch == 'e' || ch == 'E') {
 +                n += 'e';
 +                next();
 +                if (ch == '-' || ch == '+') {
 +                    n += ch;
 +                    next();
 +                }
 +                while (ch >= '0' && ch <= '9') {
 +                    n += ch;
 +                    next();
 +                }
 +            }
 +            v = +n;
 +            if (!isFinite(v)) {
 +                ////error("Bad number");
 +            } else {
 +                return v;
 +            }
 +        }
 +
 +        function word() {
 +            switch (ch) {
 +                case 't':
 +                    if (next() == 'r' && next() == 'u' && next() == 'e') {
 +                        next();
 +                        return true;
 +                    }
 +                    break;
 +                case 'f':
 +                    if (next() == 'a' && next() == 'l' && next() == 's' &&
 +                            next() == 'e') {
 +                        next();
 +                        return false;
 +                    }
 +                    break;
 +                case 'n':
 +                    if (next() == 'u' && next() == 'l' && next() == 'l') {
 +                        next();
 +                        return null;
 +                    }
 +                    break;
 +            }
 +            error("Syntax error");
 +        }
 +
 +        function value() {
 +            white();
 +            switch (ch) {
 +                case '{':
 +                    return object();
 +                case '[':
 +                    return array();
 +                case '"':
 +                    return string();
 +                case '-':
 +                    return number();
 +                default:
 +                    return ch >= '0' && ch <= '9' ? number() : word();
 +            }
 +        }
 +
 +        return value();
 +    }
 +}; + +
 +Prado.AjaxRequest = Class.create();
 +Prado.AjaxRequest.prototype = Ajax.Request.prototype;
 +
 +
 +/**
 + * Override Prototype's response implementation.
 + */
 +Object.extend(Prado.AjaxRequest.prototype,
 +{
 +	/**
 +	 * Customize the response, dispatch onXXX response code events, and
 +	 * tries to execute response actions (javascript statements).
 +	 */
 +	respondToReadyState : function(readyState)
 +	{
 +	    var event = Ajax.Request.Events[readyState];
 +	    var transport = this.transport, json = this.getBodyDataPart(Prado.CallbackRequest.DATA_HEADER);
 +
 +	    if (event == 'Complete')
 +	    {
 +			var redirectUrl = this.getBodyContentPart(Prado.CallbackRequest.REDIRECT_HEADER);
 +	    	if(redirectUrl)
 +	    		document.location.href = redirectUrl;
 +
 +	      if ((this.getHeader('Content-type') || '').match(/^text\/javascript/i))
 +	      {
 +	        try
 +			{
 +	           json = eval('(' + transport.responseText + ')');
 +	        }catch (e)
 +			{
 +				if(typeof(json) == "string")
 +					json = Prado.CallbackRequest.decode(result);
 +			}
 +	      }
 +
 +	      try
 +	      {
 +	      	Prado.CallbackRequest.updatePageState(this,transport);
 +			Ajax.Responders.dispatch('on' + transport.status, this, transport, json);
 +			Prado.CallbackRequest.dispatchActions(transport,this.getBodyDataPart(Prado.CallbackRequest.ACTION_HEADER));
 +
 +	        (this.options['on' + this.transport.status]
 +	         || this.options['on' + (this.success() ? 'Success' : 'Failure')]
 +	         || Prototype.emptyFunction)(this, json);
 +	  	      } catch (e) {
 +	        this.dispatchException(e);
 +	      }
 +	    }
 +
 +	    try {
 +	      (this.options['on' + event] || Prototype.emptyFunction)(this, json);
 +	      Ajax.Responders.dispatch('on' + event, this, transport, json);
 +	    } catch (e) {
 +	      this.dispatchException(e);
 +	    }
 +
 +	    /* Avoid memory leak in MSIE: clean up the oncomplete event handler */
 +	    if (event == 'Complete')
 +	      this.transport.onreadystatechange = Prototype.emptyFunction;
 +	},
 +
 +	/**
 +	 * Gets header data assuming JSON encoding.
 +	 * @param string header name
 +	 * @return object header data as javascript structures.
 +	 */
 +	getHeaderData : function(name)
 +	{
 +		return this.getJsonData(this.getHeader(name));
 +	},
 +
 +	getBodyContentPart : function(name)
 +	{
 +		if(typeof(this.transport.responseText)=="string")
 +			return Prado.Element.extractContent(this.transport.responseText, name);
 +	},
 +
 +	getJsonData : function(json)
 +	{
 +		try
 +		{
 +			return eval('(' + json + ')');
 +		}
 +		catch (e)
 +		{
 +			if(typeof(json) == "string")
 +				return Prado.CallbackRequest.decode(json);
 +		}
 +	},
 +
 +	getBodyDataPart : function(name)
 +	{
 +		return this.getJsonData(this.getBodyContentPart(name));
 +	}
 +});
 +
 +/**
 + * Prado Callback client-side request handler.
 + */
 +Prado.CallbackRequest = Class.create();
 +
 +/**
 + * Static definitions.
 + */
 +Object.extend(Prado.CallbackRequest,
 +{
 +	/**
 +	 * Callback request target POST field name.
 +	 */
 +	FIELD_CALLBACK_TARGET : 'PRADO_CALLBACK_TARGET',
 +	/**
 +	 * Callback request parameter POST field name.
 +	 */
 +	FIELD_CALLBACK_PARAMETER : 'PRADO_CALLBACK_PARAMETER',
 +	/**
 +	 * Callback request page state field name,
 +	 */
 +	FIELD_CALLBACK_PAGESTATE : 'PRADO_PAGESTATE',
 +
 +	FIELD_POSTBACK_TARGET : 'PRADO_POSTBACK_TARGET',
 +
 +	FIELD_POSTBACK_PARAMETER : 'PRADO_POSTBACK_PARAMETER',
 +
 +	/**
 +	 * List of form fields that will be collected during callback.
 +	 */
 +	PostDataLoaders : [],
 +	/**
 +	 * Response data header name.
 +	 */
 +	DATA_HEADER : 'X-PRADO-DATA',
 +	/**
 +	 * Response javascript execution statement header name.
 +	 */
 +	ACTION_HEADER : 'X-PRADO-ACTIONS',
 +	/**
 +	 * Response errors/exceptions header name.
 +	 */
 +	ERROR_HEADER : 'X-PRADO-ERROR',
 +	/**
 +	 * Page state header name.
 +	 */
 +	PAGESTATE_HEADER : 'X-PRADO-PAGESTATE',
 +
 +	REDIRECT_HEADER : 'X-PRADO-REDIRECT',
 +
 +	requestQueue : [],
 +
 +	//all request objects
 +	requests : {},
 +
 +	getRequestById : function(id)
 +	{
 +		var requests = Prado.CallbackRequest.requests;
 +		if(typeof(requests[id]) != "undefined")
 +			return requests[id];
 +	},
 +
 +	dispatch : function(id)
 +	{
 +		var requests = Prado.CallbackRequest.requests;
 +		if(typeof(requests[id]) != "undefined")
 +			requests[id].dispatch();
 +	},
 +
 +	/**
 +	 * Add ids of inputs element to post in the request.
 +	 */
 +	addPostLoaders : function(ids)
 +	{
 +		var self = Prado.CallbackRequest;
 +		self.PostDataLoaders = self.PostDataLoaders.concat(ids);
 +		var list = [];
 +		self.PostDataLoaders.each(function(id)
 +		{
 +			if(list.indexOf(id) < 0)
 +				list.push(id);
 +		});
 +		self.PostDataLoaders = list;
 +	},
 +
 +	/**
 +	 * Dispatch callback response actions.
 +	 */
 +	dispatchActions : function(transport,actions)
 +	{
 +		var self = Prado.CallbackRequest;
 +		if(actions && actions.length > 0)
 +			actions.each(self.__run.bind(self,transport));
 +	},
 +
 +	/**
 +	 * Prase and evaluate a Callback clien-side action
 +	 */
 +	__run : function(transport, command)
 +	{
 +		var self = Prado.CallbackRequest;
 +		self.transport = transport;
 +		for(var method in command)
 +		{
 +			try
 +			{
 +				method.toFunction().apply(self,command[method]);
 +			}
 +			catch(e)
 +			{
 +				if(typeof(Logger) != "undefined")
 +					self.Exception.onException(null,e);
 +			}
 +		}
 +	},
 +
 +	/**
 +	 * Respond to Prado Callback request exceptions.
 +	 */
 +	Exception :
 +	{
 +		/**
 +		 * Server returns 500 exception. Just log it.
 +		 */
 +		"on500" : function(request, transport, data)
 +		{
 +			var e = request.getHeaderData(Prado.CallbackRequest.ERROR_HEADER);
 +			Logger.error("Callback Server Error "+e.code, this.formatException(e));
 +		},
 +
 +		/**
 +		 * Callback OnComplete event,logs reponse and data to console.
 +		 */
 +		'on200' : function(request, transport, data)
 +		{
 +			if(transport.status < 500)
 +			{
 +				var msg = 'HTTP '+transport.status+" with response : \n";
 +				if(transport.responseText.trim().length >0)
 +				{
 +					var f = RegExp('(<!--X-PRADO[^>]+-->)([\\s\\S\\w\\W]*)(<!--//X-PRADO[^>]+-->)',"m");
 +					msg += transport.responseText.replace(f,'') + "\n";
 +				}
 +				if(typeof(data)!="undefined" && data != null)
 +					msg += "Data : \n"+inspect(data)+"\n";
 +				data = request.getBodyDataPart(Prado.CallbackRequest.ACTION_HEADER);
 +				if(data && data.length > 0)
 +				{
 +					msg += "Actions : \n";
 +					data.each(function(action)
 +					{
 +						msg += inspect(action)+"\n";
 +					});
 +				}
 +				Logger.info(msg);
 +			}
 +		},
 +
 +		/**
 +		 * Uncaught exceptions during callback response.
 +		 */
 +		onException : function(request,e)
 +		{
 +			msg = "";
 +			$H(e).each(function(item)
 +			{
 +				msg += item.key+": "+item.value+"\n";
 +			})
 +			Logger.error('Uncaught Callback Client Exception:', msg);
 +		},
 +
 +		/**
 +		 * Formats the exception message for display in console.
 +		 */
 +		formatException : function(e)
 +		{
 +			var msg = e.type + " with message \""+e.message+"\"";
 +			msg += " in "+e.file+"("+e.line+")\n";
 +			msg += "Stack trace:\n";
 +			var trace = e.trace;
 +			for(var i = 0; i<trace.length; i++)
 +			{
 +				msg += "  #"+i+" "+trace[i].file;
 +				msg += "("+trace[i].line+"): ";
 +				msg += trace[i]["class"]+"->"+trace[i]["function"]+"()"+"\n";
 +			}
 +			msg += e.version+" "+e.time+"\n";
 +			return msg;
 +		}
 +	},
 +
 +	/**
 +	 * @return string JSON encoded data.
 +	 */
 +	encode : function(data)
 +	{
 +		return Prado.JSON.stringify(data);
 +	},
 +
 +	/**
 +	 * @return mixed javascript data decoded from string using JSON decoding.
 +	 */
 +	decode : function(data)
 +	{
 +		if(typeof(data) == "string" && data.trim().length > 0)
 +			return Prado.JSON.parse(data);
 +		else
 +			return null;
 +	},
 +
 +	/**
 +	 * Dispatch a normal request, no timeouts or aborting of requests.
 +	 */
 +	dispatchNormalRequest : function(callback)
 +	{
 +		//Logger.info("dispatching normal request");
 +		new Prado.AjaxRequest(callback.url, callback.options);
 +		return true;
 +	},
 +
 +	/**
 +	 * Abort the current priority request in progress.
 +	 */
 +	tryNextRequest : function()
 +	{
 +		var self = Prado.CallbackRequest;
 +		//Logger.debug('trying next request');
 +		if(typeof(self.currentRequest) == 'undefined' || self.currentRequest==null)
 +		{
 +			if(self.requestQueue.length > 0)
 +				return self.dispatchQueue();
 +			//else
 +				//Logger.warn('empty queque');
 +		}
 +//		else
 +	//		Logger.warn('current request ' + self.currentRequest.id);
 +	},
 +
 +	/**
 +	 * Updates the page state. It will update only if EnablePageStateUpdate and
 +	 * HasPriority options are both true.
 +	 */
 +	updatePageState : function(request, transport)
 +	{
 +		var self = Prado.CallbackRequest;
 +		var pagestate = $(self.FIELD_CALLBACK_PAGESTATE);
 +		var enabled = request.options.EnablePageStateUpdate && request.options.HasPriority;
 +		var aborted = self.currentRequest == null;
 +		if(enabled && !aborted && pagestate)
 +		{
 +			var data = request.getBodyContentPart(self.PAGESTATE_HEADER);
 +			if(typeof(data) == "string" && data.length > 0)
 +				pagestate.value = data;
 +			else
 +			{
 +				if(typeof(Logger) != "undefined")
 +					Logger.warn("Missing page state:"+data);
 +//				Logger.warn('## bad state: setting current request to null');
 +				self.endCurrentRequest();
 +				//self.tryNextRequest();
 +				return false;
 +			}
 +		}
 +		self.endCurrentRequest();
 +	//	Logger.warn('## state updated: setting current request to null');
 +	//	self.tryNextRequest();
 +		return true;
 +	},
 +
 +	enqueue : function(callback)
 +	{
 +		var self = Prado.CallbackRequest;
 +		self.requestQueue.push(callback);
 +		//Logger.warn("equeued "+callback.id+", current queque length="+self.requestQueue.length);
 +		self.tryNextRequest();
 +	},
 +
 +	dispatchQueue : function()
 +	{
 +		var self = Prado.CallbackRequest;
 +		//Logger.warn("dispatching queque, length="+self.requestQueue.length+" request="+self.currentRequest);
 +		var callback = self.requestQueue.shift();
 +		self.currentRequest = callback;
 +
 +		//get data
 +		callback.options.postBody = callback._getPostData(),
 +
 +		callback.request = new Prado.AjaxRequest(callback.url, callback.options);
 +		callback.timeout = setTimeout(function()
 +		{
 +			//Logger.warn("priority timeout");
 +			self.abortRequest(callback.id);
 +		},callback.options.RequestTimeOut);
 +		//Logger.debug("dispatched "+self.currentRequest.id + " ...")
 +	},
 +
 +	endCurrentRequest : function()
 +	{
 +		var self = Prado.CallbackRequest;
 +		clearTimeout(self.currentRequest.timeout);
 +		self.currentRequest=null;
 +	},
 +
 +	abortRequest : function(id)
 +	{
 +		//Logger.warn("abort id="+id);
 +		var self = Prado.CallbackRequest;
 +		if(typeof(self.currentRequest) != 'undefined'
 +			&& self.currentRequest != null && self.currentRequest.id == id)
 +		{
 +			var request = self.currentRequest.request;
 +			if(request.transport.readyState < 4)
 +				request.transport.abort();
 +			//Logger.warn('## aborted: setting current request to null');
 +			self.endCurrentRequest();
 +		}
 +		self.tryNextRequest();
 +	}
 +})
 +
 +/**
 + * Automatically aborts the current request when a priority request has returned.
 + */
 +Ajax.Responders.register({onComplete : function(request)
 +{
 +	if(request.options.HasPriority)
 +		Prado.CallbackRequest.tryNextRequest();
 +}});
 +
 +//Add HTTP exception respones when logger is enabled.
 +Event.OnLoad(function()
 +{
 +	if(typeof Logger != "undefined")
 +		Ajax.Responders.register(Prado.CallbackRequest.Exception);
 +});
 +
 +/**
 + * Create and prepare a new callback request.
 + * Call the dispatch() method to start the callback request.
 + * <code>
 + * request = new Prado.CallbackRequest(UniqueID, callback);
 + * request.dispatch();
 + * </code>
 + */
 +Prado.CallbackRequest.prototype =
 +{
 +
 +	/**
 +	 * Prepare and inititate a callback request.
 +	 */
 +	initialize : function(id, options)
 +	{
 +	/**
 +	 * Callback URL, same url as the current page.
 +	 */
 +		this.url = this.getCallbackUrl();
 +
 +		/**
 +		 * Current callback request.
 +		 */
 +		this.request = null;
 +
 +		this.Enabled = true;
 +
 +		this.id = id;
 +		if(typeof(id)=="string")
 +			Prado.CallbackRequest.requests[id] = this;
 +
 +		this.options = Object.extend(
 +		{
 +			RequestTimeOut : 30000, // 30 second timeout.
 +			EnablePageStateUpdate : true,
 +			HasPriority : true,
 +			CausesValidation : true,
 +			ValidationGroup : null,
 +			PostInputs : true
 +		}, options || {});
 +	},
 +
 +	/**
 +	 * Gets the url from the forms that contains the PRADO_PAGESTATE
 +	 * @return {String} callback url.
 +	 */
 +	getCallbackUrl : function()
 +	{
 +		return $('PRADO_PAGESTATE').form.action;
 +	},
 +
 +	/**
 +	 * Sets the request parameter
 +	 * @param {Object} parameter value
 +	 */
 +	setCallbackParameter : function(value)
 +	{
 +		this.options['params'] = value;
 +	},
 +
 +	/**
 +	 * @return {Object} request paramater value.
 +	 */
 +	getCallbackParameter : function()
 +	{
 +		return this.options['params'];
 +	},
 +
 +	/**
 +	 * Sets the callback request timeout.
 +	 * @param {integer} timeout in  milliseconds
 +	 */
 +	setRequestTimeOut : function(timeout)
 +	{
 +		this.options['RequestTimeOut'] = timeout;
 +	},
 +
 +	/**
 +	 * @return {integer} request timeout in milliseconds
 +	 */
 +	getRequestTimeOut : function()
 +	{
 +		return this.options['RequestTimeOut'];
 +	},
 +
 +	/**
 +	 * Set true to enable validation on callback dispatch.
 +	 * @param {boolean} true to validate
 +	 */
 +	setCausesValidation : function(validate)
 +	{
 +		this.options['CausesValidation'] = validate;
 +	},
 +
 +	/**
 +	 * @return {boolean} validate on request dispatch
 +	 */
 +	getCausesValidation : function()
 +	{
 +		return this.options['CausesValidation'];
 +	},
 +
 +	/**
 +	 * Sets the validation group to validate during request dispatch.
 +	 * @param {string} validation group name
 +	 */
 +	setValidationGroup : function(group)
 +	{
 +		this.options['ValidationGroup'] = group;
 +	},
 +
 +	/**
 +	 * @return {string} validation group name.
 +	 */
 +	getValidationGroup : function()
 +	{
 +		return this.options['ValidationGroup'];
 +	},
 +
 +	/**
 +	 * Dispatch the callback request.
 +	 */
 +	dispatch : function()
 +	{
 +		//Logger.info("dispatching request");
 +		//trigger tinyMCE to save data.
 +		if(typeof tinyMCE != "undefined")
 +			tinyMCE.triggerSave();
 +
 +		//override parameter and postBody options.
 +		Object.extend(this.options,
 +		{
 +//			postBody : this._getPostData(),
 +			parameters : ''
 +		});
 +
 +		if(this.options.CausesValidation && typeof(Prado.Validation) != "undefined")
 +		{
 +			var form =  this.options.Form || Prado.Validation.getForm();
 +			if(Prado.Validation.validate(form,this.options.ValidationGroup,this) == false)
 +				return false;
 +		}
 +
 +		if(this.options.onPreDispatch)
 +			this.options.onPreDispatch(this,null);
 +
 +		if(!this.Enabled)
 +			return;
 +
 +		if(this.options.HasPriority)
 +		{
 +			return Prado.CallbackRequest.enqueue(this);
 +			//return Prado.CallbackRequest.dispatchPriorityRequest(this);
 +		}
 +		else
 +			return Prado.CallbackRequest.dispatchNormalRequest(this);
 +	},
 +
 +	abort : function()
 +	{
 +		return Prado.CallbackRequest.abortRequest(this.id);
 +	},
 +
 +	/**
 +	 * Collects the form inputs, encode the parameters, and sets the callback
 +	 * target id. The resulting string is the request content body.
 +	 * @return string request body content containing post data.
 +	 */
 +	_getPostData : function()
 +	{
 +		var data = {};
 +		var callback = Prado.CallbackRequest;
 +		if(this.options.PostInputs != false)
 +		{
 +			callback.PostDataLoaders.each(function(name)
 +			{
 +				$A(document.getElementsByName(name)).each(function(element)
 +				{
 +					//IE will try to get elements with ID == name as well.
 +					if(element.type && element.name == name)
 +					{
 +						value = $F(element);
 +						if(typeof(value) != "undefined" && value != null)
 +							data[name] = value;
 +					}
 +				})
 +			})
 +		}
 +		if(typeof(this.options.params) != "undefined")
 +			data[callback.FIELD_CALLBACK_PARAMETER] = callback.encode(this.options.params);
 +		var pageState = $F(callback.FIELD_CALLBACK_PAGESTATE);
 +		if(typeof(pageState) != "undefined")
 +			data[callback.FIELD_CALLBACK_PAGESTATE] = pageState;
 +		data[callback.FIELD_CALLBACK_TARGET] = this.id;
 +		if(this.options.EventTarget)
 +			data[callback.FIELD_POSTBACK_TARGET] = this.options.EventTarget;
 +		if(this.options.EventParameter)
 +			data[callback.FIELD_POSTBACK_PARAMETER] = this.options.EventParameter;
 +		return $H(data).toQueryString();
 +	}
 +}
 +
 +/**
 + * Create a new callback request using default settings.
 + * @param string callback handler unique ID.
 + * @param mixed parameter to pass to callback handler on the server side.
 + * @param function client side onSuccess event handler.
 + * @param object additional request options.
 + * @return boolean always false.
 + */
 +Prado.Callback = function(UniqueID, parameter, onSuccess, options)
 +{
 +	var callback =
 +	{
 +		'params' : parameter || '',
 +		'onSuccess' : onSuccess || Prototype.emptyFunction
 +	};
 +
 +	Object.extend(callback, options || {});
 +
 +	request = new Prado.CallbackRequest(UniqueID, callback);
 +	request.dispatch();
 +	return false;
 +}
 + + +/**
 + * Generic postback control.
 + */
 +Prado.WebUI.CallbackControl = Class.extend(Prado.WebUI.PostBackControl,
 +{
 +	onPostBack : function(event, options)
 +	{
 +		var request = new Prado.CallbackRequest(options.EventTarget, options);
 +		request.dispatch();
 +		Event.stop(event);
 +	}
 +});
 +
 +/**
 + * TActiveButton control.
 + */
 +Prado.WebUI.TActiveButton = Class.extend(Prado.WebUI.CallbackControl);
 +/**
 + * TActiveLinkButton control.
 + */
 +Prado.WebUI.TActiveLinkButton = Class.extend(Prado.WebUI.CallbackControl);
 +
 +Prado.WebUI.TActiveImageButton = Class.extend(Prado.WebUI.TImageButton,
 +{
 +	onPostBack : function(event, options)
 +	{
 +		this.addXYInput(event,options);
 +		var request = new Prado.CallbackRequest(options.EventTarget, options);
 +		request.dispatch();
 +		Event.stop(event);
 +	}
 +});
 +/**
 + * Active check box.
 + */
 +Prado.WebUI.TActiveCheckBox = Class.extend(Prado.WebUI.CallbackControl,
 +{
 +	onPostBack : function(event, options)
 +	{
 +		var request = new Prado.CallbackRequest(options.EventTarget, options);
 +		if(request.dispatch()==false)
 +			Event.stop(event);
 +	}
 +});
 +
 +/**
 + * TActiveRadioButton control.
 + */
 +Prado.WebUI.TActiveRadioButton = Class.extend(Prado.WebUI.TActiveCheckBox);
 +
 +
 +Prado.WebUI.TActiveCheckBoxList = Base.extend(
 +{
 +	constructor : function(options)
 +	{
 +		for(var i = 0; i<options.ItemCount; i++)
 +		{
 +			var checkBoxOptions = Object.extend(
 +			{
 +				ID : options.ListID+"_c"+i,
 +				EventTarget : options.ListName+"$c"+i
 +			}, options);
 +			new Prado.WebUI.TActiveCheckBox(checkBoxOptions);
 +		}
 +	}
 +});
 +
 +Prado.WebUI.TActiveRadioButtonList = Prado.WebUI.TActiveCheckBoxList;
 +
 +/**
 + * TActiveTextBox control, handles onchange event.
 + */
 +Prado.WebUI.TActiveTextBox = Class.extend(Prado.WebUI.TTextBox,
 +{
 +	onInit : function(options)
 +	{
 +		this.options=options;
 +		if(options['TextMode'] != 'MultiLine')
 +			Event.observe(this.element, "keydown", this.handleReturnKey.bind(this));
 +		if(this.options['AutoPostBack']==true)
 +			Event.observe(this.element, "change", this.doCallback.bindEvent(this,options));
 +	},
 +
 +	doCallback : function(event, options)
 +	{
 +		var request = new Prado.CallbackRequest(options.EventTarget, options);
 +		request.dispatch();
 +		Event.stop(event);
 +	}
 +});
 +
 +/**
 + * TAutoComplete control.
 + */
 +Prado.WebUI.TAutoComplete = Class.extend(Autocompleter.Base, Prado.WebUI.TActiveTextBox.prototype);
 +Prado.WebUI.TAutoComplete = Class.extend(Prado.WebUI.TAutoComplete,
 +{
 +	initialize : function(options)
 +	{
 +		this.options = options;
 +		this.baseInitialize(options.ID, options.ResultPanel, options);
 +		Object.extend(this.options,
 +		{
 +			onSuccess : this.onComplete.bind(this)
 +		});
 +
 +		if(options.AutoPostBack)
 +			this.onInit(options);
 +	},
 +
 +	doCallback : function(event, options)
 +	{
 +		if(!this.active)
 +		{
 +			var request = new Prado.CallbackRequest(this.options.EventTarget, options);
 +			request.dispatch();
 +			Event.stop(event);
 +		}
 +	},
 +
 +	 //Overrides parent implementation, fires onchange event.
 +	onClick: function(event)
 +	{
 +	    var element = Event.findElement(event, 'LI');
 +	    this.index = element.autocompleteIndex;
 +	    this.selectEntry();
 +	    this.hide();
 +		Event.fireEvent(this.element, "change");
 +	},
 +
 +	getUpdatedChoices : function()
 +	{
 +		var options = new Array(this.getToken(),"__TAutoComplete_onSuggest__");
 +		Prado.Callback(this.options.EventTarget, options, null, this.options);
 +	},
 +
 +  	onComplete : function(request, boundary)
 +  	{
 +  		var result = Prado.Element.extractContent(request.transport.responseText, boundary);
 +  		if(typeof(result) == "string" && result.length > 0)
 +			this.updateChoices(result);
 +	}
 +});
 +
 +/**
 + * Time Triggered Callback class.
 + */
 +Prado.WebUI.TTimeTriggeredCallback = Base.extend(
 +{
 +	constructor : function(options)
 +	{
 +		this.options = Object.extend({ Interval : 1	}, options || {});
 +		Prado.WebUI.TTimeTriggeredCallback.register(this);
 +	},
 +
 +	startTimer : function()
 +	{
 +		setTimeout(this.onTimerEvent.bind(this), 100);
 +		if(typeof(this.timer) == 'undefined' || this.timer == null)
 +			this.timer = setInterval(this.onTimerEvent.bind(this),this.options.Interval*1000);
 +	},
 +
 +	stopTimer : function()
 +	{
 +		if(typeof(this.timer) != 'undefined')
 +		{
 +			clearInterval(this.timer);
 +			this.timer = null;
 +		}
 +	},
 +
 +	onTimerEvent : function()
 +	{
 +		var request = new Prado.CallbackRequest(this.options.EventTarget, this.options);
 +		request.dispatch();
 +	}
 +},
 +//class methods
 +{
 +	timers : {},
 +
 +	register : function(timer)
 +	{
 +		Prado.WebUI.TTimeTriggeredCallback.timers[timer.options.ID] = timer;
 +	},
 +
 +	start : function(id)
 +	{
 +		Prado.WebUI.TTimeTriggeredCallback.timers[id].startTimer();
 +	},
 +
 +	stop : function(id)
 +	{
 +		Prado.WebUI.TTimeTriggeredCallback.timers[id].stopTimer();
 +	}
 +});
 +
 +Prado.WebUI.ActiveListControl = Base.extend(
 +{
 +	constructor : function(options)
 +	{
 +		this.element = $(options.ID);
 +		if(this.element)
 +		{
 +			this.options = options;
 +			Event.observe(this.element, "change", this.doCallback.bind(this));
 +		}
 +	},
 +
 +	doCallback : function(event)
 +	{
 +		var request = new Prado.CallbackRequest(this.options.EventTarget, this.options);
 +		request.dispatch();
 +		Event.stop(event);
 +	}
 +});
 +
 +Prado.WebUI.TActiveDropDownList = Prado.WebUI.ActiveListControl;
 +Prado.WebUI.TActiveListBox = Prado.WebUI.ActiveListControl;
 +
 +/**
 + * Observe event of a particular control to trigger a callback request.
 + */
 +Prado.WebUI.TEventTriggeredCallback = Base.extend(
 +{
 +	constructor : function(options)
 +	{
 +		this.options = options;
 +		var element = $(options['ControlID']);
 +		if(element)
 +			Event.observe(element, this.getEventName(element), this.doCallback.bind(this));
 +	},
 +
 +	getEventName : function(element)
 +	{
 +		var name = this.options.EventName;
 +   		if(typeof(name) == "undefined" && element.type)
 +		{
 +      		switch (element.type.toLowerCase())
 +			{
 +          		case 'password':
 +		        case 'text':
 +		        case 'textarea':
 +		        case 'select-one':
 +		        case 'select-multiple':
 +          			return 'change';
 +      		}
 +		}
 +		return typeof(name) == "undefined"  || name == "undefined" ? 'click' : name;
 +    },
 +
 +	doCallback : function(event)
 +	{
 +		var request = new Prado.CallbackRequest(this.options.EventTarget, this.options);
 +		request.dispatch();
 +		if(this.options.StopEvent == true)
 +			Event.stop(event);
 +	}
 +});
 +
 +/**
 + * Observe changes to a property of a particular control to trigger a callback.
 + */
 +Prado.WebUI.TValueTriggeredCallback = Base.extend(
 +{
 +	count : 1,
 +
 +	observing : true,
 +
 +	constructor : function(options)
 +	{
 +		this.options = options;
 +		this.options.PropertyName = this.options.PropertyName || 'value';
 +		var element = $(options['ControlID']);
 +		this.value = element ? element[this.options.PropertyName] : undefined;
 +		Prado.WebUI.TValueTriggeredCallback.register(this);
 +		this.startObserving();
 +	},
 +
 +	stopObserving : function()
 +	{
 +		clearTimeout(this.timer);
 +		this.observing = false;
 +	},
 +
 +	startObserving : function()
 +	{
 +		this.timer = setTimeout(this.checkChanges.bind(this), this.options.Interval*1000);
 +	},
 +
 +	checkChanges : function()
 +	{
 +		var element = $(this.options.ControlID);
 +		if(element)
 +		{
 +			var value = element[this.options.PropertyName];
 +			if(this.value != value)
 +			{
 +				this.doCallback(this.value, value);
 +				this.value = value;
 +				this.count=1;
 +			}
 +			else
 +				this.count = this.count + this.options.Decay;
 +			if(this.observing)
 +				this.time = setTimeout(this.checkChanges.bind(this),
 +					parseInt(this.options.Interval*1000*this.count));
 +		}
 +	},
 +
 +	doCallback : function(oldValue, newValue)
 +	{
 +		var request = new Prado.CallbackRequest(this.options.EventTarget, this.options);
 +		var param = {'OldValue' : oldValue, 'NewValue' : newValue};
 +		request.setCallbackParameter(param);
 +		request.dispatch();
 +	}
 +},
 +//class methods
 +{
 +	timers : {},
 +
 +	register : function(timer)
 +	{
 +		Prado.WebUI.TValueTriggeredCallback.timers[timer.options.ID] = timer;
 +	},
 +
 +	stop : function(id)
 +	{
 +		Prado.WebUI.TValueTriggeredCallback.timers[id].stopObserving();
 +	}
 +});
 + + +Prado.WebUI.TInPlaceTextBox = Base.extend(
 +{
 +	isSaving : false,
 +	isEditing : false,
 +	editField : null,
 +
 +	constructor : function(options)
 +	{
 +		this.options = Object.extend(
 +		{
 +			LoadTextFromSource : false,
 +			TextMode : 'SingleLine'
 +
 +		}, options || {});
 +		this.element = $(this.options.ID);
 +		Prado.WebUI.TInPlaceTextBox.register(this);
 +		this.createEditorInput();
 +		this.initializeListeners();
 +	},
 +
 +	/**
 +	 * Initialize the listeners.
 +	 */
 +	initializeListeners : function()
 +	{
 +		this.onclickListener = this.enterEditMode.bindAsEventListener(this);
 +	    Event.observe(this.element, 'click', this.onclickListener);
 +	    if (this.options.ExternalControl)
 +			Event.observe($(this.options.ExternalControl), 'click', this.onclickListener);
 +	},
 +
 +	/**
 +	 * Changes the panel to an editable input.
 +	 * @param {Event} evt event source
 +	 */
 +	enterEditMode :  function(evt)
 +	{
 +	    if (this.isSaving || this.isEditing) return;
 +	    this.isEditing = true;
 +		this.onEnterEditMode();
 +		this.createEditorInput();
 +		this.showTextBox();
 +		this.editField.disabled = false;
 +		if(this.options.LoadTextOnEdit)
 +			this.loadExternalText();
 +		Prado.Element.focus(this.editField);
 +		if (evt)
 +			Event.stop(evt);
 +    	return false;
 +	},
 +
 +	exitEditMode : function(evt)
 +	{
 +		this.isEditing = false;
 +		this.isSaving = false;
 +		this.editField.disabled = false;
 +		this.element.innerHTML = this.editField.value;
 +		this.showLabel();
 +	},
 +
 +	showTextBox : function()
 +	{
 +		Element.hide(this.element);
 +		Element.show(this.editField);
 +	},
 +
 +	showLabel : function()
 +	{
 +		Element.show(this.element);
 +		Element.hide(this.editField);
 +	},
 +
 +	/**
 +	 * Create the edit input field.
 +	 */
 +	createEditorInput : function()
 +	{
 +		if(this.editField == null)
 +			this.createTextBox();
 +
 +		this.editField.value = this.getText();
 +	},
 +
 +	loadExternalText : function()
 +	{
 +		this.editField.disabled = true;
 +		this.onLoadingText();
 +		options = new Array('__InlineEditor_loadExternalText__', this.getText());
 +		request = new Prado.CallbackRequest(this.options.EventTarget, this.options);
 +		request.setCausesValidation(false);
 +		request.setCallbackParameter(options);
 +		request.options.onSuccess = this.onloadExternalTextSuccess.bind(this);
 +		request.options.onFailure = this.onloadExternalTextFailure.bind(this);
 +		request.dispatch();
 +	},
 +
 +	/**
 +	 * Create a new input textbox or textarea
 +	 */
 +	createTextBox : function()
 +	{
 +		cssClass= this.element.className || '';
 +		inputName = this.options.EventTarget;
 +		options = {'className' : cssClass, name : inputName, id : this.options.TextBoxID};
 +		if(this.options.TextMode == 'SingleLine')
 +		{
 +			if(this.options.MaxLength > 0)
 +				options['maxlength'] = this.options.MaxLength;
 +			this.editField = INPUT(options);
 +		}
 +		else
 +		{
 +			if(this.options.Rows > 0)
 +				options['rows'] = this.options.Rows;
 +			if(this.options.Columns > 0)
 +				options['cols'] = this.options.Columns;
 +			if(this.options.Wrap)
 +				options['wrap'] = 'off';
 +			this.editField = TEXTAREA(options);
 +		}
 +
 +		this.editField.style.display="none";
 +		this.element.parentNode.insertBefore(this.editField,this.element)
 +
 +		//handle return key within single line textbox
 +		if(this.options.TextMode == 'SingleLine')
 +		{
 +			Event.observe(this.editField, "keydown", function(e)
 +			{
 +				 if(Event.keyCode(e) == Event.KEY_RETURN)
 +		        {
 +					var target = Event.element(e);
 +					if(target)
 +					{
 +						Event.fireEvent(target, "blur");
 +						Event.stop(e);
 +					}
 +				}
 +			});
 +		}
 +
 +		Event.observe(this.editField, "blur", this.onTextBoxBlur.bind(this));
 +	},
 +
 +	/**
 +	 * @return {String} panel inner html text.
 +	 */
 +	getText: function()
 +	{
 +    	return this.element.innerHTML;
 +  	},
 +
 +	/**
 +	 * Edit mode entered, calls optional event handlers.
 +	 */
 +	onEnterEditMode : function()
 +	{
 +		if(typeof(this.options.onEnterEditMode) == "function")
 +			this.options.onEnterEditMode(this,null);
 +	},
 +
 +	onTextBoxBlur : function(e)
 +	{
 +		text = this.element.innerHTML;
 +		if(this.options.AutoPostBack && text != this.editField.value)
 +			this.onTextChanged(text);
 +		else
 +		{
 +			this.element.innerHTML = this.editField.value;
 +			this.isEditing = false;
 +			if(this.options.AutoHide)
 +				this.showLabel();
 +		}
 +	},
 +
 +	/**
 +	 * When the text input value has changed.
 +	 * @param {String} original text
 +	 */
 +	onTextChanged : function(text)
 +	{
 +		request = new Prado.CallbackRequest(this.options.EventTarget, this.options);
 +		request.setCallbackParameter(text);
 +		request.options.onSuccess = this.onTextChangedSuccess.bind(this);
 +		request.options.onFailure = this.onTextChangedFailure.bind(this);
 +		if(request.dispatch())
 +		{
 +			this.isSaving = true;
 +			this.editField.disabled = true;
 +		}
 +	},
 +
 +	/**
 +	 * When loading external text.
 +	 */
 +	onLoadingText : function()
 +	{
 +		//Logger.info("on loading text");
 +	},
 +
 +	onloadExternalTextSuccess : function(request, parameter)
 +	{
 +		this.isEditing = true;
 +		this.editField.disabled = false;
 +		this.editField.value = this.getText();
 +		Prado.Element.focus(this.editField);
 +		if(typeof(this.options.onSuccess)=="function")
 +			this.options.onSuccess(sender,parameter);
 +	},
 +
 +	onloadExternalTextFailure : function(request, parameter)
 +	{
 +		this.isSaving = false;
 +		this.isEditing = false;
 +		this.showLabel();
 +		if(typeof(this.options.onFailure)=="function")
 +			this.options.onFailure(sender,parameter);
 +	},
 +
 +	/**
 +	 * Text change successfully.
 +	 * @param {Object} sender
 +	 * @param {Object} parameter
 +	 */
 +	onTextChangedSuccess : function(sender, parameter)
 +	{
 +		this.isSaving = false;
 +		this.isEditing = false;
 +		if(this.options.AutoHide)
 +			this.showLabel();
 +		this.element.innerHTML = parameter == null ? this.editField.value : parameter;
 +		this.editField.disabled = false;
 +		if(typeof(this.options.onSuccess)=="function")
 +			this.options.onSuccess(sender,parameter);
 +	},
 +
 +	onTextChangedFailure : function(sender, parameter)
 +	{
 +		this.editField.disabled = false;
 +		this.isSaving = false;
 +		this.isEditing = false;
 +		if(typeof(this.options.onFailure)=="function")
 +			this.options.onFailure(sender,parameter);
 +	}
 +},
 +{
 +	textboxes : {},
 +
 +	register : function(obj)
 +	{
 +		Prado.WebUI.TInPlaceTextBox.textboxes[obj.options.TextBoxID] = obj;
 +	},
 +
 +	setDisplayTextBox : function(id,value)
 +	{
 +		var textbox = Prado.WebUI.TInPlaceTextBox.textboxes[id];
 +		if(textbox)
 +		{
 +			if(value)
 +				textbox.enterEditMode(null);
 +			else
 +			{
 +				textbox.exitEditMode(null);
 +			}
 +		}
 +	}
 +}); + diff --git a/framework/Web/Javascripts/js/debug/clientscripts.php b/framework/Web/Javascripts/js/debug/clientscripts.php index a0321bc2..3bc76734 100644 --- a/framework/Web/Javascripts/js/debug/clientscripts.php +++ b/framework/Web/Javascripts/js/debug/clientscripts.php @@ -12,7 +12,7 @@ $debugMode=(isset($_GET['mode']) && $_GET['mode']==='debug');  $expiresOffset = $debugMode ? -10000 : 3600 * 24 * 10; //no cache
  //allowed libraries
 -$library = array('prado', 'effects', 'validator', 'logger', 'datepicker', 'colorpicker');
 +$library = array('prado', 'effects', 'validator', 'logger', 'datepicker', 'colorpicker', 'ajax');
  $param = isset($_GET['js']) ? $_GET['js'] : '';
 diff --git a/framework/Web/Javascripts/js/debug/effects.js b/framework/Web/Javascripts/js/debug/effects.js index 685e57ae..d306ae44 100644 --- a/framework/Web/Javascripts/js/debug/effects.js +++ b/framework/Web/Javascripts/js/debug/effects.js @@ -1,6 +1,6 @@ -// script.aculo.us effects.js v1.7.0, Fri Jan 19 19:16:36 CET 2007 +// script.aculo.us effects.js v1.7.1_beta1, Mon Mar 12 14:40:50 +0100 2007 -// Copyright (c) 2005, 2006 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us) +// Copyright (c) 2005-2007 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)  // Contributors:  //  Justin Palmer (http://encytemedia.com/)  //  Mark Pilgrim (http://diveintomark.org/) @@ -45,18 +45,10 @@ Element.collectTextNodesIgnoreClass = function(element, className) {  Element.setContentZoom = function(element, percent) {    element = $(element);      element.setStyle({fontSize: (percent/100) + 'em'});    -  if(navigator.appVersion.indexOf('AppleWebKit')>0) window.scrollBy(0,0); +  if(Prototype.Browser.WebKit) window.scrollBy(0,0);    return element;  } -Element.getOpacity = function(element){ -  return $(element).getStyle('opacity'); -} - -Element.setOpacity = function(element, value){ -  return $(element).setStyle({opacity:value}); -} -  Element.getInlineOpacity = function(element){    return $(element).style.opacity || '';  } @@ -89,7 +81,7 @@ var Effect = {        throw("Effect.tagifyText requires including script.aculo.us' builder.js library");      var tagifyStyle = 'position:relative'; -    if(/MSIE/.test(navigator.userAgent) && !window.opera) tagifyStyle += ';zoom:1'; +    if(Prototype.Browser.IE) tagifyStyle += ';zoom:1';      element = $(element);      $A(element.childNodes).each( function(child) { @@ -152,7 +144,8 @@ Effect.Transitions = {      return 1-pos;    },    flicker: function(pos) { -    return ((-Math.cos(pos*Math.PI)/4) + 0.75) + Math.random()/4; +    var pos = ((-Math.cos(pos*Math.PI)/4) + 0.75) + Math.random()/4; +    return (pos > 1 ? 1 : pos);    },    wobble: function(pos) {      return (-Math.cos(pos*Math.PI*(9*pos))/2) + 0.5; @@ -179,7 +172,7 @@ Effect.ScopedQueue = Class.create();  Object.extend(Object.extend(Effect.ScopedQueue.prototype, Enumerable), {    initialize: function() {      this.effects  = []; -    this.interval = null; +    this.interval = null;        },    _each: function(iterator) {      this.effects._each(iterator); @@ -213,7 +206,7 @@ Object.extend(Object.extend(Effect.ScopedQueue.prototype, Enumerable), {      if(!effect.options.queue.limit || (this.effects.length < effect.options.queue.limit))        this.effects.push(effect); -    if(!this.interval)  +    if(!this.interval)        this.interval = setInterval(this.loop.bind(this), 15);    },    remove: function(effect) { @@ -226,7 +219,7 @@ Object.extend(Object.extend(Effect.ScopedQueue.prototype, Enumerable), {    loop: function() {      var timePos = new Date().getTime();      for(var i=0, len=this.effects.length;i<len;i++)  -      if(this.effects[i]) this.effects[i].loop(timePos); +      this.effects[i] && this.effects[i].loop(timePos);    }  }); @@ -246,7 +239,7 @@ Effect.Queue = Effect.Queues.get('global');  Effect.DefaultOptions = {    transition: Effect.Transitions.sinoidal,    duration:   1.0,   // seconds -  fps:        60.0,  // max. 60fps due to Effect.Queue implementation +  fps:        100,   // 100= assume 66fps max.    sync:       false, // true for combining    from:       0.0,    to:         1.0, @@ -258,11 +251,35 @@ Effect.Base = function() {};  Effect.Base.prototype = {    position: null,    start: function(options) { +    function codeForEvent(options,eventName){ +      return ( +        (options[eventName+'Internal'] ? 'this.options.'+eventName+'Internal(this);' : '') + +        (options[eventName] ? 'this.options.'+eventName+'(this);' : '') +      ); +    } +    if(options.transition === false) options.transition = Effect.Transitions.linear;      this.options      = Object.extend(Object.extend({},Effect.DefaultOptions), options || {});      this.currentFrame = 0;      this.state        = 'idle';      this.startOn      = this.options.delay*1000; -    this.finishOn     = this.startOn + (this.options.duration*1000); +    this.finishOn     = this.startOn+(this.options.duration*1000); +    this.fromToDelta  = this.options.to-this.options.from; +    this.totalTime    = this.finishOn-this.startOn; +    this.totalFrames  = this.options.fps*this.options.duration; +     +    eval('this.render = function(pos){ '+ +      'if(this.state=="idle"){this.state="running";'+ +      codeForEvent(options,'beforeSetup')+ +      (this.setup ? 'this.setup();':'')+  +      codeForEvent(options,'afterSetup')+ +      '};if(this.state=="running"){'+ +      'pos=this.options.transition(pos)*'+this.fromToDelta+'+'+this.options.from+';'+ +      'this.position=pos;'+ +      codeForEvent(options,'beforeUpdate')+ +      (this.update ? 'this.update(pos);':'')+ +      codeForEvent(options,'afterUpdate')+ +      '}}'); +          this.event('beforeStart');      if(!this.options.sync)        Effect.Queues.get(typeof this.options.queue == 'string' ?  @@ -278,31 +295,14 @@ Effect.Base.prototype = {          this.event('afterFinish');          return;          } -      var pos   = (timePos - this.startOn) / (this.finishOn - this.startOn); -      var frame = Math.round(pos * this.options.fps * this.options.duration); +      var pos   = (timePos - this.startOn) / this.totalTime, +          frame = Math.round(pos * this.totalFrames);        if(frame > this.currentFrame) {          this.render(pos);          this.currentFrame = frame;        }      }    }, -  render: function(pos) { -    if(this.state == 'idle') { -      this.state = 'running'; -      this.event('beforeSetup'); -      if(this.setup) this.setup(); -      this.event('afterSetup'); -    } -    if(this.state == 'running') { -      if(this.options.transition) pos = this.options.transition(pos); -      pos *= (this.options.to-this.options.from); -      pos += this.options.from; -      this.position = pos; -      this.event('beforeUpdate'); -      if(this.update) this.update(pos); -      this.event('afterUpdate'); -    } -  },    cancel: function() {      if(!this.options.sync)        Effect.Queues.get(typeof this.options.queue == 'string' ?  @@ -358,7 +358,7 @@ Object.extend(Object.extend(Effect.Opacity.prototype, Effect.Base.prototype), {      this.element = $(element);      if(!this.element) throw(Effect._elementDoesNotExistError);      // make this work on IE on elements without 'layout' -    if(/MSIE/.test(navigator.userAgent) && !window.opera && (!this.element.currentStyle.hasLayout)) +    if(Prototype.Browser.IE && (!this.element.currentStyle.hasLayout))        this.element.setStyle({zoom: 1});      var options = Object.extend({        from: this.element.getOpacity() || 0.0, @@ -953,7 +953,7 @@ Object.extend(Object.extend(Effect.Morph.prototype, Effect.Base.prototype), {            effect.element.addClassName(effect.options.style);            effect.transforms.each(function(transform) {              if(transform.style != 'opacity') -              effect.element.style[transform.style.camelize()] = ''; +              effect.element.style[transform.style] = '';            });          }        } else this.style = options.style.parseStyle(); @@ -969,26 +969,28 @@ Object.extend(Object.extend(Effect.Morph.prototype, Effect.Base.prototype), {        });      }      this.transforms = this.style.map(function(pair){ -      var property = pair[0].underscore().dasherize(), value = pair[1], unit = null; +      var property = pair[0], value = pair[1], unit = null;        if(value.parseColor('#zzzzzz') != '#zzzzzz') {          value = value.parseColor();          unit  = 'color';        } else if(property == 'opacity') {          value = parseFloat(value); -        if(/MSIE/.test(navigator.userAgent) && !window.opera && (!this.element.currentStyle.hasLayout)) +        if(Prototype.Browser.IE && (!this.element.currentStyle.hasLayout))            this.element.setStyle({zoom: 1}); -      } else if(Element.CSS_LENGTH.test(value))  -        var components = value.match(/^([\+\-]?[0-9\.]+)(.*)$/), -          value = parseFloat(components[1]), unit = (components.length == 3) ? components[2] : null; +      } else if(Element.CSS_LENGTH.test(value)) { +          var components = value.match(/^([\+\-]?[0-9\.]+)(.*)$/); +          value = parseFloat(components[1]); +          unit = (components.length == 3) ? components[2] : null; +      }        var originalValue = this.element.getStyle(property); -      return $H({  -        style: property,  +      return {  +        style: property.camelize(),           originalValue: unit=='color' ? parseColor(originalValue) : parseFloat(originalValue || 0),           targetValue: unit=='color' ? parseColor(value) : value,          unit: unit -      }); +      };      }.bind(this)).reject(function(transform){        return (          (transform.originalValue == transform.targetValue) || @@ -1000,17 +1002,19 @@ Object.extend(Object.extend(Effect.Morph.prototype, Effect.Base.prototype), {      });    },    update: function(position) { -    var style = $H(), value = null; -    this.transforms.each(function(transform){ -      value = transform.unit=='color' ? -        $R(0,2).inject('#',function(m,v,i){ -          return m+(Math.round(transform.originalValue[i]+ -            (transform.targetValue[i] - transform.originalValue[i])*position)).toColorPart() }) :  +    var style = {}, transform, i = this.transforms.length; +    while(i--) +      style[(transform = this.transforms[i]).style] =  +        transform.unit=='color' ? '#'+ +          (Math.round(transform.originalValue[0]+ +            (transform.targetValue[0]-transform.originalValue[0])*position)).toColorPart() + +          (Math.round(transform.originalValue[1]+ +            (transform.targetValue[1]-transform.originalValue[1])*position)).toColorPart() + +          (Math.round(transform.originalValue[2]+ +            (transform.targetValue[2]-transform.originalValue[2])*position)).toColorPart() :          transform.originalValue + Math.round(            ((transform.targetValue - transform.originalValue) * position) * 1000)/1000 + transform.unit; -      style[transform.style] = value; -    }); -    this.element.setStyle(style); +    this.element.setStyle(style, true);    }  }); @@ -1057,14 +1061,14 @@ Element.CSS_PROPERTIES = $w(  Element.CSS_LENGTH = /^(([\+\-]?[0-9\.]+)(em|ex|px|in|cm|mm|pt|pc|\%))|0$/;  String.prototype.parseStyle = function(){ -  var element = Element.extend(document.createElement('div')); +  var element = document.createElement('div');    element.innerHTML = '<div style="' + this + '"></div>'; -  var style = element.down().style, styleRules = $H(); +  var style = element.childNodes[0].style, styleRules = $H();    Element.CSS_PROPERTIES.each(function(property){      if(style[property]) styleRules[property] = style[property];     }); -  if(/MSIE/.test(navigator.userAgent) && !window.opera && this.indexOf('opacity') > -1) { +  if(Prototype.Browser.IE && this.indexOf('opacity') > -1) {      styleRules.opacity = this.match(/opacity:\s*((?:0|1)?(?:\.\d*)?)/)[1];    }    return styleRules; @@ -1075,13 +1079,13 @@ Element.morph = function(element, style) {    return element;  }; -['setOpacity','getOpacity','getInlineOpacity','forceRerendering','setContentZoom', +['getInlineOpacity','forceRerendering','setContentZoom',   'collectTextNodes','collectTextNodesIgnoreClass','morph'].each(     function(f) { Element.Methods[f] = Element[f]; }  );  Element.Methods.visualEffect = function(element, effect, options) { -  s = effect.gsub(/_/, '-').camelize(); +  s = effect.dasherize().camelize();    effect_class = s.charAt(0).toUpperCase() + s.substring(1);    new Effect[effect_class](element, options);    return $(element); diff --git a/framework/Web/Javascripts/js/debug/prado.js b/framework/Web/Javascripts/js/debug/prado.js index 87622c93..1388135d 100644 --- a/framework/Web/Javascripts/js/debug/prado.js +++ b/framework/Web/Javascripts/js/debug/prado.js @@ -3215,9 +3215,9 @@ if (Prototype.Browser.WebKit) {  Element.addMethods(); -// script.aculo.us builder.js v1.7.0, Fri Jan 19 19:16:36 CET 2007 +// script.aculo.us builder.js v1.7.1_beta1, Mon Mar 12 14:40:50 +0100 2007 -// Copyright (c) 2005, 2006 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us) +// Copyright (c) 2005-2007 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)  //  // script.aculo.us is freely distributable under the terms of an MIT-style license.  // For details, see the script.aculo.us web site: http://script.aculo.us/ @@ -3265,7 +3265,8 @@ var Builder = {      // attributes (or text)      if(arguments[1])        if(this._isStringOrNumber(arguments[1]) || -        (arguments[1] instanceof Array)) { +        (arguments[1] instanceof Array) || +        arguments[1].tagName) {            this._children(element, arguments[1]);          } else {            var attrs = this._attributes(arguments[1]); @@ -3283,7 +3284,7 @@ var Builder = {              }              if(element.tagName.toUpperCase() != elementName)                element = parentElement.getElementsByTagName(elementName)[0]; -            } +          }          }       // text, or array of children @@ -3309,6 +3310,10 @@ var Builder = {      return attrs.join(" ");    },    _children: function(element, children) { +    if(children.tagName) { +      element.appendChild(children); +      return; +    }      if(typeof children=='object') { // array can hold nodes and text        children.flatten().each( function(e) {          if(typeof e=='object') @@ -3318,8 +3323,8 @@ var Builder = {              element.appendChild(Builder._text(e));        });      } else -      if(Builder._isStringOrNumber(children))  -         element.appendChild(Builder._text(children)); +      if(Builder._isStringOrNumber(children)) +        element.appendChild(Builder._text(children));    },    _isStringOrNumber: function(param) {      return(typeof param=='string' || typeof param=='number'); @@ -3584,9 +3589,6 @@ Prado.PostBack = function(event,options)  	Event.fireEvent(form,"submit");
  }
 -/**
 - * Additional element utilities.
 - */
  Prado.Element =
  {
  	/**
 @@ -3601,15 +3603,16 @@ Prado.Element =  			el.value = value;
  	},
 -	select : function(element, method, value)
 +	select : function(element, method, value, total)
  	{
  		var el = $(element);
 -		var isList = element.indexOf('[]') > -1;
 -		if(!el && !isList) return;
 -		method = isList ? 'check'+method : el.tagName.toLowerCase()+method;
 +		if(!el) return;
  		var selection = Prado.Element.Selection;
 -		if(isFunction(selection[method]))
 -			selection[method](isList ? element : el,value);
 +		if(typeof(selection[method]) == "function")
 +		{
 +			control = selection.isSelectable(el) ? [el] : selection.getListElements(element,total);
 +			selection[method](control, value);
 +		}
  	},
  	click : function(element)
 @@ -3622,8 +3625,21 @@ Prado.Element =  	setAttribute : function(element, attribute, value)
  	{
  		var el = $(element);
 -		if(attribute == "disabled" && value==false)
 +		if(!el) return;
 +		if((attribute == "disabled" || attribute == "multiple") && value==false)
  			el.removeAttribute(attribute);
 +		else if(attribute.match(/^on/i)) //event methods
 +		{
 +			try
 +			{
 +				eval("(func = function(event){"+value+"})");
 +				el[attribute] = func;
 +			}
 +			catch(e)
 +			{
 +				throw "Error in evaluating '"+value+"' for attribute "+attribute+" for element "+element.id;
 +			}
 +		}
  		else
  			el.setAttribute(attribute, value);
  	},
 @@ -3631,12 +3647,12 @@ Prado.Element =  	setOptions : function(element, options)
  	{
  		var el = $(element);
 +		if(!el) return;
  		if(el && el.tagName.toLowerCase() == "select")
  		{
 -			while(el.length > 0)
 -				el.remove(0);
 +			el.options.length = options.length;
  			for(var i = 0; i<options.length; i++)
 -				el.options[el.options.length] = new Option(options[i][0],options[i][1]);
 +				el.options[i] = new Option(options[i][0],options[i][1]);
  		}
  	},
 @@ -3650,14 +3666,62 @@ Prado.Element =  		if(typeof(obj) != "undefined" && typeof(obj.focus) != "undefined")
  			setTimeout(function(){ obj.focus(); }, 100);
  		return false;
 +	},
 +
 +	replace : function(element, method, content, boundary)
 +	{
 +		if(boundary)
 +		{
 +			result = Prado.Element.extractContent(this.transport.responseText, boundary);
 +			if(result != null)
 +				content = result;
 +		}
 +		if(typeof(element) == "string")
 +		{
 +			if($(element))
 +				method.toFunction().apply(this,[element,""+content]);
 +		}
 +		else
 +		{
 +			method.toFunction().apply(this,[""+content]);
 +		}
 +	},
 +
 +	extractContent : function(text, boundary)
 +	{
 +		var f = RegExp('(<!--'+boundary+'-->)([\\s\\S\\w\\W]*)(<!--//'+boundary+'-->)',"m");
 +		var result = text.match(f);
 +		if(result && result.length >= 2)
 +			return result[2];
 +		else
 +			return null;
 +	},
 +
 +	evaluateScript : function(content)
 +	{
 +		content.evalScripts();
  	}
  }
 -/**
 - * Selectable element utilities
 - */
  Prado.Element.Selection =
  {
 +	isSelectable : function(el)
 +	{
 +		if(el && el.type)
 +		{
 +			switch(el.type.toLowerCase())
 +			{
 +				case 'checkbox':
 +				case 'radio':
 +				case 'select':
 +				case 'select-multiple':
 +				case 'select-one':
 +				return true;
 +			}
 +		}
 +		return false;
 +	},
 +
  	inputValue : function(el, value)
  	{
  		switch(el.type.toLowerCase())
 @@ -3668,61 +3732,125 @@ Prado.Element.Selection =  		}
  	},
 -	selectValue : function(el, value)
 +	selectValue : function(elements, value)
  	{
 -		$A(el.options).each(function(option)
 +		elements.each(function(el)
  		{
 -			option.selected = option.value == value;
 -		});
 +			$A(el.options).each(function(option)
 +			{
 +				if(typeof(value) == "boolean")
 +					options.selected = value;
 +				else if(option.value == value)
 +					option.selected = true;
 +			});
 +		})
  	},
 -	selectIndex : function(el, index)
 +	selectValues : function(elements, values)
  	{
 -		if(el.type == 'select-one')
 -			el.selectedIndex = index;
 -		else
 +		selection = this;
 +		values.each(function(value)
 +		{
 +			selection.selectValue(elements,value);
 +		})
 +	},
 +
 +	selectIndex : function(elements, index)
 +	{
 +		elements.each(function(el)
  		{
 -			for(var i = 0; i<el.length; i++)
 +			if(el.type.toLowerCase() == 'select-one')
 +				el.selectedIndex = index;
 +			else
  			{
 -				if(i == index)
 -					el.options[i].selected = true;
 +				for(var i = 0; i<el.length; i++)
 +				{
 +					if(i == index)
 +						el.options[i].selected = true;
 +				}
  			}
 -		}
 +		})
  	},
 -	selectClear : function(el)
 +	selectAll : function(elements)
  	{
 -		el.selectedIndex = -1;
 +		elements.each(function(el)
 +		{
 +			if(el.type.toLowerCase() != 'select-one')
 +			{
 +				$A(el.options).each(function(option)
 +				{
 +					option.selected = true;
 +				})
 +			}
 +		})
  	},
 -	selectAll : function(el)
 +	selectInvert : function(elements)
  	{
 -		$A(el.options).each(function(option)
 +		elements.each(function(el)
  		{
 -			option.selected = true;
 -			Logger.warn(option.value);
 -		});
 +			if(el.type.toLowerCase() != 'select-one')
 +			{
 +				$A(el.options).each(function(option)
 +				{
 +					option.selected = !options.selected;
 +				})
 +			}
 +		})
  	},
 -	selectInvert : function(el)
 +	selectIndices : function(elements, indices)
  	{
 -		$A(el.options).each(function(option)
 +		selection = this;
 +		indices.each(function(index)
  		{
 -			option.selected = !option.selected;
 -		});
 +			selection.selectIndex(elements,index);
 +		})
 +	},
 +
 +	selectClear : function(elements)
 +	{
 +		elements.each(function(el)
 +		{
 +			el.selectedIndex = -1;
 +		})
 +	},
 +
 +	getListElements : function(element, total)
 +	{
 +		elements = new Array();
 +		for(i = 0; i < total; i++)
 +		{
 +			el = $(element+"_c"+i);
 +			if(el)
 +				elements.push(el);
 +		}
 +		return elements;
  	},
 -	checkValue : function(name, value)
 +	checkValue : function(elements, value)
  	{
 -		$A(document.getElementsByName(name)).each(function(el)
 +		elements.each(function(el)
  		{
 -			el.checked = el.value == value
 +			if(typeof(value) == "boolean")
 +				el.checked = value;
 +			else if(el.value == value)
 +				el.checked = true;
  		});
  	},
 -	checkIndex : function(name, index)
 +	checkValues : function(elements, values)
 +	{
 +		selection = this;
 +		values.each(function(value)
 +		{
 +			selection.checkValue(elements, value);
 +		})
 +	},
 +
 +	checkIndex : function(elements, index)
  	{
 -		var elements = $A(document.getElementsByName(name));
  		for(var i = 0; i<elements.length; i++)
  		{
  			if(i == index)
 @@ -3730,31 +3858,65 @@ Prado.Element.Selection =  		}
  	},
 -	checkClear : function(name)
 +	checkIndices : function(elements, indices)
 +	{
 +		selection = this;
 +		indices.each(function(index)
 +		{
 +			selection.checkIndex(elements, index);
 +		})
 +	},
 +
 +	checkClear : function(elements)
  	{
 -		$A(document.getElementsByName(name)).each(function(el)
 +		elements.each(function(el)
  		{
  			el.checked = false;
  		});
  	},
 -	checkAll : function(name)
 +	checkAll : function(elements)
  	{
 -		$A(document.getElementsByName(name)).each(function(el)
 +		elements.each(function(el)
  		{
  			el.checked = true;
 -		});
 +		})
  	},
 -	checkInvert : function(name)
 +
 +	checkInvert : function(elements)
  	{
 -		$A(document.getElementsByName(name)).each(function(el)
 +		elements.each(function(el)
  		{
 -			el.checked = !el.checked;
 -		});
 +			el.checked != el.checked;
 +		})
  	}
  };
 +Prado.Element.Insert =
 +{
 +	append: function(element, content)
 +	{
 +		new Insertion.Bottom(element, content);
 +	},
 +
 +	prepend: function(element, content)
 +	{
 +		new Insertion.Top(element, content);
 +	},
 +
 +	after: function(element, content)
 +	{
 +		new Insertion.After(element, content);
 +	},
 +
 +	before: function(element, content)
 +	{
 +		new Insertion.Before(element, content);
 +	}
 +}
 +
 +
  /**
   * Export scripaculous builder utilities as window[functions]
   */
 diff --git a/framework/Web/Javascripts/js/debug/validator.js b/framework/Web/Javascripts/js/debug/validator.js index 170d7ff1..83453052 100644 --- a/framework/Web/Javascripts/js/debug/validator.js +++ b/framework/Web/Javascripts/js/debug/validator.js @@ -1,4 +1,3 @@ -
  /**
   * Prado client-side javascript validation fascade.
   *
 @@ -90,6 +89,15 @@ Object.extend(Prado.Validation,  	},
  	/**
 +	 * @return string first form ID.
 +	 */
 +	getForm : function()
 +	{
 +		var keys = $H(this.managers).keys();
 +		return keys[0];
 +	},
 +
 +	/**
  	 * Check if the validators are valid for a particular form (and group).
  	 * The validators states will not be changed.
  	 * The <tt>validate</tt> function should be called first.
 @@ -131,6 +139,21 @@ Object.extend(Prado.Validation,  		else
  			throw new Error("A validation manager for form '"+formID+"' needs to be created first.");
  		return this.managers[formID];
 +	},
 +
 +	setErrorMessage : function(validatorID, message)
 +	{
 +		$H(Prado.Validation.managers).each(function(manager)
 +		{
 +			manager[1].validators.each(function(validator)
 +			{
 +				if(validator.options.ID == validatorID)
 +				{
 +					validator.options.ErrorMessage = message;
 +					$(validatorID).innerHTML = message;
 +				}
 +			});
 +		});
  	}
  });
 @@ -144,11 +167,6 @@ Prado.ValidationManager = Class.create();   */
  Prado.ValidationManager.prototype =
  {
 -	validators : [], // list of validators
 -	summaries : [], // validation summaries
 -	groups : [], // validation groups
 -	options : {},
 -
  	/**
  	 * <code>
  	 * options['FormID']*	The ID of HTML form to manage.
 @@ -156,6 +174,11 @@ Prado.ValidationManager.prototype =  	 */
  	initialize : function(options)
  	{
 +		this.validators = []; // list of validators
 +		this.summaries = []; // validation summaries
 +		this.groups = []; // validation groups
 +		this.options = {};
 +
  		this.options = options;
  		Prado.Validation.managers[options.FormID] = this;
  	},
 @@ -333,11 +356,6 @@ Prado.ValidationManager.prototype =  Prado.WebUI.TValidationSummary = Class.create();
  Prado.WebUI.TValidationSummary.prototype =
  {
 -	group : null,
 -	options : {},
 -	visible : false,
 -	messages : null,
 -
  	/**
  	 * <code>
  	 * options['ID']*				Validation summary ID, i.e., an HTML element ID
 @@ -357,9 +375,12 @@ Prado.WebUI.TValidationSummary.prototype =  		this.options = options;
  		this.group = options.ValidationGroup;
  		this.messages = $(options.ID);
 -		this.visible = this.messages.style.visibility != "hidden"
 -		this.visible = this.visible && this.messages.style.display != "none";
 -		Prado.Validation.addSummary(options.FormID, this);
 +		if(this.messages)
 +		{
 +			this.visible = this.messages.style.visibility != "hidden"
 +			this.visible = this.visible && this.messages.style.display != "none";
 +			Prado.Validation.addSummary(options.FormID, this);
 +		}
  	},
  	/**
 @@ -540,15 +561,6 @@ Prado.WebUI.TValidationSummary.prototype =  Prado.WebUI.TBaseValidator = Class.create();
  Prado.WebUI.TBaseValidator.prototype =
  {
 -	enabled : true,
 -	visible : false,
 -	isValid : true,
 -	options : {},
 -	_isObserving : {},
 -	group : null,
 -	manager : null,
 -	message : null,
 -
  	/**
  	 * <code>
  	 * options['ID']*				Validator ID, e.g. span with message
 @@ -561,8 +573,8 @@ Prado.WebUI.TBaseValidator.prototype =  	 * options['ValidationGroup']	Validation group
  	 * options['ControlCssClass']	Css class to use on the input upon error
  	 * options['OnValidate']		Function to call immediately after validation
 -	 * options['OnSuccess']			Function to call upon after successful validation
 -	 * options['OnError']			Function to call upon after error in validation.
 +	 * options['OnValidationSuccess']			Function to call upon after successful validation
 +	 * options['OnValidationError']			Function to call upon after error in validation.
  	 * options['ObserveChanges'] 	True to observe changes in input
  	 * </code>
  	 */
 @@ -572,12 +584,23 @@ Prado.WebUI.TBaseValidator.prototype =  		options.OnSuccess = options.OnSuccess || Prototype.emptyFunction;
  		options.OnError = options.OnError || Prototype.emptyFunction;
  	*/
 +
 +		this.enabled = true;
 +		this.visible = false;
 +		this.isValid = true;
 +		this._isObserving = {};
 +		this.group = null;
 +		this.requestDispatched = false;
 +
  		this.options = options;
  		this.control = $(options.ControlToValidate);
  		this.message = $(options.ID);
 -		this.group = options.ValidationGroup;
 +		if(this.control && this.message)
 +		{
 +			this.group = options.ValidationGroup;
 -		this.manager = Prado.Validation.addValidator(options.FormID, this);
 +			this.manager = Prado.Validation.addValidator(options.FormID, this);
 +		}
  	},
  	/**
 @@ -599,6 +622,8 @@ Prado.WebUI.TBaseValidator.prototype =  		if(this.options.FocusOnError && !this.isValid )
  			Prado.Element.focus(this.options.FocusElementID);
 +
 +		this.visible = true;
  	},
  	refreshControlAndMessage : function()
 @@ -650,8 +675,21 @@ Prado.WebUI.TBaseValidator.prototype =  	 */
  	validate : function(invoker)
  	{
 +		//try to find the control.
 +		if(!this.control)
 +			this.control = $(this.options.ControlToValidate);
 +
 +		if(!this.control)
 +		{
 +			this.isValid = true;
 +			return this.isValid;
 +		}
 +
  		if(typeof(this.options.OnValidate) == "function")
 -			this.options.OnValidate(this, invoker);
 +		{
 +			if(this.requestDispatched == false)
 +				this.options.OnValidate(this, invoker);
 +		}
  		if(this.enabled)
  			this.isValid = this.evaluateIsValid();
 @@ -660,20 +698,26 @@ Prado.WebUI.TBaseValidator.prototype =  		if(this.isValid)
  		{
 -			if(typeof(this.options.OnSuccess) == "function")
 +			if(typeof(this.options.OnValidationSuccess) == "function")
  			{
 -				this.refreshControlAndMessage();
 -				this.options.OnSuccess(this, invoker);
 +				if(this.requestDispatched == false)
 +				{
 +					this.refreshControlAndMessage();
 +					this.options.OnValidationSuccess(this, invoker);
 +				}
  			}
  			else
  				this.updateControl();
  		}
  		else
  		{
 -			if(typeof(this.options.OnError) == "function")
 +			if(typeof(this.options.OnValidationError) == "function")
  			{
 -				this.refreshControlAndMessage();
 -				this.options.OnError(this, invoker);
 +				if(this.requestDispatched == false)
 +				{
 +					this.refreshControlAndMessage();
 +					this.options.OnValidationError(this, invoker)
 +				}
  			}
  			else
  				this.updateControl();
 @@ -1081,6 +1125,51 @@ Prado.WebUI.TCustomValidator = Class.extend(Prado.WebUI.TBaseValidator,  });
  /**
 + * Uses callback request to perform validation.
 + */
 +Prado.WebUI.TActiveCustomValidator = Class.extend(Prado.WebUI.TBaseValidator,
 +{
 +	validatingValue : null,
 +
 +	/**
 +	 * Calls custom validation function.
 +	 */
 +	evaluateIsValid : function()
 +	{
 +		value = this.getValidationValue();
 +		if(!this.requestDispatched && value != this.validatingValue)
 +		{
 +			this.validatingValue = value;
 +			request = new Prado.CallbackRequest(this.options.EventTarget, this.options);
 +			request.setCallbackParameter(value);
 +			request.setCausesValidation(false);
 +			request.options.onSuccess = this.callbackOnSuccess.bind(this);
 +			request.options.onFailure = this.callbackOnFailure.bind(this);
 +			request.dispatch();
 +			this.requestDispatched = true;
 +			return false;
 +		}
 +		return this.isValid;
 +	},
 +
 +	callbackOnSuccess : function(request, data)
 +	{
 +		this.isValid = data;
 +		this.requestDispatched = false;
 +		if(typeof(this.options.onSuccess) == "function")
 +			this.options.onSuccess(request,data);
 +		Prado.Validation.validate(this.options.FormID, this.group,null);
 +	},
 +
 +	callbackOnFailure : function(request, data)
 +	{
 +		this.requestDispatched = false;
 +		if(typeof(this.options.onFailure) == "function")
 +			this.options.onFailure(request,data);
 +	}
 +});
 +
 +/**
   * TRangeValidator tests whether an input value is within a specified range.
   *
   * TRangeValidator uses three key properties to perform its validation.
 @@ -1273,6 +1362,5 @@ Prado.WebUI.TDataTypeValidator = Class.extend(Prado.WebUI.TBaseValidator,              return this.convert(this.options.DataType, value) != null;
          }
  });
 -
  | 
