diff options
Diffstat (limited to 'module/webui/themes/Default/js/static/mootools-more.js')
-rw-r--r-- | module/webui/themes/Default/js/static/mootools-more.js | 2856 |
1 files changed, 2856 insertions, 0 deletions
diff --git a/module/webui/themes/Default/js/static/mootools-more.js b/module/webui/themes/Default/js/static/mootools-more.js new file mode 100644 index 000000000..c7f4a1a0e --- /dev/null +++ b/module/webui/themes/Default/js/static/mootools-more.js @@ -0,0 +1,2856 @@ +/* +--- +MooTools: the javascript framework + +web build: + - http://mootools.net/more/c1cc18c2fff04bcc58921b4dff80a6f1 + +packager build: + - packager build More/Form.Request More/Fx.Reveal More/Sortables More/Request.Periodical More/Color + +... +*/ + +/* +--- + +script: More.js + +name: More + +description: MooTools More + +license: MIT-style license + +authors: + - Guillermo Rauch + - Thomas Aylott + - Scott Kyle + - Arian Stolwijk + - Tim Wienk + - Christoph Pojer + - Aaron Newton + - Jacob Thornton + +requires: + - Core/MooTools + +provides: [MooTools.More] + +... +*/ + +MooTools.More = { + version: '1.5.0', + build: '73db5e24e6e9c5c87b3a27aebef2248053f7db37' +}; + + +/* +--- + +script: Class.Binds.js + +name: Class.Binds + +description: Automagically binds specified methods in a class to the instance of the class. + +license: MIT-style license + +authors: + - Aaron Newton + +requires: + - Core/Class + - MooTools.More + +provides: [Class.Binds] + +... +*/ + +Class.Mutators.Binds = function(binds){ + if (!this.prototype.initialize) this.implement('initialize', function(){}); + return Array.from(binds).concat(this.prototype.Binds || []); +}; + +Class.Mutators.initialize = function(initialize){ + return function(){ + Array.from(this.Binds).each(function(name){ + var original = this[name]; + if (original) this[name] = original.bind(this); + }, this); + return initialize.apply(this, arguments); + }; +}; + + +/* +--- + +script: Class.Occlude.js + +name: Class.Occlude + +description: Prevents a class from being applied to a DOM element twice. + +license: MIT-style license. + +authors: + - Aaron Newton + +requires: + - Core/Class + - Core/Element + - MooTools.More + +provides: [Class.Occlude] + +... +*/ + +Class.Occlude = new Class({ + + occlude: function(property, element){ + element = document.id(element || this.element); + var instance = element.retrieve(property || this.property); + if (instance && !this.occluded) + return (this.occluded = instance); + + this.occluded = false; + element.store(property || this.property, this); + return this.occluded; + } + +}); + + +/* +--- + +script: Class.Refactor.js + +name: Class.Refactor + +description: Extends a class onto itself with new property, preserving any items attached to the class's namespace. + +license: MIT-style license + +authors: + - Aaron Newton + +requires: + - Core/Class + - MooTools.More + +# Some modules declare themselves dependent on Class.Refactor +provides: [Class.refactor, Class.Refactor] + +... +*/ + +Class.refactor = function(original, refactors){ + + Object.each(refactors, function(item, name){ + var origin = original.prototype[name]; + origin = (origin && origin.$origin) || origin || function(){}; + original.implement(name, (typeof item == 'function') ? function(){ + var old = this.previous; + this.previous = origin; + var value = item.apply(this, arguments); + this.previous = old; + return value; + } : item); + }); + + return original; + +}; + + +/* +--- + +script: Element.Measure.js + +name: Element.Measure + +description: Extends the Element native object to include methods useful in measuring dimensions. + +credits: "Element.measure / .expose methods by Daniel Steigerwald License: MIT-style license. Copyright: Copyright (c) 2008 Daniel Steigerwald, daniel.steigerwald.cz" + +license: MIT-style license + +authors: + - Aaron Newton + +requires: + - Core/Element.Style + - Core/Element.Dimensions + - MooTools.More + +provides: [Element.Measure] + +... +*/ + +(function(){ + +var getStylesList = function(styles, planes){ + var list = []; + Object.each(planes, function(directions){ + Object.each(directions, function(edge){ + styles.each(function(style){ + list.push(style + '-' + edge + (style == 'border' ? '-width' : '')); + }); + }); + }); + return list; +}; + +var calculateEdgeSize = function(edge, styles){ + var total = 0; + Object.each(styles, function(value, style){ + if (style.test(edge)) total = total + value.toInt(); + }); + return total; +}; + +var isVisible = function(el){ + return !!(!el || el.offsetHeight || el.offsetWidth); +}; + + +Element.implement({ + + measure: function(fn){ + if (isVisible(this)) return fn.call(this); + var parent = this.getParent(), + toMeasure = []; + while (!isVisible(parent) && parent != document.body){ + toMeasure.push(parent.expose()); + parent = parent.getParent(); + } + var restore = this.expose(), + result = fn.call(this); + restore(); + toMeasure.each(function(restore){ + restore(); + }); + return result; + }, + + expose: function(){ + if (this.getStyle('display') != 'none') return function(){}; + var before = this.style.cssText; + this.setStyles({ + display: 'block', + position: 'absolute', + visibility: 'hidden' + }); + return function(){ + this.style.cssText = before; + }.bind(this); + }, + + getDimensions: function(options){ + options = Object.merge({computeSize: false}, options); + var dim = {x: 0, y: 0}; + + var getSize = function(el, options){ + return (options.computeSize) ? el.getComputedSize(options) : el.getSize(); + }; + + var parent = this.getParent('body'); + + if (parent && this.getStyle('display') == 'none'){ + dim = this.measure(function(){ + return getSize(this, options); + }); + } else if (parent){ + try { //safari sometimes crashes here, so catch it + dim = getSize(this, options); + }catch(e){} + } + + return Object.append(dim, (dim.x || dim.x === 0) ? { + width: dim.x, + height: dim.y + } : { + x: dim.width, + y: dim.height + } + ); + }, + + getComputedSize: function(options){ + + + options = Object.merge({ + styles: ['padding','border'], + planes: { + height: ['top','bottom'], + width: ['left','right'] + }, + mode: 'both' + }, options); + + var styles = {}, + size = {width: 0, height: 0}, + dimensions; + + if (options.mode == 'vertical'){ + delete size.width; + delete options.planes.width; + } else if (options.mode == 'horizontal'){ + delete size.height; + delete options.planes.height; + } + + getStylesList(options.styles, options.planes).each(function(style){ + styles[style] = this.getStyle(style).toInt(); + }, this); + + Object.each(options.planes, function(edges, plane){ + + var capitalized = plane.capitalize(), + style = this.getStyle(plane); + + if (style == 'auto' && !dimensions) dimensions = this.getDimensions(); + + style = styles[plane] = (style == 'auto') ? dimensions[plane] : style.toInt(); + size['total' + capitalized] = style; + + edges.each(function(edge){ + var edgesize = calculateEdgeSize(edge, styles); + size['computed' + edge.capitalize()] = edgesize; + size['total' + capitalized] += edgesize; + }); + + }, this); + + return Object.append(size, styles); + } + +}); + +})(); + + +/* +--- + +script: Element.Position.js + +name: Element.Position + +description: Extends the Element native object to include methods useful positioning elements relative to others. + +license: MIT-style license + +authors: + - Aaron Newton + - Jacob Thornton + +requires: + - Core/Options + - Core/Element.Dimensions + - Element.Measure + +provides: [Element.Position] + +... +*/ + +(function(original){ + +var local = Element.Position = { + + options: {/* + edge: false, + returnPos: false, + minimum: {x: 0, y: 0}, + maximum: {x: 0, y: 0}, + relFixedPosition: false, + ignoreMargins: false, + ignoreScroll: false, + allowNegative: false,*/ + relativeTo: document.body, + position: { + x: 'center', //left, center, right + y: 'center' //top, center, bottom + }, + offset: {x: 0, y: 0} + }, + + getOptions: function(element, options){ + options = Object.merge({}, local.options, options); + local.setPositionOption(options); + local.setEdgeOption(options); + local.setOffsetOption(element, options); + local.setDimensionsOption(element, options); + return options; + }, + + setPositionOption: function(options){ + options.position = local.getCoordinateFromValue(options.position); + }, + + setEdgeOption: function(options){ + var edgeOption = local.getCoordinateFromValue(options.edge); + options.edge = edgeOption ? edgeOption : + (options.position.x == 'center' && options.position.y == 'center') ? {x: 'center', y: 'center'} : + {x: 'left', y: 'top'}; + }, + + setOffsetOption: function(element, options){ + var parentOffset = {x: 0, y: 0}; + var parentScroll = {x: 0, y: 0}; + var offsetParent = element.measure(function(){ + return document.id(this.getOffsetParent()); + }); + + if (!offsetParent || offsetParent == element.getDocument().body) return; + + parentScroll = offsetParent.getScroll(); + parentOffset = offsetParent.measure(function(){ + var position = this.getPosition(); + if (this.getStyle('position') == 'fixed'){ + var scroll = window.getScroll(); + position.x += scroll.x; + position.y += scroll.y; + } + return position; + }); + + options.offset = { + parentPositioned: offsetParent != document.id(options.relativeTo), + x: options.offset.x - parentOffset.x + parentScroll.x, + y: options.offset.y - parentOffset.y + parentScroll.y + }; + }, + + setDimensionsOption: function(element, options){ + options.dimensions = element.getDimensions({ + computeSize: true, + styles: ['padding', 'border', 'margin'] + }); + }, + + getPosition: function(element, options){ + var position = {}; + options = local.getOptions(element, options); + var relativeTo = document.id(options.relativeTo) || document.body; + + local.setPositionCoordinates(options, position, relativeTo); + if (options.edge) local.toEdge(position, options); + + var offset = options.offset; + position.left = ((position.x >= 0 || offset.parentPositioned || options.allowNegative) ? position.x : 0).toInt(); + position.top = ((position.y >= 0 || offset.parentPositioned || options.allowNegative) ? position.y : 0).toInt(); + + local.toMinMax(position, options); + + if (options.relFixedPosition || relativeTo.getStyle('position') == 'fixed') local.toRelFixedPosition(relativeTo, position); + if (options.ignoreScroll) local.toIgnoreScroll(relativeTo, position); + if (options.ignoreMargins) local.toIgnoreMargins(position, options); + + position.left = Math.ceil(position.left); + position.top = Math.ceil(position.top); + delete position.x; + delete position.y; + + return position; + }, + + setPositionCoordinates: function(options, position, relativeTo){ + var offsetY = options.offset.y, + offsetX = options.offset.x, + calc = (relativeTo == document.body) ? window.getScroll() : relativeTo.getPosition(), + top = calc.y, + left = calc.x, + winSize = window.getSize(); + + switch(options.position.x){ + case 'left': position.x = left + offsetX; break; + case 'right': position.x = left + offsetX + relativeTo.offsetWidth; break; + default: position.x = left + ((relativeTo == document.body ? winSize.x : relativeTo.offsetWidth) / 2) + offsetX; break; + } + + switch(options.position.y){ + case 'top': position.y = top + offsetY; break; + case 'bottom': position.y = top + offsetY + relativeTo.offsetHeight; break; + default: position.y = top + ((relativeTo == document.body ? winSize.y : relativeTo.offsetHeight) / 2) + offsetY; break; + } + }, + + toMinMax: function(position, options){ + var xy = {left: 'x', top: 'y'}, value; + ['minimum', 'maximum'].each(function(minmax){ + ['left', 'top'].each(function(lr){ + value = options[minmax] ? options[minmax][xy[lr]] : null; + if (value != null && ((minmax == 'minimum') ? position[lr] < value : position[lr] > value)) position[lr] = value; + }); + }); + }, + + toRelFixedPosition: function(relativeTo, position){ + var winScroll = window.getScroll(); + position.top += winScroll.y; + position.left += winScroll.x; + }, + + toIgnoreScroll: function(relativeTo, position){ + var relScroll = relativeTo.getScroll(); + position.top -= relScroll.y; + position.left -= relScroll.x; + }, + + toIgnoreMargins: function(position, options){ + position.left += options.edge.x == 'right' + ? options.dimensions['margin-right'] + : (options.edge.x != 'center' + ? -options.dimensions['margin-left'] + : -options.dimensions['margin-left'] + ((options.dimensions['margin-right'] + options.dimensions['margin-left']) / 2)); + + position.top += options.edge.y == 'bottom' + ? options.dimensions['margin-bottom'] + : (options.edge.y != 'center' + ? -options.dimensions['margin-top'] + : -options.dimensions['margin-top'] + ((options.dimensions['margin-bottom'] + options.dimensions['margin-top']) / 2)); + }, + + toEdge: function(position, options){ + var edgeOffset = {}, + dimensions = options.dimensions, + edge = options.edge; + + switch(edge.x){ + case 'left': edgeOffset.x = 0; break; + case 'right': edgeOffset.x = -dimensions.x - dimensions.computedRight - dimensions.computedLeft; break; + // center + default: edgeOffset.x = -(Math.round(dimensions.totalWidth / 2)); break; + } + + switch(edge.y){ + case 'top': edgeOffset.y = 0; break; + case 'bottom': edgeOffset.y = -dimensions.y - dimensions.computedTop - dimensions.computedBottom; break; + // center + default: edgeOffset.y = -(Math.round(dimensions.totalHeight / 2)); break; + } + + position.x += edgeOffset.x; + position.y += edgeOffset.y; + }, + + getCoordinateFromValue: function(option){ + if (typeOf(option) != 'string') return option; + option = option.toLowerCase(); + + return { + x: option.test('left') ? 'left' + : (option.test('right') ? 'right' : 'center'), + y: option.test(/upper|top/) ? 'top' + : (option.test('bottom') ? 'bottom' : 'center') + }; + } + +}; + +Element.implement({ + + position: function(options){ + if (options && (options.x != null || options.y != null)){ + return (original ? original.apply(this, arguments) : this); + } + var position = this.setStyle('position', 'absolute').calculatePosition(options); + return (options && options.returnPos) ? position : this.setStyles(position); + }, + + calculatePosition: function(options){ + return local.getPosition(this, options); + } + +}); + +})(Element.prototype.position); + + +/* +--- + +script: IframeShim.js + +name: IframeShim + +description: Defines IframeShim, a class for obscuring select lists and flash objects in IE. + +license: MIT-style license + +authors: + - Aaron Newton + +requires: + - Core/Element.Event + - Core/Element.Style + - Core/Options + - Core/Events + - Element.Position + - Class.Occlude + +provides: [IframeShim] + +... +*/ + +(function(){ + +var browsers = false; + + +this.IframeShim = new Class({ + + Implements: [Options, Events, Class.Occlude], + + options: { + className: 'iframeShim', + src: 'javascript:false;document.write("");', + display: false, + zIndex: null, + margin: 0, + offset: {x: 0, y: 0}, + browsers: browsers + }, + + property: 'IframeShim', + + initialize: function(element, options){ + this.element = document.id(element); + if (this.occlude()) return this.occluded; + this.setOptions(options); + this.makeShim(); + return this; + }, + + makeShim: function(){ + if (this.options.browsers){ + var zIndex = this.element.getStyle('zIndex').toInt(); + + if (!zIndex){ + zIndex = 1; + var pos = this.element.getStyle('position'); + if (pos == 'static' || !pos) this.element.setStyle('position', 'relative'); + this.element.setStyle('zIndex', zIndex); + } + zIndex = ((this.options.zIndex != null || this.options.zIndex === 0) && zIndex > this.options.zIndex) ? this.options.zIndex : zIndex - 1; + if (zIndex < 0) zIndex = 1; + this.shim = new Element('iframe', { + src: this.options.src, + scrolling: 'no', + frameborder: 0, + styles: { + zIndex: zIndex, + position: 'absolute', + border: 'none', + filter: 'progid:DXImageTransform.Microsoft.Alpha(style=0,opacity=0)' + }, + 'class': this.options.className + }).store('IframeShim', this); + var inject = (function(){ + this.shim.inject(this.element, 'after'); + this[this.options.display ? 'show' : 'hide'](); + this.fireEvent('inject'); + }).bind(this); + if (!IframeShim.ready) window.addEvent('load', inject); + else inject(); + } else { + this.position = this.hide = this.show = this.dispose = Function.from(this); + } + }, + + position: function(){ + if (!IframeShim.ready || !this.shim) return this; + var size = this.element.measure(function(){ + return this.getSize(); + }); + if (this.options.margin != undefined){ + size.x = size.x - (this.options.margin * 2); + size.y = size.y - (this.options.margin * 2); + this.options.offset.x += this.options.margin; + this.options.offset.y += this.options.margin; + } + this.shim.set({width: size.x, height: size.y}).position({ + relativeTo: this.element, + offset: this.options.offset + }); + return this; + }, + + hide: function(){ + if (this.shim) this.shim.setStyle('display', 'none'); + return this; + }, + + show: function(){ + if (this.shim) this.shim.setStyle('display', 'block'); + return this.position(); + }, + + dispose: function(){ + if (this.shim) this.shim.dispose(); + return this; + }, + + destroy: function(){ + if (this.shim) this.shim.destroy(); + return this; + } + +}); + +})(); + +window.addEvent('load', function(){ + IframeShim.ready = true; +}); + + +/* +--- + +script: Mask.js + +name: Mask + +description: Creates a mask element to cover another. + +license: MIT-style license + +authors: + - Aaron Newton + +requires: + - Core/Options + - Core/Events + - Core/Element.Event + - Class.Binds + - Element.Position + - IframeShim + +provides: [Mask] + +... +*/ + +var Mask = new Class({ + + Implements: [Options, Events], + + Binds: ['position'], + + options: {/* + onShow: function(){}, + onHide: function(){}, + onDestroy: function(){}, + onClick: function(event){}, + inject: { + where: 'after', + target: null, + }, + hideOnClick: false, + id: null, + destroyOnHide: false,*/ + style: {}, + 'class': 'mask', + maskMargins: false, + useIframeShim: true, + iframeShimOptions: {} + }, + + initialize: function(target, options){ + this.target = document.id(target) || document.id(document.body); + this.target.store('mask', this); + this.setOptions(options); + this.render(); + this.inject(); + }, + + render: function(){ + this.element = new Element('div', { + 'class': this.options['class'], + id: this.options.id || 'mask-' + String.uniqueID(), + styles: Object.merge({}, this.options.style, { + display: 'none' + }), + events: { + click: function(event){ + this.fireEvent('click', event); + if (this.options.hideOnClick) this.hide(); + }.bind(this) + } + }); + + this.hidden = true; + }, + + toElement: function(){ + return this.element; + }, + + inject: function(target, where){ + where = where || (this.options.inject ? this.options.inject.where : '') || (this.target == document.body ? 'inside' : 'after'); + target = target || (this.options.inject && this.options.inject.target) || this.target; + + this.element.inject(target, where); + + if (this.options.useIframeShim){ + this.shim = new IframeShim(this.element, this.options.iframeShimOptions); + + this.addEvents({ + show: this.shim.show.bind(this.shim), + hide: this.shim.hide.bind(this.shim), + destroy: this.shim.destroy.bind(this.shim) + }); + } + }, + + position: function(){ + this.resize(this.options.width, this.options.height); + + this.element.position({ + relativeTo: this.target, + position: 'topLeft', + ignoreMargins: !this.options.maskMargins, + ignoreScroll: this.target == document.body + }); + + return this; + }, + + resize: function(x, y){ + var opt = { + styles: ['padding', 'border'] + }; + if (this.options.maskMargins) opt.styles.push('margin'); + + var dim = this.target.getComputedSize(opt); + if (this.target == document.body){ + this.element.setStyles({width: 0, height: 0}); + var win = window.getScrollSize(); + if (dim.totalHeight < win.y) dim.totalHeight = win.y; + if (dim.totalWidth < win.x) dim.totalWidth = win.x; + } + this.element.setStyles({ + width: Array.pick([x, dim.totalWidth, dim.x]), + height: Array.pick([y, dim.totalHeight, dim.y]) + }); + + return this; + }, + + show: function(){ + if (!this.hidden) return this; + + window.addEvent('resize', this.position); + this.position(); + this.showMask.apply(this, arguments); + + return this; + }, + + showMask: function(){ + this.element.setStyle('display', 'block'); + this.hidden = false; + this.fireEvent('show'); + }, + + hide: function(){ + if (this.hidden) return this; + + window.removeEvent('resize', this.position); + this.hideMask.apply(this, arguments); + if (this.options.destroyOnHide) return this.destroy(); + + return this; + }, + + hideMask: function(){ + this.element.setStyle('display', 'none'); + this.hidden = true; + this.fireEvent('hide'); + }, + + toggle: function(){ + this[this.hidden ? 'show' : 'hide'](); + }, + + destroy: function(){ + this.hide(); + this.element.destroy(); + this.fireEvent('destroy'); + this.target.eliminate('mask'); + } + +}); + +Element.Properties.mask = { + + set: function(options){ + var mask = this.retrieve('mask'); + if (mask) mask.destroy(); + return this.eliminate('mask').store('mask:options', options); + }, + + get: function(){ + var mask = this.retrieve('mask'); + if (!mask){ + mask = new Mask(this, this.retrieve('mask:options')); + this.store('mask', mask); + } + return mask; + } + +}; + +Element.implement({ + + mask: function(options){ + if (options) this.set('mask', options); + this.get('mask').show(); + return this; + }, + + unmask: function(){ + this.get('mask').hide(); + return this; + } + +}); + + +/* +--- + +script: Spinner.js + +name: Spinner + +description: Adds a semi-transparent overlay over a dom element with a spinnin ajax icon. + +license: MIT-style license + +authors: + - Aaron Newton + +requires: + - Core/Fx.Tween + - Core/Request + - Class.refactor + - Mask + +provides: [Spinner] + +... +*/ + +var Spinner = new Class({ + + Extends: Mask, + + Implements: Chain, + + options: {/* + message: false,*/ + 'class': 'spinner', + containerPosition: {}, + content: { + 'class': 'spinner-content' + }, + messageContainer: { + 'class': 'spinner-msg' + }, + img: { + 'class': 'spinner-img' + }, + fxOptions: { + link: 'chain' + } + }, + + initialize: function(target, options){ + this.target = document.id(target) || document.id(document.body); + this.target.store('spinner', this); + this.setOptions(options); + this.render(); + this.inject(); + + // Add this to events for when noFx is true; parent methods handle hide/show. + var deactivate = function(){ this.active = false; }.bind(this); + this.addEvents({ + hide: deactivate, + show: deactivate + }); + }, + + render: function(){ + this.parent(); + + this.element.set('id', this.options.id || 'spinner-' + String.uniqueID()); + + this.content = document.id(this.options.content) || new Element('div', this.options.content); + this.content.inject(this.element); + + if (this.options.message){ + this.msg = document.id(this.options.message) || new Element('p', this.options.messageContainer).appendText(this.options.message); + this.msg.inject(this.content); + } + + if (this.options.img){ + this.img = document.id(this.options.img) || new Element('div', this.options.img); + this.img.inject(this.content); + } + + this.element.set('tween', this.options.fxOptions); + }, + + show: function(noFx){ + if (this.active) return this.chain(this.show.bind(this)); + if (!this.hidden){ + this.callChain.delay(20, this); + return this; + } + + this.target.set('aria-busy', 'true'); + this.active = true; + + return this.parent(noFx); + }, + + showMask: function(noFx){ + var pos = function(){ + this.content.position(Object.merge({ + relativeTo: this.element + }, this.options.containerPosition)); + }.bind(this); + + if (noFx){ + this.parent(); + pos(); + } else { + if (!this.options.style.opacity) this.options.style.opacity = this.element.getStyle('opacity').toFloat(); + this.element.setStyles({ + display: 'block', + opacity: 0 + }).tween('opacity', this.options.style.opacity); + pos(); + this.hidden = false; + this.fireEvent('show'); + this.callChain(); + } + }, + + hide: function(noFx){ + if (this.active) return this.chain(this.hide.bind(this)); + if (this.hidden){ + this.callChain.delay(20, this); + return this; + } + + this.target.set('aria-busy', 'false'); + this.active = true; + + return this.parent(noFx); + }, + + hideMask: function(noFx){ + if (noFx) return this.parent(); + this.element.tween('opacity', 0).get('tween').chain(function(){ + this.element.setStyle('display', 'none'); + this.hidden = true; + this.fireEvent('hide'); + this.callChain(); + }.bind(this)); + }, + + destroy: function(){ + this.content.destroy(); + this.parent(); + this.target.eliminate('spinner'); + } + +}); + +Request = Class.refactor(Request, { + + options: { + useSpinner: false, + spinnerOptions: {}, + spinnerTarget: false + }, + + initialize: function(options){ + this._send = this.send; + this.send = function(options){ + var spinner = this.getSpinner(); + if (spinner) spinner.chain(this._send.pass(options, this)).show(); + else this._send(options); + return this; + }; + this.previous(options); + }, + + getSpinner: function(){ + if (!this.spinner){ + var update = document.id(this.options.spinnerTarget) || document.id(this.options.update); + if (this.options.useSpinner && update){ + update.set('spinner', this.options.spinnerOptions); + var spinner = this.spinner = update.get('spinner'); + ['complete', 'exception', 'cancel'].each(function(event){ + this.addEvent(event, spinner.hide.bind(spinner)); + }, this); + } + } + return this.spinner; + } + +}); + +Element.Properties.spinner = { + + set: function(options){ + var spinner = this.retrieve('spinner'); + if (spinner) spinner.destroy(); + return this.eliminate('spinner').store('spinner:options', options); + }, + + get: function(){ + var spinner = this.retrieve('spinner'); + if (!spinner){ + spinner = new Spinner(this, this.retrieve('spinner:options')); + this.store('spinner', spinner); + } + return spinner; + } + +}; + +Element.implement({ + + spin: function(options){ + if (options) this.set('spinner', options); + this.get('spinner').show(); + return this; + }, + + unspin: function(){ + this.get('spinner').hide(); + return this; + } + +}); + + +/* +--- + +script: String.QueryString.js + +name: String.QueryString + +description: Methods for dealing with URI query strings. + +license: MIT-style license + +authors: + - Sebastian Markbåge + - Aaron Newton + - Lennart Pilon + - Valerio Proietti + +requires: + - Core/Array + - Core/String + - MooTools.More + +provides: [String.QueryString] + +... +*/ + +String.implement({ + + parseQueryString: function(decodeKeys, decodeValues){ + if (decodeKeys == null) decodeKeys = true; + if (decodeValues == null) decodeValues = true; + + var vars = this.split(/[&;]/), + object = {}; + if (!vars.length) return object; + + vars.each(function(val){ + var index = val.indexOf('=') + 1, + value = index ? val.substr(index) : '', + keys = index ? val.substr(0, index - 1).match(/([^\]\[]+|(\B)(?=\]))/g) : [val], + obj = object; + if (!keys) return; + if (decodeValues) value = decodeURIComponent(value); + keys.each(function(key, i){ + if (decodeKeys) key = decodeURIComponent(key); + var current = obj[key]; + + if (i < keys.length - 1) obj = obj[key] = current || {}; + else if (typeOf(current) == 'array') current.push(value); + else obj[key] = current != null ? [current, value] : value; + }); + }); + + return object; + }, + + cleanQueryString: function(method){ + return this.split('&').filter(function(val){ + var index = val.indexOf('='), + key = index < 0 ? '' : val.substr(0, index), + value = val.substr(index + 1); + + return method ? method.call(null, key, value) : (value || value === 0); + }).join('&'); + } + +}); + + +/* +--- + +name: Events.Pseudos + +description: Adds the functionality to add pseudo events + +license: MIT-style license + +authors: + - Arian Stolwijk + +requires: [Core/Class.Extras, Core/Slick.Parser, MooTools.More] + +provides: [Events.Pseudos] + +... +*/ + +(function(){ + +Events.Pseudos = function(pseudos, addEvent, removeEvent){ + + var storeKey = '_monitorEvents:'; + + var storageOf = function(object){ + return { + store: object.store ? function(key, value){ + object.store(storeKey + key, value); + } : function(key, value){ + (object._monitorEvents || (object._monitorEvents = {}))[key] = value; + }, + retrieve: object.retrieve ? function(key, dflt){ + return object.retrieve(storeKey + key, dflt); + } : function(key, dflt){ + if (!object._monitorEvents) return dflt; + return object._monitorEvents[key] || dflt; + } + }; + }; + + var splitType = function(type){ + if (type.indexOf(':') == -1 || !pseudos) return null; + + var parsed = Slick.parse(type).expressions[0][0], + parsedPseudos = parsed.pseudos, + l = parsedPseudos.length, + splits = []; + + while (l--){ + var pseudo = parsedPseudos[l].key, + listener = pseudos[pseudo]; + if (listener != null) splits.push({ + event: parsed.tag, + value: parsedPseudos[l].value, + pseudo: pseudo, + original: type, + listener: listener + }); + } + return splits.length ? splits : null; + }; + + return { + + addEvent: function(type, fn, internal){ + var split = splitType(type); + if (!split) return addEvent.call(this, type, fn, internal); + + var storage = storageOf(this), + events = storage.retrieve(type, []), + eventType = split[0].event, + args = Array.slice(arguments, 2), + stack = fn, + self = this; + + split.each(function(item){ + var listener = item.listener, + stackFn = stack; + if (listener == false) eventType += ':' + item.pseudo + '(' + item.value + ')'; + else stack = function(){ + listener.call(self, item, stackFn, arguments, stack); + }; + }); + + events.include({type: eventType, event: fn, monitor: stack}); + storage.store(type, events); + + if (type != eventType) addEvent.apply(this, [type, fn].concat(args)); + return addEvent.apply(this, [eventType, stack].concat(args)); + }, + + removeEvent: function(type, fn){ + var split = splitType(type); + if (!split) return removeEvent.call(this, type, fn); + + var storage = storageOf(this), + events = storage.retrieve(type); + if (!events) return this; + + var args = Array.slice(arguments, 2); + + removeEvent.apply(this, [type, fn].concat(args)); + events.each(function(monitor, i){ + if (!fn || monitor.event == fn) removeEvent.apply(this, [monitor.type, monitor.monitor].concat(args)); + delete events[i]; + }, this); + + storage.store(type, events); + return this; + } + + }; + +}; + +var pseudos = { + + once: function(split, fn, args, monitor){ + fn.apply(this, args); + this.removeEvent(split.event, monitor) + .removeEvent(split.original, fn); + }, + + throttle: function(split, fn, args){ + if (!fn._throttled){ + fn.apply(this, args); + fn._throttled = setTimeout(function(){ + fn._throttled = false; + }, split.value || 250); + } + }, + + pause: function(split, fn, args){ + clearTimeout(fn._pause); + fn._pause = fn.delay(split.value || 250, this, args); + } + +}; + +Events.definePseudo = function(key, listener){ + pseudos[key] = listener; + return this; +}; + +Events.lookupPseudo = function(key){ + return pseudos[key]; +}; + +var proto = Events.prototype; +Events.implement(Events.Pseudos(pseudos, proto.addEvent, proto.removeEvent)); + +['Request', 'Fx'].each(function(klass){ + if (this[klass]) this[klass].implement(Events.prototype); +}); + +})(); + + +/* +--- + +name: Element.Event.Pseudos + +description: Adds the functionality to add pseudo events for Elements + +license: MIT-style license + +authors: + - Arian Stolwijk + +requires: [Core/Element.Event, Core/Element.Delegation, Events.Pseudos] + +provides: [Element.Event.Pseudos, Element.Delegation.Pseudo] + +... +*/ + +(function(){ + +var pseudos = {relay: false}, + copyFromEvents = ['once', 'throttle', 'pause'], + count = copyFromEvents.length; + +while (count--) pseudos[copyFromEvents[count]] = Events.lookupPseudo(copyFromEvents[count]); + +DOMEvent.definePseudo = function(key, listener){ + pseudos[key] = listener; + return this; +}; + +var proto = Element.prototype; +[Element, Window, Document].invoke('implement', Events.Pseudos(pseudos, proto.addEvent, proto.removeEvent)); + +})(); + + +/* +--- + +script: Form.Request.js + +name: Form.Request + +description: Handles the basic functionality of submitting a form and updating a dom element with the result. + +license: MIT-style license + +authors: + - Aaron Newton + +requires: + - Core/Request.HTML + - Class.Binds + - Class.Occlude + - Spinner + - String.QueryString + - Element.Delegation.Pseudo + +provides: [Form.Request] + +... +*/ + +if (!window.Form) window.Form = {}; + +(function(){ + + Form.Request = new Class({ + + Binds: ['onSubmit', 'onFormValidate'], + + Implements: [Options, Events, Class.Occlude], + + options: {/* + onFailure: function(){}, + onSuccess: function(){}, // aliased to onComplete, + onSend: function(){}*/ + requestOptions: { + evalScripts: true, + useSpinner: true, + emulation: false, + link: 'ignore' + }, + sendButtonClicked: true, + extraData: {}, + resetForm: true + }, + + property: 'form.request', + + initialize: function(form, target, options){ + this.element = document.id(form); + if (this.occlude()) return this.occluded; + this.setOptions(options) + .setTarget(target) + .attach(); + }, + + setTarget: function(target){ + this.target = document.id(target); + if (!this.request){ + this.makeRequest(); + } else { + this.request.setOptions({ + update: this.target + }); + } + return this; + }, + + toElement: function(){ + return this.element; + }, + + makeRequest: function(){ + var self = this; + this.request = new Request.HTML(Object.merge({ + update: this.target, + emulation: false, + spinnerTarget: this.element, + method: this.element.get('method') || 'post' + }, this.options.requestOptions)).addEvents({ + success: function(tree, elements, html, javascript){ + ['complete', 'success'].each(function(evt){ + self.fireEvent(evt, [self.target, tree, elements, html, javascript]); + }); + }, + failure: function(){ + self.fireEvent('complete', arguments).fireEvent('failure', arguments); + }, + exception: function(){ + self.fireEvent('failure', arguments); + } + }); + return this.attachReset(); + }, + + attachReset: function(){ + if (!this.options.resetForm) return this; + this.request.addEvent('success', function(){ + Function.attempt(function(){ + this.element.reset(); + }.bind(this)); + if (window.OverText) OverText.update(); + }.bind(this)); + return this; + }, + + attach: function(attach){ + var method = (attach != false) ? 'addEvent' : 'removeEvent'; + this.element[method]('click:relay(button, input[type=submit])', this.saveClickedButton.bind(this)); + + var fv = this.element.retrieve('validator'); + if (fv) fv[method]('onFormValidate', this.onFormValidate); + else this.element[method]('submit', this.onSubmit); + + return this; + }, + + detach: function(){ + return this.attach(false); + }, + + //public method + enable: function(){ + return this.attach(); + }, + + //public method + disable: function(){ + return this.detach(); + }, + + onFormValidate: function(valid, form, event){ + //if there's no event, then this wasn't a submit event + if (!event) return; + var fv = this.element.retrieve('validator'); + if (valid || (fv && !fv.options.stopOnFailure)){ + event.stop(); + this.send(); + } + }, + + onSubmit: function(event){ + var fv = this.element.retrieve('validator'); + if (fv){ + //form validator was created after Form.Request + this.element.removeEvent('submit', this.onSubmit); + fv.addEvent('onFormValidate', this.onFormValidate); + fv.validate(event); + return; + } + if (event) event.stop(); + this.send(); + }, + + saveClickedButton: function(event, target){ + var targetName = target.get('name'); + if (!targetName || !this.options.sendButtonClicked) return; + this.options.extraData[targetName] = target.get('value') || true; + this.clickedCleaner = function(){ + delete this.options.extraData[targetName]; + this.clickedCleaner = function(){}; + }.bind(this); + }, + + clickedCleaner: function(){}, + + send: function(){ + var str = this.element.toQueryString().trim(), + data = Object.toQueryString(this.options.extraData); + + if (str) str += "&" + data; + else str = data; + + this.fireEvent('send', [this.element, str.parseQueryString()]); + this.request.send({ + data: str, + url: this.options.requestOptions.url || this.element.get('action') + }); + this.clickedCleaner(); + return this; + } + + }); + + Element.implement('formUpdate', function(update, options){ + var fq = this.retrieve('form.request'); + if (!fq){ + fq = new Form.Request(this, update, options); + } else { + if (update) fq.setTarget(update); + if (options) fq.setOptions(options).makeRequest(); + } + fq.send(); + return this; + }); + +})(); + + +/* +--- + +script: Element.Shortcuts.js + +name: Element.Shortcuts + +description: Extends the Element native object to include some shortcut methods. + +license: MIT-style license + +authors: + - Aaron Newton + +requires: + - Core/Element.Style + - MooTools.More + +provides: [Element.Shortcuts] + +... +*/ + +Element.implement({ + + isDisplayed: function(){ + return this.getStyle('display') != 'none'; + }, + + isVisible: function(){ + var w = this.offsetWidth, + h = this.offsetHeight; + return (w == 0 && h == 0) ? false : (w > 0 && h > 0) ? true : this.style.display != 'none'; + }, + + toggle: function(){ + return this[this.isDisplayed() ? 'hide' : 'show'](); + }, + + hide: function(){ + var d; + try { + //IE fails here if the element is not in the dom + d = this.getStyle('display'); + } catch(e){} + if (d == 'none') return this; + return this.store('element:_originalDisplay', d || '').setStyle('display', 'none'); + }, + + show: function(display){ + if (!display && this.isDisplayed()) return this; + display = display || this.retrieve('element:_originalDisplay') || 'block'; + return this.setStyle('display', (display == 'none') ? 'block' : display); + }, + + swapClass: function(remove, add){ + return this.removeClass(remove).addClass(add); + } + +}); + +Document.implement({ + + clearSelection: function(){ + if (window.getSelection){ + var selection = window.getSelection(); + if (selection && selection.removeAllRanges) selection.removeAllRanges(); + } else if (document.selection && document.selection.empty){ + try { + //IE fails here if selected element is not in dom + document.selection.empty(); + } catch(e){} + } + } + +}); + + +/* +--- + +script: Fx.Reveal.js + +name: Fx.Reveal + +description: Defines Fx.Reveal, a class that shows and hides elements with a transition. + +license: MIT-style license + +authors: + - Aaron Newton + +requires: + - Core/Fx.Morph + - Element.Shortcuts + - Element.Measure + +provides: [Fx.Reveal] + +... +*/ + +(function(){ + + +var hideTheseOf = function(object){ + var hideThese = object.options.hideInputs; + if (window.OverText){ + var otClasses = [null]; + OverText.each(function(ot){ + otClasses.include('.' + ot.options.labelClass); + }); + if (otClasses) hideThese += otClasses.join(', '); + } + return (hideThese) ? object.element.getElements(hideThese) : null; +}; + + +Fx.Reveal = new Class({ + + Extends: Fx.Morph, + + options: {/* + onShow: function(thisElement){}, + onHide: function(thisElement){}, + onComplete: function(thisElement){}, + heightOverride: null, + widthOverride: null,*/ + link: 'cancel', + styles: ['padding', 'border', 'margin'], + transitionOpacity: 'opacity' in document.documentElement, + mode: 'vertical', + display: function(){ + return this.element.get('tag') != 'tr' ? 'block' : 'table-row'; + }, + opacity: 1, + hideInputs: !('opacity' in document.documentElement) ? 'select, input, textarea, object, embed' : null + }, + + dissolve: function(){ + if (!this.hiding && !this.showing){ + if (this.element.getStyle('display') != 'none'){ + this.hiding = true; + this.showing = false; + this.hidden = true; + this.cssText = this.element.style.cssText; + + var startStyles = this.element.getComputedSize({ + styles: this.options.styles, + mode: this.options.mode + }); + if (this.options.transitionOpacity) startStyles.opacity = this.options.opacity; + + var zero = {}; + Object.each(startStyles, function(style, name){ + zero[name] = [style, 0]; + }); + + this.element.setStyles({ + display: Function.from(this.options.display).call(this), + overflow: 'hidden' + }); + + var hideThese = hideTheseOf(this); + if (hideThese) hideThese.setStyle('visibility', 'hidden'); + + this.$chain.unshift(function(){ + if (this.hidden){ + this.hiding = false; + this.element.style.cssText = this.cssText; + this.element.setStyle('display', 'none'); + if (hideThese) hideThese.setStyle('visibility', 'visible'); + } + this.fireEvent('hide', this.element); + this.callChain(); + }.bind(this)); + + this.start(zero); + } else { + this.callChain.delay(10, this); + this.fireEvent('complete', this.element); + this.fireEvent('hide', this.element); + } + } else if (this.options.link == 'chain'){ + this.chain(this.dissolve.bind(this)); + } else if (this.options.link == 'cancel' && !this.hiding){ + this.cancel(); + this.dissolve(); + } + return this; + }, + + reveal: function(){ + if (!this.showing && !this.hiding){ + if (this.element.getStyle('display') == 'none'){ + this.hiding = false; + this.showing = true; + this.hidden = false; + this.cssText = this.element.style.cssText; + + var startStyles; + this.element.measure(function(){ + startStyles = this.element.getComputedSize({ + styles: this.options.styles, + mode: this.options.mode + }); + }.bind(this)); + if (this.options.heightOverride != null) startStyles.height = this.options.heightOverride.toInt(); + if (this.options.widthOverride != null) startStyles.width = this.options.widthOverride.toInt(); + if (this.options.transitionOpacity){ + this.element.setStyle('opacity', 0); + startStyles.opacity = this.options.opacity; + } + + var zero = { + height: 0, + display: Function.from(this.options.display).call(this) + }; + Object.each(startStyles, function(style, name){ + zero[name] = 0; + }); + zero.overflow = 'hidden'; + + this.element.setStyles(zero); + + var hideThese = hideTheseOf(this); + if (hideThese) hideThese.setStyle('visibility', 'hidden'); + + this.$chain.unshift(function(){ + this.element.style.cssText = this.cssText; + this.element.setStyle('display', Function.from(this.options.display).call(this)); + if (!this.hidden) this.showing = false; + if (hideThese) hideThese.setStyle('visibility', 'visible'); + this.callChain(); + this.fireEvent('show', this.element); + }.bind(this)); + + this.start(startStyles); + } else { + this.callChain(); + this.fireEvent('complete', this.element); + this.fireEvent('show', this.element); + } + } else if (this.options.link == 'chain'){ + this.chain(this.reveal.bind(this)); + } else if (this.options.link == 'cancel' && !this.showing){ + this.cancel(); + this.reveal(); + } + return this; + }, + + toggle: function(){ + if (this.element.getStyle('display') == 'none'){ + this.reveal(); + } else { + this.dissolve(); + } + return this; + }, + + cancel: function(){ + this.parent.apply(this, arguments); + if (this.cssText != null) this.element.style.cssText = this.cssText; + this.hiding = false; + this.showing = false; + return this; + } + +}); + +Element.Properties.reveal = { + + set: function(options){ + this.get('reveal').cancel().setOptions(options); + return this; + }, + + get: function(){ + var reveal = this.retrieve('reveal'); + if (!reveal){ + reveal = new Fx.Reveal(this); + this.store('reveal', reveal); + } + return reveal; + } + +}; + +Element.Properties.dissolve = Element.Properties.reveal; + +Element.implement({ + + reveal: function(options){ + this.get('reveal').setOptions(options).reveal(); + return this; + }, + + dissolve: function(options){ + this.get('reveal').setOptions(options).dissolve(); + return this; + }, + + nix: function(options){ + var params = Array.link(arguments, {destroy: Type.isBoolean, options: Type.isObject}); + this.get('reveal').setOptions(options).dissolve().chain(function(){ + this[params.destroy ? 'destroy' : 'dispose'](); + }.bind(this)); + return this; + }, + + wink: function(){ + var params = Array.link(arguments, {duration: Type.isNumber, options: Type.isObject}); + var reveal = this.get('reveal').setOptions(params.options); + reveal.reveal().chain(function(){ + (function(){ + reveal.dissolve(); + }).delay(params.duration || 2000); + }); + } + +}); + +})(); + + +/* +--- + +script: Drag.js + +name: Drag + +description: The base Drag Class. Can be used to drag and resize Elements using mouse events. + +license: MIT-style license + +authors: + - Valerio Proietti + - Tom Occhinno + - Jan Kassens + +requires: + - Core/Events + - Core/Options + - Core/Element.Event + - Core/Element.Style + - Core/Element.Dimensions + - MooTools.More + +provides: [Drag] +... + +*/ + +var Drag = new Class({ + + Implements: [Events, Options], + + options: {/* + onBeforeStart: function(thisElement){}, + onStart: function(thisElement, event){}, + onSnap: function(thisElement){}, + onDrag: function(thisElement, event){}, + onCancel: function(thisElement){}, + onComplete: function(thisElement, event){},*/ + snap: 6, + unit: 'px', + grid: false, + style: true, + limit: false, + handle: false, + invert: false, + preventDefault: false, + stopPropagation: false, + modifiers: {x: 'left', y: 'top'} + }, + + initialize: function(){ + var params = Array.link(arguments, { + 'options': Type.isObject, + 'element': function(obj){ + return obj != null; + } + }); + + this.element = document.id(params.element); + this.document = this.element.getDocument(); + this.setOptions(params.options || {}); + var htype = typeOf(this.options.handle); + this.handles = ((htype == 'array' || htype == 'collection') ? $$(this.options.handle) : document.id(this.options.handle)) || this.element; + this.mouse = {'now': {}, 'pos': {}}; + this.value = {'start': {}, 'now': {}}; + + this.selection = 'selectstart' in document ? 'selectstart' : 'mousedown'; + + + if ('ondragstart' in document && !('FileReader' in window) && !Drag.ondragstartFixed){ + document.ondragstart = Function.from(false); + Drag.ondragstartFixed = true; + } + + this.bound = { + start: this.start.bind(this), + check: this.check.bind(this), + drag: this.drag.bind(this), + stop: this.stop.bind(this), + cancel: this.cancel.bind(this), + eventStop: Function.from(false) + }; + this.attach(); + }, + + attach: function(){ + this.handles.addEvent('mousedown', this.bound.start); + return this; + }, + + detach: function(){ + this.handles.removeEvent('mousedown', this.bound.start); + return this; + }, + + start: function(event){ + var options = this.options; + + if (event.rightClick) return; + + if (options.preventDefault) event.preventDefault(); + if (options.stopPropagation) event.stopPropagation(); + this.mouse.start = event.page; + + this.fireEvent('beforeStart', this.element); + + var limit = options.limit; + this.limit = {x: [], y: []}; + + var z, coordinates; + for (z in options.modifiers){ + if (!options.modifiers[z]) continue; + + var style = this.element.getStyle(options.modifiers[z]); + + // Some browsers (IE and Opera) don't always return pixels. + if (style && !style.match(/px$/)){ + if (!coordinates) coordinates = this.element.getCoordinates(this.element.getOffsetParent()); + style = coordinates[options.modifiers[z]]; + } + + if (options.style) this.value.now[z] = (style || 0).toInt(); + else this.value.now[z] = this.element[options.modifiers[z]]; + + if (options.invert) this.value.now[z] *= -1; + + this.mouse.pos[z] = event.page[z] - this.value.now[z]; + + if (limit && limit[z]){ + var i = 2; + while (i--){ + var limitZI = limit[z][i]; + if (limitZI || limitZI === 0) this.limit[z][i] = (typeof limitZI == 'function') ? limitZI() : limitZI; + } + } + } + + if (typeOf(this.options.grid) == 'number') this.options.grid = { + x: this.options.grid, + y: this.options.grid + }; + + var events = { + mousemove: this.bound.check, + mouseup: this.bound.cancel + }; + events[this.selection] = this.bound.eventStop; + this.document.addEvents(events); + }, + + check: function(event){ + if (this.options.preventDefault) event.preventDefault(); + var distance = Math.round(Math.sqrt(Math.pow(event.page.x - this.mouse.start.x, 2) + Math.pow(event.page.y - this.mouse.start.y, 2))); + if (distance > this.options.snap){ + this.cancel(); + this.document.addEvents({ + mousemove: this.bound.drag, + mouseup: this.bound.stop + }); + this.fireEvent('start', [this.element, event]).fireEvent('snap', this.element); + } + }, + + drag: function(event){ + var options = this.options; + + if (options.preventDefault) event.preventDefault(); + this.mouse.now = event.page; + + for (var z in options.modifiers){ + if (!options.modifiers[z]) continue; + this.value.now[z] = this.mouse.now[z] - this.mouse.pos[z]; + + if (options.invert) this.value.now[z] *= -1; + + if (options.limit && this.limit[z]){ + if ((this.limit[z][1] || this.limit[z][1] === 0) && (this.value.now[z] > this.limit[z][1])){ + this.value.now[z] = this.limit[z][1]; + } else if ((this.limit[z][0] || this.limit[z][0] === 0) && (this.value.now[z] < this.limit[z][0])){ + this.value.now[z] = this.limit[z][0]; + } + } + + if (options.grid[z]) this.value.now[z] -= ((this.value.now[z] - (this.limit[z][0]||0)) % options.grid[z]); + + if (options.style) this.element.setStyle(options.modifiers[z], this.value.now[z] + options.unit); + else this.element[options.modifiers[z]] = this.value.now[z]; + } + + this.fireEvent('drag', [this.element, event]); + }, + + cancel: function(event){ + this.document.removeEvents({ + mousemove: this.bound.check, + mouseup: this.bound.cancel + }); + if (event){ + this.document.removeEvent(this.selection, this.bound.eventStop); + this.fireEvent('cancel', this.element); + } + }, + + stop: function(event){ + var events = { + mousemove: this.bound.drag, + mouseup: this.bound.stop + }; + events[this.selection] = this.bound.eventStop; + this.document.removeEvents(events); + if (event) this.fireEvent('complete', [this.element, event]); + } + +}); + +Element.implement({ + + makeResizable: function(options){ + var drag = new Drag(this, Object.merge({ + modifiers: { + x: 'width', + y: 'height' + } + }, options)); + + this.store('resizer', drag); + return drag.addEvent('drag', function(){ + this.fireEvent('resize', drag); + }.bind(this)); + } + +}); + + +/* +--- + +script: Drag.Move.js + +name: Drag.Move + +description: A Drag extension that provides support for the constraining of draggables to containers and droppables. + +license: MIT-style license + +authors: + - Valerio Proietti + - Tom Occhinno + - Jan Kassens + - Aaron Newton + - Scott Kyle + +requires: + - Core/Element.Dimensions + - Drag + +provides: [Drag.Move] + +... +*/ + +Drag.Move = new Class({ + + Extends: Drag, + + options: {/* + onEnter: function(thisElement, overed){}, + onLeave: function(thisElement, overed){}, + onDrop: function(thisElement, overed, event){},*/ + droppables: [], + container: false, + precalculate: false, + includeMargins: true, + checkDroppables: true + }, + + initialize: function(element, options){ + this.parent(element, options); + element = this.element; + + this.droppables = $$(this.options.droppables); + this.setContainer(this.options.container); + + if (this.options.style){ + if (this.options.modifiers.x == 'left' && this.options.modifiers.y == 'top'){ + var parent = element.getOffsetParent(), + styles = element.getStyles('left', 'top'); + if (parent && (styles.left == 'auto' || styles.top == 'auto')){ + element.setPosition(element.getPosition(parent)); + } + } + + if (element.getStyle('position') == 'static') element.setStyle('position', 'absolute'); + } + + this.addEvent('start', this.checkDroppables, true); + this.overed = null; + }, + + setContainer: function(container) { + this.container = document.id(container); + if (this.container && typeOf(this.container) != 'element'){ + this.container = document.id(this.container.getDocument().body); + } + }, + + start: function(event){ + if (this.container) this.options.limit = this.calculateLimit(); + + if (this.options.precalculate){ + this.positions = this.droppables.map(function(el){ + return el.getCoordinates(); + }); + } + + this.parent(event); + }, + + calculateLimit: function(){ + var element = this.element, + container = this.container, + + offsetParent = document.id(element.getOffsetParent()) || document.body, + containerCoordinates = container.getCoordinates(offsetParent), + elementMargin = {}, + elementBorder = {}, + containerMargin = {}, + containerBorder = {}, + offsetParentPadding = {}; + + ['top', 'right', 'bottom', 'left'].each(function(pad){ + elementMargin[pad] = element.getStyle('margin-' + pad).toInt(); + elementBorder[pad] = element.getStyle('border-' + pad).toInt(); + containerMargin[pad] = container.getStyle('margin-' + pad).toInt(); + containerBorder[pad] = container.getStyle('border-' + pad).toInt(); + offsetParentPadding[pad] = offsetParent.getStyle('padding-' + pad).toInt(); + }, this); + + var width = element.offsetWidth + elementMargin.left + elementMargin.right, + height = element.offsetHeight + elementMargin.top + elementMargin.bottom, + left = 0, + top = 0, + right = containerCoordinates.right - containerBorder.right - width, + bottom = containerCoordinates.bottom - containerBorder.bottom - height; + + if (this.options.includeMargins){ + left += elementMargin.left; + top += elementMargin.top; + } else { + right += elementMargin.right; + bottom += elementMargin.bottom; + } + + if (element.getStyle('position') == 'relative'){ + var coords = element.getCoordinates(offsetParent); + coords.left -= element.getStyle('left').toInt(); + coords.top -= element.getStyle('top').toInt(); + + left -= coords.left; + top -= coords.top; + if (container.getStyle('position') != 'relative'){ + left += containerBorder.left; + top += containerBorder.top; + } + right += elementMargin.left - coords.left; + bottom += elementMargin.top - coords.top; + + if (container != offsetParent){ + left += containerMargin.left + offsetParentPadding.left; + if (!offsetParentPadding.left && left < 0) left = 0; + top += offsetParent == document.body ? 0 : containerMargin.top + offsetParentPadding.top; + if (!offsetParentPadding.top && top < 0) top = 0; + } + } else { + left -= elementMargin.left; + top -= elementMargin.top; + if (container != offsetParent){ + left += containerCoordinates.left + containerBorder.left; + top += containerCoordinates.top + containerBorder.top; + } + } + + return { + x: [left, right], + y: [top, bottom] + }; + }, + + getDroppableCoordinates: function(element){ + var position = element.getCoordinates(); + if (element.getStyle('position') == 'fixed'){ + var scroll = window.getScroll(); + position.left += scroll.x; + position.right += scroll.x; + position.top += scroll.y; + position.bottom += scroll.y; + } + return position; + }, + + checkDroppables: function(){ + var overed = this.droppables.filter(function(el, i){ + el = this.positions ? this.positions[i] : this.getDroppableCoordinates(el); + var now = this.mouse.now; + return (now.x > el.left && now.x < el.right && now.y < el.bottom && now.y > el.top); + }, this).getLast(); + + if (this.overed != overed){ + if (this.overed) this.fireEvent('leave', [this.element, this.overed]); + if (overed) this.fireEvent('enter', [this.element, overed]); + this.overed = overed; + } + }, + + drag: function(event){ + this.parent(event); + if (this.options.checkDroppables && this.droppables.length) this.checkDroppables(); + }, + + stop: function(event){ + this.checkDroppables(); + this.fireEvent('drop', [this.element, this.overed, event]); + this.overed = null; + return this.parent(event); + } + +}); + +Element.implement({ + + makeDraggable: function(options){ + var drag = new Drag.Move(this, options); + this.store('dragger', drag); + return drag; + } + +}); + + +/* +--- + +script: Sortables.js + +name: Sortables + +description: Class for creating a drag and drop sorting interface for lists of items. + +license: MIT-style license + +authors: + - Tom Occhino + +requires: + - Core/Fx.Morph + - Drag.Move + +provides: [Sortables] + +... +*/ + +var Sortables = new Class({ + + Implements: [Events, Options], + + options: {/* + onSort: function(element, clone){}, + onStart: function(element, clone){}, + onComplete: function(element){},*/ + opacity: 1, + clone: false, + revert: false, + handle: false, + dragOptions: {}, + unDraggableTags: ['button', 'input', 'a', 'textarea', 'select', 'option'] + }, + + initialize: function(lists, options){ + this.setOptions(options); + + this.elements = []; + this.lists = []; + this.idle = true; + + this.addLists($$(document.id(lists) || lists)); + + if (!this.options.clone) this.options.revert = false; + if (this.options.revert) this.effect = new Fx.Morph(null, Object.merge({ + duration: 250, + link: 'cancel' + }, this.options.revert)); + }, + + attach: function(){ + this.addLists(this.lists); + return this; + }, + + detach: function(){ + this.lists = this.removeLists(this.lists); + return this; + }, + + addItems: function(){ + Array.flatten(arguments).each(function(element){ + this.elements.push(element); + var start = element.retrieve('sortables:start', function(event){ + this.start.call(this, event, element); + }.bind(this)); + (this.options.handle ? element.getElement(this.options.handle) || element : element).addEvent('mousedown', start); + }, this); + return this; + }, + + addLists: function(){ + Array.flatten(arguments).each(function(list){ + this.lists.include(list); + this.addItems(list.getChildren()); + }, this); + return this; + }, + + removeItems: function(){ + return $$(Array.flatten(arguments).map(function(element){ + this.elements.erase(element); + var start = element.retrieve('sortables:start'); + (this.options.handle ? element.getElement(this.options.handle) || element : element).removeEvent('mousedown', start); + + return element; + }, this)); + }, + + removeLists: function(){ + return $$(Array.flatten(arguments).map(function(list){ + this.lists.erase(list); + this.removeItems(list.getChildren()); + + return list; + }, this)); + }, + + getDroppableCoordinates: function (element){ + var offsetParent = element.getOffsetParent(); + var position = element.getPosition(offsetParent); + var scroll = { + w: window.getScroll(), + offsetParent: offsetParent.getScroll() + }; + position.x += scroll.offsetParent.x; + position.y += scroll.offsetParent.y; + + if (offsetParent.getStyle('position') == 'fixed'){ + position.x -= scroll.w.x; + position.y -= scroll.w.y; + } + + return position; + }, + + getClone: function(event, element){ + if (!this.options.clone) return new Element(element.tagName).inject(document.body); + if (typeOf(this.options.clone) == 'function') return this.options.clone.call(this, event, element, this.list); + var clone = element.clone(true).setStyles({ + margin: 0, + position: 'absolute', + visibility: 'hidden', + width: element.getStyle('width') + }).addEvent('mousedown', function(event){ + element.fireEvent('mousedown', event); + }); + //prevent the duplicated radio inputs from unchecking the real one + if (clone.get('html').test('radio')){ + clone.getElements('input[type=radio]').each(function(input, i){ + input.set('name', 'clone_' + i); + if (input.get('checked')) element.getElements('input[type=radio]')[i].set('checked', true); + }); + } + + return clone.inject(this.list).setPosition(this.getDroppableCoordinates(this.element)); + }, + + getDroppables: function(){ + var droppables = this.list.getChildren().erase(this.clone).erase(this.element); + if (!this.options.constrain) droppables.append(this.lists).erase(this.list); + return droppables; + }, + + insert: function(dragging, element){ + var where = 'inside'; + if (this.lists.contains(element)){ + this.list = element; + this.drag.droppables = this.getDroppables(); + } else { + where = this.element.getAllPrevious().contains(element) ? 'before' : 'after'; + } + this.element.inject(element, where); + this.fireEvent('sort', [this.element, this.clone]); + }, + + start: function(event, element){ + if ( + !this.idle || + event.rightClick || + (!this.options.handle && this.options.unDraggableTags.contains(event.target.get('tag'))) + ) return; + + this.idle = false; + this.element = element; + this.opacity = element.getStyle('opacity'); + this.list = element.getParent(); + this.clone = this.getClone(event, element); + + this.drag = new Drag.Move(this.clone, Object.merge({ + + droppables: this.getDroppables() + }, this.options.dragOptions)).addEvents({ + onSnap: function(){ + event.stop(); + this.clone.setStyle('visibility', 'visible'); + this.element.setStyle('opacity', this.options.opacity || 0); + this.fireEvent('start', [this.element, this.clone]); + }.bind(this), + onEnter: this.insert.bind(this), + onCancel: this.end.bind(this), + onComplete: this.end.bind(this) + }); + + this.clone.inject(this.element, 'before'); + this.drag.start(event); + }, + + end: function(){ + this.drag.detach(); + this.element.setStyle('opacity', this.opacity); + var self = this; + if (this.effect){ + var dim = this.element.getStyles('width', 'height'), + clone = this.clone, + pos = clone.computePosition(this.getDroppableCoordinates(clone)); + + var destroy = function(){ + this.removeEvent('cancel', destroy); + clone.destroy(); + self.reset(); + }; + + this.effect.element = clone; + this.effect.start({ + top: pos.top, + left: pos.left, + width: dim.width, + height: dim.height, + opacity: 0.25 + }).addEvent('cancel', destroy).chain(destroy); + } else { + this.clone.destroy(); + self.reset(); + } + + }, + + reset: function(){ + this.idle = true; + this.fireEvent('complete', this.element); + }, + + serialize: function(){ + var params = Array.link(arguments, { + modifier: Type.isFunction, + index: function(obj){ + return obj != null; + } + }); + var serial = this.lists.map(function(list){ + return list.getChildren().map(params.modifier || function(element){ + return element.get('id'); + }, this); + }, this); + + var index = params.index; + if (this.lists.length == 1) index = 0; + return (index || index === 0) && index >= 0 && index < this.lists.length ? serial[index] : serial; + } + +}); + + +/* +--- + +script: Request.Periodical.js + +name: Request.Periodical + +description: Requests the same URL to pull data from a server but increases the intervals if no data is returned to reduce the load + +license: MIT-style license + +authors: + - Christoph Pojer + +requires: + - Core/Request + - MooTools.More + +provides: [Request.Periodical] + +... +*/ + +Request.implement({ + + options: { + initialDelay: 5000, + delay: 5000, + limit: 60000 + }, + + startTimer: function(data){ + var fn = function(){ + if (!this.running) this.send({data: data}); + }; + this.lastDelay = this.options.initialDelay; + this.timer = fn.delay(this.lastDelay, this); + this.completeCheck = function(response){ + clearTimeout(this.timer); + this.lastDelay = (response) ? this.options.delay : (this.lastDelay + this.options.delay).min(this.options.limit); + this.timer = fn.delay(this.lastDelay, this); + }; + return this.addEvent('complete', this.completeCheck); + }, + + stopTimer: function(){ + clearTimeout(this.timer); + return this.removeEvent('complete', this.completeCheck); + } + +}); + + +/* +--- + +script: Color.js + +name: Color + +description: Class for creating and manipulating colors in JavaScript. Supports HSB -> RGB Conversions and vice versa. + +license: MIT-style license + +authors: + - Valerio Proietti + +requires: + - Core/Array + - Core/String + - Core/Number + - Core/Hash + - Core/Function + - MooTools.More + +provides: [Color] + +... +*/ + +(function(){ + +var Color = this.Color = new Type('Color', function(color, type){ + if (arguments.length >= 3){ + type = 'rgb'; color = Array.slice(arguments, 0, 3); + } else if (typeof color == 'string'){ + if (color.match(/rgb/)) color = color.rgbToHex().hexToRgb(true); + else if (color.match(/hsb/)) color = color.hsbToRgb(); + else color = color.hexToRgb(true); + } + type = type || 'rgb'; + switch (type){ + case 'hsb': + var old = color; + color = color.hsbToRgb(); + color.hsb = old; + break; + case 'hex': color = color.hexToRgb(true); break; + } + color.rgb = color.slice(0, 3); + color.hsb = color.hsb || color.rgbToHsb(); + color.hex = color.rgbToHex(); + return Object.append(color, this); +}); + +Color.implement({ + + mix: function(){ + var colors = Array.slice(arguments); + var alpha = (typeOf(colors.getLast()) == 'number') ? colors.pop() : 50; + var rgb = this.slice(); + colors.each(function(color){ + color = new Color(color); + for (var i = 0; i < 3; i++) rgb[i] = Math.round((rgb[i] / 100 * (100 - alpha)) + (color[i] / 100 * alpha)); + }); + return new Color(rgb, 'rgb'); + }, + + invert: function(){ + return new Color(this.map(function(value){ + return 255 - value; + })); + }, + + setHue: function(value){ + return new Color([value, this.hsb[1], this.hsb[2]], 'hsb'); + }, + + setSaturation: function(percent){ + return new Color([this.hsb[0], percent, this.hsb[2]], 'hsb'); + }, + + setBrightness: function(percent){ + return new Color([this.hsb[0], this.hsb[1], percent], 'hsb'); + } + +}); + +this.$RGB = function(r, g, b){ + return new Color([r, g, b], 'rgb'); +}; + +this.$HSB = function(h, s, b){ + return new Color([h, s, b], 'hsb'); +}; + +this.$HEX = function(hex){ + return new Color(hex, 'hex'); +}; + +Array.implement({ + + rgbToHsb: function(){ + var red = this[0], + green = this[1], + blue = this[2], + hue = 0; + var max = Math.max(red, green, blue), + min = Math.min(red, green, blue); + var delta = max - min; + var brightness = max / 255, + saturation = (max != 0) ? delta / max : 0; + if (saturation != 0){ + var rr = (max - red) / delta; + var gr = (max - green) / delta; + var br = (max - blue) / delta; + if (red == max) hue = br - gr; + else if (green == max) hue = 2 + rr - br; + else hue = 4 + gr - rr; + hue /= 6; + if (hue < 0) hue++; + } + return [Math.round(hue * 360), Math.round(saturation * 100), Math.round(brightness * 100)]; + }, + + hsbToRgb: function(){ + var br = Math.round(this[2] / 100 * 255); + if (this[1] == 0){ + return [br, br, br]; + } else { + var hue = this[0] % 360; + var f = hue % 60; + var p = Math.round((this[2] * (100 - this[1])) / 10000 * 255); + var q = Math.round((this[2] * (6000 - this[1] * f)) / 600000 * 255); + var t = Math.round((this[2] * (6000 - this[1] * (60 - f))) / 600000 * 255); + switch (Math.floor(hue / 60)){ + case 0: return [br, t, p]; + case 1: return [q, br, p]; + case 2: return [p, br, t]; + case 3: return [p, q, br]; + case 4: return [t, p, br]; + case 5: return [br, p, q]; + } + } + return false; + } + +}); + +String.implement({ + + rgbToHsb: function(){ + var rgb = this.match(/\d{1,3}/g); + return (rgb) ? rgb.rgbToHsb() : null; + }, + + hsbToRgb: function(){ + var hsb = this.match(/\d{1,3}/g); + return (hsb) ? hsb.hsbToRgb() : null; + } + +}); + +})(); + + |