From 1b9b64637fe08848f610bbd19b80c031cb7dba63 Mon Sep 17 00:00:00 2001 From: wei <> Date: Tue, 27 Mar 2007 02:23:20 +0000 Subject: set prado trunk to use prototype.js v1.5.1rc2, script.aculo.us to v1.7.1beta --- framework/Web/Javascripts/js/debug/ajax.js | 2474 ++++++++++++++++++++ .../Web/Javascripts/js/debug/clientscripts.php | 2 +- framework/Web/Javascripts/js/debug/effects.js | 126 +- framework/Web/Javascripts/js/debug/prado.js | 280 ++- framework/Web/Javascripts/js/debug/validator.js | 158 +- 5 files changed, 2884 insertions(+), 156 deletions(-) create mode 100644 framework/Web/Javascripts/js/debug/ajax.js (limited to 'framework/Web/Javascripts/js/debug') 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, + ''); + 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 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("
  • " + elem.substr(0, entry.length) + "" + + elem.substr(entry.length) + "
  • "); + 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("
  • " + elem.substr(0, foundPos) + "" + + elem.substr(foundPos, entry.length) + "" + elem.substr( + foundPos + entry.length) + "
  • "); + 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 ""; + } + }, 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(/
    /i); + }, + convertHTMLLineBreaks: function(string) { + return string.replace(/
    /gi, "\n").replace(//gi, "\n").replace(/<\/p>/gi, "\n").replace(/

    /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('()([\\s\\S\\w\\W]*)()',"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[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. + * + * request = new Prado.CallbackRequest(UniqueID, callback); + * request.dispatch(); + * + */ +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 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 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 = '

    '; - 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)([\\s\\S\\w\\W]*)()',"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 * 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, - /** * * 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, - /** * * 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 * */ @@ -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(); @@ -1080,6 +1124,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. * @@ -1273,6 +1362,5 @@ Prado.WebUI.TDataTypeValidator = Class.extend(Prado.WebUI.TBaseValidator, return this.convert(this.options.DataType, value) != null; } }); - -- cgit v1.2.3