From ccf76e430b7703db028966a845a966f50956f490 Mon Sep 17 00:00:00 2001 From: xue <> Date: Mon, 5 Dec 2005 01:00:16 +0000 Subject: --- framework/Web/Javascripts/effects/controls.js | 711 ++++++++ framework/Web/Javascripts/effects/dragdrop.js | 516 ++++++ framework/Web/Javascripts/effects/effects.js | 811 +++++++++ framework/Web/Javascripts/effects/rico.js | 2289 +++++++++++++++++++++++++ framework/Web/Javascripts/effects/slider.js | 258 +++ framework/Web/Javascripts/effects/util.js | 548 ++++++ 6 files changed, 5133 insertions(+) create mode 100644 framework/Web/Javascripts/effects/controls.js create mode 100644 framework/Web/Javascripts/effects/dragdrop.js create mode 100644 framework/Web/Javascripts/effects/effects.js create mode 100644 framework/Web/Javascripts/effects/rico.js create mode 100644 framework/Web/Javascripts/effects/slider.js create mode 100644 framework/Web/Javascripts/effects/util.js (limited to 'framework/Web/Javascripts/effects') diff --git a/framework/Web/Javascripts/effects/controls.js b/framework/Web/Javascripts/effects/controls.js new file mode 100644 index 00000000..5212df51 --- /dev/null +++ b/framework/Web/Javascripts/effects/controls.js @@ -0,0 +1,711 @@ +// Copyright (c) 2005 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us) +// (c) 2005 Ivan Krstic (http://blogs.law.harvard.edu/ivan) +// (c) 2005 Jon Tirsen (http://www.tirsen.com) +// Contributors: +// Richard Livsey +// Rahul Bhargava +// Rob Wills +// +// See scriptaculous.js for full license. + +// 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. + +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 && (navigator.appVersion.indexOf('MSIE')>0) && (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); + 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(navigator.appVersion.indexOf('AppleWebKit')>0) Event.stop(event); + return; + case Event.KEY_DOWN: + this.markNext(); + this.render(); + if(navigator.appVersion.indexOf('AppleWebKit')>0) Event.stop(event); + return; + } + else + if(event.keyCode==Event.KEY_TAB || event.keyCode==Event.KEY_RETURN) + return; + + this.changed = true; + this.hasFocus = true; + + if(this.observer) clearTimeout(this.observer); + this.observer = + setTimeout(this.onObserverEvent.bind(this), this.options.frequency*1000); + }, + + 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.hide(); + }, + + markPrevious: function() { + if(this.index > 0) this.index-- + else this.index = this.entryCount-1; + }, + + markNext: function() { + if(this.index < this.entryCount-1) this.index++ + else this.index = 0; + }, + + 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 = 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.firstChild); + + if(this.update.firstChild && this.update.firstChild.childNodes) { + this.entryCount = + this.update.firstChild.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; + 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 +/* +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({ + okText: "ok", + cancelText: "cancel", + 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, + ajaxOptions: {} + }, 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() { + 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); + Field.focus(this.editField); + // stop the event to avoid a page refresh in Safari + if (arguments.length > 1) { + Event.stop(arguments[0]); + } + }, + 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); + } + + okButton = document.createElement("input"); + okButton.type = "submit"; + okButton.value = this.options.okText; + this.form.appendChild(okButton); + + cancelLink = document.createElement("a"); + cancelLink.href = "#"; + cancelLink.appendChild(document.createTextNode(this.options.cancelText)); + cancelLink.onclick = this.onclickCancel.bind(this); + this.form.appendChild(cancelLink); + }, + 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(); + } + + if (this.options.rows == 1 && !this.hasHTMLLineBreaks(text)) { + this.options.textarea = false; + var textField = document.createElement("input"); + textField.type = "text"; + textField.name = "value"; + textField.value = text; + textField.style.backgroundColor = this.options.highlightcolor; + var size = this.options.size || this.options.cols || 0; + if (size != 0) textField.size = size; + this.editField = textField; + } else { + this.options.textarea = true; + var textArea = document.createElement("textarea"); + textArea.name = "value"; + textArea.value = this.convertHTMLLineBreaks(text); + textArea.rows = this.options.rows; + textArea.cols = this.options.cols || 40; + 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(); + }, + 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(); + + 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); + } + } +}; +*/ \ No newline at end of file diff --git a/framework/Web/Javascripts/effects/dragdrop.js b/framework/Web/Javascripts/effects/dragdrop.js new file mode 100644 index 00000000..bfb6e8e9 --- /dev/null +++ b/framework/Web/Javascripts/effects/dragdrop.js @@ -0,0 +1,516 @@ +// Copyright (c) 2005 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us) +// +// Element.Class part Copyright (c) 2005 by Rick Olson +// +// See scriptaculous.js for full license. + +/*--------------------------------------------------------------------------*/ + +var Droppables = { + drops: [], + + remove: function(element) { + this.drops = this.drops.reject(function(d) { return d.element==element }); + }, + + add: function(element) { + element = $(element); + var options = Object.extend({ + greedy: true, + hoverclass: null + }, arguments[1] || {}); + + // cache containers + if(options.containment) { + options._containers = []; + var containment = options.containment; + if((typeof containment == 'object') && + (containment.constructor == Array)) { + containment.each( function(c) { options._containers.push($(c)) }); + } else { + options._containers.push($(containment)); + } + } + + Element.makePositioned(element); // fix IE + options.element = element; + + this.drops.push(options); + }, + + isContained: function(element, drop) { + var parentNode = element.parentNode; + return drop._containers.detect(function(c) { return parentNode == c }); + }, + + isAffected: function(pX, pY, element, drop) { + return ( + (drop.element!=element) && + ((!drop._containers) || + this.isContained(element, drop)) && + ((!drop.accept) || + (Element.Class.has_any(element, drop.accept))) && + Position.within(drop.element, pX, pY) ); + }, + + deactivate: function(drop) { + if(drop.hoverclass) + Element.Class.remove(drop.element, drop.hoverclass); + this.last_active = null; + }, + + activate: function(drop) { + if(this.last_active) this.deactivate(this.last_active); + if(drop.hoverclass) + Element.Class.add(drop.element, drop.hoverclass); + this.last_active = drop; + }, + + show: function(event, element) { + if(!this.drops.length) return; + var pX = Event.pointerX(event); + var pY = Event.pointerY(event); + Position.prepare(); + + var i = this.drops.length-1; do { + var drop = this.drops[i]; + if(this.isAffected(pX, pY, element, drop)) { + if(drop.onHover) + drop.onHover(element, drop.element, Position.overlap(drop.overlap, drop.element)); + if(drop.greedy) { + this.activate(drop); + return; + } + } + } while (i--); + + if(this.last_active) this.deactivate(this.last_active); + }, + + fire: function(event, element) { + if(!this.last_active) return; + Position.prepare(); + + if (this.isAffected(Event.pointerX(event), Event.pointerY(event), element, this.last_active)) + if (this.last_active.onDrop) + this.last_active.onDrop(element, this.last_active.element, event); + }, + + reset: function() { + if(this.last_active) + this.deactivate(this.last_active); + } +} + +var Draggables = { + observers: [], + addObserver: function(observer) { + this.observers.push(observer); + }, + removeObserver: function(element) { // element instead of obsever fixes mem leaks + this.observers = this.observers.reject( function(o) { return o.element==element }); + }, + notify: function(eventName, draggable) { // 'onStart', 'onEnd' + this.observers.invoke(eventName, draggable); + } +} + +/*--------------------------------------------------------------------------*/ + +var Draggable = Class.create(); +Draggable.prototype = { + initialize: function(element) { + this.element = $(element); + var options = Object.extend({ + handle: false, + starteffect: function(element) { + new Effect.Opacity(element, {duration:0.2, from:1.0, to:0.7}); + }, + reverteffect: function(element, top_offset, left_offset) { + var dur = Math.sqrt(Math.abs(top_offset^2)+Math.abs(left_offset^2))*0.02; + new Effect.MoveBy(element, -top_offset, -left_offset, {duration:dur}); + }, + endeffect: function(element) { + new Effect.Opacity(element, {duration:0.2, from:0.7, to:1.0}); + }, + zindex: 1000, + revert: false + }, arguments[1] || {}); + + if(options.handle && (typeof options.handle == 'string')) + this.handle = Element.Class.childrenWith(this.element, options.handle)[0]; + + if(!this.handle) this.handle = $(options.handle); + if(!this.handle) this.handle = this.element; + + Element.makePositioned(this.element); // fix IE + + this.offsetX = 0; + this.offsetY = 0; + this.originalLeft = this.currentLeft(); + this.originalTop = this.currentTop(); + this.originalX = this.element.offsetLeft; + this.originalY = this.element.offsetTop; + + this.options = options; + + this.active = false; + this.dragging = false; + + this.eventMouseDown = this.startDrag.bindAsEventListener(this); + this.eventMouseUp = this.endDrag.bindAsEventListener(this); + this.eventMouseMove = this.update.bindAsEventListener(this); + this.eventKeypress = this.keyPress.bindAsEventListener(this); + + this.registerEvents(); + }, + destroy: function() { + Event.stopObserving(this.handle, "mousedown", this.eventMouseDown); + this.unregisterEvents(); + }, + registerEvents: function() { + Event.observe(document, "mouseup", this.eventMouseUp); + Event.observe(document, "mousemove", this.eventMouseMove); + Event.observe(document, "keypress", this.eventKeypress); + Event.observe(this.handle, "mousedown", this.eventMouseDown); + }, + unregisterEvents: function() { + //if(!this.active) return; + //Event.stopObserving(document, "mouseup", this.eventMouseUp); + //Event.stopObserving(document, "mousemove", this.eventMouseMove); + //Event.stopObserving(document, "keypress", this.eventKeypress); + }, + currentLeft: function() { + return parseInt(this.element.style.left || '0'); + }, + currentTop: function() { + return parseInt(this.element.style.top || '0') + }, + startDrag: function(event) { + if(Event.isLeftClick(event)) { + + // abort on form elements, fixes a Firefox issue + var src = Event.element(event); + if(src.tagName && ( + src.tagName=='INPUT' || + src.tagName=='SELECT' || + src.tagName=='BUTTON' || + src.tagName=='TEXTAREA')) return; + + // this.registerEvents(); + this.active = true; + var pointer = [Event.pointerX(event), Event.pointerY(event)]; + var offsets = Position.cumulativeOffset(this.element); + this.offsetX = (pointer[0] - offsets[0]); + this.offsetY = (pointer[1] - offsets[1]); + Event.stop(event); + } + }, + finishDrag: function(event, success) { + // this.unregisterEvents(); + + this.active = false; + this.dragging = false; + + if(this.options.ghosting) { + Position.relativize(this.element); + Element.remove(this._clone); + this._clone = null; + } + + if(success) Droppables.fire(event, this.element); + Draggables.notify('onEnd', this); + + var revert = this.options.revert; + if(revert && typeof revert == 'function') revert = revert(this.element); + + if(revert && this.options.reverteffect) { + this.options.reverteffect(this.element, + this.currentTop()-this.originalTop, + this.currentLeft()-this.originalLeft); + } else { + this.originalLeft = this.currentLeft(); + this.originalTop = this.currentTop(); + } + + if(this.options.zindex) + this.element.style.zIndex = this.originalZ; + + if(this.options.endeffect) + this.options.endeffect(this.element); + + + Droppables.reset(); + }, + keyPress: function(event) { + if(this.active) { + if(event.keyCode==Event.KEY_ESC) { + this.finishDrag(event, false); + Event.stop(event); + } + } + }, + endDrag: function(event) { + if(this.active && this.dragging) { + this.finishDrag(event, true); + Event.stop(event); + } + this.active = false; + this.dragging = false; + }, + draw: function(event) { + var pointer = [Event.pointerX(event), Event.pointerY(event)]; + var offsets = Position.cumulativeOffset(this.element); + offsets[0] -= this.currentLeft(); + offsets[1] -= this.currentTop(); + var style = this.element.style; + if((!this.options.constraint) || (this.options.constraint=='horizontal')) + style.left = (pointer[0] - offsets[0] - this.offsetX) + "px"; + if((!this.options.constraint) || (this.options.constraint=='vertical')) + style.top = (pointer[1] - offsets[1] - this.offsetY) + "px"; + if(style.visibility=="hidden") style.visibility = ""; // fix gecko rendering + }, + update: function(event) { + if(this.active) { + if(!this.dragging) { + var style = this.element.style; + this.dragging = true; + + if(Element.getStyle(this.element,'position')=='') + style.position = "relative"; + + if(this.options.zindex) { + this.options.originalZ = parseInt(Element.getStyle(this.element,'z-index') || 0); + style.zIndex = this.options.zindex; + } + + if(this.options.ghosting) { + this._clone = this.element.cloneNode(true); + Position.absolutize(this.element); + this.element.parentNode.insertBefore(this._clone, this.element); + } + + Draggables.notify('onStart', this); + if(this.options.starteffect) this.options.starteffect(this.element); + } + + Droppables.show(event, this.element); + this.draw(event); + if(this.options.change) this.options.change(this); + + // fix AppleWebKit rendering + if(navigator.appVersion.indexOf('AppleWebKit')>0) window.scrollBy(0,0); + + Event.stop(event); + } + } +} + +/*--------------------------------------------------------------------------*/ + +var SortableObserver = Class.create(); +SortableObserver.prototype = { + initialize: function(element, observer) { + this.element = $(element); + this.observer = observer; + this.lastValue = Sortable.serialize(this.element); + }, + onStart: function() { + this.lastValue = Sortable.serialize(this.element); + }, + onEnd: function() { + Sortable.unmark(); + if(this.lastValue != Sortable.serialize(this.element)) + this.observer(this.element) + } +} + +var Sortable = { + sortables: new Array(), + options: function(element){ + element = $(element); + return this.sortables.detect(function(s) { return s.element == element }); + }, + destroy: function(element){ + element = $(element); + this.sortables.findAll(function(s) { return s.element == element }).each(function(s){ + Draggables.removeObserver(s.element); + s.droppables.each(function(d){ Droppables.remove(d) }); + s.draggables.invoke('destroy'); + }); + this.sortables = this.sortables.reject(function(s) { return s.element == element }); + }, + create: function(element) { + element = $(element); + var options = Object.extend({ + element: element, + tag: 'li', // assumes li children, override with tag: 'tagname' + dropOnEmpty: false, + tree: false, // fixme: unimplemented + overlap: 'vertical', // one of 'vertical', 'horizontal' + constraint: 'vertical', // one of 'vertical', 'horizontal', false + containment: element, // also takes array of elements (or id's); or false + handle: false, // or a CSS class + only: false, + hoverclass: null, + ghosting: false, + format: null, + onChange: function() {}, + onUpdate: function() {} + }, arguments[1] || {}); + + // clear any old sortable with same element + this.destroy(element); + + // build options for the draggables + var options_for_draggable = { + revert: true, + ghosting: options.ghosting, + constraint: options.constraint, + handle: options.handle }; + + if(options.starteffect) + options_for_draggable.starteffect = options.starteffect; + + if(options.reverteffect) + options_for_draggable.reverteffect = options.reverteffect; + else + if(options.ghosting) options_for_draggable.reverteffect = function(element) { + element.style.top = 0; + element.style.left = 0; + }; + + if(options.endeffect) + options_for_draggable.endeffect = options.endeffect; + + if(options.zindex) + options_for_draggable.zindex = options.zindex; + + // build options for the droppables + var options_for_droppable = { + overlap: options.overlap, + containment: options.containment, + hoverclass: options.hoverclass, + onHover: Sortable.onHover, + greedy: !options.dropOnEmpty + } + + // fix for gecko engine + Element.cleanWhitespace(element); + + options.draggables = []; + options.droppables = []; + + // make it so + + // drop on empty handling + if(options.dropOnEmpty) { + Droppables.add(element, + {containment: options.containment, onHover: Sortable.onEmptyHover, greedy: false}); + options.droppables.push(element); + } + + (this.findElements(element, options) || []).each( function(e) { + // handles are per-draggable + var handle = options.handle ? + Element.Class.childrenWith(e, options.handle)[0] : e; + options.draggables.push( + new Draggable(e, Object.extend(options_for_draggable, { handle: handle }))); + Droppables.add(e, options_for_droppable); + options.droppables.push(e); + }); + + // keep reference + this.sortables.push(options); + + // for onupdate + Draggables.addObserver(new SortableObserver(element, options.onUpdate)); + + }, + + // return all suitable-for-sortable elements in a guaranteed order + findElements: function(element, options) { + if(!element.hasChildNodes()) return null; + var elements = []; + $A(element.childNodes).each( function(e) { + if(e.tagName && e.tagName==options.tag.toUpperCase() && + (!options.only || (Element.Class.has(e, options.only)))) + elements.push(e); + if(options.tree) { + var grandchildren = this.findElements(e, options); + if(grandchildren) elements.push(grandchildren); + } + }); + + return (elements.length>0 ? elements.flatten() : null); + }, + + onHover: function(element, dropon, overlap) { + if(overlap>0.5) { + Sortable.mark(dropon, 'before'); + if(dropon.previousSibling != element) { + var oldParentNode = element.parentNode; + element.style.visibility = "hidden"; // fix gecko rendering + dropon.parentNode.insertBefore(element, dropon); + if(dropon.parentNode!=oldParentNode) + Sortable.options(oldParentNode).onChange(element); + Sortable.options(dropon.parentNode).onChange(element); + } + } else { + Sortable.mark(dropon, 'after'); + var nextElement = dropon.nextSibling || null; + if(nextElement != element) { + var oldParentNode = element.parentNode; + element.style.visibility = "hidden"; // fix gecko rendering + dropon.parentNode.insertBefore(element, nextElement); + if(dropon.parentNode!=oldParentNode) + Sortable.options(oldParentNode).onChange(element); + Sortable.options(dropon.parentNode).onChange(element); + } + } + }, + + onEmptyHover: function(element, dropon) { + if(element.parentNode!=dropon) { + dropon.appendChild(element); + } + }, + + unmark: function() { + if(Sortable._marker) Element.hide(Sortable._marker); + }, + + mark: function(dropon, position) { + // mark on ghosting only + var sortable = Sortable.options(dropon.parentNode); + if(sortable && !sortable.ghosting) return; + + if(!Sortable._marker) { + Sortable._marker = $('dropmarker') || document.createElement('DIV'); + Element.hide(Sortable._marker); + Element.Class.add(Sortable._marker, 'dropmarker'); + Sortable._marker.style.position = 'absolute'; + document.getElementsByTagName("body").item(0).appendChild(Sortable._marker); + } + var offsets = Position.cumulativeOffset(dropon); + Sortable._marker.style.top = offsets[1] + 'px'; + if(position=='after') Sortable._marker.style.top = (offsets[1]+dropon.clientHeight) + 'px'; + Sortable._marker.style.left = offsets[0] + 'px'; + Element.show(Sortable._marker); + }, + + serialize: function(element) { + element = $(element); + var sortableOptions = this.options(element); + var options = Object.extend({ + tag: sortableOptions.tag, + only: sortableOptions.only, + name: element.id, + format: sortableOptions.format || /^[^_]*_(.*)$/ + }, arguments[1] || {}); + return $(this.findElements(element, options) || []).collect( function(item) { + return (encodeURIComponent(options.name) + "[]=" + + encodeURIComponent(item.id.match(options.format) ? item.id.match(options.format)[1] : '')); + }).join("&"); + } +} \ No newline at end of file diff --git a/framework/Web/Javascripts/effects/effects.js b/framework/Web/Javascripts/effects/effects.js new file mode 100644 index 00000000..62aa9a3e --- /dev/null +++ b/framework/Web/Javascripts/effects/effects.js @@ -0,0 +1,811 @@ +// Copyright (c) 2005 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us) +// Contributors: +// Justin Palmer (http://encytemedia.com/) +// Mark Pilgrim (http://diveintomark.org/) +// Martin Bialasinki +// +// See scriptaculous.js for full license. + +var Effect = { + tagifyText: function(element) { + var tagifyStyle = "position:relative"; + if(/MSIE/.test(navigator.userAgent)) tagifyStyle += ";zoom:1"; + element = $(element); + $A(element.childNodes).each( function(child) { + if(child.nodeType==3) { + child.nodeValue.toArray().each( function(character) { + element.insertBefore( + Builder.node('span',{style: tagifyStyle}, + character == " " ? String.fromCharCode(160) : character), + child); + }); + Element.remove(child); + } + }); + }, + multiple: function(element, effect) { + var elements; + if(((typeof element == 'object') || + (typeof element == 'function')) && + (element.length)) + elements = element; + else + elements = $(element).childNodes; + + var options = Object.extend({ + speed: 0.1, + delay: 0.0 + }, arguments[2] || {}); + var speed = options.speed; + var delay = options.delay; + + $A(elements).each( function(element, index) { + new effect(element, Object.extend(options, { delay: delay + index * speed })); + }); + } +}; + +var Effect2 = Effect; // deprecated + +/* ------------- transitions ------------- */ + +Effect.Transitions = {} + +Effect.Transitions.linear = function(pos) { + return pos; +} +Effect.Transitions.sinoidal = function(pos) { + return (-Math.cos(pos*Math.PI)/2) + 0.5; +} +Effect.Transitions.reverse = function(pos) { + return 1-pos; +} +Effect.Transitions.flicker = function(pos) { + return ((-Math.cos(pos*Math.PI)/4) + 0.75) + Math.random()/4; +} +Effect.Transitions.wobble = function(pos) { + return (-Math.cos(pos*Math.PI*(9*pos))/2) + 0.5; +} +Effect.Transitions.pulse = function(pos) { + return (Math.floor(pos*10) % 2 == 0 ? + (pos*10-Math.floor(pos*10)) : 1-(pos*10-Math.floor(pos*10))); +} +Effect.Transitions.none = function(pos) { + return 0; +} +Effect.Transitions.full = function(pos) { + return 1; +} + +/* ------------- core effects ------------- */ + +Effect.Queue = { + effects: [], + interval: null, + add: function(effect) { + var timestamp = new Date().getTime(); + + switch(effect.options.queue) { + case 'front': + // move unstarted effects after this effect + this.effects.findAll(function(e){ return e.state=='idle' }).each( function(e) { + e.startOn += effect.finishOn; + e.finishOn += effect.finishOn; + }); + break; + case 'end': + // start effect after last queued effect has finished + timestamp = this.effects.pluck('finishOn').max() || timestamp; + break; + } + + effect.startOn += timestamp; + effect.finishOn += timestamp; + this.effects.push(effect); + if(!this.interval) + this.interval = setInterval(this.loop.bind(this), 40); + }, + remove: function(effect) { + this.effects = this.effects.reject(function(e) { return e==effect }); + if(this.effects.length == 0) { + clearInterval(this.interval); + this.interval = null; + } + }, + loop: function() { + var timePos = new Date().getTime(); + this.effects.invoke('loop', timePos); + } +} + +Effect.Base = function() {}; +Effect.Base.prototype = { + position: null, + setOptions: function(options) { + this.options = Object.extend({ + transition: Effect.Transitions.sinoidal, + duration: 1.0, // seconds + fps: 25.0, // max. 25fps due to Effect.Queue implementation + sync: false, // true for combining + from: 0.0, + to: 1.0, + delay: 0.0, + queue: 'parallel' + }, options || {}); + }, + start: function(options) { + this.setOptions(options || {}); + this.currentFrame = 0; + this.state = 'idle'; + this.startOn = this.options.delay*1000; + this.finishOn = this.startOn + (this.options.duration*1000); + this.event('beforeStart'); + if(!this.options.sync) Effect.Queue.add(this); + }, + loop: function(timePos) { + if(timePos >= this.startOn) { + if(timePos >= this.finishOn) { + this.render(1.0); + this.cancel(); + this.event('beforeFinish'); + if(this.finish) this.finish(); + this.event('afterFinish'); + return; + } + var pos = (timePos - this.startOn) / (this.finishOn - this.startOn); + var frame = Math.round(pos * this.options.fps * this.options.duration); + 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.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.Queue.remove(this); + this.state = 'finished'; + }, + event: function(eventName) { + if(this.options[eventName + 'Internal']) this.options[eventName + 'Internal'](this); + if(this.options[eventName]) this.options[eventName](this); + } +} + +Effect.Parallel = Class.create(); +Object.extend(Object.extend(Effect.Parallel.prototype, Effect.Base.prototype), { + initialize: function(effects) { + this.effects = effects || []; + this.start(arguments[1]); + }, + update: function(position) { + this.effects.invoke('render', position); + }, + finish: function(position) { + this.effects.each( function(effect) { + effect.render(1.0); + effect.cancel(); + effect.event('beforeFinish'); + if(effect.finish) effect.finish(position); + effect.event('afterFinish'); + }); + } +}); + +Effect.Opacity = Class.create(); +Object.extend(Object.extend(Effect.Opacity.prototype, Effect.Base.prototype), { + initialize: function(element) { + this.element = $(element); + // make this work on IE on elements without 'layout' + if(/MSIE/.test(navigator.userAgent) && (!this.element.hasLayout)) + this.element.style.zoom = 1; + var options = Object.extend({ + from: Element.getOpacity(this.element) || 0.0, + to: 1.0 + }, arguments[1] || {}); + this.start(options); + }, + update: function(position) { + Element.setOpacity(this.element, position); + } +}); + +Effect.MoveBy = Class.create(); +Object.extend(Object.extend(Effect.MoveBy.prototype, Effect.Base.prototype), { + initialize: function(element, toTop, toLeft) { + this.element = $(element); + this.toTop = toTop; + this.toLeft = toLeft; + this.start(arguments[3]); + }, + setup: function() { + // Bug in Opera: Opera returns the "real" position of a static element or + // relative element that does not have top/left explicitly set. + // ==> Always set top and left for position relative elements in your stylesheets + // (to 0 if you do not need them) + + Element.makePositioned(this.element); + this.originalTop = parseFloat(Element.getStyle(this.element,'top') || '0'); + this.originalLeft = parseFloat(Element.getStyle(this.element,'left') || '0'); + }, + update: function(position) { + var topd = this.toTop * position + this.originalTop; + var leftd = this.toLeft * position + this.originalLeft; + this.setPosition(topd, leftd); + }, + setPosition: function(topd, leftd) { + this.element.style.top = topd + "px"; + this.element.style.left = leftd + "px"; + } +}); + +Effect.Scale = Class.create(); +Object.extend(Object.extend(Effect.Scale.prototype, Effect.Base.prototype), { + initialize: function(element, percent) { + this.element = $(element) + var options = Object.extend({ + scaleX: true, + scaleY: true, + scaleContent: true, + scaleFromCenter: false, + scaleMode: 'box', // 'box' or 'contents' or {} with provided values + scaleFrom: 100.0, + scaleTo: percent + }, arguments[2] || {}); + this.start(options); + }, + setup: function() { + var effect = this; + + this.restoreAfterFinish = this.options.restoreAfterFinish || false; + this.elementPositioning = Element.getStyle(this.element,'position'); + + effect.originalStyle = {}; + ['top','left','width','height','fontSize'].each( function(k) { + effect.originalStyle[k] = effect.element.style[k]; + }); + + this.originalTop = this.element.offsetTop; + this.originalLeft = this.element.offsetLeft; + + var fontSize = Element.getStyle(this.element,'font-size') || "100%"; + ['em','px','%'].each( function(fontSizeType) { + if(fontSize.indexOf(fontSizeType)>0) { + effect.fontSize = parseFloat(fontSize); + effect.fontSizeType = fontSizeType; + } + }); + + this.factor = (this.options.scaleTo - this.options.scaleFrom)/100; + + this.dims = null; + if(this.options.scaleMode=='box') + this.dims = [this.element.clientHeight, this.element.clientWidth]; + if(this.options.scaleMode=='content') + this.dims = [this.element.scrollHeight, this.element.scrollWidth]; + if(!this.dims) + this.dims = [this.options.scaleMode.originalHeight, + this.options.scaleMode.originalWidth]; + }, + update: function(position) { + var currentScale = (this.options.scaleFrom/100.0) + (this.factor * position); + if(this.options.scaleContent && this.fontSize) + this.element.style.fontSize = this.fontSize*currentScale + this.fontSizeType; + this.setDimensions(this.dims[0] * currentScale, this.dims[1] * currentScale); + }, + finish: function(position) { + if (this.restoreAfterFinish) { + var effect = this; + ['top','left','width','height','fontSize'].each( function(k) { + effect.element.style[k] = effect.originalStyle[k]; + }); + } + }, + setDimensions: function(height, width) { + var els = this.element.style; + if(this.options.scaleX) els.width = width + 'px'; + if(this.options.scaleY) els.height = height + 'px'; + if(this.options.scaleFromCenter) { + var topd = (height - this.dims[0])/2; + var leftd = (width - this.dims[1])/2; + if(this.elementPositioning == 'absolute') { + if(this.options.scaleY) els.top = this.originalTop-topd + "px"; + if(this.options.scaleX) els.left = this.originalLeft-leftd + "px"; + } else { + if(this.options.scaleY) els.top = -topd + "px"; + if(this.options.scaleX) els.left = -leftd + "px"; + } + } + } +}); + +Effect.Highlight = Class.create(); +Object.extend(Object.extend(Effect.Highlight.prototype, Effect.Base.prototype), { + initialize: function(element) { + this.element = $(element); + var options = Object.extend({ + startcolor: "#ffff99" + }, arguments[1] || {}); + this.start(options); + }, + setup: function() { + // Disable background image during the effect + this.oldBgImage = this.element.style.backgroundImage; + this.element.style.backgroundImage = "none"; + if(!this.options.endcolor) + this.options.endcolor = Element.getStyle(this.element, 'background-color').parseColor('#ffffff'); + if (typeof this.options.restorecolor == "undefined") + this.options.restorecolor = this.element.style.backgroundColor; + // init color calculations + this.colors_base = [ + parseInt(this.options.startcolor.slice(1,3),16), + parseInt(this.options.startcolor.slice(3,5),16), + parseInt(this.options.startcolor.slice(5),16) ]; + this.colors_delta = [ + parseInt(this.options.endcolor.slice(1,3),16)-this.colors_base[0], + parseInt(this.options.endcolor.slice(3,5),16)-this.colors_base[1], + parseInt(this.options.endcolor.slice(5),16)-this.colors_base[2]]; + }, + update: function(position) { + var effect = this; var colors = $R(0,2).map( function(i){ + return Math.round(effect.colors_base[i]+(effect.colors_delta[i]*position)) + }); + this.element.style.backgroundColor = "#" + + colors[0].toColorPart() + colors[1].toColorPart() + colors[2].toColorPart(); + }, + finish: function() { + this.element.style.backgroundColor = this.options.restorecolor; + this.element.style.backgroundImage = this.oldBgImage; + } +}); + +Effect.ScrollTo = Class.create(); +Object.extend(Object.extend(Effect.ScrollTo.prototype, Effect.Base.prototype), { + initialize: function(element) { + this.element = $(element); + this.start(arguments[1] || {}); + }, + setup: function() { + Position.prepare(); + var offsets = Position.cumulativeOffset(this.element); + var max = window.innerHeight ? + window.height - window.innerHeight : + document.body.scrollHeight - + (document.documentElement.clientHeight ? + document.documentElement.clientHeight : document.body.clientHeight); + this.scrollStart = Position.deltaY; + this.delta = (offsets[1] > max ? max : offsets[1]) - this.scrollStart; + }, + update: function(position) { + Position.prepare(); + window.scrollTo(Position.deltaX, + this.scrollStart + (position*this.delta)); + } +}); + +/* ------------- combination effects ------------- */ + +Effect.Fade = function(element) { + var oldOpacity = Element.getInlineOpacity(element); + var options = Object.extend({ + from: Element.getOpacity(element) || 1.0, + to: 0.0, + afterFinishInternal: function(effect) + { if (effect.options.to == 0) { + Element.hide(effect.element); + Element.setInlineOpacity(effect.element, oldOpacity); + } + } + }, arguments[1] || {}); + return new Effect.Opacity(element,options); +} + +Effect.Appear = function(element) { + var options = Object.extend({ + from: (Element.getStyle(element, "display") == "none" ? 0.0 : Element.getOpacity(element) || 0.0), + to: 1.0, + beforeSetup: function(effect) + { Element.setOpacity(effect.element, effect.options.from); + Element.show(effect.element); } + }, arguments[1] || {}); + return new Effect.Opacity(element,options); +} + +Effect.Puff = function(element) { + element = $(element); + var oldOpacity = Element.getInlineOpacity(element); + var oldPosition = element.style.position; + return new Effect.Parallel( + [ new Effect.Scale(element, 200, + { sync: true, scaleFromCenter: true, scaleContent: true, restoreAfterFinish: true }), + new Effect.Opacity(element, { sync: true, to: 0.0 } ) ], + Object.extend({ duration: 1.0, + beforeSetupInternal: function(effect) + { effect.effects[0].element.style.position = 'absolute'; }, + afterFinishInternal: function(effect) + { Element.hide(effect.effects[0].element); + effect.effects[0].element.style.position = oldPosition; + Element.setInlineOpacity(effect.effects[0].element, oldOpacity); } + }, arguments[1] || {}) + ); +} + +Effect.BlindUp = function(element) { + element = $(element); + Element.makeClipping(element); + return new Effect.Scale(element, 0, + Object.extend({ scaleContent: false, + scaleX: false, + restoreAfterFinish: true, + afterFinishInternal: function(effect) + { + Element.hide(effect.element); + Element.undoClipping(effect.element); + } + }, arguments[1] || {}) + ); +} + +Effect.BlindDown = function(element) { + element = $(element); + var oldHeight = element.style.height; + var elementDimensions = Element.getDimensions(element); + return new Effect.Scale(element, 100, + Object.extend({ scaleContent: false, + scaleX: false, + scaleFrom: 0, + scaleMode: {originalHeight: elementDimensions.height, originalWidth: elementDimensions.width}, + restoreAfterFinish: true, + afterSetup: function(effect) { + Element.makeClipping(effect.element); + effect.element.style.height = "0px"; + Element.show(effect.element); + }, + afterFinishInternal: function(effect) { + Element.undoClipping(effect.element); + effect.element.style.height = oldHeight; + } + }, arguments[1] || {}) + ); +} + +Effect.SwitchOff = function(element) { + element = $(element); + var oldOpacity = Element.getInlineOpacity(element); + return new Effect.Appear(element, { + duration: 0.4, + from: 0, + transition: Effect.Transitions.flicker, + afterFinishInternal: function(effect) { + new Effect.Scale(effect.element, 1, { + duration: 0.3, scaleFromCenter: true, + scaleX: false, scaleContent: false, restoreAfterFinish: true, + beforeSetup: function(effect) { + Element.makePositioned(effect.element); + Element.makeClipping(effect.element); + }, + afterFinishInternal: function(effect) { + Element.hide(effect.element); + Element.undoClipping(effect.element); + Element.undoPositioned(effect.element); + Element.setInlineOpacity(effect.element, oldOpacity); + } + }) + } + }); +} + +Effect.DropOut = function(element) { + element = $(element); + var oldTop = element.style.top; + var oldLeft = element.style.left; + var oldOpacity = Element.getInlineOpacity(element); + return new Effect.Parallel( + [ new Effect.MoveBy(element, 100, 0, { sync: true }), + new Effect.Opacity(element, { sync: true, to: 0.0 }) ], + Object.extend( + { duration: 0.5, + beforeSetup: function(effect) { + Element.makePositioned(effect.effects[0].element); }, + afterFinishInternal: function(effect) { + Element.hide(effect.effects[0].element); + Element.undoPositioned(effect.effects[0].element); + effect.effects[0].element.style.left = oldLeft; + effect.effects[0].element.style.top = oldTop; + Element.setInlineOpacity(effect.effects[0].element, oldOpacity); } + }, arguments[1] || {})); +} + +Effect.Shake = function(element) { + element = $(element); + var oldTop = element.style.top; + var oldLeft = element.style.left; + return new Effect.MoveBy(element, 0, 20, + { duration: 0.05, afterFinishInternal: function(effect) { + new Effect.MoveBy(effect.element, 0, -40, + { duration: 0.1, afterFinishInternal: function(effect) { + new Effect.MoveBy(effect.element, 0, 40, + { duration: 0.1, afterFinishInternal: function(effect) { + new Effect.MoveBy(effect.element, 0, -40, + { duration: 0.1, afterFinishInternal: function(effect) { + new Effect.MoveBy(effect.element, 0, 40, + { duration: 0.1, afterFinishInternal: function(effect) { + new Effect.MoveBy(effect.element, 0, -20, + { duration: 0.05, afterFinishInternal: function(effect) { + Element.undoPositioned(effect.element); + effect.element.style.left = oldLeft; + effect.element.style.top = oldTop; + }}) }}) }}) }}) }}) }}); +} + +Effect.SlideDown = function(element) { + element = $(element); + Element.cleanWhitespace(element); + // SlideDown need to have the content of the element wrapped in a container element with fixed height! + var oldInnerBottom = element.firstChild.style.bottom; + var elementDimensions = Element.getDimensions(element); + return new Effect.Scale(element, 100, + Object.extend({ scaleContent: false, + scaleX: false, + scaleFrom: 0, + scaleMode: {originalHeight: elementDimensions.height, originalWidth: elementDimensions.width}, + restoreAfterFinish: true, + afterSetup: function(effect) { + Element.makePositioned(effect.element.firstChild); + if (window.opera) effect.element.firstChild.style.top = ""; + Element.makeClipping(effect.element); + element.style.height = '0'; + Element.show(element); + }, + afterUpdateInternal: function(effect) { + effect.element.firstChild.style.bottom = + (effect.originalHeight - effect.element.clientHeight) + 'px'; }, + afterFinishInternal: function(effect) { + Element.undoClipping(effect.element); + Element.undoPositioned(effect.element.firstChild); + effect.element.firstChild.style.bottom = oldInnerBottom; } + }, arguments[1] || {}) + ); +} + +Effect.SlideUp = function(element) { + element = $(element); + Element.cleanWhitespace(element); + var oldInnerBottom = element.firstChild.style.bottom; + return new Effect.Scale(element, 0, + Object.extend({ scaleContent: false, + scaleX: false, + scaleMode: 'box', + scaleFrom: 100, + restoreAfterFinish: true, + beforeStartInternal: function(effect) { + Element.makePositioned(effect.element.firstChild); + if (window.opera) effect.element.firstChild.style.top = ""; + Element.makeClipping(effect.element); + Element.show(element); + }, + afterUpdateInternal: function(effect) { + effect.element.firstChild.style.bottom = + (effect.originalHeight - effect.element.clientHeight) + 'px'; }, + afterFinishInternal: function(effect) { + Element.hide(effect.element); + Element.undoClipping(effect.element); + Element.undoPositioned(effect.element.firstChild); + effect.element.firstChild.style.bottom = oldInnerBottom; } + }, arguments[1] || {}) + ); +} + +Effect.Squish = function(element) { + // Bug in opera makes the TD containing this element expand for a instance after finish + return new Effect.Scale(element, window.opera ? 1 : 0, + { restoreAfterFinish: true, + beforeSetup: function(effect) { + Element.makeClipping(effect.element); }, + afterFinishInternal: function(effect) { + Element.hide(effect.element); + Element.undoClipping(effect.element); } + }); +} + +Effect.Grow = function(element) { + element = $(element); + var options = arguments[1] || {}; + + var elementDimensions = Element.getDimensions(element); + var originalWidth = elementDimensions.width; + var originalHeight = elementDimensions.height; + var oldTop = element.style.top; + var oldLeft = element.style.left; + var oldHeight = element.style.height; + var oldWidth = element.style.width; + var oldOpacity = Element.getInlineOpacity(element); + + var direction = options.direction || 'center'; + var moveTransition = options.moveTransition || Effect.Transitions.sinoidal; + var scaleTransition = options.scaleTransition || Effect.Transitions.sinoidal; + var opacityTransition = options.opacityTransition || Effect.Transitions.full; + + var initialMoveX, initialMoveY; + var moveX, moveY; + + switch (direction) { + case 'top-left': + initialMoveX = initialMoveY = moveX = moveY = 0; + break; + case 'top-right': + initialMoveX = originalWidth; + initialMoveY = moveY = 0; + moveX = -originalWidth; + break; + case 'bottom-left': + initialMoveX = moveX = 0; + initialMoveY = originalHeight; + moveY = -originalHeight; + break; + case 'bottom-right': + initialMoveX = originalWidth; + initialMoveY = originalHeight; + moveX = -originalWidth; + moveY = -originalHeight; + break; + case 'center': + initialMoveX = originalWidth / 2; + initialMoveY = originalHeight / 2; + moveX = -originalWidth / 2; + moveY = -originalHeight / 2; + break; + } + + return new Effect.MoveBy(element, initialMoveY, initialMoveX, { + duration: 0.01, + beforeSetup: function(effect) { + Element.hide(effect.element); + Element.makeClipping(effect.element); + Element.makePositioned(effect.element); + }, + afterFinishInternal: function(effect) { + new Effect.Parallel( + [ new Effect.Opacity(effect.element, { sync: true, to: 1.0, from: 0.0, transition: opacityTransition }), + new Effect.MoveBy(effect.element, moveY, moveX, { sync: true, transition: moveTransition }), + new Effect.Scale(effect.element, 100, { + scaleMode: { originalHeight: originalHeight, originalWidth: originalWidth }, + sync: true, scaleFrom: window.opera ? 1 : 0, transition: scaleTransition, restoreAfterFinish: true}) + ], Object.extend({ + beforeSetup: function(effect) { + effect.effects[0].element.style.height = 0; + Element.show(effect.effects[0].element); + }, + afterFinishInternal: function(effect) { + var el = effect.effects[0].element; + var els = el.style; + Element.undoClipping(el); + Element.undoPositioned(el); + els.top = oldTop; + els.left = oldLeft; + els.height = oldHeight; + els.width = originalWidth; + Element.setInlineOpacity(el, oldOpacity); + } + }, options) + ) + } + }); +} + +Effect.Shrink = function(element) { + element = $(element); + var options = arguments[1] || {}; + + var originalWidth = element.clientWidth; + var originalHeight = element.clientHeight; + var oldTop = element.style.top; + var oldLeft = element.style.left; + var oldHeight = element.style.height; + var oldWidth = element.style.width; + var oldOpacity = Element.getInlineOpacity(element); + + var direction = options.direction || 'center'; + var moveTransition = options.moveTransition || Effect.Transitions.sinoidal; + var scaleTransition = options.scaleTransition || Effect.Transitions.sinoidal; + var opacityTransition = options.opacityTransition || Effect.Transitions.none; + + var moveX, moveY; + + switch (direction) { + case 'top-left': + moveX = moveY = 0; + break; + case 'top-right': + moveX = originalWidth; + moveY = 0; + break; + case 'bottom-left': + moveX = 0; + moveY = originalHeight; + break; + case 'bottom-right': + moveX = originalWidth; + moveY = originalHeight; + break; + case 'center': + moveX = originalWidth / 2; + moveY = originalHeight / 2; + break; + } + + return new Effect.Parallel( + [ new Effect.Opacity(element, { sync: true, to: 0.0, from: 1.0, transition: opacityTransition }), + new Effect.Scale(element, window.opera ? 1 : 0, { sync: true, transition: scaleTransition, restoreAfterFinish: true}), + new Effect.MoveBy(element, moveY, moveX, { sync: true, transition: moveTransition }) + ], Object.extend({ + beforeStartInternal: function(effect) { + Element.makePositioned(effect.effects[0].element); + Element.makeClipping(effect.effects[0].element); + }, + afterFinishInternal: function(effect) { + var el = effect.effects[0].element; + var els = el.style; + Element.hide(el); + Element.undoClipping(el); + Element.undoPositioned(el); + els.top = oldTop; + els.left = oldLeft; + els.height = oldHeight; + els.width = oldWidth; + Element.setInlineOpacity(el, oldOpacity); + } + }, options) + ); +} + +Effect.Pulsate = function(element) { + element = $(element); + var options = arguments[1] || {}; + var oldOpacity = Element.getInlineOpacity(element); + var transition = options.transition || Effect.Transitions.sinoidal; + var reverser = function(pos){ return transition(1-Effect.Transitions.pulse(pos)) }; + reverser.bind(transition); + return new Effect.Opacity(element, + Object.extend(Object.extend({ duration: 3.0, from: 0, + afterFinishInternal: function(effect) { Element.setInlineOpacity(effect.element, oldOpacity); } + }, options), {transition: reverser})); +} + +Effect.Fold = function(element) { + element = $(element); + var originalTop = element.style.top; + var originalLeft = element.style.left; + var originalWidth = element.style.width; + var originalHeight = element.style.height; + Element.makeClipping(element); + return new Effect.Scale(element, 5, Object.extend({ + scaleContent: false, + scaleX: false, + afterFinishInternal: function(effect) { + new Effect.Scale(element, 1, { + scaleContent: false, + scaleY: false, + afterFinishInternal: function(effect) { + Element.hide(effect.element); + Element.undoClipping(effect.element); + effect.element.style.top = originalTop; + effect.element.style.left = originalLeft; + effect.element.style.width = originalWidth; + effect.element.style.height = originalHeight; + } }); + }}, arguments[1] || {})); +} diff --git a/framework/Web/Javascripts/effects/rico.js b/framework/Web/Javascripts/effects/rico.js new file mode 100644 index 00000000..4253d4b0 --- /dev/null +++ b/framework/Web/Javascripts/effects/rico.js @@ -0,0 +1,2289 @@ +/* openrico.org rico.js */ + +/** + * + * Copyright 2005 Sabre Airline Solutions + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the + * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific language governing permissions + * and limitations under the License. + **/ + +// rico.js -------------------- + +var Rico = { + Version: '1.1-beta' +} + +Rico.ArrayExtensions = new Array(); + +if (Object.prototype.extend) { + // in prototype.js... + Rico.ArrayExtensions[ Rico.ArrayExtensions.length ] = Object.prototype.extend; +} + +if (Array.prototype.push) { + // in prototype.js... + Rico.ArrayExtensions[ Rico.ArrayExtensions.length ] = Array.prototype.push; +} + +if (!Array.prototype.remove) { + Array.prototype.remove = function(dx) { + if( isNaN(dx) || dx > this.length ) + return false; + for( var i=0,n=0; i 1 ) + queryString = this._createQueryString(arguments, 1); + + new Ajax.Request(requestURL, this._requestOptions(queryString)); + }, + + sendRequestWithData: function(requestName, xmlDocument) { + var requestURL = this.requestURLS[requestName]; + if ( requestURL == null ) + return; + + var queryString = ""; + if ( arguments.length > 2 ) + queryString = this._createQueryString(arguments, 2); + + new Ajax.Request(requestURL + "?" + queryString, this._requestOptions(null,xmlDocument)); + }, + + sendRequestAndUpdate: function(requestName,container,options) { + var requestURL = this.requestURLS[requestName]; + if ( requestURL == null ) + return; + + var queryString = ""; + if ( arguments.length > 3 ) + queryString = this._createQueryString(arguments, 3); + + var updaterOptions = this._requestOptions(queryString); + updaterOptions.onComplete = null; + updaterOptions.extend(options); + + new Ajax.Updater(container, requestURL, updaterOptions); + }, + + sendRequestWithDataAndUpdate: function(requestName,xmlDocument,container,options) { + var requestURL = this.requestURLS[requestName]; + if ( requestURL == null ) + return; + + var queryString = ""; + if ( arguments.length > 4 ) + queryString = this._createQueryString(arguments, 4); + + + var updaterOptions = this._requestOptions(queryString,xmlDocument); + updaterOptions.onComplete = null; + updaterOptions.extend(options); + + new Ajax.Updater(container, requestURL + "?" + queryString, updaterOptions); + }, + + // Private -- not part of intended engine API -------------------------------------------------------------------- + + _requestOptions: function(queryString,xmlDoc) { + var self = this; + + var requestHeaders = ['X-Rico-Version', Rico.Version ]; + var sendMethod = "post"; + if ( arguments[1] ) + requestHeaders.push( 'Content-type', 'text/xml' ); + else + sendMethod = "get"; + + return { requestHeaders: requestHeaders, + parameters: queryString, + postBody: arguments[1] ? xmlDoc : null, + method: sendMethod, + onComplete: self._onRequestComplete.bind(self) }; + }, + + _createQueryString: function( theArgs, offset ) { + var queryString = ""; + for ( var i = offset ; i < theArgs.length ; i++ ) { + if ( i != offset ) + queryString += "&"; + + var anArg = theArgs[i]; + + if ( anArg.name != undefined && anArg.value != undefined ) { + queryString += anArg.name + "=" + escape(anArg.value); + } + else { + var ePos = anArg.indexOf('='); + var argName = anArg.substring( 0, ePos ); + var argValue = anArg.substring( ePos + 1 ); + queryString += argName + "=" + escape(argValue); + } + } + + return queryString; + }, + + _onRequestComplete : function(request) { + + //!!TODO: error handling infrastructure?? + if (request.status != 200) + return; + + var response = request.responseXML.getElementsByTagName("ajax-response"); + if (response == null || response.length != 1) + return; + this._processAjaxResponse( response[0].childNodes ); + }, + + _processAjaxResponse: function( xmlResponseElements ) { + for ( var i = 0 ; i < xmlResponseElements.length ; i++ ) { + var responseElement = xmlResponseElements[i]; + + // only process nodes of type element..... + if ( responseElement.nodeType != 1 ) + continue; + + var responseType = responseElement.getAttribute("type"); + var responseId = responseElement.getAttribute("id"); + + if ( responseType == "object" ) + this._processAjaxObjectUpdate( this.ajaxObjects[ responseId ], responseElement ); + else if ( responseType == "element" ) + this._processAjaxElementUpdate( this.ajaxElements[ responseId ], responseElement ); + else + alert('unrecognized AjaxResponse type : ' + responseType ); + } + }, + + _processAjaxObjectUpdate: function( ajaxObject, responseElement ) { + ajaxObject.ajaxUpdate( responseElement ); + }, + + _processAjaxElementUpdate: function( ajaxElement, responseElement ) { + if ( responseElement.xml != undefined ) + this._processAjaxElementUpdateIE( ajaxElement, responseElement ); + else + this._processAjaxElementUpdateMozilla( ajaxElement, responseElement ); + }, + + _processAjaxElementUpdateIE: function( ajaxElement, responseElement ) { + var newHTML = ""; + for ( var i = 0 ; i < responseElement.childNodes.length ; i++ ) + newHTML += responseElement.childNodes[i].xml; + + ajaxElement.innerHTML = newHTML; + }, + + _processAjaxElementUpdateMozilla: function( ajaxElement, responseElement ) { + var xmlSerializer = new XMLSerializer(); + var newHTML = ""; + for ( var i = 0 ; i < responseElement.childNodes.length ; i++ ) + newHTML += xmlSerializer.serializeToString(responseElement.childNodes[i]); + + ajaxElement.innerHTML = newHTML; + } +} + +var ajaxEngine = new Rico.AjaxEngine(); + +// ricoColor.js -------------------- + +Rico.Color = Class.create(); + +Rico.Color.prototype = { + + initialize: function(red, green, blue) { + this.rgb = { r: red, g : green, b : blue }; + }, + + setRed: function(r) { + this.rgb.r = r; + }, + + setGreen: function(g) { + this.rgb.g = g; + }, + + setBlue: function(b) { + this.rgb.b = b; + }, + + setHue: function(h) { + + // get an HSB model, and set the new hue... + var hsb = this.asHSB(); + hsb.h = h; + + // convert back to RGB... + this.rgb = Rico.Color.HSBtoRGB(hsb.h, hsb.s, hsb.b); + }, + + setSaturation: function(s) { + // get an HSB model, and set the new hue... + var hsb = this.asHSB(); + hsb.s = s; + + // convert back to RGB and set values... + this.rgb = Rico.Color.HSBtoRGB(hsb.h, hsb.s, hsb.b); + }, + + setBrightness: function(b) { + // get an HSB model, and set the new hue... + var hsb = this.asHSB(); + hsb.b = b; + + // convert back to RGB and set values... + this.rgb = Rico.Color.HSBtoRGB( hsb.h, hsb.s, hsb.b ); + }, + + darken: function(percent) { + var hsb = this.asHSB(); + this.rgb = Rico.Color.HSBtoRGB(hsb.h, hsb.s, Math.max(hsb.b - percent,0)); + }, + + brighten: function(percent) { + var hsb = this.asHSB(); + this.rgb = Rico.Color.HSBtoRGB(hsb.h, hsb.s, Math.min(hsb.b + percent,1)); + }, + + blend: function(other) { + this.rgb.r = Math.floor((this.rgb.r + other.rgb.r)/2); + this.rgb.g = Math.floor((this.rgb.g + other.rgb.g)/2); + this.rgb.b = Math.floor((this.rgb.b + other.rgb.b)/2); + }, + + isBright: function() { + var hsb = this.asHSB(); + return this.asHSB().b > 0.5; + }, + + isDark: function() { + return ! this.isBright(); + }, + + asRGB: function() { + return "rgb(" + this.rgb.r + "," + this.rgb.g + "," + this.rgb.b + ")"; + }, + + asHex: function() { + return "#" + this.rgb.r.toColorPart() + this.rgb.g.toColorPart() + this.rgb.b.toColorPart(); + }, + + asHSB: function() { + return Rico.Color.RGBtoHSB(this.rgb.r, this.rgb.g, this.rgb.b); + }, + + toString: function() { + return this.asHex(); + } + +}; + +Rico.Color.createFromHex = function(hexCode) { + + if ( hexCode.indexOf('#') == 0 ) + hexCode = hexCode.substring(1); + var red = hexCode.substring(0,2); + var green = hexCode.substring(2,4); + var blue = hexCode.substring(4,6); + return new Rico.Color( parseInt(red,16), parseInt(green,16), parseInt(blue,16) ); +} + +/** + * Factory method for creating a color from the background of + * an HTML element. + */ +Rico.Color.createColorFromBackground = function(elem) { + + var actualColor = RicoUtil.getElementsComputedStyle($(elem), "backgroundColor", "background-color"); + + if ( actualColor == "transparent" && elem.parent ) + return Rico.Color.createColorFromBackground(elem.parent); + + if ( actualColor == null ) + return new Rico.Color(255,255,255); + + if ( actualColor.indexOf("rgb(") == 0 ) { + var colors = actualColor.substring(4, actualColor.length - 1 ); + var colorArray = colors.split(","); + return new Rico.Color( parseInt( colorArray[0] ), + parseInt( colorArray[1] ), + parseInt( colorArray[2] ) ); + + } + else if ( actualColor.indexOf("#") == 0 ) { + var redPart = parseInt(actualColor.substring(1,3), 16); + var greenPart = parseInt(actualColor.substring(3,5), 16); + var bluePart = parseInt(actualColor.substring(5), 16); + return new Rico.Color( redPart, greenPart, bluePart ); + } + else + return new Rico.Color(255,255,255); +} + +Rico.Color.HSBtoRGB = function(hue, saturation, brightness) { + + var red = 0; + var green = 0; + var blue = 0; + + if (saturation == 0) { + red = parseInt(brightness * 255.0 + 0.5); + green = red; + blue = red; + } + else { + var h = (hue - Math.floor(hue)) * 6.0; + var f = h - Math.floor(h); + var p = brightness * (1.0 - saturation); + var q = brightness * (1.0 - saturation * f); + var t = brightness * (1.0 - (saturation * (1.0 - f))); + + switch (parseInt(h)) { + case 0: + red = (brightness * 255.0 + 0.5); + green = (t * 255.0 + 0.5); + blue = (p * 255.0 + 0.5); + break; + case 1: + red = (q * 255.0 + 0.5); + green = (brightness * 255.0 + 0.5); + blue = (p * 255.0 + 0.5); + break; + case 2: + red = (p * 255.0 + 0.5); + green = (brightness * 255.0 + 0.5); + blue = (t * 255.0 + 0.5); + break; + case 3: + red = (p * 255.0 + 0.5); + green = (q * 255.0 + 0.5); + blue = (brightness * 255.0 + 0.5); + break; + case 4: + red = (t * 255.0 + 0.5); + green = (p * 255.0 + 0.5); + blue = (brightness * 255.0 + 0.5); + break; + case 5: + red = (brightness * 255.0 + 0.5); + green = (p * 255.0 + 0.5); + blue = (q * 255.0 + 0.5); + break; + } + } + + return { r : parseInt(red), g : parseInt(green) , b : parseInt(blue) }; +} + +Rico.Color.RGBtoHSB = function(r, g, b) { + + var hue; + var saturaton; + var brightness; + + var cmax = (r > g) ? r : g; + if (b > cmax) + cmax = b; + + var cmin = (r < g) ? r : g; + if (b < cmin) + cmin = b; + + brightness = cmax / 255.0; + if (cmax != 0) + saturation = (cmax - cmin)/cmax; + else + saturation = 0; + + if (saturation == 0) + hue = 0; + else { + var redc = (cmax - r)/(cmax - cmin); + var greenc = (cmax - g)/(cmax - cmin); + var bluec = (cmax - b)/(cmax - cmin); + + if (r == cmax) + hue = bluec - greenc; + else if (g == cmax) + hue = 2.0 + redc - bluec; + else + hue = 4.0 + greenc - redc; + + hue = hue / 6.0; + if (hue < 0) + hue = hue + 1.0; + } + + return { h : hue, s : saturation, b : brightness }; +} + +// ricoCorner.js -------------------- + + +Rico.Corner = { + + round: function(e, options) { + var e = $(e); + this._setOptions(options); + + var color = this.options.color; + if ( this.options.color == "fromElement" ) + color = this._background(e); + + var bgColor = this.options.bgColor; + if ( this.options.bgColor == "fromParent" ) + bgColor = this._background(e.offsetParent); + + this._roundCornersImpl(e, color, bgColor); + }, + + _roundCornersImpl: function(e, color, bgColor) { + if(this.options.border) + this._renderBorder(e,bgColor); + if(this._isTopRounded()) + this._roundTopCorners(e,color,bgColor); + if(this._isBottomRounded()) + this._roundBottomCorners(e,color,bgColor); + }, + + _renderBorder: function(el,bgColor) { + var borderValue = "1px solid " + this._borderColor(bgColor); + var borderL = "border-left: " + borderValue; + var borderR = "border-right: " + borderValue; + var style = "style='" + borderL + ";" + borderR + "'"; + el.innerHTML = "

    " + el.innerHTML + "
    " + }, + + _roundTopCorners: function(el, color, bgColor) { + var corner = this._createCorner(bgColor); + for(var i=0 ; i < this.options.numSlices ; i++ ) + corner.appendChild(this._createCornerSlice(color,bgColor,i,"top")); + el.style.paddingTop = 0; + el.insertBefore(corner,el.firstChild); + }, + + _roundBottomCorners: function(el, color, bgColor) { + var corner = this._createCorner(bgColor); + for(var i=(this.options.numSlices-1) ; i >= 0 ; i-- ) + corner.appendChild(this._createCornerSlice(color,bgColor,i,"bottom")); + el.style.paddingBottom = 0; + el.appendChild(corner); + }, + + _createCorner: function(bgColor) { + var corner = document.createElement("div"); + corner.style.backgroundColor = (this._isTransparent() ? "transparent" : bgColor); + return corner; + }, + + _createCornerSlice: function(color,bgColor, n, position) { + var slice = document.createElement("span"); + + var inStyle = slice.style; + inStyle.backgroundColor = color; + inStyle.display = "block"; + inStyle.height = "1px"; + inStyle.overflow = "hidden"; + inStyle.fontSize = "1px"; + + var borderColor = this._borderColor(color,bgColor); + if ( this.options.border && n == 0 ) { + inStyle.borderTopStyle = "solid"; + inStyle.borderTopWidth = "1px"; + inStyle.borderLeftWidth = "0px"; + inStyle.borderRightWidth = "0px"; + inStyle.borderBottomWidth = "0px"; + inStyle.height = "0px"; // assumes css compliant box model + inStyle.borderColor = borderColor; + } + else if(borderColor) { + inStyle.borderColor = borderColor; + inStyle.borderStyle = "solid"; + inStyle.borderWidth = "0px 1px"; + } + + if ( !this.options.compact && (n == (this.options.numSlices-1)) ) + inStyle.height = "2px"; + + this._setMargin(slice, n, position); + this._setBorder(slice, n, position); + + return slice; + }, + + _setOptions: function(options) { + this.options = { + corners : "all", + color : "fromElement", + bgColor : "fromParent", + blend : true, + border : false, + compact : false + }.extend(options || {}); + + this.options.numSlices = this.options.compact ? 2 : 4; + if ( this._isTransparent() ) + this.options.blend = false; + }, + + _whichSideTop: function() { + if ( this._hasString(this.options.corners, "all", "top") ) + return ""; + + if ( this.options.corners.indexOf("tl") >= 0 && this.options.corners.indexOf("tr") >= 0 ) + return ""; + + if (this.options.corners.indexOf("tl") >= 0) + return "left"; + else if (this.options.corners.indexOf("tr") >= 0) + return "right"; + return ""; + }, + + _whichSideBottom: function() { + if ( this._hasString(this.options.corners, "all", "bottom") ) + return ""; + + if ( this.options.corners.indexOf("bl")>=0 && this.options.corners.indexOf("br")>=0 ) + return ""; + + if(this.options.corners.indexOf("bl") >=0) + return "left"; + else if(this.options.corners.indexOf("br")>=0) + return "right"; + return ""; + }, + + _borderColor : function(color,bgColor) { + if ( color == "transparent" ) + return bgColor; + else if ( this.options.border ) + return this.options.border; + else if ( this.options.blend ) + return this._blend( bgColor, color ); + else + return ""; + }, + + + _setMargin: function(el, n, corners) { + var marginSize = this._marginSize(n); + var whichSide = corners == "top" ? this._whichSideTop() : this._whichSideBottom(); + + if ( whichSide == "left" ) { + el.style.marginLeft = marginSize + "px"; el.style.marginRight = "0px"; + } + else if ( whichSide == "right" ) { + el.style.marginRight = marginSize + "px"; el.style.marginLeft = "0px"; + } + else { + el.style.marginLeft = marginSize + "px"; el.style.marginRight = marginSize + "px"; + } + }, + + _setBorder: function(el,n,corners) { + var borderSize = this._borderSize(n); + var whichSide = corners == "top" ? this._whichSideTop() : this._whichSideBottom(); + + if ( whichSide == "left" ) { + el.style.borderLeftWidth = borderSize + "px"; el.style.borderRightWidth = "0px"; + } + else if ( whichSide == "right" ) { + el.style.borderRightWidth = borderSize + "px"; el.style.borderLeftWidth = "0px"; + } + else { + el.style.borderLeftWidth = borderSize + "px"; el.style.borderRightWidth = borderSize + "px"; + } + }, + + _marginSize: function(n) { + if ( this._isTransparent() ) + return 0; + + var marginSizes = [ 5, 3, 2, 1 ]; + var blendedMarginSizes = [ 3, 2, 1, 0 ]; + var compactMarginSizes = [ 2, 1 ]; + var smBlendedMarginSizes = [ 1, 0 ]; + + if ( this.options.compact && this.options.blend ) + return smBlendedMarginSizes[n]; + else if ( this.options.compact ) + return compactMarginSizes[n]; + else if ( this.options.blend ) + return blendedMarginSizes[n]; + else + return marginSizes[n]; + }, + + _borderSize: function(n) { + var transparentBorderSizes = [ 5, 3, 2, 1 ]; + var blendedBorderSizes = [ 2, 1, 1, 1 ]; + var compactBorderSizes = [ 1, 0 ]; + var actualBorderSizes = [ 0, 2, 0, 0 ]; + + if ( this.options.compact && (this.options.blend || this._isTransparent()) ) + return 1; + else if ( this.options.compact ) + return compactBorderSizes[n]; + else if ( this.options.blend ) + return blendedBorderSizes[n]; + else if ( this.options.border ) + return actualBorderSizes[n]; + else if ( this._isTransparent() ) + return transparentBorderSizes[n]; + return 0; + }, + + _hasString: function(str) { for(var i=1 ; i= 0) return true; return false; }, + _blend: function(c1, c2) { var cc1 = Rico.Color.createFromHex(c1); cc1.blend(Rico.Color.createFromHex(c2)); return cc1; }, + _background: function(el) { try { return Rico.Color.createColorFromBackground(el).asHex(); } catch(err) { return "#ffffff"; } }, + _isTransparent: function() { return this.options.color == "transparent"; }, + _isTopRounded: function() { return this._hasString(this.options.corners, "all", "top", "tl", "tr"); }, + _isBottomRounded: function() { return this._hasString(this.options.corners, "all", "bottom", "bl", "br"); }, + _hasSingleTextChild: function(el) { return el.childNodes.length == 1 && el.childNodes[0].nodeType == 3; } +} + +// ricoDragAndDrop.js -------------------- + +Rico.DragAndDrop = Class.create(); + +Rico.DragAndDrop.prototype = { + + initialize: function() { + this.dropZones = new Array(); + this.draggables = new Array(); + this.currentDragObjects = new Array(); + this.dragElement = null; + this.lastSelectedDraggable = null; + this.currentDragObjectVisible = false; + this.interestedInMotionEvents = false; + }, + + registerDropZone: function(aDropZone) { + this.dropZones[ this.dropZones.length ] = aDropZone; + }, + + deregisterDropZone: function(aDropZone) { + var newDropZones = new Array(); + var j = 0; + for ( var i = 0 ; i < this.dropZones.length ; i++ ) { + if ( this.dropZones[i] != aDropZone ) + newDropZones[j++] = this.dropZones[i]; + } + + this.dropZones = newDropZones; + }, + + clearDropZones: function() { + this.dropZones = new Array(); + }, + + registerDraggable: function( aDraggable ) { + this.draggables[ this.draggables.length ] = aDraggable; + this._addMouseDownHandler( aDraggable ); + }, + + clearSelection: function() { + for ( var i = 0 ; i < this.currentDragObjects.length ; i++ ) + this.currentDragObjects[i].deselect(); + this.currentDragObjects = new Array(); + this.lastSelectedDraggable = null; + }, + + hasSelection: function() { + return this.currentDragObjects.length > 0; + }, + + setStartDragFromElement: function( e, mouseDownElement ) { + this.origPos = RicoUtil.toDocumentPosition(mouseDownElement); + this.startx = e.screenX - this.origPos.x; + this.starty = e.screenY - this.origPos.y; + //this.startComponentX = e.layerX ? e.layerX : e.offsetX; + //this.startComponentY = e.layerY ? e.layerY : e.offsetY; + //this.adjustedForDraggableSize = false; + + this.interestedInMotionEvents = this.hasSelection(); + this._terminateEvent(e); + }, + + updateSelection: function( draggable, extendSelection ) { + if ( ! extendSelection ) + this.clearSelection(); + + if ( draggable.isSelected() ) { + this.currentDragObjects.removeItem(draggable); + draggable.deselect(); + if ( draggable == this.lastSelectedDraggable ) + this.lastSelectedDraggable = null; + } + else { + this.currentDragObjects[ this.currentDragObjects.length ] = draggable; + draggable.select(); + this.lastSelectedDraggable = draggable; + } + }, + + _mouseDownHandler: function(e) { + if ( arguments.length == 0 ) + e = event; + + // if not button 1 ignore it... + var nsEvent = e.which != undefined; + if ( (nsEvent && e.which != 1) || (!nsEvent && e.button != 1)) + return; + + var eventTarget = e.target ? e.target : e.srcElement; + var draggableObject = eventTarget.draggable; + + this.updateSelection( draggableObject, e.ctrlKey ); + + // clear the drop zones postion cache... + if ( this.hasSelection() ) + for ( var i = 0 ; i < this.dropZones.length ; i++ ) + this.dropZones[i].clearPositionCache(); + + this.setStartDragFromElement( e, draggableObject.getMouseDownHTMLElement() ); + }, + + + _mouseMoveHandler: function(e) { + var nsEvent = e.which != undefined; + if ( !this.interestedInMotionEvents ) { + this._terminateEvent(e); + return; + } + + if ( ! this.hasSelection() ) + return; + + if ( ! this.currentDragObjectVisible ) + this._startDrag(e); + + if ( !this.activatedDropZones ) + this._activateRegisteredDropZones(); + + //if ( !this.adjustedForDraggableSize ) + // this._adjustForDraggableSize(e); + + this._updateDraggableLocation(e); + this._updateDropZonesHover(e); + + this._terminateEvent(e); + }, + + _makeDraggableObjectVisible: function(e) + { + if ( !this.hasSelection() ) + return; + + var dragElement; + if ( this.currentDragObjects.length > 1 ) + dragElement = this.currentDragObjects[0].getMultiObjectDragGUI(this.currentDragObjects); + else + dragElement = this.currentDragObjects[0].getSingleObjectDragGUI(); + + // go ahead and absolute position it... + if ( RicoUtil.getElementsComputedStyle(dragElement, "position") != "absolute" ) + dragElement.style.position = "absolute"; + + // need to parent him into the document... + if ( dragElement.parentNode == null || dragElement.parentNode.nodeType == 11 ) + document.body.appendChild(dragElement); + + this.dragElement = dragElement; + this._updateDraggableLocation(e); + + this.currentDragObjectVisible = true; + }, + + /** + _adjustForDraggableSize: function(e) { + var dragElementWidth = this.dragElement.offsetWidth; + var dragElementHeight = this.dragElement.offsetHeight; + if ( this.startComponentX > dragElementWidth ) + this.startx -= this.startComponentX - dragElementWidth + 2; + if ( e.offsetY ) { + if ( this.startComponentY > dragElementHeight ) + this.starty -= this.startComponentY - dragElementHeight + 2; + } + this.adjustedForDraggableSize = true; + }, + **/ + + _updateDraggableLocation: function(e) { + var dragObjectStyle = this.dragElement.style; + dragObjectStyle.left = (e.screenX - this.startx) + "px"; + dragObjectStyle.top = (e.screenY - this.starty) + "px"; + }, + + _updateDropZonesHover: function(e) { + var n = this.dropZones.length; + for ( var i = 0 ; i < n ; i++ ) { + if ( ! this._mousePointInDropZone( e, this.dropZones[i] ) ) + this.dropZones[i].hideHover(); + } + + for ( var i = 0 ; i < n ; i++ ) { + if ( this._mousePointInDropZone( e, this.dropZones[i] ) ) { + if ( this.dropZones[i].canAccept(this.currentDragObjects) ) + this.dropZones[i].showHover(); + } + } + }, + + _startDrag: function(e) { + for ( var i = 0 ; i < this.currentDragObjects.length ; i++ ) + this.currentDragObjects[i].startDrag(); + + this._makeDraggableObjectVisible(e); + }, + + _mouseUpHandler: function(e) { + if ( ! this.hasSelection() ) + return; + + var nsEvent = e.which != undefined; + if ( (nsEvent && e.which != 1) || (!nsEvent && e.button != 1)) + return; + + this.interestedInMotionEvents = false; + + if ( this.dragElement == null ) { + this._terminateEvent(e); + return; + } + + if ( this._placeDraggableInDropZone(e) ) + this._completeDropOperation(e); + else { + this._terminateEvent(e); + new Effect.Position( this.dragElement, + this.origPos.x, + this.origPos.y, + 200, + 20, + { complete : this._doCancelDragProcessing.bind(this) } ); + } + }, + + _completeDropOperation: function(e) { + if ( this.dragElement != this.currentDragObjects[0].getMouseDownHTMLElement() ) { + if ( this.dragElement.parentNode != null ) + this.dragElement.parentNode.removeChild(this.dragElement); + } + + this._deactivateRegisteredDropZones(); + this._endDrag(); + this.clearSelection(); + this.dragElement = null; + this.currentDragObjectVisible = false; + this._terminateEvent(e); + }, + + _doCancelDragProcessing: function() { + this._cancelDrag(); + + if ( this.dragElement != this.currentDragObjects[0].getMouseDownHTMLElement() ) { + if ( this.dragElement.parentNode != null ) { + this.dragElement.parentNode.removeChild(this.dragElement); + } + } + + this._deactivateRegisteredDropZones(); + this.dragElement = null; + this.currentDragObjectVisible = false; + }, + + _placeDraggableInDropZone: function(e) { + var foundDropZone = false; + var n = this.dropZones.length; + for ( var i = 0 ; i < n ; i++ ) { + if ( this._mousePointInDropZone( e, this.dropZones[i] ) ) { + if ( this.dropZones[i].canAccept(this.currentDragObjects) ) { + this.dropZones[i].hideHover(); + this.dropZones[i].accept(this.currentDragObjects); + foundDropZone = true; + break; + } + } + } + + return foundDropZone; + }, + + _cancelDrag: function() { + for ( var i = 0 ; i < this.currentDragObjects.length ; i++ ) + this.currentDragObjects[i].cancelDrag(); + }, + + _endDrag: function() { + for ( var i = 0 ; i < this.currentDragObjects.length ; i++ ) + this.currentDragObjects[i].endDrag(); + }, + + _mousePointInDropZone: function( e, dropZone ) { + + var absoluteRect = dropZone.getAbsoluteRect(); + + return e.clientX > absoluteRect.left && + e.clientX < absoluteRect.right && + e.clientY > absoluteRect.top && + e.clientY < absoluteRect.bottom; + }, + + _addMouseDownHandler: function( aDraggable ) + { + var htmlElement = aDraggable.getMouseDownHTMLElement(); + if ( htmlElement != null ) { + htmlElement.draggable = aDraggable; + this._addMouseDownEvent( htmlElement ); + } + }, + + _activateRegisteredDropZones: function() { + var n = this.dropZones.length; + for ( var i = 0 ; i < n ; i++ ) { + var dropZone = this.dropZones[i]; + if ( dropZone.canAccept(this.currentDragObjects) ) + dropZone.activate(); + } + + this.activatedDropZones = true; + }, + + _deactivateRegisteredDropZones: function() { + var n = this.dropZones.length; + for ( var i = 0 ; i < n ; i++ ) + this.dropZones[i].deactivate(); + this.activatedDropZones = false; + }, + + _addMouseDownEvent: function( htmlElement ) { + if ( typeof document.implementation != "undefined" && + document.implementation.hasFeature("HTML", "1.0") && + document.implementation.hasFeature("Events", "2.0") && + document.implementation.hasFeature("CSS", "2.0") ) { + htmlElement.addEventListener("mousedown", this._mouseDownHandler.bindAsEventListener(this), false); + } + else { + htmlElement.attachEvent( "onmousedown", this._mouseDownHandler.bindAsEventListener(this) ); + } + }, + + _terminateEvent: function(e) { + if ( e.stopPropagation != undefined ) + e.stopPropagation(); + else if ( e.cancelBubble != undefined ) + e.cancelBubble = true; + + if ( e.preventDefault != undefined ) + e.preventDefault(); + else + { + e.returnValue = false; + } + }, + + initializeEventHandlers: function() { + if ( typeof document.implementation != "undefined" && + document.implementation.hasFeature("HTML", "1.0") && + document.implementation.hasFeature("Events", "2.0") && + document.implementation.hasFeature("CSS", "2.0") ) { + document.addEventListener("mouseup", this._mouseUpHandler.bindAsEventListener(this), false); + document.addEventListener("mousemove", this._mouseMoveHandler.bindAsEventListener(this), false); + } + else { + document.attachEvent( "onmouseup", this._mouseUpHandler.bindAsEventListener(this) ); + document.attachEvent( "onmousemove", this._mouseMoveHandler.bindAsEventListener(this) ); + } + } +}; + +var dndMgr = new Rico.DragAndDrop(); +dndMgr.initializeEventHandlers(); + +// ricoDraggable.js -------------------- + +Rico.Draggable = Class.create(); + +Rico.Draggable.prototype = { + + initialize: function( type, htmlElement ) { + this.type = type; + this.htmlElement = $(htmlElement); + this.selected = false; + }, + + /** + * Returns the HTML element that should have a mouse down event + * added to it in order to initiate a drag operation + * + **/ + getMouseDownHTMLElement: function() { + return this.htmlElement; + }, + + select: function() { + this.selected = true; + + if ( this.showingSelected ) + return; + + var htmlElement = this.getMouseDownHTMLElement(); + + var color = Rico.Color.createColorFromBackground(htmlElement); + color.isBright() ? color.darken(0.033) : color.brighten(0.033); + + this.saveBackground = RicoUtil.getElementsComputedStyle(htmlElement, "backgroundColor", "background-color"); + htmlElement.style.backgroundColor = color.asHex(); + this.showingSelected = true; + }, + + deselect: function() { + this.selected = false; + if ( !this.showingSelected ) + return; + + var htmlElement = this.getMouseDownHTMLElement(); + + htmlElement.style.backgroundColor = this.saveBackground; + this.showingSelected = false; + }, + + isSelected: function() { + return this.selected; + }, + + startDrag: function() { + }, + + cancelDrag: function() { + }, + + endDrag: function() { + }, + + getSingleObjectDragGUI: function() { + return this.htmlElement; + }, + + getMultiObjectDragGUI: function( draggables ) { + return this.htmlElement; + }, + + getDroppedGUI: function() { + return this.htmlElement; + }, + + toString: function() { + return this.type + ":" + this.htmlElement + ":"; + } + +} + +// ricoDropzone.js -------------------- + +Rico.Dropzone = Class.create(); + +Rico.Dropzone.prototype = { + + initialize: function( htmlElement ) { + this.htmlElement = $(htmlElement); + this.absoluteRect = null; + }, + + getHTMLElement: function() { + return this.htmlElement; + }, + + clearPositionCache: function() { + this.absoluteRect = null; + }, + + getAbsoluteRect: function() { + if ( this.absoluteRect == null ) { + var htmlElement = this.getHTMLElement(); + var pos = RicoUtil.toViewportPosition(htmlElement); + + this.absoluteRect = { + top: pos.y, + left: pos.x, + bottom: pos.y + htmlElement.offsetHeight, + right: pos.x + htmlElement.offsetWidth + }; + } + return this.absoluteRect; + }, + + activate: function() { + var htmlElement = this.getHTMLElement(); + if (htmlElement == null || this.showingActive) + return; + + this.showingActive = true; + this.saveBackgroundColor = htmlElement.style.backgroundColor; + + var fallbackColor = "#ffea84"; + var currentColor = Rico.Color.createColorFromBackground(htmlElement); + if ( currentColor == null ) + htmlElement.style.backgroundColor = fallbackColor; + else { + currentColor.isBright() ? currentColor.darken(0.2) : currentColor.brighten(0.2); + htmlElement.style.backgroundColor = currentColor.asHex(); + } + }, + + deactivate: function() { + var htmlElement = this.getHTMLElement(); + if (htmlElement == null || !this.showingActive) + return; + + htmlElement.style.backgroundColor = this.saveBackgroundColor; + this.showingActive = false; + this.saveBackgroundColor = null; + }, + + showHover: function() { + var htmlElement = this.getHTMLElement(); + if ( htmlElement == null || this.showingHover ) + return; + + this.saveBorderWidth = htmlElement.style.borderWidth; + this.saveBorderStyle = htmlElement.style.borderStyle; + this.saveBorderColor = htmlElement.style.borderColor; + + this.showingHover = true; + htmlElement.style.borderWidth = "1px"; + htmlElement.style.borderStyle = "solid"; + //htmlElement.style.borderColor = "#ff9900"; + htmlElement.style.borderColor = "#ffff00"; + }, + + hideHover: function() { + var htmlElement = this.getHTMLElement(); + if ( htmlElement == null || !this.showingHover ) + return; + + htmlElement.style.borderWidth = this.saveBorderWidth; + htmlElement.style.borderStyle = this.saveBorderStyle; + htmlElement.style.borderColor = this.saveBorderColor; + this.showingHover = false; + }, + + canAccept: function(draggableObjects) { + return true; + }, + + accept: function(draggableObjects) { + var htmlElement = this.getHTMLElement(); + if ( htmlElement == null ) + return; + + n = draggableObjects.length; + for ( var i = 0 ; i < n ; i++ ) + { + var theGUI = draggableObjects[i].getDroppedGUI(); + if ( RicoUtil.getElementsComputedStyle( theGUI, "position" ) == "absolute" ) + { + theGUI.style.position = "static"; + theGUI.style.top = ""; + theGUI.style.top = ""; + } + htmlElement.appendChild(theGUI); + } + } +} + +// ricoEffects.js -------------------- + + +Effect.SizeAndPosition = Class.create(); +Effect.SizeAndPosition.prototype = { + + initialize: function(element, x, y, w, h, duration, steps, options) { + this.element = $(element); + this.x = x; + this.y = y; + this.w = w; + this.h = h; + this.duration = duration; + this.steps = steps; + this.options = arguments[7] || {}; + + this.sizeAndPosition(); + }, + + sizeAndPosition: function() { + if (this.isFinished()) { + if(this.options.complete) this.options.complete(this); + return; + } + + if (this.timer) + clearTimeout(this.timer); + + var stepDuration = Math.round(this.duration/this.steps) ; + + // Get original values: x,y = top left corner; w,h = width height + var currentX = this.element.offsetLeft; + var currentY = this.element.offsetTop; + var currentW = this.element.offsetWidth; + var currentH = this.element.offsetHeight; + + // If values not set, or zero, we do not modify them, and take original as final as well + this.x = (this.x) ? this.x : currentX; + this.y = (this.y) ? this.y : currentY; + this.w = (this.w) ? this.w : currentW; + this.h = (this.h) ? this.h : currentH; + + // how much do we need to modify our values for each step? + var difX = this.steps > 0 ? (this.x - currentX)/this.steps : 0; + var difY = this.steps > 0 ? (this.y - currentY)/this.steps : 0; + var difW = this.steps > 0 ? (this.w - currentW)/this.steps : 0; + var difH = this.steps > 0 ? (this.h - currentH)/this.steps : 0; + + this.moveBy(difX, difY); + this.resizeBy(difW, difH); + + this.duration -= stepDuration; + this.steps--; + + this.timer = setTimeout(this.sizeAndPosition.bind(this), stepDuration); + }, + + isFinished: function() { + return this.steps <= 0; + }, + + moveBy: function( difX, difY ) { + var currentLeft = this.element.offsetLeft; + var currentTop = this.element.offsetTop; + var intDifX = parseInt(difX); + var intDifY = parseInt(difY); + + var style = this.element.style; + if ( intDifX != 0 ) + style.left = (currentLeft + intDifX) + "px"; + if ( intDifY != 0 ) + style.top = (currentTop + intDifY) + "px"; + }, + + resizeBy: function( difW, difH ) { + var currentWidth = this.element.offsetWidth; + var currentHeight = this.element.offsetHeight; + var intDifW = parseInt(difW); + var intDifH = parseInt(difH); + + var style = this.element.style; + if ( intDifW != 0 ) + style.width = (currentWidth + intDifW) + "px"; + if ( intDifH != 0 ) + style.height = (currentHeight + intDifH) + "px"; + } +} + +Effect.Size = Class.create(); +Effect.Size.prototype = { + + initialize: function(element, w, h, duration, steps, options) { + new Effect.SizeAndPosition(element, null, null, w, h, duration, steps, options); + } +} + +Effect.Position = Class.create(); +Effect.Position.prototype = { + + initialize: function(element, x, y, duration, steps, options) { + new Effect.SizeAndPosition(element, x, y, null, null, duration, steps, options); + } +} + +Effect.Round = Class.create(); +Effect.Round.prototype = { + + initialize: function(tagName, className, options) { + var elements = document.getElementsByTagAndClassName(tagName,className); + for ( var i = 0 ; i < elements.length ; i++ ) + Rico.Corner.round( elements[i], options ); + } +}; + +Effect.FadeTo = Class.create(); +Effect.FadeTo.prototype = { + + initialize: function( element, opacity, duration, steps, options) { + this.element = $(element); + this.opacity = opacity; + this.duration = duration; + this.steps = steps; + this.options = arguments[4] || {}; + this.fadeTo(); + }, + + fadeTo: function() { + if (this.isFinished()) { + if(this.options.complete) this.options.complete(this); + return; + } + + if (this.timer) + clearTimeout(this.timer); + + var stepDuration = Math.round(this.duration/this.steps) ; + var currentOpacity = this.getElementOpacity(); + var delta = this.steps > 0 ? (this.opacity - currentOpacity)/this.steps : 0; + + this.changeOpacityBy(delta); + this.duration -= stepDuration; + this.steps--; + + this.timer = setTimeout(this.fadeTo.bind(this), stepDuration); + }, + + changeOpacityBy: function(v) { + var currentOpacity = this.getElementOpacity(); + var newOpacity = Math.max(0, Math.min(currentOpacity+v, 1)); + this.element.ricoOpacity = newOpacity; + + this.element.style.filter = "alpha(opacity:"+Math.round(newOpacity*100)+")"; + this.element.style.opacity = newOpacity; /*//*/; + }, + + isFinished: function() { + return this.steps <= 0; + }, + + getElementOpacity: function() { + if ( this.element.ricoOpacity == undefined ) { + var opacity; + if ( this.element.currentStyle ) { + opacity = this.element.currentStyle.opacity; + } + else if ( document.defaultView.getComputedStyle != undefined ) { + var computedStyle = document.defaultView.getComputedStyle; + opacity = computedStyle(this.element, null).getPropertyValue('opacity'); + } + + this.element.ricoOpacity = opacity != undefined ? opacity : 1.0; + } + + return parseFloat(this.element.ricoOpacity); + } +} + +Effect.AccordionSize = Class.create(); + +Effect.AccordionSize.prototype = { + + initialize: function(e1, e2, start, end, duration, steps, options) { + this.e1 = $(e1); + this.e2 = $(e2); + this.start = start; + this.end = end; + this.duration = duration; + this.steps = steps; + this.options = arguments[6] || {}; + + this.accordionSize(); + }, + + accordionSize: function() { + + if (this.isFinished()) { + // just in case there are round errors or such... + this.e1.style.height = this.start + "px"; + this.e2.style.height = this.end + "px"; + + if(this.options.complete) + this.options.complete(this); + return; + } + + if (this.timer) + clearTimeout(this.timer); + + var stepDuration = Math.round(this.duration/this.steps) ; + + var diff = this.steps > 0 ? (parseInt(this.e1.offsetHeight) - this.start)/this.steps : 0; + this.resizeBy(diff); + + this.duration -= stepDuration; + this.steps--; + + this.timer = setTimeout(this.accordionSize.bind(this), stepDuration); + }, + + isFinished: function() { + return this.steps <= 0; + }, + + resizeBy: function(diff) { + var h1Height = this.e1.offsetHeight; + var h2Height = this.e2.offsetHeight; + var intDiff = parseInt(diff); + if ( diff != 0 ) { + this.e1.style.height = (h1Height - intDiff) + "px"; + this.e2.style.height = (h2Height + intDiff) + "px"; + } + } + +}; + + +// ricoLiveGrid.js -------------------- + + +// Rico.LiveGridMetaData ----------------------------------------------------- + +Rico.LiveGridMetaData = Class.create(); + +Rico.LiveGridMetaData.prototype = { + + initialize: function( pageSize, totalRows, options ) { + this.pageSize = pageSize; + this.totalRows = totalRows; + this.setOptions(options); + this.scrollArrowHeight = 16; + }, + + setOptions: function(options) { + this.options = { + largeBufferSize : 7.0, // 7 pages + smallBufferSize : 1.0, // 1 page + nearLimitFactor : 0.2 // 20% of buffer + }.extend(options || {}); + }, + + getPageSize: function() { + return this.pageSize; + }, + + getTotalRows: function() { + return this.totalRows; + }, + + setTotalRows: function(n) { + this.totalRows = n; + }, + + getLargeBufferSize: function() { + return parseInt(this.options.largeBufferSize * this.pageSize); + }, + + getSmallBufferSize: function() { + return parseInt(this.options.smallBufferSize * this.pageSize); + }, + + getLimitTolerance: function() { + return parseInt(this.getLargeBufferSize() * this.options.nearLimitFactor); + }, + + getBufferSize: function(isFull) { + return isFull ? this.getLargeBufferSize() : this.getSmallBufferSize(); + } +}; + +// Rico.LiveGridScroller ----------------------------------------------------- + +Rico.LiveGridScroller = Class.create(); + +Rico.LiveGridScroller.prototype = { + + initialize: function(liveGrid) { + this.isIE = navigator.userAgent.toLowerCase().indexOf("msie") >= 0; + this.liveGrid = liveGrid; + this.metaData = liveGrid.metaData; + this.createScrollBar(); + this.scrollTimeout = null; + //this.sizeIEHeaderHack(); + this.lastScrollPos = 0; + }, + + isUnPlugged: function() { + return this.scrollerDiv.onscroll == null; + }, + + plugin: function() { + this.scrollerDiv.onscroll = this.handleScroll.bindAsEventListener(this); + }, + + unplug: function() { + this.scrollerDiv.onscroll = null; + }, + + sizeIEHeaderHack: function() { + if ( !this.isIE ) return; + var headerTable = $(this.liveGrid.tableId + "_header"); + if ( headerTable ) + headerTable.rows[0].cells[0].style.width = + (headerTable.rows[0].cells[0].offsetWidth + 1) + "px"; + }, + + createScrollBar: function() { + var table = this.liveGrid.table; + var visibleHeight = table.offsetHeight; + + // create the outer div... + this.scrollerDiv = document.createElement("div"); + var scrollerStyle = this.scrollerDiv.style; + scrollerStyle.borderRight = "1px solid #ababab"; // hard coded color!!! + scrollerStyle.position = "relative"; + scrollerStyle.left = this.isIE ? "-6px" : "-3px"; + scrollerStyle.width = "19px"; + scrollerStyle.height = visibleHeight + "px"; + scrollerStyle.overflow = "auto"; + + // create the inner div... + this.heightDiv = document.createElement("div"); + this.heightDiv.style.width = "1px"; + this.heightDiv.style.height = parseInt(visibleHeight * + this.metaData.getTotalRows()/this.metaData.getPageSize()) + "px" ; + this.lineHeight = visibleHeight/this.metaData.getPageSize(); + + this.scrollerDiv.appendChild(this.heightDiv); + this.scrollerDiv.onscroll = this.handleScroll.bindAsEventListener(this); + table.parentNode.insertBefore( this.scrollerDiv, table.nextSibling ); + }, + + updateSize: function() { + var table = this.liveGrid.table; + var visibleHeight = table.offsetHeight; + this.heightDiv.style.height = parseInt(visibleHeight * + this.metaData.getTotalRows()/this.metaData.getPageSize()) + "px"; + }, + + adjustScrollTop: function() { + this.unplug(); + var rem = this.scrollerDiv.scrollTop % this.lineHeight; + if (rem != 0) { + if (this.lastScrollPos < this.scrollerDiv.scrollTop) + this.scrollerDiv.scrollTop = this.scrollerDiv.scrollTop + this.lineHeight -rem; + else + this.scrollerDiv.scrollTop = this.scrollerDiv.scrollTop - rem; + } + this.lastScrollPos = this.scrollerDiv.scrollTop; + this.plugin(); + }, + + handleScroll: function() { + if ( this.scrollTimeout ) + clearTimeout( this.scrollTimeout ); + + //this.adjustScrollTop(); + var contentOffset = parseInt(this.scrollerDiv.scrollTop * + this.metaData.getTotalRows() / this.heightDiv.offsetHeight); + this.liveGrid.requestContentRefresh(contentOffset); + if ( this.metaData.options.onscroll ) + this.metaData.options.onscroll( contentOffset, this.metaData ); + + this.scrollTimeout = setTimeout( this.scrollIdle.bind(this), 1200 ); + }, + + scrollIdle: function() { + if ( this.metaData.options.onscrollidle ) + this.metaData.options.onscrollidle(); + } +}; + +// Rico.LiveGridBuffer ----------------------------------------------------- + +Rico.LiveGridBuffer = Class.create(); + +Rico.LiveGridBuffer.prototype = { + + initialize: function(metaData) { + this.startPos = 0; + this.size = 0; + this.metaData = metaData; + this.rows = new Array(); + this.updateInProgress = false; + }, + + update: function(ajaxResponse,start) { + + this.startPos = start; + this.rows = new Array(); + + var rowsElement = ajaxResponse.getElementsByTagName('rows')[0]; + this.updateUI = rowsElement.getAttribute("update_ui") == "true"; + var trs = rowsElement.getElementsByTagName("tr"); + for ( var i=0 ; i < trs.length; i++ ) { + var row = this.rows[i] = new Array(); + var cells = trs[i].getElementsByTagName("td"); + for ( var j=0; j < cells.length ; j++ ) { + var cell = cells[j]; + var convertSpaces = cell.getAttribute("convert_spaces") == "true"; + var cellContent = cell.text != undefined ? cell.text : cell.textContent; + row[j] = convertSpaces ? this.convertSpaces(cellContent) : cellContent; + } + } + this.size = trs.length; + }, + + isFullP: function() { + return this.metaData.pageSize != this.size; + }, + + isClose: function(start) { + return (start < this.startPos + this.size + (this.size/2)) && + (start + this.size + (this.size/2) > this.startPos) + }, + + isInRange: function(start, count) { + return (start < this.startPos + this.size) && (start + count > this.startPos) + }, + + isFullyInRange: function(position) { + return (position >= this.startPos) && (position+this.metaData.getPageSize()) <= (this.startPos + this.size) + }, + + isNearingTopLimit: function(position) { + return position - this.startPos < this.metaData.getLimitTolerance(); + }, + + isNearingBottomLimit: function(position) { + var myEnd = position + this.metaData.getPageSize(); + var bufferEnd = this.startPos + this.size; + return bufferEnd - myEnd < this.metaData.getLimitTolerance(); + }, + + isAtTop: function() { + return this.startPos == 0; + }, + + isAtBottom: function() { + return this.startPos + this.size == this.metaData.getTotalRows(); + }, + + isNearingLimit: function(position) { + return ( !this.isAtTop() && this.isNearingTopLimit(position)) || + ( !this.isAtBottom() && this.isNearingBottomLimit(position) ) + }, + + getRows: function(start, count) { + var begPos = start - this.startPos; + var endPos = begPos + count; + + // er? need more data... + if ( endPos > this.size ) + endPos = this.size; + + var results = new Array(); + var index = 0; + for ( var i=begPos ; i < endPos; i++ ) { + results[index++] = this.rows[i] + } + return results + }, + + convertSpaces: function(s) { + return s.split(" ").join(" "); + } + +}; + +Rico.LiveGridRequest = Class.create(); +Rico.LiveGridRequest.prototype = { + initialize: function( requestOffset, options ) { + this.requestOffset = requestOffset; + } +}; + +// Rico.LiveGrid ----------------------------------------------------- + +Rico.LiveGrid = Class.create(); + +Rico.LiveGrid.prototype = { + + initialize: function( tableId, visibleRows, totalRows, url, options ) { + + if ( options == null ) + options = {}; + + this.tableId = tableId; + this.table = $(tableId); + this.metaData = new Rico.LiveGridMetaData(visibleRows, totalRows, options); + this.buffer = new Rico.LiveGridBuffer(this.metaData); + this.scroller = new Rico.LiveGridScroller(this); + + this.lastDisplayedStartPos = 0; + this.timeoutHander = null; + this.additionalParms = options.requestParameters || []; + + this.processingRequest = null; + this.unprocessedRequest = null; + + this.initAjax(url); + if ( options.prefetchBuffer ) + this.fetchBuffer(0, true); + }, + + setRequestParams: function() { + this.additionalParms = []; + for ( var i=0 ; i < arguments.length ; i++ ) + this.additionalParms[i] = arguments[i]; + }, + + setTotalRows: function( newTotalRows ) { + this.metaData.setTotalRows(newTotalRows); + this.scroller.updateSize(); + }, + + initAjax: function(url) { + ajaxEngine.registerRequest( this.tableId + '_request', url ); + ajaxEngine.registerAjaxObject( this.tableId + '_updater', this ); + }, + + invokeAjax: function() { + }, + + largeBufferWindowStart: function(offset) { + val = offset - ( (.5 * this.metaData.getLargeBufferSize()) - (.5 * this.metaData.getPageSize()) ); + return Math.max(parseInt(val), 0); + }, + + handleTimedOut: function() { + //server did not respond in 4 seconds... assume that there could have been + //an error or something, and allow requests to be processed again... + this.processingRequest = null; + }, + + fetchBuffer: function(offset, fullBufferp) { + if (this.processingRequest) { + this.unprocessedRequest = new Rico.LiveGridRequest(offset); + return; + } + + var fetchSize = this.metaData.getBufferSize(fullBufferp); + bufferStartPos = Math.max(0,fullBufferp ? this.largeBufferWindowStart(offset) : offset); + + this.processingRequest = new Rico.LiveGridRequest(offset); + this.processingRequest.bufferOffset = bufferStartPos; + + var callParms = []; + callParms.push(this.tableId + '_request'); + callParms.push('id=' + this.tableId); + callParms.push('page_size=' + fetchSize); + callParms.push('offset=' + bufferStartPos); + + for( var i=0 ; i < this.additionalParms.length ; i++ ) + callParms.push(this.additionalParms[i]); + + ajaxEngine.sendRequest.apply( ajaxEngine, callParms ); + this.timeoutHandler = setTimeout( this.handleTimedOut.bind(this), 4000 ); + }, + + requestContentRefresh: function(contentOffset) { + if ( this.buffer && this.buffer.isFullyInRange(contentOffset) ) { + this.updateContent(contentOffset); + if (this.buffer.isNearingLimit(contentOffset)) + this.fetchBuffer(contentOffset, true); + } + else if (this.buffer && this.buffer.isClose(contentOffset)) + this.fetchBuffer(contentOffset, true); + else + this.fetchBuffer(contentOffset, false); + }, + + ajaxUpdate: function(ajaxResponse) { + try { + clearTimeout( this.timeoutHandler ); + this.buffer = new Rico.LiveGridBuffer(this.metaData); + this.buffer.update(ajaxResponse,this.processingRequest.bufferOffset); + if (this.unprocessedRequest == null) { + offset = this.processingRequest.requestOffset; + this.updateContent (offset); + } + this.processingRequest = null; + if (this.unprocessedRequest != null) { + this.requestContentRefresh(this.unprocessedRequest.requestOffset); + this.unprocessedRequest = null + } + } + catch(err) { + } + }, + + updateContent: function( offset ) { + this.replaceCellContents(this.buffer, offset); + }, + + replaceCellContents: function(buffer, startPos) { + if (startPos == this.lastDisplayedStartPos){ + return; + } + + this.lastDisplayedStartPos = startPos; + var rows = buffer.getRows(startPos, this.metaData.getPageSize()); + for (var i=0; i < rows.length; i++) { + var row = rows[i]; + for (var j=0; j < row.length; j++) { + this.table.rows[i].cells[j].innerHTML = rows[i][j] + } + } + } +}; + +// ricoUtil.js -------------------- + + +var RicoUtil = { + + getElementsComputedStyle: function ( htmlElement, cssProperty, mozillaEquivalentCSS) { + if ( arguments.length == 2 ) + mozillaEquivalentCSS = cssProperty; + + var el = $(htmlElement); + if ( el.currentStyle ) + return el.currentStyle[cssProperty]; + else + return document.defaultView.getComputedStyle(el, null).getPropertyValue(mozillaEquivalentCSS); + }, + + createXmlDocument : function() { + if (document.implementation && document.implementation.createDocument) { + var doc = document.implementation.createDocument("", "", null); + + if (doc.readyState == null) { + doc.readyState = 1; + doc.addEventListener("load", function () { + doc.readyState = 4; + if (typeof doc.onreadystatechange == "function") + doc.onreadystatechange(); + }, false); + } + + return doc; + } + + if (window.ActiveXObject) + return Try.these( + function() { return new ActiveXObject('MSXML2.DomDocument') }, + function() { return new ActiveXObject('Microsoft.DomDocument')}, + function() { return new ActiveXObject('MSXML.DomDocument') }, + function() { return new ActiveXObject('MSXML3.DomDocument') } + ) || false; + + return null; + }, + + toViewportPosition: function(element) { + return this._toAbsolute(element,true); + }, + + toDocumentPosition: function(element) { + return this._toAbsolute(element,false); + }, + + /** + * Compute the elements position in terms of the window viewport + * so that it can be compared to the position of the mouse (dnd) + * This is additions of all the offsetTop,offsetLeft values up the + * offsetParent hierarchy, ...taking into account any scrollTop, + * scrollLeft values along the way... + * + * IE has a bug reporting a correct offsetLeft of elements within a + * a relatively positioned parent!!! + **/ + _toAbsolute: function(element,accountForDocScroll) { + + if ( navigator.userAgent.toLowerCase().indexOf("msie") == -1 ) + return this._toAbsoluteMozilla(element,accountForDocScroll); + + var x = 0; + var y = 0; + var parent = element; + while ( parent ) { + + var borderXOffset = 0; + var borderYOffset = 0; + if ( parent != element ) { + var borderXOffset = parseInt(this.getElementsComputedStyle(parent, "borderLeftWidth" )); + var borderYOffset = parseInt(this.getElementsComputedStyle(parent, "borderTopWidth" )); + borderXOffset = isNaN(borderXOffset) ? 0 : borderXOffset; + borderYOffset = isNaN(borderYOffset) ? 0 : borderYOffset; + } + + x += parent.offsetLeft - parent.scrollLeft + borderXOffset; + y += parent.offsetTop - parent.scrollTop + borderYOffset; + parent = parent.offsetParent; + } + + if ( accountForDocScroll ) { + x -= this.docScrollLeft(); + y -= this.docScrollTop(); + } + + return { x:x, y:y }; + }, + + /** + * Mozilla did not report all of the parents up the hierarchy via the + * offsetParent property that IE did. So for the calculation of the + * offsets we use the offsetParent property, but for the calculation of + * the scrollTop/scrollLeft adjustments we navigate up via the parentNode + * property instead so as to get the scroll offsets... + * + **/ + _toAbsoluteMozilla: function(element,accountForDocScroll) { + var x = 0; + var y = 0; + var parent = element; + while ( parent ) { + x += parent.offsetLeft; + y += parent.offsetTop; + parent = parent.offsetParent; + } + + parent = element; + while ( parent && + parent != document.body && + parent != document.documentElement ) { + if ( parent.scrollLeft ) + x -= parent.scrollLeft; + if ( parent.scrollTop ) + y -= parent.scrollTop; + parent = parent.parentNode; + } + + if ( accountForDocScroll ) { + x -= this.docScrollLeft(); + y -= this.docScrollTop(); + } + + return { x:x, y:y }; + }, + + docScrollLeft: function() { + if ( window.pageXOffset ) + return window.pageXOffset; + else if ( document.documentElement && document.documentElement.scrollLeft ) + return document.documentElement.scrollLeft; + else if ( document.body ) + return document.body.scrollLeft; + else + return 0; + }, + + docScrollTop: function() { + if ( window.pageYOffset ) + return window.pageYOffset; + else if ( document.documentElement && document.documentElement.scrollTop ) + return document.documentElement.scrollTop; + else if ( document.body ) + return document.body.scrollTop; + else + return 0; + } + +}; \ No newline at end of file diff --git a/framework/Web/Javascripts/effects/slider.js b/framework/Web/Javascripts/effects/slider.js new file mode 100644 index 00000000..dc3ccab4 --- /dev/null +++ b/framework/Web/Javascripts/effects/slider.js @@ -0,0 +1,258 @@ +// Copyright (c) 2005 Marty Haught +// +// See scriptaculous.js for full license. + +if(!Control) var Control = {}; +Control.Slider = Class.create(); + +// options: +// axis: 'vertical', or 'horizontal' (default) +// increment: (default: 1) +// step: (default: 1) +// +// callbacks: +// onChange(value) +// onSlide(value) +Control.Slider.prototype = { + initialize: function(handle, track, options) { + this.handle = $(handle); + this.track = $(track); + + this.options = options || {}; + + this.axis = this.options.axis || 'horizontal'; + this.increment = this.options.increment || 1; + this.step = parseInt(this.options.step) || 1; + this.value = 0; + + var defaultMaximum = Math.round(this.track.offsetWidth / this.increment); + if(this.isVertical()) defaultMaximum = Math.round(this.track.offsetHeight / this.increment); + + this.maximum = this.options.maximum || defaultMaximum; + this.minimum = this.options.minimum || 0; + + // Will be used to align the handle onto the track, if necessary + this.alignX = parseInt (this.options.alignX) || 0; + this.alignY = parseInt (this.options.alignY) || 0; + + // Zero out the slider position + this.setCurrentLeft(Position.cumulativeOffset(this.track)[0] - Position.cumulativeOffset(this.handle)[0] + this.alignX); + this.setCurrentTop(this.trackTop() - Position.cumulativeOffset(this.handle)[1] + this.alignY); + + this.offsetX = 0; + this.offsetY = 0; + + this.originalLeft = this.currentLeft(); + this.originalTop = this.currentTop(); + this.originalZ = parseInt(this.handle.style.zIndex || "0"); + + // Prepopulate Slider value + this.setSliderValue(parseInt(this.options.sliderValue) || 0); + + this.active = false; + this.dragging = false; + this.disabled = false; + + // FIXME: use css + this.handleImage = $(this.options.handleImage) || false; + this.handleDisabled = this.options.handleDisabled || false; + this.handleEnabled = false; + if(this.handleImage) + this.handleEnabled = this.handleImage.src || false; + + if(this.options.disabled) + this.setDisabled(); + + // Value Array + this.values = this.options.values || false; // Add method to validate and sort?? + + Element.makePositioned(this.handle); // fix IE + + this.eventMouseDown = this.startDrag.bindAsEventListener(this); + this.eventMouseUp = this.endDrag.bindAsEventListener(this); + this.eventMouseMove = this.update.bindAsEventListener(this); + this.eventKeypress = this.keyPress.bindAsEventListener(this); + + Event.observe(this.handle, "mousedown", this.eventMouseDown); + Event.observe(document, "mouseup", this.eventMouseUp); + Event.observe(document, "mousemove", this.eventMouseMove); + Event.observe(document, "keypress", this.eventKeypress); + }, + dispose: function() { + Event.stopObserving(this.handle, "mousedown", this.eventMouseDown); + Event.stopObserving(document, "mouseup", this.eventMouseUp); + Event.stopObserving(document, "mousemove", this.eventMouseMove); + Event.stopObserving(document, "keypress", this.eventKeypress); + }, + setDisabled: function(){ + this.disabled = true; + if(this.handleDisabled) + this.handleImage.src = this.handleDisabled; + }, + setEnabled: function(){ + this.disabled = false; + if(this.handleEnabled) + this.handleImage.src = this.handleEnabled; + }, + currentLeft: function() { + return parseInt(this.handle.style.left || '0'); + }, + currentTop: function() { + return parseInt(this.handle.style.top || '0'); + }, + setCurrentLeft: function(left) { + this.handle.style.left = left +"px"; + }, + setCurrentTop: function(top) { + this.handle.style.top = top +"px"; + }, + trackLeft: function(){ + return Position.cumulativeOffset(this.track)[0]; + }, + trackTop: function(){ + return Position.cumulativeOffset(this.track)[1]; + }, + getNearestValue: function(value){ + if(this.values){ + var i = 0; + var offset = Math.abs(this.values[0] - value); + var newValue = this.values[0]; + + for(i=0; i < this.values.length; i++){ + var currentOffset = Math.abs(this.values[i] - value); + if(currentOffset < offset){ + newValue = this.values[i]; + offset = currentOffset; + } + } + return newValue; + } + return value; + }, + setSliderValue: function(sliderValue){ + // First check our max and minimum and nearest values + sliderValue = this.getNearestValue(sliderValue); + if(sliderValue > this.maximum) sliderValue = this.maximum; + if(sliderValue < this.minimum) sliderValue = this.minimum; + var offsetDiff = (sliderValue - (this.value||this.minimum)) * this.increment; + + if(this.isVertical()){ + this.setCurrentTop(offsetDiff + this.currentTop()); + } else { + this.setCurrentLeft(offsetDiff + this.currentLeft()); + } + this.value = sliderValue; + this.updateFinished(); + }, + minimumOffset: function(){ + return(this.isVertical() ? + this.trackTop() + this.alignY : + this.trackLeft() + this.alignX); + }, + maximumOffset: function(){ + return(this.isVertical() ? + this.trackTop() + this.alignY + (this.maximum - this.minimum) * this.increment : + this.trackLeft() + this.alignX + (this.maximum - this.minimum) * this.increment); + }, + isVertical: function(){ + return (this.axis == 'vertical'); + }, + startDrag: function(event) { + if(Event.isLeftClick(event)) { + if(!this.disabled){ + this.active = true; + var pointer = [Event.pointerX(event), Event.pointerY(event)]; + var offsets = Position.cumulativeOffset(this.handle); + this.offsetX = (pointer[0] - offsets[0]); + this.offsetY = (pointer[1] - offsets[1]); + this.originalLeft = this.currentLeft(); + this.originalTop = this.currentTop(); + } + Event.stop(event); + } + }, + update: function(event) { + if(this.active) { + if(!this.dragging) { + var style = this.handle.style; + this.dragging = true; + if(style.position=="") style.position = "relative"; + style.zIndex = this.options.zindex; + } + this.draw(event); + // fix AppleWebKit rendering + if(navigator.appVersion.indexOf('AppleWebKit')>0) window.scrollBy(0,0); + Event.stop(event); + } + }, + draw: function(event) { + var pointer = [Event.pointerX(event), Event.pointerY(event)]; + var offsets = Position.cumulativeOffset(this.handle); + + offsets[0] -= this.currentLeft(); + offsets[1] -= this.currentTop(); + + // Adjust for the pointer's position on the handle + pointer[0] -= this.offsetX; + pointer[1] -= this.offsetY; + var style = this.handle.style; + + if(this.isVertical()){ + if(pointer[1] > this.maximumOffset()) + pointer[1] = this.maximumOffset(); + if(pointer[1] < this.minimumOffset()) + pointer[1] = this.minimumOffset(); + + // Increment by values + if(this.values){ + this.value = this.getNearestValue(Math.round((pointer[1] - this.minimumOffset()) / this.increment) + this.minimum); + pointer[1] = this.trackTop() + this.alignY + (this.value - this.minimum) * this.increment; + } else { + this.value = Math.round((pointer[1] - this.minimumOffset()) / this.increment) + this.minimum; + } + style.top = pointer[1] - offsets[1] + "px"; + } else { + if(pointer[0] > this.maximumOffset()) pointer[0] = this.maximumOffset(); + if(pointer[0] < this.minimumOffset()) pointer[0] = this.minimumOffset(); + // Increment by values + if(this.values){ + this.value = this.getNearestValue(Math.round((pointer[0] - this.minimumOffset()) / this.increment) + this.minimum); + pointer[0] = this.trackLeft() + this.alignX + (this.value - this.minimum) * this.increment; + } else { + this.value = Math.round((pointer[0] - this.minimumOffset()) / this.increment) + this.minimum; + } + style.left = (pointer[0] - offsets[0]) + "px"; + } + if(this.options.onSlide) this.options.onSlide(this.value); + }, + endDrag: function(event) { + if(this.active && this.dragging) { + this.finishDrag(event, true); + Event.stop(event); + } + this.active = false; + this.dragging = false; + }, + finishDrag: function(event, success) { + this.active = false; + this.dragging = false; + this.handle.style.zIndex = this.originalZ; + this.originalLeft = this.currentLeft(); + this.originalTop = this.currentTop(); + this.updateFinished(); + }, + updateFinished: function() { + if(this.options.onChange) this.options.onChange(this.value); + }, + keyPress: function(event) { + if(this.active && !this.disabled) { + switch(event.keyCode) { + case Event.KEY_ESC: + this.finishDrag(event, false); + Event.stop(event); + break; + } + if(navigator.appVersion.indexOf('AppleWebKit')>0) Event.stop(event); + } + } +} diff --git a/framework/Web/Javascripts/effects/util.js b/framework/Web/Javascripts/effects/util.js new file mode 100644 index 00000000..b4a31a97 --- /dev/null +++ b/framework/Web/Javascripts/effects/util.js @@ -0,0 +1,548 @@ +// Copyright (c) 2005 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us) +// +// See scriptaculous.js for full license. + + +Object.debug = function(obj) { + var info = []; + + if(typeof obj in ["string","number"]) { + return obj; + } else { + for(property in obj) + if(typeof obj[property]!="function") + info.push(property + ' => ' + + (typeof obj[property] == "string" ? + '"' + obj[property] + '"' : + obj[property])); + } + + return ("'" + obj + "' #" + typeof obj + + ": {" + info.join(", ") + "}"); +} + + +String.prototype.toArray = function() { + var results = []; + for (var i = 0; i < this.length; i++) + results.push(this.charAt(i)); + return results; +} + +/*--------------------------------------------------------------------------*/ + +var Builder = { + NODEMAP: { + AREA: 'map', + CAPTION: 'table', + COL: 'table', + COLGROUP: 'table', + LEGEND: 'fieldset', + OPTGROUP: 'select', + OPTION: 'select', + PARAM: 'object', + TBODY: 'table', + TD: 'table', + TFOOT: 'table', + TH: 'table', + THEAD: 'table', + TR: 'table' + }, + // note: For Firefox < 1.5, OPTION and OPTGROUP tags are currently broken, + // due to a Firefox bug + node: function(elementName) { + elementName = elementName.toUpperCase(); + + // try innerHTML approach + var parentTag = this.NODEMAP[elementName] || 'div'; + var parentElement = document.createElement(parentTag); + parentElement.innerHTML = "<" + elementName + ">"; + var element = parentElement.firstChild || null; + + // see if browser added wrapping tags + if(element && (element.tagName != elementName)) + element = element.getElementsByTagName(elementName)[0]; + + // fallback to createElement approach + if(!element) element = document.createElement(elementName); + + // abort if nothing could be created + if(!element) return; + + // attributes (or text) + if(arguments[1]) + if(this._isStringOrNumber(arguments[1]) || + (arguments[1] instanceof Array)) { + this._children(element, arguments[1]); + } else { + var attrs = this._attributes(arguments[1]); + if(attrs.length) { + parentElement.innerHTML = "<" +elementName + " " + + attrs + ">"; + element = parentElement.firstChild || null; + // workaround firefox 1.0.X bug + if(!element) { + element = document.createElement(elementName); + for(attr in arguments[1]) + element[attr == 'class' ? 'className' : attr] = arguments[1][attr]; + } + if(element.tagName != elementName) + element = parentElement.getElementsByTagName(elementName)[0]; + } + } + + // text, or array of children + if(arguments[2]) + this._children(element, arguments[2]); + + return element; + }, + _text: function(text) { + return document.createTextNode(text); + }, + _attributes: function(attributes) { + var attrs = []; + for(attribute in attributes) + attrs.push((attribute=='className' ? 'class' : attribute) + + '="' + attributes[attribute].toString().escapeHTML() + '"'); + return attrs.join(" "); + }, + _children: function(element, children) { + if(typeof children=='object') { // array can hold nodes and text + children.flatten().each( function(e) { + if(typeof e=='object') + element.appendChild(e) + else + if(Builder._isStringOrNumber(e)) + element.appendChild(Builder._text(e)); + }); + } else + if(Builder._isStringOrNumber(children)) + element.appendChild(Builder._text(children)); + }, + _isStringOrNumber: function(param) { + return(typeof param=='string' || typeof param=='number'); + } +} + +/* ------------- element ext -------------- */ + +// adapted from http://dhtmlkitchen.com/learn/js/setstyle/index4.jsp +// note: Safari return null on elements with display:none; see http://bugzilla.opendarwin.org/show_bug.cgi?id=4125 +// instead of "auto" values returns null so it's easier to use with || constructs + +String.prototype.camelize = function() { + var oStringList = this.split('-'); + if(oStringList.length == 1) + return oStringList[0]; + var ret = this.indexOf("-") == 0 ? + oStringList[0].charAt(0).toUpperCase() + oStringList[0].substring(1) : oStringList[0]; + for(var i = 1, len = oStringList.length; i < len; i++){ + var s = oStringList[i]; + ret += s.charAt(0).toUpperCase() + s.substring(1) + } + return ret; +} + +Element.getStyle = function(element, style) { + element = $(element); + var value = element.style[style.camelize()]; + if(!value) + if(document.defaultView && document.defaultView.getComputedStyle) { + var css = document.defaultView.getComputedStyle(element, null); + value = (css!=null) ? css.getPropertyValue(style) : null; + } else if(element.currentStyle) { + value = element.currentStyle[style.camelize()]; + } + + // If top, left, bottom, or right values have been queried, return "auto" for consistency resaons + // if position is "static", as Opera (and others?) returns the pixel values relative to root element + // (or positioning context?) + if (window.opera && (style == "left" || style == "top" || style == "right" || style == "bottom")) + if (Element.getStyle(element, "position") == "static") value = "auto"; + + if(value=='auto') value = null; + return value; +} + +// converts rgb() and #xxx to #xxxxxx format, +// returns self (or first argument) if not convertable +String.prototype.parseColor = function() { + color = "#"; + if(this.slice(0,4) == "rgb(") { + var cols = this.slice(4,this.length-1).split(','); + var i=0; do { color += parseInt(cols[i]).toColorPart() } while (++i<3); + } else { + if(this.slice(0,1) == '#') { + if(this.length==4) for(var i=1;i<4;i++) color += (this.charAt(i) + this.charAt(i)).toLowerCase(); + if(this.length==7) color = this.toLowerCase(); + } + } + return(color.length==7 ? color : (arguments[0] || this)); +} + +Element.makePositioned = function(element) { + element = $(element); + var pos = Element.getStyle(element, 'position'); + if(pos =='static' || !pos) { + element._madePositioned = true; + element.style.position = "relative"; + // Opera returns the offset relative to the positioning context, when an element is position relative + // but top and left have not been defined + if (window.opera){ + element.style.top = 0; + element.style.left = 0; + } + } +} + +Element.undoPositioned = function(element) { + element = $(element); + if(typeof element._madePositioned != "undefined"){ + element._madePositioned = undefined; + element.style.position = ""; + element.style.top = ""; + element.style.left = ""; + element.style.bottom = ""; + element.style.right = ""; + } +} + +Element.makeClipping = function(element) { + element = $(element); + if (typeof element._overflow != 'undefined') return; + element._overflow = element.style.overflow; + if((Element.getStyle(element, 'overflow') || 'visible') != 'hidden') element.style.overflow = 'hidden'; +} + +Element.undoClipping = function(element) { + element = $(element); + if (typeof element._overflow == 'undefined') return; + element.style.overflow = element._overflow; + element._overflow = undefined; +} + +Element.collectTextNodesIgnoreClass = function(element, ignoreclass) { + var children = $(element).childNodes; + var text = ""; + var classtest = new RegExp("^([^ ]+ )*" + ignoreclass+ "( [^ ]+)*$","i"); + + for (var i = 0; i < children.length; i++) { + if(children[i].nodeType==3) { + text+=children[i].nodeValue; + } else { + if((!children[i].className.match(classtest)) && children[i].hasChildNodes()) + text += Element.collectTextNodesIgnoreClass(children[i], ignoreclass); + } + } + + return text; +} + +Element.setContentZoom = function(element, percent) { + element = $(element); + element.style.fontSize = (percent/100) + "em"; + if(navigator.appVersion.indexOf('AppleWebKit')>0) window.scrollBy(0,0); +} + +Element.getOpacity = function(element){ + var opacity; + if (opacity = Element.getStyle(element, "opacity")) + return parseFloat(opacity); + if (opacity = (Element.getStyle(element, "filter") || '').match(/alpha\(opacity=(.*)\)/)) + if(opacity[1]) return parseFloat(opacity[1]) / 100; + return 1.0; +} + +Element.setOpacity = function(element, value){ + element= $(element); + var els = element.style; + if (value == 1){ + els.opacity = '0.999999'; + if(/MSIE/.test(navigator.userAgent)) + els.filter = Element.getStyle(element,'filter').replace(/alpha\([^\)]*\)/gi,''); + } else { + if(value < 0.00001) value = 0; + els.opacity = value; + if(/MSIE/.test(navigator.userAgent)) + els.filter = Element.getStyle(element,'filter').replace(/alpha\([^\)]*\)/gi,'') + + "alpha(opacity="+value*100+")"; + } +} + +Element.getInlineOpacity = function(element){ + element= $(element); + var op; + op = element.style.opacity; + if (typeof op != "undefined" && op != "") return op; + return ""; +} + +Element.setInlineOpacity = function(element, value){ + element= $(element); + var els = element.style; + els.opacity = value; +} + +Element.getDimensions = function(element){ + element = $(element); + // All *Width and *Height properties give 0 on elements with display "none", + // so enable the element temporarily + if (Element.getStyle(element,'display') == "none"){ + var els = element.style; + var originalVisibility = els.visibility; + var originalPosition = els.position; + els.visibility = "hidden"; + els.position = "absolute"; + els.display = ""; + var originalWidth = element.clientWidth; + var originalHeight = element.clientHeight; + els.display = "none"; + els.position = originalPosition; + els.visibility = originalVisibility; + return {width: originalWidth, height: originalHeight}; + } + + return {width: element.offsetWidth, height: element.offsetHeight}; +} + +/*--------------------------------------------------------------------------*/ + +Position.positionedOffset = function(element) { + var valueT = 0, valueL = 0; + do { + valueT += element.offsetTop || 0; + valueL += element.offsetLeft || 0; + element = element.offsetParent; + if (element) { + p = Element.getStyle(element,'position'); + if(p == 'relative' || p == 'absolute') break; + } + } while (element); + return [valueL, valueT]; +} + +// Safari returns margins on body which is incorrect if the child is absolutely positioned. +// for performance reasons, we create a specialized version of Position.cumulativeOffset for +// KHTML/WebKit only + +if(/Konqueror|Safari|KHTML/.test(navigator.userAgent)) { + Position.cumulativeOffset = function(element) { + var valueT = 0, valueL = 0; + do { + valueT += element.offsetTop || 0; + valueL += element.offsetLeft || 0; + + if (element.offsetParent==document.body) + if (Element.getStyle(element,'position')=='absolute') break; + + element = element.offsetParent; + } while (element); + return [valueL, valueT]; + } +} + +Position.page = function(forElement) { + var valueT = 0, valueL = 0; + + var element = forElement; + do { + valueT += element.offsetTop || 0; + valueL += element.offsetLeft || 0; + + // Safari fix + if (element.offsetParent==document.body) + if (Element.getStyle(element,'position')=='absolute') break; + + } while (element = element.offsetParent); + + element = forElement; + do { + valueT -= element.scrollTop || 0; + valueL -= element.scrollLeft || 0; + } while (element = element.parentNode); + + return [valueL, valueT]; +} + +// elements with display:none don't return an offsetParent, +// fall back to manual calculation +Position.offsetParent = function(element) { + if(element.offsetParent) return element.offsetParent; + if(element == document.body) return element; + + while ((element = element.parentNode) && element != document.body) + if (Element.getStyle(element,'position')!='static') + return element; + + return document.body; +} + +Position.clone = function(source, target) { + var options = Object.extend({ + setLeft: true, + setTop: true, + setWidth: true, + setHeight: true, + offsetTop: 0, + offsetLeft: 0 + }, arguments[2] || {}) + + // find page position of source + source = $(source); + var p = Position.page(source); + + // find coordinate system to use + target = $(target); + var delta = [0, 0]; + var parent = null; + // delta [0,0] will do fine with position: fixed elements, + // position:absolute needs offsetParent deltas + if (Element.getStyle(target,'position') == 'absolute') { + parent = Position.offsetParent(target); + delta = Position.page(parent); + } + + // correct by body offsets (fixes Safari) + if (parent==document.body) { + delta[0] -= document.body.offsetLeft; + delta[1] -= document.body.offsetTop; + } + + // set position + if(options.setLeft) target.style.left = (p[0] - delta[0] + options.offsetLeft) + "px"; + if(options.setTop) target.style.top = (p[1] - delta[1] + options.offsetTop) + "px"; + if(options.setWidth) target.style.width = source.offsetWidth + "px"; + if(options.setHeight) target.style.height = source.offsetHeight + "px"; +} + +Position.absolutize = function(element) { + element = $(element); + if(element.style.position=='absolute') return; + Position.prepare(); + + var offsets = Position.positionedOffset(element); + var top = offsets[1]; + var left = offsets[0]; + var width = element.clientWidth; + var height = element.clientHeight; + + element._originalLeft = left - parseFloat(element.style.left || 0); + element._originalTop = top - parseFloat(element.style.top || 0); + element._originalWidth = element.style.width; + element._originalHeight = element.style.height; + + element.style.position = 'absolute'; + element.style.top = top + 'px';; + element.style.left = left + 'px';; + element.style.width = width + 'px';; + element.style.height = height + 'px';; +} + +Position.relativize = function(element) { + element = $(element); + if(element.style.position=='relative') return; + Position.prepare(); + + element.style.position = 'relative'; + var top = parseFloat(element.style.top || 0) - (element._originalTop || 0); + var left = parseFloat(element.style.left || 0) - (element._originalLeft || 0); + + element.style.top = top + 'px'; + element.style.left = left + 'px'; + element.style.height = element._originalHeight; + element.style.width = element._originalWidth; +} + +/*--------------------------------------------------------------------------*/ + +Element.Class = { + // Element.toggleClass(element, className) toggles the class being on/off + // Element.toggleClass(element, className1, className2) toggles between both classes, + // defaulting to className1 if neither exist + toggle: function(element, className) { + if(Element.Class.has(element, className)) { + Element.Class.remove(element, className); + if(arguments.length == 3) Element.Class.add(element, arguments[2]); + } else { + Element.Class.add(element, className); + if(arguments.length == 3) Element.Class.remove(element, arguments[2]); + } + }, + + // gets space-delimited classnames of an element as an array + get: function(element) { + return $(element).className.split(' '); + }, + + // functions adapted from original functions by Gavin Kistner + remove: function(element) { + element = $(element); + var removeClasses = arguments; + $R(1,arguments.length-1).each( function(index) { + element.className = + element.className.split(' ').reject( + function(klass) { return (klass == removeClasses[index]) } ).join(' '); + }); + }, + + add: function(element) { + element = $(element); + for(var i = 1; i < arguments.length; i++) { + Element.Class.remove(element, arguments[i]); + element.className += (element.className.length > 0 ? ' ' : '') + arguments[i]; + } + }, + + // returns true if all given classes exist in said element + has: function(element) { + element = $(element); + if(!element || !element.className) return false; + var regEx; + for(var i = 1; i < arguments.length; i++) { + if((typeof arguments[i] == 'object') && + (arguments[i].constructor == Array)) { + for(var j = 0; j < arguments[i].length; j++) { + regEx = new RegExp("(^|\\s)" + arguments[i][j] + "(\\s|$)"); + if(!regEx.test(element.className)) return false; + } + } else { + regEx = new RegExp("(^|\\s)" + arguments[i] + "(\\s|$)"); + if(!regEx.test(element.className)) return false; + } + } + return true; + }, + + // expects arrays of strings and/or strings as optional paramters + // Element.Class.has_any(element, ['classA','classB','classC'], 'classD') + has_any: function(element) { + element = $(element); + if(!element || !element.className) return false; + var regEx; + for(var i = 1; i < arguments.length; i++) { + if((typeof arguments[i] == 'object') && + (arguments[i].constructor == Array)) { + for(var j = 0; j < arguments[i].length; j++) { + regEx = new RegExp("(^|\\s)" + arguments[i][j] + "(\\s|$)"); + if(regEx.test(element.className)) return true; + } + } else { + regEx = new RegExp("(^|\\s)" + arguments[i] + "(\\s|$)"); + if(regEx.test(element.className)) return true; + } + } + return false; + }, + + childrenWith: function(element, className) { + var children = $(element).getElementsByTagName('*'); + var elements = new Array(); + + for (var i = 0; i < children.length; i++) + if (Element.Class.has(children[i], className)) + elements.push(children[i]); + + return elements; + } +} \ No newline at end of file -- cgit v1.2.3