diff --git a/xCAT-web/js/effects.js b/xCAT-web/js/effects.js index 70d07526c..233aba86f 100644 --- a/xCAT-web/js/effects.js +++ b/xCAT-web/js/effects.js @@ -1,51 +1,51 @@ -// script.aculo.us effects.js v1.7.1_beta3, Fri May 25 17:19:41 +0200 2007 +// script.aculo.us effects.js v1.7.1_beta1, Mon Mar 12 14:40:50 +0100 2007 // Copyright (c) 2005-2007 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us) // Contributors: // Justin Palmer (http://encytemedia.com/) // Mark Pilgrim (http://diveintomark.org/) // Martin Bialasinki -// +// // script.aculo.us is freely distributable under the terms of an MIT-style license. -// For details, see the script.aculo.us web site: http://script.aculo.us/ +// For details, see the script.aculo.us web site: http://script.aculo.us/ -// converts rgb() and #xxx to #xxxxxx format, -// returns self (or first argument) if not convertable -String.prototype.parseColor = function() { +// converts rgb() and #xxx to #xxxxxx format, +// returns self (or first argument) if not convertable +String.prototype.parseColor = function() { var 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)); + 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.collectTextNodes = function(element) { +Element.collectTextNodes = function(element) { return $A($(element).childNodes).collect( function(node) { - return (node.nodeType==3 ? node.nodeValue : + return (node.nodeType==3 ? node.nodeValue : (node.hasChildNodes() ? Element.collectTextNodes(node) : '')); }).flatten().join(''); } -Element.collectTextNodesIgnoreClass = function(element, className) { +Element.collectTextNodesIgnoreClass = function(element, className) { return $A($(element).childNodes).collect( function(node) { - return (node.nodeType==3 ? node.nodeValue : - ((node.hasChildNodes() && !Element.hasClassName(node,className)) ? + return (node.nodeType==3 ? node.nodeValue : + ((node.hasChildNodes() && !Element.hasClassName(node,className)) ? Element.collectTextNodesIgnoreClass(node, className) : '')); }).flatten().join(''); } Element.setContentZoom = function(element, percent) { - element = $(element); - element.setStyle({fontSize: (percent/100) + 'em'}); - if(Prototype.Browser.WebKit) window.scrollBy(0,0); + element = $(element); + element.setStyle({fontSize: (percent/100) + 'em'}); + if(Prototype.BrowserFeatures.WebKit) window.scrollBy(0,0); return element; } @@ -79,17 +79,17 @@ var Effect = { tagifyText: function(element) { if(typeof Builder == 'undefined') throw("Effect.tagifyText requires including script.aculo.us' builder.js library"); - + var tagifyStyle = 'position:relative'; if(Prototype.Browser.IE) 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), + character == ' ' ? String.fromCharCode(160) : character), child); }); Element.remove(child); @@ -98,13 +98,13 @@ var Effect = { }, multiple: function(element, effect) { var elements; - if(((typeof element == 'object') || - (typeof element == 'function')) && + 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 @@ -126,7 +126,7 @@ var Effect = { var options = Object.extend({ queue: { position:'end', scope:(element.id || 'global'), limit: 1 } }, arguments[2] || {}); - Effect[element.visible() ? + Effect[element.visible() ? Effect.PAIRS[effect][1] : Effect.PAIRS[effect][0]](element, options); } }; @@ -150,11 +150,11 @@ Effect.Transitions = { wobble: function(pos) { return (-Math.cos(pos*Math.PI*(9*pos))/2) + 0.5; }, - pulse: function(pos, pulses) { - pulses = pulses || 5; + pulse: function(pos, pulses) { + pulses = pulses || 5; return ( - Math.round((pos % (1/pulses)) * pulses) == 0 ? - ((pos * pulses * 2) - Math.floor(pos * pulses * 2)) : + Math.round((pos % (1/pulses)) * pulses) == 0 ? + ((pos * pulses * 2) - Math.floor(pos * pulses * 2)) : 1 - ((pos * pulses * 2) - Math.floor(pos * pulses * 2)) ); }, @@ -172,20 +172,20 @@ Effect.ScopedQueue = Class.create(); Object.extend(Object.extend(Effect.ScopedQueue.prototype, Enumerable), { initialize: function() { this.effects = []; - this.interval = null; + this.interval = null; }, _each: function(iterator) { this.effects._each(iterator); }, add: function(effect) { var timestamp = new Date().getTime(); - - var position = (typeof effect.options.queue == 'string') ? + + var position = (typeof effect.options.queue == 'string') ? effect.options.queue : effect.options.queue.position; - + switch(position) { case 'front': - // move unstarted effects after this effect + // 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; @@ -199,13 +199,13 @@ Object.extend(Object.extend(Effect.ScopedQueue.prototype, Enumerable), { timestamp = this.effects.pluck('finishOn').max() || timestamp; break; } - + effect.startOn += timestamp; effect.finishOn += timestamp; if(!effect.options.queue.limit || (this.effects.length < effect.options.queue.limit)) this.effects.push(effect); - + if(!this.interval) this.interval = setInterval(this.loop.bind(this), 15); }, @@ -218,7 +218,7 @@ Object.extend(Object.extend(Effect.ScopedQueue.prototype, Enumerable), { }, loop: function() { var timePos = new Date().getTime(); - for(var i=0, len=this.effects.length;i Always set top and left for position relative elements in your stylesheets - // (to 0 if you do not need them) + // ==> Always set top and left for position relative elements in your stylesheets + // (to 0 if you do not need them) this.element.makePositioned(); this.originalLeft = parseFloat(this.element.getStyle('left') || '0'); this.originalTop = parseFloat(this.element.getStyle('top') || '0'); @@ -407,7 +407,7 @@ Object.extend(Object.extend(Effect.Move.prototype, Effect.Base.prototype), { // for backwards compatibility Effect.MoveBy = function(element, toTop, toLeft) { - return new Effect.Move(element, + return new Effect.Move(element, Object.extend({ x: toLeft, y: toTop }, arguments[3] || {})); }; @@ -430,15 +430,15 @@ Object.extend(Object.extend(Effect.Scale.prototype, Effect.Base.prototype), { setup: function() { this.restoreAfterFinish = this.options.restoreAfterFinish || false; this.elementPositioning = this.element.getStyle('position'); - + this.originalStyle = {}; ['top','left','width','height','fontSize'].each( function(k) { this.originalStyle[k] = this.element.style[k]; }.bind(this)); - + this.originalTop = this.element.offsetTop; this.originalLeft = this.element.offsetLeft; - + var fontSize = this.element.getStyle('font-size') || '100%'; ['em','px','%','pt'].each( function(fontSizeType) { if(fontSize.indexOf(fontSizeType)>0) { @@ -446,9 +446,9 @@ Object.extend(Object.extend(Effect.Scale.prototype, Effect.Base.prototype), { this.fontSizeType = fontSizeType; } }.bind(this)); - + this.factor = (this.options.scaleTo - this.options.scaleFrom)/100; - + this.dims = null; if(this.options.scaleMode=='box') this.dims = [this.element.offsetHeight, this.element.offsetWidth]; @@ -532,17 +532,17 @@ Object.extend(Object.extend(Effect.ScrollTo.prototype, Effect.Base.prototype), { Position.prepare(); var offsets = Position.cumulativeOffset(this.element); if(this.options.offset) offsets[1] += this.options.offset; - var max = window.innerHeight ? + var max = window.innerHeight ? window.height - window.innerHeight : - document.body.scrollHeight - - (document.documentElement.clientHeight ? + 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, + window.scrollTo(Position.deltaX, this.scrollStart + (position*this.delta)); } }); @@ -555,9 +555,9 @@ Effect.Fade = function(element) { var options = Object.extend({ from: element.getOpacity() || 1.0, to: 0.0, - afterFinishInternal: function(effect) { + afterFinishInternal: function(effect) { if(effect.options.to!=0) return; - effect.element.hide().setStyle({opacity: oldOpacity}); + effect.element.hide().setStyle({opacity: oldOpacity}); }}, arguments[1] || {}); return new Effect.Opacity(element,options); } @@ -572,15 +572,15 @@ Effect.Appear = function(element) { effect.element.forceRerendering(); }, beforeSetup: function(effect) { - effect.element.setOpacity(effect.options.from).show(); + effect.element.setOpacity(effect.options.from).show(); }}, arguments[1] || {}); return new Effect.Opacity(element,options); } Effect.Puff = function(element) { element = $(element); - var oldStyle = { - opacity: element.getInlineOpacity(), + var oldStyle = { + opacity: element.getInlineOpacity(), position: element.getStyle('position'), top: element.style.top, left: element.style.left, @@ -588,10 +588,10 @@ Effect.Puff = function(element) { height: element.style.height }; 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, + [ 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) { Position.absolutize(effect.effects[0].element) }, @@ -605,12 +605,12 @@ Effect.BlindUp = function(element) { element = $(element); element.makeClipping(); return new Effect.Scale(element, 0, - Object.extend({ scaleContent: false, - scaleX: false, + Object.extend({ scaleContent: false, + scaleX: false, restoreAfterFinish: true, afterFinishInternal: function(effect) { effect.element.hide().undoClipping(); - } + } }, arguments[1] || {}) ); } @@ -618,15 +618,15 @@ Effect.BlindUp = function(element) { Effect.BlindDown = function(element) { element = $(element); var elementDimensions = element.getDimensions(); - return new Effect.Scale(element, 100, Object.extend({ - scaleContent: false, + 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) { - effect.element.makeClipping().setStyle({height: '0px'}).show(); - }, + effect.element.makeClipping().setStyle({height: '0px'}).show(); + }, afterFinishInternal: function(effect) { effect.element.undoClipping(); } @@ -641,10 +641,10 @@ Effect.SwitchOff = function(element) { from: 0, transition: Effect.Transitions.flicker, afterFinishInternal: function(effect) { - new Effect.Scale(effect.element, 1, { + new Effect.Scale(effect.element, 1, { duration: 0.3, scaleFromCenter: true, scaleX: false, scaleContent: false, restoreAfterFinish: true, - beforeSetup: function(effect) { + beforeSetup: function(effect) { effect.element.makePositioned().makeClipping(); }, afterFinishInternal: function(effect) { @@ -662,16 +662,16 @@ Effect.DropOut = function(element) { left: element.getStyle('left'), opacity: element.getInlineOpacity() }; return new Effect.Parallel( - [ new Effect.Move(element, {x: 0, y: 100, sync: true }), + [ new Effect.Move(element, {x: 0, y: 100, sync: true }), new Effect.Opacity(element, { sync: true, to: 0.0 }) ], Object.extend( { duration: 0.5, beforeSetup: function(effect) { - effect.effects[0].element.makePositioned(); + effect.effects[0].element.makePositioned(); }, afterFinishInternal: function(effect) { effect.effects[0].element.hide().undoPositioned().setStyle(oldStyle); - } + } }, arguments[1] || {})); } @@ -680,7 +680,7 @@ Effect.Shake = function(element) { var oldStyle = { top: element.getStyle('top'), left: element.getStyle('left') }; - return new Effect.Move(element, + return new Effect.Move(element, { x: 20, y: 0, duration: 0.05, afterFinishInternal: function(effect) { new Effect.Move(effect.element, { x: -40, y: 0, duration: 0.1, afterFinishInternal: function(effect) { @@ -701,9 +701,9 @@ Effect.SlideDown = function(element) { // SlideDown need to have the content of the element wrapped in a container element with fixed height! var oldInnerBottom = element.down().getStyle('bottom'); var elementDimensions = element.getDimensions(); - return new Effect.Scale(element, 100, Object.extend({ - scaleContent: false, - scaleX: false, + return new Effect.Scale(element, 100, Object.extend({ + scaleContent: false, + scaleX: false, scaleFrom: window.opera ? 0 : 1, scaleMode: {originalHeight: elementDimensions.height, originalWidth: elementDimensions.width}, restoreAfterFinish: true, @@ -711,11 +711,11 @@ Effect.SlideDown = function(element) { effect.element.makePositioned(); effect.element.down().makePositioned(); if(window.opera) effect.element.setStyle({top: ''}); - effect.element.makeClipping().setStyle({height: '0px'}).show(); + effect.element.makeClipping().setStyle({height: '0px'}).show(); }, afterUpdateInternal: function(effect) { effect.element.down().setStyle({bottom: - (effect.dims[0] - effect.element.clientHeight) + 'px' }); + (effect.dims[0] - effect.element.clientHeight) + 'px' }); }, afterFinishInternal: function(effect) { effect.element.undoClipping().undoPositioned(); @@ -728,8 +728,8 @@ Effect.SlideUp = function(element) { element = $(element).cleanWhitespace(); var oldInnerBottom = element.down().getStyle('bottom'); return new Effect.Scale(element, window.opera ? 0 : 1, - Object.extend({ scaleContent: false, - scaleX: false, + Object.extend({ scaleContent: false, + scaleX: false, scaleMode: 'box', scaleFrom: 100, restoreAfterFinish: true, @@ -738,7 +738,7 @@ Effect.SlideUp = function(element) { effect.element.down().makePositioned(); if(window.opera) effect.element.setStyle({top: ''}); effect.element.makeClipping().show(); - }, + }, afterUpdateInternal: function(effect) { effect.element.down().setStyle({bottom: (effect.dims[0] - effect.element.clientHeight) + 'px' }); @@ -751,15 +751,15 @@ Effect.SlideUp = function(element) { ); } -// Bug in opera makes the TD containing this element expand for a instance after finish +// Bug in opera makes the TD containing this element expand for a instance after finish Effect.Squish = function(element) { - return new Effect.Scale(element, window.opera ? 1 : 0, { + return new Effect.Scale(element, window.opera ? 1 : 0, { restoreAfterFinish: true, beforeSetup: function(effect) { - effect.element.makeClipping(); - }, + effect.element.makeClipping(); + }, afterFinishInternal: function(effect) { - effect.element.hide().undoClipping(); + effect.element.hide().undoClipping(); } }); } @@ -779,13 +779,13 @@ Effect.Grow = function(element) { width: element.style.width, opacity: element.getInlineOpacity() }; - var dims = element.getDimensions(); + var dims = element.getDimensions(); var initialMoveX, initialMoveY; var moveX, moveY; - + switch (options.direction) { case 'top-left': - initialMoveX = initialMoveY = moveX = moveY = 0; + initialMoveX = initialMoveY = moveX = moveY = 0; break; case 'top-right': initialMoveX = dims.width; @@ -810,11 +810,11 @@ Effect.Grow = function(element) { moveY = -dims.height / 2; break; } - + return new Effect.Move(element, { x: initialMoveX, y: initialMoveY, - duration: 0.01, + duration: 0.01, beforeSetup: function(effect) { effect.element.hide().makeClipping().makePositioned(); }, @@ -823,14 +823,14 @@ Effect.Grow = function(element) { [ new Effect.Opacity(effect.element, { sync: true, to: 1.0, from: 0.0, transition: options.opacityTransition }), new Effect.Move(effect.element, { x: moveX, y: moveY, sync: true, transition: options.moveTransition }), new Effect.Scale(effect.element, 100, { - scaleMode: { originalHeight: dims.height, originalWidth: dims.width }, + scaleMode: { originalHeight: dims.height, originalWidth: dims.width }, sync: true, scaleFrom: window.opera ? 1 : 0, transition: options.scaleTransition, restoreAfterFinish: true}) ], Object.extend({ beforeSetup: function(effect) { - effect.effects[0].element.setStyle({height: '0px'}).show(); + effect.effects[0].element.setStyle({height: '0px'}).show(); }, afterFinishInternal: function(effect) { - effect.effects[0].element.undoClipping().undoPositioned().setStyle(oldStyle); + effect.effects[0].element.undoClipping().undoPositioned().setStyle(oldStyle); } }, options) ) @@ -855,7 +855,7 @@ Effect.Shrink = function(element) { var dims = element.getDimensions(); var moveX, moveY; - + switch (options.direction) { case 'top-left': moveX = moveY = 0; @@ -872,19 +872,19 @@ Effect.Shrink = function(element) { moveX = dims.width; moveY = dims.height; break; - case 'center': + case 'center': moveX = dims.width / 2; moveY = dims.height / 2; break; } - + return new Effect.Parallel( [ new Effect.Opacity(element, { sync: true, to: 0.0, from: 1.0, transition: options.opacityTransition }), new Effect.Scale(element, window.opera ? 1 : 0, { sync: true, transition: options.scaleTransition, restoreAfterFinish: true}), new Effect.Move(element, { x: moveX, y: moveY, sync: true, transition: options.moveTransition }) - ], Object.extend({ + ], Object.extend({ beforeStartInternal: function(effect) { - effect.effects[0].element.makePositioned().makeClipping(); + effect.effects[0].element.makePositioned().makeClipping(); }, afterFinishInternal: function(effect) { effect.effects[0].element.hide().undoClipping().undoPositioned().setStyle(oldStyle); } @@ -899,7 +899,7 @@ Effect.Pulsate = function(element) { var transition = options.transition || Effect.Transitions.sinoidal; var reverser = function(pos){ return transition(1-Effect.Transitions.pulse(pos, options.pulses)) }; reverser.bind(transition); - return new Effect.Opacity(element, + return new Effect.Opacity(element, Object.extend(Object.extend({ duration: 2.0, from: 0, afterFinishInternal: function(effect) { effect.element.setStyle({opacity: oldOpacity}); } }, options), {transition: reverser})); @@ -913,12 +913,12 @@ Effect.Fold = function(element) { width: element.style.width, height: element.style.height }; element.makeClipping(); - return new Effect.Scale(element, 5, Object.extend({ + return new Effect.Scale(element, 5, Object.extend({ scaleContent: false, scaleX: false, afterFinishInternal: function(effect) { - new Effect.Scale(element, 1, { - scaleContent: false, + new Effect.Scale(element, 1, { + scaleContent: false, scaleY: false, afterFinishInternal: function(effect) { effect.element.hide().undoClipping().setStyle(oldStyle); @@ -965,7 +965,7 @@ Object.extend(Object.extend(Effect.Morph.prototype, Effect.Base.prototype), { if(!color || ['rgba(0, 0, 0, 0)','transparent'].include(color)) color = '#ffffff'; color = color.parseColor(); return $R(0,2).map(function(i){ - return parseInt( color.slice(i*2+1,i*2+3), 16 ) + return parseInt( color.slice(i*2+1,i*2+3), 16 ) }); } this.transforms = this.style.map(function(pair){ @@ -985,9 +985,9 @@ Object.extend(Object.extend(Effect.Morph.prototype, Effect.Base.prototype), { } var originalValue = this.element.getStyle(property); - return { - style: property.camelize(), - originalValue: unit=='color' ? parseColor(originalValue) : parseFloat(originalValue || 0), + return { + style: property.camelize(), + originalValue: unit=='color' ? parseColor(originalValue) : parseFloat(originalValue || 0), targetValue: unit=='color' ? parseColor(value) : value, unit: unit }; @@ -1004,7 +1004,7 @@ Object.extend(Object.extend(Effect.Morph.prototype, Effect.Base.prototype), { update: function(position) { var style = {}, transform, i = this.transforms.length; while(i--) - style[(transform = this.transforms[i]).style] = + style[(transform = this.transforms[i]).style] = transform.unit=='color' ? '#'+ (Math.round(transform.originalValue[0]+ (transform.targetValue[0]-transform.originalValue[0])*position)).toColorPart() + @@ -1048,7 +1048,7 @@ Object.extend(Effect.Transform.prototype, { }); Element.CSS_PROPERTIES = $w( - 'backgroundColor backgroundPosition borderBottomColor borderBottomStyle ' + + 'backgroundColor backgroundPosition borderBottomColor borderBottomStyle ' + 'borderBottomWidth borderLeftColor borderLeftStyle borderLeftWidth ' + 'borderRightColor borderRightStyle borderRightWidth borderSpacing ' + 'borderTopColor borderTopStyle borderTopWidth bottom clip color ' + @@ -1057,16 +1057,16 @@ Element.CSS_PROPERTIES = $w( 'maxWidth minHeight minWidth opacity outlineColor outlineOffset ' + 'outlineWidth paddingBottom paddingLeft paddingRight paddingTop ' + 'right textIndent top width wordSpacing zIndex'); - + Element.CSS_LENGTH = /^(([\+\-]?[0-9\.]+)(em|ex|px|in|cm|mm|pt|pc|\%))|0$/; String.prototype.parseStyle = function(){ var element = document.createElement('div'); element.innerHTML = '
'; var style = element.childNodes[0].style, styleRules = $H(); - + Element.CSS_PROPERTIES.each(function(property){ - if(style[property]) styleRules[property] = style[property]; + if(style[property]) styleRules[property] = style[property]; }); if(Prototype.Browser.IE && this.indexOf('opacity') > -1) { styleRules.opacity = this.match(/opacity:\s*((?:0|1)?(?:\.\d*)?)/)[1]; @@ -1080,7 +1080,7 @@ Element.morph = function(element, style) { }; ['getInlineOpacity','forceRerendering','setContentZoom', - 'collectTextNodes','collectTextNodesIgnoreClass','morph'].each( + 'collectTextNodes','collectTextNodesIgnoreClass','morph'].each( function(f) { Element.Methods[f] = Element[f]; } ); diff --git a/xCAT-web/js/prototype_rc3.js b/xCAT-web/js/prototype_rc3.js new file mode 100644 index 000000000..826de9fa3 --- /dev/null +++ b/xCAT-web/js/prototype_rc3.js @@ -0,0 +1,3269 @@ +/* Prototype JavaScript framework, version 1.5.1_rc3 + * (c) 2005-2007 Sam Stephenson + * + * Prototype is freely distributable under the terms of an MIT-style license. + * For details, see the Prototype web site: http://www.prototypejs.org/ + * +/*--------------------------------------------------------------------------*/ + +var Prototype = { + Version: '1.5.1_rc3', + + Browser: { + IE: !!(window.attachEvent && !window.opera), + Opera: !!window.opera, + WebKit: navigator.userAgent.indexOf('AppleWebKit/') > -1, + Gecko: navigator.userAgent.indexOf('Gecko') > -1 && navigator.userAgent.indexOf('KHTML') == -1 + }, + + BrowserFeatures: { + XPath: !!document.evaluate, + ElementExtensions: !!window.HTMLElement, + SpecificElementExtensions: + (document.createElement('div').__proto__ !== + document.createElement('form').__proto__) + }, + + ScriptFragment: ']*>([\u0001-\uFFFF]*?)', + JSONFilter: /^\/\*-secure-\s*(.*)\s*\*\/\s*$/, + + emptyFunction: function() { }, + K: function(x) { return x } +} + +var Class = { + create: function() { + return function() { + this.initialize.apply(this, arguments); + } + } +} + +var Abstract = new Object(); + +Object.extend = function(destination, source) { + for (var property in source) { + destination[property] = source[property]; + } + return destination; +} + +Object.extend(Object, { + inspect: function(object) { + try { + if (object === undefined) return 'undefined'; + if (object === null) return 'null'; + return object.inspect ? object.inspect() : object.toString(); + } catch (e) { + if (e instanceof RangeError) return '...'; + throw e; + } + }, + + toJSON: function(object) { + var type = typeof object; + switch(type) { + case 'undefined': + case 'function': + case 'unknown': return; + case 'boolean': return object.toString(); + } + if (object === null) return 'null'; + if (object.toJSON) return object.toJSON(); + if (object.ownerDocument === document) return; + var results = []; + for (var property in object) { + var value = Object.toJSON(object[property]); + if (value !== undefined) + results.push(property.toJSON() + ': ' + value); + } + return '{' + results.join(', ') + '}'; + }, + + keys: function(object) { + var keys = []; + for (var property in object) + keys.push(property); + return keys; + }, + + values: function(object) { + var values = []; + for (var property in object) + values.push(object[property]); + return values; + }, + + clone: function(object) { + return Object.extend({}, object); + } +}); + +Function.prototype.bind = function() { + var __method = this, args = $A(arguments), object = args.shift(); + return function() { + return __method.apply(object, args.concat($A(arguments))); + } +} + +Function.prototype.bindAsEventListener = function(object) { + var __method = this, args = $A(arguments), object = args.shift(); + return function(event) { + return __method.apply(object, [( event || window.event)].concat(args).concat($A(arguments))); + } +} + +Object.extend(Number.prototype, { + toColorPart: function() { + return this.toPaddedString(2, 16); + }, + + succ: function() { + return this + 1; + }, + + times: function(iterator) { + $R(0, this, true).each(iterator); + return this; + }, + + toPaddedString: function(length, radix) { + var string = this.toString(radix || 10); + return '0'.times(length - string.length) + string; + }, + + toJSON: function() { + return isFinite(this) ? this.toString() : 'null'; + } +}); + +Date.prototype.toJSON = function() { + return '"' + this.getFullYear() + '-' + + (this.getMonth() + 1).toPaddedString(2) + '-' + + this.getDate().toPaddedString(2) + 'T' + + this.getHours().toPaddedString(2) + ':' + + this.getMinutes().toPaddedString(2) + ':' + + this.getSeconds().toPaddedString(2) + '"'; +}; + +var Try = { + these: function() { + var returnValue; + + for (var i = 0, length = arguments.length; i < length; i++) { + var lambda = arguments[i]; + try { + returnValue = lambda(); + break; + } catch (e) {} + } + + return returnValue; + } +} + +/*--------------------------------------------------------------------------*/ + +var PeriodicalExecuter = Class.create(); +PeriodicalExecuter.prototype = { + initialize: function(callback, frequency) { + this.callback = callback; + this.frequency = frequency; + this.currentlyExecuting = false; + + this.registerCallback(); + }, + + registerCallback: function() { + this.timer = setInterval(this.onTimerEvent.bind(this), this.frequency * 1000); + }, + + stop: function() { + if (!this.timer) return; + clearInterval(this.timer); + this.timer = null; + }, + + onTimerEvent: function() { + if (!this.currentlyExecuting) { + try { + this.currentlyExecuting = true; + this.callback(this); + } finally { + this.currentlyExecuting = false; + } + } + } +} +Object.extend(String, { + interpret: function(value) { + return value == null ? '' : String(value); + }, + specialChar: { + '\b': '\\b', + '\t': '\\t', + '\n': '\\n', + '\f': '\\f', + '\r': '\\r', + '\\': '\\\\' + } +}); + +Object.extend(String.prototype, { + gsub: function(pattern, replacement) { + var result = '', source = this, match; + replacement = arguments.callee.prepareReplacement(replacement); + + while (source.length > 0) { + if (match = source.match(pattern)) { + result += source.slice(0, match.index); + result += String.interpret(replacement(match)); + source = source.slice(match.index + match[0].length); + } else { + result += source, source = ''; + } + } + return result; + }, + + sub: function(pattern, replacement, count) { + replacement = this.gsub.prepareReplacement(replacement); + count = count === undefined ? 1 : count; + + return this.gsub(pattern, function(match) { + if (--count < 0) return match[0]; + return replacement(match); + }); + }, + + scan: function(pattern, iterator) { + this.gsub(pattern, iterator); + return this; + }, + + truncate: function(length, truncation) { + length = length || 30; + truncation = truncation === undefined ? '...' : truncation; + return this.length > length ? + this.slice(0, length - truncation.length) + truncation : this; + }, + + strip: function() { + return this.replace(/^\s+/, '').replace(/\s+$/, ''); + }, + + stripTags: function() { + return this.replace(/<\/?[^>]+>/gi, ''); + }, + + stripScripts: function() { + return this.replace(new RegExp(Prototype.ScriptFragment, 'img'), ''); + }, + + extractScripts: function() { + var matchAll = new RegExp(Prototype.ScriptFragment, 'img'); + var matchOne = new RegExp(Prototype.ScriptFragment, 'im'); + return (this.match(matchAll) || []).map(function(scriptTag) { + return (scriptTag.match(matchOne) || ['', ''])[1]; + }); + }, + + evalScripts: function() { + return this.extractScripts().map(function(script) { return eval(script) }); + }, + + escapeHTML: function() { + var self = arguments.callee; + self.text.data = this; + return self.div.innerHTML; + }, + + unescapeHTML: function() { + var div = document.createElement('div'); + div.innerHTML = this.stripTags(); + return div.childNodes[0] ? (div.childNodes.length > 1 ? + $A(div.childNodes).inject('', function(memo, node) { return memo+node.nodeValue }) : + div.childNodes[0].nodeValue) : ''; + }, + + toQueryParams: function(separator) { + var match = this.strip().match(/([^?#]*)(#.*)?$/); + if (!match) return {}; + + return match[1].split(separator || '&').inject({}, function(hash, pair) { + if ((pair = pair.split('='))[0]) { + var key = decodeURIComponent(pair.shift()); + var value = pair.length > 1 ? pair.join('=') : pair[0]; + if (value != undefined) value = decodeURIComponent(value); + + if (key in hash) { + if (hash[key].constructor != Array) hash[key] = [hash[key]]; + hash[key].push(value); + } + else hash[key] = value; + } + return hash; + }); + }, + + toArray: function() { + return this.split(''); + }, + + succ: function() { + return this.slice(0, this.length - 1) + + String.fromCharCode(this.charCodeAt(this.length - 1) + 1); + }, + + times: function(count) { + var result = ''; + for (var i = 0; i < count; i++) result += this; + return result; + }, + + camelize: function() { + var parts = this.split('-'), len = parts.length; + if (len == 1) return parts[0]; + + var camelized = this.charAt(0) == '-' + ? parts[0].charAt(0).toUpperCase() + parts[0].substring(1) + : parts[0]; + + for (var i = 1; i < len; i++) + camelized += parts[i].charAt(0).toUpperCase() + parts[i].substring(1); + + return camelized; + }, + + capitalize: function() { + return this.charAt(0).toUpperCase() + this.substring(1).toLowerCase(); + }, + + underscore: function() { + return this.gsub(/::/, '/').gsub(/([A-Z]+)([A-Z][a-z])/,'#{1}_#{2}').gsub(/([a-z\d])([A-Z])/,'#{1}_#{2}').gsub(/-/,'_').toLowerCase(); + }, + + dasherize: function() { + return this.gsub(/_/,'-'); + }, + + inspect: function(useDoubleQuotes) { + var escapedString = this.gsub(/[\x00-\x1f\\]/, function(match) { + var character = String.specialChar[match[0]]; + return character ? character : '\\u00' + match[0].charCodeAt().toPaddedString(2, 16); + }); + if (useDoubleQuotes) return '"' + escapedString.replace(/"/g, '\\"') + '"'; + return "'" + escapedString.replace(/'/g, '\\\'') + "'"; + }, + + toJSON: function() { + return this.inspect(true); + }, + + unfilterJSON: function(filter) { + return this.sub(filter || Prototype.JSONFilter, '#{1}'); + }, + + evalJSON: function(sanitize) { + var json = this.unfilterJSON(); + try { + if (!sanitize || (/^("(\\.|[^"\\\n\r])*?"|[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t])+?$/.test(json))) + return eval('(' + json + ')'); + } catch (e) { } + throw new SyntaxError('Badly formed JSON string: ' + this.inspect()); + }, + + include: function(pattern) { + return this.indexOf(pattern) > -1; + }, + + startsWith: function(pattern) { + return this.indexOf(pattern) === 0; + }, + + endsWith: function(pattern) { + var d = this.length - pattern.length; + return d >= 0 && this.lastIndexOf(pattern) === d; + }, + + empty: function() { + return this == ''; + }, + + blank: function() { + return /^\s*$/.test(this); + } +}); + +if (Prototype.BrowserFeatures.WebKit || Prototype.BrowserFeatures.IE) Object.extend(String.prototype, { + escapeHTML: function() { + return this.replace(/&/g,'&').replace(//g,'>'); + }, + unescapeHTML: function() { + return this.replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>'); + } +}); + +String.prototype.gsub.prepareReplacement = function(replacement) { + if (typeof replacement == 'function') return replacement; + var template = new Template(replacement); + return function(match) { return template.evaluate(match) }; +} + +String.prototype.parseQuery = String.prototype.toQueryParams; + +Object.extend(String.prototype.escapeHTML, { + div: document.createElement('div'), + text: document.createTextNode('') +}); + +with (String.prototype.escapeHTML) div.appendChild(text); + +var Template = Class.create(); +Template.Pattern = /(^|.|\r|\n)(#\{(.*?)\})/; +Template.prototype = { + initialize: function(template, pattern) { + this.template = template.toString(); + this.pattern = pattern || Template.Pattern; + }, + + evaluate: function(object) { + return this.template.gsub(this.pattern, function(match) { + var before = match[1]; + if (before == '\\') return match[2]; + return before + String.interpret(object[match[3]]); + }); + } +} + +var $break = new Object(); +var $continue = new Object(); + +var Enumerable = { + each: function(iterator) { + var index = 0; + try { + this._each(function(value) { + iterator(value, index++); + }); + } catch (e) { + if (e != $break) throw e; + } + return this; + }, + + eachSlice: function(number, iterator) { + var index = -number, slices = [], array = this.toArray(); + while ((index += number) < array.length) + slices.push(array.slice(index, index+number)); + return slices.map(iterator); + }, + + all: function(iterator) { + var result = true; + this.each(function(value, index) { + result = result && !!(iterator || Prototype.K)(value, index); + if (!result) throw $break; + }); + return result; + }, + + any: function(iterator) { + var result = false; + this.each(function(value, index) { + if (result = !!(iterator || Prototype.K)(value, index)) + throw $break; + }); + return result; + }, + + collect: function(iterator) { + var results = []; + this.each(function(value, index) { + results.push((iterator || Prototype.K)(value, index)); + }); + return results; + }, + + detect: function(iterator) { + var result; + this.each(function(value, index) { + if (iterator(value, index)) { + result = value; + throw $break; + } + }); + return result; + }, + + findAll: function(iterator) { + var results = []; + this.each(function(value, index) { + if (iterator(value, index)) + results.push(value); + }); + return results; + }, + + grep: function(pattern, iterator) { + var results = []; + this.each(function(value, index) { + var stringValue = value.toString(); + if (stringValue.match(pattern)) + results.push((iterator || Prototype.K)(value, index)); + }) + return results; + }, + + include: function(object) { + var found = false; + this.each(function(value) { + if (value == object) { + found = true; + throw $break; + } + }); + return found; + }, + + inGroupsOf: function(number, fillWith) { + fillWith = fillWith === undefined ? null : fillWith; + return this.eachSlice(number, function(slice) { + while(slice.length < number) slice.push(fillWith); + return slice; + }); + }, + + inject: function(memo, iterator) { + this.each(function(value, index) { + memo = iterator(memo, value, index); + }); + return memo; + }, + + invoke: function(method) { + var args = $A(arguments).slice(1); + return this.map(function(value) { + return value[method].apply(value, args); + }); + }, + + max: function(iterator) { + var result; + this.each(function(value, index) { + value = (iterator || Prototype.K)(value, index); + if (result == undefined || value >= result) + result = value; + }); + return result; + }, + + min: function(iterator) { + var result; + this.each(function(value, index) { + value = (iterator || Prototype.K)(value, index); + if (result == undefined || value < result) + result = value; + }); + return result; + }, + + partition: function(iterator) { + var trues = [], falses = []; + this.each(function(value, index) { + ((iterator || Prototype.K)(value, index) ? + trues : falses).push(value); + }); + return [trues, falses]; + }, + + pluck: function(property) { + var results = []; + this.each(function(value, index) { + results.push(value[property]); + }); + return results; + }, + + reject: function(iterator) { + var results = []; + this.each(function(value, index) { + if (!iterator(value, index)) + results.push(value); + }); + return results; + }, + + sortBy: function(iterator) { + return this.map(function(value, index) { + return {value: value, criteria: iterator(value, index)}; + }).sort(function(left, right) { + var a = left.criteria, b = right.criteria; + return a < b ? -1 : a > b ? 1 : 0; + }).pluck('value'); + }, + + toArray: function() { + return this.map(); + }, + + zip: function() { + var iterator = Prototype.K, args = $A(arguments); + if (typeof args.last() == 'function') + iterator = args.pop(); + + var collections = [this].concat(args).map($A); + return this.map(function(value, index) { + return iterator(collections.pluck(index)); + }); + }, + + size: function() { + return this.toArray().length; + }, + + inspect: function() { + return '#'; + } +} + +Object.extend(Enumerable, { + map: Enumerable.collect, + find: Enumerable.detect, + select: Enumerable.findAll, + member: Enumerable.include, + entries: Enumerable.toArray +}); +var $A = Array.from = function(iterable) { + if (!iterable) return []; + if (iterable.toArray) { + return iterable.toArray(); + } else { + var results = []; + for (var i = 0, length = iterable.length; i < length; i++) + results.push(iterable[i]); + return results; + } +} + +if (Prototype.BrowserFeatures.WebKit) { + $A = Array.from = function(iterable) { + if (!iterable) return []; + if (!(typeof iterable == 'function' && iterable == '[object NodeList]') && + iterable.toArray) { + return iterable.toArray(); + } else { + var results = []; + for (var i = 0, length = iterable.length; i < length; i++) + results.push(iterable[i]); + return results; + } + } +} + +Object.extend(Array.prototype, Enumerable); + +if (!Array.prototype._reverse) + Array.prototype._reverse = Array.prototype.reverse; + +Object.extend(Array.prototype, { + _each: function(iterator) { + for (var i = 0, length = this.length; i < length; i++) + iterator(this[i]); + }, + + clear: function() { + this.length = 0; + return this; + }, + + first: function() { + return this[0]; + }, + + last: function() { + return this[this.length - 1]; + }, + + compact: function() { + return this.select(function(value) { + return value != null; + }); + }, + + flatten: function() { + return this.inject([], function(array, value) { + return array.concat(value && value.constructor == Array ? + value.flatten() : [value]); + }); + }, + + without: function() { + var values = $A(arguments); + return this.select(function(value) { + return !values.include(value); + }); + }, + + indexOf: function(object) { + for (var i = 0, length = this.length; i < length; i++) + if (this[i] == object) return i; + return -1; + }, + + reverse: function(inline) { + return (inline !== false ? this : this.toArray())._reverse(); + }, + + reduce: function() { + return this.length > 1 ? this : this[0]; + }, + + uniq: function(sorted) { + return this.inject([], function(array, value, index) { + if (0 == index || (sorted ? array.last() != value : !array.include(value))) + array.push(value); + return array; + }); + }, + + clone: function() { + return [].concat(this); + }, + + size: function() { + return this.length; + }, + + inspect: function() { + return '[' + this.map(Object.inspect).join(', ') + ']'; + }, + + toJSON: function() { + var results = []; + this.each(function(object) { + var value = Object.toJSON(object); + if (value !== undefined) results.push(value); + }); + return '[' + results.join(', ') + ']'; + } +}); + +Array.prototype.toArray = Array.prototype.clone; + +function $w(string) { + string = string.strip(); + return string ? string.split(/\s+/) : []; +} + +if (Prototype.BrowserFeatures.Opera){ + Array.prototype.concat = function() { + var array = []; + for (var i = 0, length = this.length; i < length; i++) array.push(this[i]); + for (var i = 0, length = arguments.length; i < length; i++) { + if (arguments[i].constructor == Array) { + for (var j = 0, arrayLength = arguments[i].length; j < arrayLength; j++) + array.push(arguments[i][j]); + } else { + array.push(arguments[i]); + } + } + return array; + } +} +var Hash = function(object) { + if (object instanceof Hash) this.merge(object); + else Object.extend(this, object || {}); +}; + +Object.extend(Hash, { + toQueryString: function(obj) { + var parts = []; + parts.add = arguments.callee.addPair; + + this.prototype._each.call(obj, function(pair) { + if (!pair.key) return; + var value = pair.value; + + if (value && typeof value == 'object') { + if (value.constructor == Array) value.each(function(value) { + parts.add(pair.key, value); + }); + return; + } + parts.add(pair.key, value); + }); + + return parts.join('&'); + }, + + toJSON: function(object) { + var results = []; + this.prototype._each.call(object, function(pair) { + var value = Object.toJSON(pair.value); + if (value !== undefined) results.push(pair.key.toJSON() + ': ' + value); + }); + return '{' + results.join(', ') + '}'; + } +}); + +Hash.toQueryString.addPair = function(key, value, prefix) { + key = encodeURIComponent(key); + if (value === undefined) this.push(key); + else this.push(key + '=' + (value == null ? '' : encodeURIComponent(value))); +} + +Object.extend(Hash.prototype, Enumerable); +Object.extend(Hash.prototype, { + _each: function(iterator) { + for (var key in this) { + var value = this[key]; + if (value && value == Hash.prototype[key]) continue; + + var pair = [key, value]; + pair.key = key; + pair.value = value; + iterator(pair); + } + }, + + keys: function() { + return this.pluck('key'); + }, + + values: function() { + return this.pluck('value'); + }, + + merge: function(hash) { + return $H(hash).inject(this, function(mergedHash, pair) { + mergedHash[pair.key] = pair.value; + return mergedHash; + }); + }, + + remove: function() { + var result; + for(var i = 0, length = arguments.length; i < length; i++) { + var value = this[arguments[i]]; + if (value !== undefined){ + if (result === undefined) result = value; + else { + if (result.constructor != Array) result = [result]; + result.push(value) + } + } + delete this[arguments[i]]; + } + return result; + }, + + toQueryString: function() { + return Hash.toQueryString(this); + }, + + inspect: function() { + return '#'; + }, + + toJSON: function() { + return Hash.toJSON(this); + } +}); + +function $H(object) { + if (object instanceof Hash) return object; + return new Hash(object); +}; + +// Safari iterates over shadowed properties +if (function() { + var i = 0, Test = function(value) { this.key = value }; + Test.prototype.key = 'foo'; + for (var property in new Test('bar')) i++; + return i > 1; +}()) Hash.prototype._each = function(iterator) { + var cache = []; + for (var key in this) { + var value = this[key]; + if ((value && value == Hash.prototype[key]) || cache.include(key)) continue; + cache.push(key); + var pair = [key, value]; + pair.key = key; + pair.value = value; + iterator(pair); + } +}; +ObjectRange = Class.create(); +Object.extend(ObjectRange.prototype, Enumerable); +Object.extend(ObjectRange.prototype, { + initialize: function(start, end, exclusive) { + this.start = start; + this.end = end; + this.exclusive = exclusive; + }, + + _each: function(iterator) { + var value = this.start; + while (this.include(value)) { + iterator(value); + value = value.succ(); + } + }, + + include: function(value) { + if (value < this.start) + return false; + if (this.exclusive) + return value < this.end; + return value <= this.end; + } +}); + +var $R = function(start, end, exclusive) { + return new ObjectRange(start, end, exclusive); +} + +var Ajax = { + getTransport: function() { + return Try.these( + function() {return new XMLHttpRequest()}, + function() {return new ActiveXObject('Msxml2.XMLHTTP')}, + function() {return new ActiveXObject('Microsoft.XMLHTTP')} + ) || false; + }, + + activeRequestCount: 0 +} + +Ajax.Responders = { + responders: [], + + _each: function(iterator) { + this.responders._each(iterator); + }, + + register: function(responder) { + if (!this.include(responder)) + this.responders.push(responder); + }, + + unregister: function(responder) { + this.responders = this.responders.without(responder); + }, + + dispatch: function(callback, request, transport, json) { + this.each(function(responder) { + if (typeof responder[callback] == 'function') { + try { + responder[callback].apply(responder, [request, transport, json]); + } catch (e) {} + } + }); + } +}; + +Object.extend(Ajax.Responders, Enumerable); + +Ajax.Responders.register({ + onCreate: function() { + Ajax.activeRequestCount++; + }, + onComplete: function() { + Ajax.activeRequestCount--; + } +}); + +Ajax.Base = function() {}; +Ajax.Base.prototype = { + setOptions: function(options) { + this.options = { + method: 'post', + asynchronous: true, + contentType: 'application/x-www-form-urlencoded', + encoding: 'UTF-8', + parameters: '' + } + Object.extend(this.options, options || {}); + + this.options.method = this.options.method.toLowerCase(); + if (typeof this.options.parameters == 'string') + this.options.parameters = this.options.parameters.toQueryParams(); + } +} + +Ajax.Request = Class.create(); +Ajax.Request.Events = + ['Uninitialized', 'Loading', 'Loaded', 'Interactive', 'Complete']; + +Ajax.Request.prototype = Object.extend(new Ajax.Base(), { + _complete: false, + + initialize: function(url, options) { + this.transport = Ajax.getTransport(); + this.setOptions(options); + this.request(url); + }, + + request: function(url) { + this.url = url; + this.method = this.options.method; + var params = Object.clone(this.options.parameters); + + if (!['get', 'post'].include(this.method)) { + // simulate other verbs over post + params['_method'] = this.method; + this.method = 'post'; + } + + this.parameters = params; + + if (params = Hash.toQueryString(params)) { + // when GET, append parameters to URL + if (this.method == 'get') + this.url += (this.url.include('?') ? '&' : '?') + params; + else if (/Konqueror|Safari|KHTML/.test(navigator.userAgent)) + params += '&_='; + } + + try { + if (this.options.onCreate) this.options.onCreate(this.transport); + Ajax.Responders.dispatch('onCreate', this, this.transport); + + this.transport.open(this.method.toUpperCase(), this.url, + this.options.asynchronous); + + if (this.options.asynchronous) + setTimeout(function() { this.respondToReadyState(1) }.bind(this), 10); + + this.transport.onreadystatechange = this.onStateChange.bind(this); + this.setRequestHeaders(); + + this.body = this.method == 'post' ? (this.options.postBody || params) : null; + this.transport.send(this.body); + + /* Force Firefox to handle ready state 4 for synchronous requests */ + if (!this.options.asynchronous && this.transport.overrideMimeType) + this.onStateChange(); + + } + catch (e) { + this.dispatchException(e); + } + }, + + onStateChange: function() { + var readyState = this.transport.readyState; + if (readyState > 1 && !((readyState == 4) && this._complete)) + this.respondToReadyState(this.transport.readyState); + }, + + setRequestHeaders: function() { + var headers = { + 'X-Requested-With': 'XMLHttpRequest', + 'X-Prototype-Version': Prototype.Version, + 'Accept': 'text/javascript, text/html, application/xml, text/xml, */*' + }; + + if (this.method == 'post') { + headers['Content-type'] = this.options.contentType + + (this.options.encoding ? '; charset=' + this.options.encoding : ''); + + /* Force "Connection: close" for older Mozilla browsers to work + * around a bug where XMLHttpRequest sends an incorrect + * Content-length header. See Mozilla Bugzilla #246651. + */ + if (this.transport.overrideMimeType && + (navigator.userAgent.match(/Gecko\/(\d{4})/) || [0,2005])[1] < 2005) + headers['Connection'] = 'close'; + } + + // user-defined headers + if (typeof this.options.requestHeaders == 'object') { + var extras = this.options.requestHeaders; + + if (typeof extras.push == 'function') + for (var i = 0, length = extras.length; i < length; i += 2) + headers[extras[i]] = extras[i+1]; + else + $H(extras).each(function(pair) { headers[pair.key] = pair.value }); + } + + for (var name in headers) + this.transport.setRequestHeader(name, headers[name]); + }, + + success: function() { + return !this.transport.status + || (this.transport.status >= 200 && this.transport.status < 300); + }, + + respondToReadyState: function(readyState) { + var state = Ajax.Request.Events[readyState]; + var transport = this.transport, json = this.evalJSON(); + + if (state == 'Complete') { + try { + this._complete = true; + (this.options['on' + this.transport.status] + || this.options['on' + (this.success() ? 'Success' : 'Failure')] + || Prototype.emptyFunction)(transport, json); + } catch (e) { + this.dispatchException(e); + } + + var contentType = this.getHeader('Content-type'); + if (contentType && contentType.strip(). + match(/^(text|application)\/(x-)?(java|ecma)script(;.*)?$/i)) + this.evalResponse(); + } + + try { + (this.options['on' + state] || Prototype.emptyFunction)(transport, json); + Ajax.Responders.dispatch('on' + state, this, transport, json); + } catch (e) { + this.dispatchException(e); + } + + if (state == 'Complete') { + // avoid memory leak in MSIE: clean up + this.transport.onreadystatechange = Prototype.emptyFunction; + } + }, + + getHeader: function(name) { + try { + return this.transport.getResponseHeader(name); + } catch (e) { return null } + }, + + evalJSON: function() { + try { + var json = this.getHeader('X-JSON'); + return json ? json.evalJSON() : null; + } catch (e) { return null } + }, + + evalResponse: function() { + try { + return eval((this.transport.responseText || '').unfilterJSON()); + } catch (e) { + this.dispatchException(e); + } + }, + + dispatchException: function(exception) { + (this.options.onException || Prototype.emptyFunction)(this, exception); + Ajax.Responders.dispatch('onException', this, exception); + } +}); + +Ajax.Updater = Class.create(); + +Object.extend(Object.extend(Ajax.Updater.prototype, Ajax.Request.prototype), { + initialize: function(container, url, options) { + this.container = { + success: (container.success || container), + failure: (container.failure || (container.success ? null : container)) + } + + this.transport = Ajax.getTransport(); + this.setOptions(options); + + var onComplete = this.options.onComplete || Prototype.emptyFunction; + this.options.onComplete = (function(transport, param) { + this.updateContent(); + onComplete(transport, param); + }).bind(this); + + this.request(url); + }, + + updateContent: function() { + var receiver = this.container[this.success() ? 'success' : 'failure']; + var response = this.transport.responseText; + + if (!this.options.evalScripts) response = response.stripScripts(); + + if (receiver = $(receiver)) { + if (this.options.insertion) + new this.options.insertion(receiver, response); + else + receiver.update(response); + } + + if (this.success()) { + if (this.onComplete) + setTimeout(this.onComplete.bind(this), 10); + } + } +}); + +Ajax.PeriodicalUpdater = Class.create(); +Ajax.PeriodicalUpdater.prototype = Object.extend(new Ajax.Base(), { + initialize: function(container, url, options) { + this.setOptions(options); + this.onComplete = this.options.onComplete; + + this.frequency = (this.options.frequency || 2); + this.decay = (this.options.decay || 1); + + this.updater = {}; + this.container = container; + this.url = url; + + this.start(); + }, + + start: function() { + this.options.onComplete = this.updateComplete.bind(this); + this.onTimerEvent(); + }, + + stop: function() { + this.updater.options.onComplete = undefined; + clearTimeout(this.timer); + (this.onComplete || Prototype.emptyFunction).apply(this, arguments); + }, + + updateComplete: function(request) { + if (this.options.decay) { + this.decay = (request.responseText == this.lastText ? + this.decay * this.options.decay : 1); + + this.lastText = request.responseText; + } + this.timer = setTimeout(this.onTimerEvent.bind(this), + this.decay * this.frequency * 1000); + }, + + onTimerEvent: function() { + this.updater = new Ajax.Updater(this.container, this.url, this.options); + } +}); +function $(element) { + if (arguments.length > 1) { + for (var i = 0, elements = [], length = arguments.length; i < length; i++) + elements.push($(arguments[i])); + return elements; + } + if (typeof element == 'string') + element = document.getElementById(element); + return Element.extend(element); +} + +if (Prototype.BrowserFeatures.XPath) { + document._getElementsByXPath = function(expression, parentElement) { + var results = []; + var query = document.evaluate(expression, $(parentElement) || document, + null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null); + for (var i = 0, length = query.snapshotLength; i < length; i++) + results.push(query.snapshotItem(i)); + return results; + }; + + document.getElementsByClassName = function(className, parentElement) { + var q = ".//*[contains(concat(' ', @class, ' '), ' " + className + " ')]"; + return document._getElementsByXPath(q, parentElement); + } + +} else document.getElementsByClassName = function(className, parentElement) { + var children = ($(parentElement) || document.body).getElementsByTagName('*'); + var elements = [], child; + for (var i = 0, length = children.length; i < length; i++) { + child = children[i]; + if (Element.hasClassName(child, className)) + elements.push(Element.extend(child)); + } + return elements; +}; + +/*--------------------------------------------------------------------------*/ + +if (!window.Element) var Element = {}; + +Element.extend = function(element) { + var F = Prototype.BrowserFeatures; + if (!element || !element.tagName || element.nodeType == 3 || + element._extended || F.SpecificElementExtensions || element == window) + return element; + + var methods = {}, tagName = element.tagName, cache = Element.extend.cache, + T = Element.Methods.ByTag; + + // extend methods for all tags (Safari doesn't need this) + if (!F.ElementExtensions) { + Object.extend(methods, Element.Methods), + Object.extend(methods, Element.Methods.Simulated); + } + + // extend methods for specific tags + if (T[tagName]) Object.extend(methods, T[tagName]); + + for (var property in methods) { + var value = methods[property]; + if (typeof value == 'function' && !(property in element)) + element[property] = cache.findOrStore(value); + } + + element._extended = Prototype.emptyFunction; + return element; +}; + +Element.extend.cache = { + findOrStore: function(value) { + return this[value] = this[value] || function() { + return value.apply(null, [this].concat($A(arguments))); + } + } +}; + +Element.Methods = { + visible: function(element) { + return $(element).style.display != 'none'; + }, + + toggle: function(element) { + element = $(element); + Element[Element.visible(element) ? 'hide' : 'show'](element); + return element; + }, + + hide: function(element) { + $(element).style.display = 'none'; + return element; + }, + + show: function(element) { + $(element).style.display = ''; + return element; + }, + + remove: function(element) { + element = $(element); + element.parentNode.removeChild(element); + return element; + }, + + update: function(element, html) { + html = typeof html == 'undefined' ? '' : html.toString(); + $(element).innerHTML = html.stripScripts(); + setTimeout(function() {html.evalScripts()}, 10); + return element; + }, + + replace: function(element, html) { + element = $(element); + html = typeof html == 'undefined' ? '' : html.toString(); + if (element.outerHTML) { + element.outerHTML = html.stripScripts(); + } else { + var range = element.ownerDocument.createRange(); + range.selectNodeContents(element); + element.parentNode.replaceChild( + range.createContextualFragment(html.stripScripts()), element); + } + setTimeout(function() {html.evalScripts()}, 10); + return element; + }, + + inspect: function(element) { + element = $(element); + var result = '<' + element.tagName.toLowerCase(); + $H({'id': 'id', 'className': 'class'}).each(function(pair) { + var property = pair.first(), attribute = pair.last(); + var value = (element[property] || '').toString(); + if (value) result += ' ' + attribute + '=' + value.inspect(true); + }); + return result + '>'; + }, + + recursivelyCollect: function(element, property) { + element = $(element); + var elements = []; + while (element = element[property]) + if (element.nodeType == 1) + elements.push(Element.extend(element)); + return elements; + }, + + ancestors: function(element) { + return $(element).recursivelyCollect('parentNode'); + }, + + descendants: function(element) { + return $A($(element).getElementsByTagName('*')).each(Element.extend); + }, + + firstDescendant: function(element) { + element = $(element).firstChild; + while (element && element.nodeType != 1) element = element.nextSibling; + return $(element); + }, + + immediateDescendants: function(element) { + if (!(element = $(element).firstChild)) return []; + while (element && element.nodeType != 1) element = element.nextSibling; + if (element) return [element].concat($(element).nextSiblings()); + return []; + }, + + previousSiblings: function(element) { + return $(element).recursivelyCollect('previousSibling'); + }, + + nextSiblings: function(element) { + return $(element).recursivelyCollect('nextSibling'); + }, + + siblings: function(element) { + element = $(element); + return element.previousSiblings().reverse().concat(element.nextSiblings()); + }, + + match: function(element, selector) { + if (typeof selector == 'string') + selector = new Selector(selector); + return selector.match($(element)); + }, + + up: function(element, expression, index) { + element = $(element); + if (arguments.length == 1) return $(element.parentNode); + var ancestors = element.ancestors(); + return expression ? Selector.findElement(ancestors, expression, index) : + ancestors[index || 0]; + }, + + down: function(element, expression, index) { + element = $(element); + if (arguments.length == 1) return element.firstDescendant(); + var descendants = element.descendants(); + return expression ? Selector.findElement(descendants, expression, index) : + descendants[index || 0]; + }, + + previous: function(element, expression, index) { + element = $(element); + if (arguments.length == 1) return $(Selector.handlers.previousElementSibling(element)); + var previousSiblings = element.previousSiblings(); + return expression ? Selector.findElement(previousSiblings, expression, index) : + previousSiblings[index || 0]; + }, + + next: function(element, expression, index) { + element = $(element); + if (arguments.length == 1) return $(Selector.handlers.nextElementSibling(element)); + var nextSiblings = element.nextSiblings(); + return expression ? Selector.findElement(nextSiblings, expression, index) : + nextSiblings[index || 0]; + }, + + getElementsBySelector: function() { + var args = $A(arguments), element = $(args.shift()); + return Selector.findChildElements(element, args); + }, + + getElementsByClassName: function(element, className) { + return document.getElementsByClassName(className, element); + }, + + readAttribute: function(element, name) { + element = $(element); + if (Prototype.BrowserFeatures.IE) { + if (!element.attributes) return null; + var t = Element._attributeTranslations; + if (t.values[name]) return t.values[name](element, name); + if (t.names[name]) name = t.names[name]; + var attribute = element.attributes[name]; + return attribute ? attribute.nodeValue : null; + } + return element.getAttribute(name); + }, + + getHeight: function(element) { + return $(element).getDimensions().height; + }, + + getWidth: function(element) { + return $(element).getDimensions().width; + }, + + classNames: function(element) { + return new Element.ClassNames(element); + }, + + hasClassName: function(element, className) { + if (!(element = $(element))) return; + var elementClassName = element.className; + if (elementClassName.length == 0) return false; + if (elementClassName == className || + elementClassName.match(new RegExp("(^|\\s)" + className + "(\\s|$)"))) + return true; + return false; + }, + + addClassName: function(element, className) { + if (!(element = $(element))) return; + Element.classNames(element).add(className); + return element; + }, + + removeClassName: function(element, className) { + if (!(element = $(element))) return; + Element.classNames(element).remove(className); + return element; + }, + + toggleClassName: function(element, className) { + if (!(element = $(element))) return; + Element.classNames(element)[element.hasClassName(className) ? 'remove' : 'add'](className); + return element; + }, + + observe: function() { + Event.observe.apply(Event, arguments); + return $A(arguments).first(); + }, + + stopObserving: function() { + Event.stopObserving.apply(Event, arguments); + return $A(arguments).first(); + }, + + // removes whitespace-only text node children + cleanWhitespace: function(element) { + element = $(element); + var node = element.firstChild; + while (node) { + var nextNode = node.nextSibling; + if (node.nodeType == 3 && !/\S/.test(node.nodeValue)) + element.removeChild(node); + node = nextNode; + } + return element; + }, + + empty: function(element) { + return $(element).innerHTML.blank(); + }, + + descendantOf: function(element, ancestor) { + element = $(element), ancestor = $(ancestor); + while (element = element.parentNode) + if (element == ancestor) return true; + return false; + }, + + scrollTo: function(element) { + element = $(element); + var pos = Position.cumulativeOffset(element); + window.scrollTo(pos[0], pos[1]); + return element; + }, + + getStyle: function(element, style) { + element = $(element); + style = style == 'float' ? 'cssFloat' : style.camelize(); + var value = element.style[style]; + if (!value) { + var css = document.defaultView.getComputedStyle(element, null); + value = css ? css[style] : null; + } + if (style == 'opacity') return value ? parseFloat(value) : 1.0; + return value == 'auto' ? null : value; + }, + + getOpacity: function(element) { + return $(element).getStyle('opacity'); + }, + + setStyle: function(element, styles, camelized) { + element = $(element); + var elementStyle = element.style; + + for (var property in styles) + if (property == 'opacity') element.setOpacity(styles[property]) + else + elementStyle[(property == 'float' || property == 'cssFloat') ? + (elementStyle.styleFloat === undefined ? 'cssFloat' : 'styleFloat') : + (camelized ? property : property.camelize())] = styles[property]; + + return element; + }, + + setOpacity: function(element, value) { + element = $(element); + element.style.opacity = (value == 1 || value === '') ? '' : + (value < 0.00001) ? 0 : value; + return element; + }, + + getDimensions: function(element) { + element = $(element); + var display = $(element).getStyle('display'); + if (display != 'none' && display != null) // Safari bug + return {width: element.offsetWidth, height: element.offsetHeight}; + + // All *Width and *Height properties give 0 on elements with display none, + // so enable the element temporarily + var els = element.style; + var originalVisibility = els.visibility; + var originalPosition = els.position; + var originalDisplay = els.display; + els.visibility = 'hidden'; + els.position = 'absolute'; + els.display = 'block'; + var originalWidth = element.clientWidth; + var originalHeight = element.clientHeight; + els.display = originalDisplay; + els.position = originalPosition; + els.visibility = originalVisibility; + return {width: originalWidth, height: originalHeight}; + }, + + 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; + } + } + return element; + }, + + undoPositioned: function(element) { + element = $(element); + if (element._madePositioned) { + element._madePositioned = undefined; + element.style.position = + element.style.top = + element.style.left = + element.style.bottom = + element.style.right = ''; + } + return element; + }, + + makeClipping: function(element) { + element = $(element); + if (element._overflow) return element; + element._overflow = element.style.overflow || 'auto'; + if ((Element.getStyle(element, 'overflow') || 'visible') != 'hidden') + element.style.overflow = 'hidden'; + return element; + }, + + undoClipping: function(element) { + element = $(element); + if (!element._overflow) return element; + element.style.overflow = element._overflow == 'auto' ? '' : element._overflow; + element._overflow = null; + return element; + } +}; + +Object.extend(Element.Methods, { + childOf: Element.Methods.descendantOf, + childElements: Element.Methods.immediateDescendants +}); + +if (Prototype.BrowserFeatures.Opera) { + Element.Methods._getStyle = Element.Methods.getStyle; + Element.Methods.getStyle = function(element, style) { + switch(style) { + case 'left': + case 'top': + case 'right': + case 'bottom': + if (Element._getStyle(element, 'position') == 'static') return null; + default: return Element._getStyle(element, style); + } + }; +} +else if (Prototype.BrowserFeatures.IE) { + Element.Methods.getStyle = function(element, style) { + element = $(element); + style = (style == 'float' || style == 'cssFloat') ? 'styleFloat' : style.camelize(); + var value = element.style[style]; + if (!value && element.currentStyle) value = element.currentStyle[style]; + + if (style == 'opacity') { + if (value = (element.getStyle('filter') || '').match(/alpha\(opacity=(.*)\)/)) + if (value[1]) return parseFloat(value[1]) / 100; + return 1.0; + } + + if (value == 'auto') { + if ((style == 'width' || style == 'height') && (element.getStyle('display') != 'none')) + return element['offset'+style.capitalize()] + 'px'; + return null; + } + return value; + }; + + Element.Methods.setOpacity = function(element, value) { + element = $(element); + var filter = element.getStyle('filter'), style = element.style; + if (value == 1 || value === '') { + style.filter = filter.replace(/alpha\([^\)]*\)/gi,''); + return element; + } else if (value < 0.00001) value = 0; + style.filter = filter.replace(/alpha\([^\)]*\)/gi, '') + + 'alpha(opacity=' + (value * 100) + ')'; + return element; + }; + + // IE is missing .innerHTML support for TABLE-related elements + Element.Methods.update = function(element, html) { + element = $(element); + html = typeof html == 'undefined' ? '' : html.toString(); + var tagName = element.tagName.toUpperCase(); + if (['THEAD','TBODY','TR','TD'].include(tagName)) { + var div = document.createElement('div'); + switch (tagName) { + case 'THEAD': + case 'TBODY': + div.innerHTML = '' + html.stripScripts() + '
'; + depth = 2; + break; + case 'TR': + div.innerHTML = '' + html.stripScripts() + '
'; + depth = 3; + break; + case 'TD': + div.innerHTML = '
' + html.stripScripts() + '
'; + depth = 4; + } + $A(element.childNodes).each(function(node) { element.removeChild(node) }); + depth.times(function() { div = div.firstChild }); + $A(div.childNodes).each(function(node) { element.appendChild(node) }); + } else { + element.innerHTML = html.stripScripts(); + } + setTimeout(function() { html.evalScripts() }, 10); + return element; + } +} +else if (Prototype.BrowserFeatures.Gecko) { + Element.Methods.setOpacity = function(element, value) { + element = $(element); + element.style.opacity = (value == 1) ? 0.999999 : + (value === '') ? '' : (value < 0.00001) ? 0 : value; + return element; + }; +} + +Element._attributeTranslations = { + names: { + colspan: "colSpan", + rowspan: "rowSpan", + valign: "vAlign", + datetime: "dateTime", + accesskey: "accessKey", + tabindex: "tabIndex", + enctype: "encType", + maxlength: "maxLength", + readonly: "readOnly", + longdesc: "longDesc" + }, + values: { + _getAttr: function(element, attribute) { + return element.getAttribute(attribute, 2); + }, + _flag: function(element, attribute) { + return $(element).hasAttribute(attribute) ? attribute : null; + }, + style: function(element) { + return element.style.cssText.toLowerCase(); + }, + title: function(element) { + var node = element.getAttributeNode('title'); + return node.specified ? node.nodeValue : null; + } + } +}; + +(function() { + Object.extend(this, { + href: this._getAttr, + src: this._getAttr, + disabled: this._flag, + checked: this._flag, + readonly: this._flag, + multiple: this._flag + }); +}).call(Element._attributeTranslations.values); + +Element.Methods.Simulated = { + hasAttribute: function(element, attribute) { + var t = Element._attributeTranslations, node; + attribute = t.names[attribute] || attribute; + node = $(element).getAttributeNode(attribute); + return node && node.specified; + } +}; + +Element.Methods.ByTag = {}; + +Object.extend(Element, Element.Methods); + +if (!Prototype.BrowserFeatures.ElementExtensions && + document.createElement('div').__proto__) { + window.HTMLElement = {}; + window.HTMLElement.prototype = document.createElement('div').__proto__; + Prototype.BrowserFeatures.ElementExtensions = true; +} + +Element.hasAttribute = function(element, attribute) { + if (element.hasAttribute) return element.hasAttribute(attribute); + return Element.Methods.Simulated.hasAttribute(element, attribute); +}; + +Element.addMethods = function(methods) { + var F = Prototype.BrowserFeatures, T = Element.Methods.ByTag; + if (arguments.length == 2) { + var tagName = methods; + methods = arguments[1]; + } + + if (!tagName) Object.extend(Element.Methods, methods || {}); + else { + if (tagName.constructor == Array) tagName.each(extend); + else extend(tagName); + } + + function extend(tagName) { + tagName = tagName.toUpperCase(); + if (!Element.Methods.ByTag[tagName]) + Element.Methods.ByTag[tagName] = {}; + Object.extend(Element.Methods.ByTag[tagName], methods); + } + + function copy(methods, destination, onlyIfAbsent) { + onlyIfAbsent = onlyIfAbsent || false; + var cache = Element.extend.cache; + for (var property in methods) { + var value = methods[property]; + if (!onlyIfAbsent || !(property in destination)) + destination[property] = cache.findOrStore(value); + } + } + + function findDOMClass(tagName) { + var klass; + var trans = { + "OPTGROUP": "OptGroup", "TEXTAREA": "TextArea", "P": "Paragraph", + "FIELDSET": "FieldSet", "UL": "UList", "OL": "OList", "DL": "DList", + "DIR": "Directory", "H1": "Heading", "H2": "Heading", "H3": "Heading", + "H4": "Heading", "H5": "Heading", "H6": "Heading", "Q": "Quote", + "INS": "Mod", "DEL": "Mod", "A": "Anchor", "IMG": "Image", "CAPTION": + "TableCaption", "COL": "TableCol", "COLGROUP": "TableCol", "THEAD": + "TableSection", "TFOOT": "TableSection", "TBODY": "TableSection", "TR": + "TableRow", "TH": "TableCell", "TD": "TableCell", "FRAMESET": + "FrameSet", "IFRAME": "IFrame" + }; + if (trans[tagName]) klass = 'HTML' + trans[tagName] + 'Element'; + if (window[klass]) return window[klass]; + klass = 'HTML' + tagName + 'Element'; + if (window[klass]) return window[klass]; + klass = 'HTML' + tagName.capitalize() + 'Element'; + if (window[klass]) return window[klass]; + + window[klass] = {}; + window[klass].prototype = document.createElement(tagName).__proto__; + return window[klass]; + } + + if (F.ElementExtensions) { + copy(Element.Methods, HTMLElement.prototype); + copy(Element.Methods.Simulated, HTMLElement.prototype, true); + } + + if (F.SpecificElementExtensions) { + for (var tag in Element.Methods.ByTag) { + var klass = findDOMClass(tag); + if (typeof klass == "undefined") continue; + copy(T[tag], klass.prototype); + } + } + + Object.extend(Element, Element.Methods); + delete Element.ByTag; +}; + +var Toggle = { display: Element.toggle }; + +/*--------------------------------------------------------------------------*/ + +Abstract.Insertion = function(adjacency) { + this.adjacency = adjacency; +} + +Abstract.Insertion.prototype = { + initialize: function(element, content) { + this.element = $(element); + this.content = content.stripScripts(); + + if (this.adjacency && this.element.insertAdjacentHTML) { + try { + this.element.insertAdjacentHTML(this.adjacency, this.content); + } catch (e) { + var tagName = this.element.tagName.toUpperCase(); + if (['TBODY', 'TR'].include(tagName)) { + this.insertContent(this.contentFromAnonymousTable()); + } else { + throw e; + } + } + } else { + this.range = this.element.ownerDocument.createRange(); + if (this.initializeRange) this.initializeRange(); + this.insertContent([this.range.createContextualFragment(this.content)]); + } + + setTimeout(function() {content.evalScripts()}, 10); + }, + + contentFromAnonymousTable: function() { + var div = document.createElement('div'); + div.innerHTML = '' + this.content + '
'; + return $A(div.childNodes[0].childNodes[0].childNodes); + } +} + +var Insertion = new Object(); + +Insertion.Before = Class.create(); +Insertion.Before.prototype = Object.extend(new Abstract.Insertion('beforeBegin'), { + initializeRange: function() { + this.range.setStartBefore(this.element); + }, + + insertContent: function(fragments) { + fragments.each((function(fragment) { + this.element.parentNode.insertBefore(fragment, this.element); + }).bind(this)); + } +}); + +Insertion.Top = Class.create(); +Insertion.Top.prototype = Object.extend(new Abstract.Insertion('afterBegin'), { + initializeRange: function() { + this.range.selectNodeContents(this.element); + this.range.collapse(true); + }, + + insertContent: function(fragments) { + fragments.reverse(false).each((function(fragment) { + this.element.insertBefore(fragment, this.element.firstChild); + }).bind(this)); + } +}); + +Insertion.Bottom = Class.create(); +Insertion.Bottom.prototype = Object.extend(new Abstract.Insertion('beforeEnd'), { + initializeRange: function() { + this.range.selectNodeContents(this.element); + this.range.collapse(this.element); + }, + + insertContent: function(fragments) { + fragments.each((function(fragment) { + this.element.appendChild(fragment); + }).bind(this)); + } +}); + +Insertion.After = Class.create(); +Insertion.After.prototype = Object.extend(new Abstract.Insertion('afterEnd'), { + initializeRange: function() { + this.range.setStartAfter(this.element); + }, + + insertContent: function(fragments) { + fragments.each((function(fragment) { + this.element.parentNode.insertBefore(fragment, + this.element.nextSibling); + }).bind(this)); + } +}); + +/*--------------------------------------------------------------------------*/ + +Element.ClassNames = Class.create(); +Element.ClassNames.prototype = { + initialize: function(element) { + this.element = $(element); + }, + + _each: function(iterator) { + this.element.className.split(/\s+/).select(function(name) { + return name.length > 0; + })._each(iterator); + }, + + set: function(className) { + this.element.className = className; + }, + + add: function(classNameToAdd) { + if (this.include(classNameToAdd)) return; + this.set($A(this).concat(classNameToAdd).join(' ')); + }, + + remove: function(classNameToRemove) { + if (!this.include(classNameToRemove)) return; + this.set($A(this).without(classNameToRemove).join(' ')); + }, + + toString: function() { + return $A(this).join(' '); + } +}; + +Object.extend(Element.ClassNames.prototype, Enumerable); +/* Portions of the Selector class are derived from Jack Slocum’s DomQuery, + * part of YUI-Ext version 0.40, distributed under the terms of an MIT-style + * license. Please see http://www.yui-ext.com/ for more information. */ + +var Selector = Class.create(); + +Selector.prototype = { + initialize: function(expression) { + this.expression = expression.strip(); + this.compileMatcher(); + }, + + compileMatcher: function() { + // Selectors with namespaced attributes can't use the XPath version + if (Prototype.BrowserFeatures.XPath && !(/\[[\w-]*?:/).test(this.expression)) + return this.compileXPathMatcher(); + + var e = this.expression, ps = Selector.patterns, h = Selector.handlers, + c = Selector.criteria, le, p, m; + + if (Selector._cache[e]) { + this.matcher = Selector._cache[e]; return; + } + this.matcher = ["this.matcher = function(root) {", + "var r = root, h = Selector.handlers, c = false, n;"]; + + while (e && le != e && (/\S/).test(e)) { + le = e; + for (var i in ps) { + p = ps[i]; + if (m = e.match(p)) { + this.matcher.push(typeof c[i] == 'function' ? c[i](m) : + new Template(c[i]).evaluate(m)); + e = e.replace(m[0], ''); + break; + } + } + } + + this.matcher.push("return h.unique(n);\n}"); + eval(this.matcher.join('\n')); + Selector._cache[this.expression] = this.matcher; + }, + + compileXPathMatcher: function() { + var e = this.expression, ps = Selector.patterns, + x = Selector.xpath, le, m; + + if (Selector._cache[e]) { + this.xpath = Selector._cache[e]; return; + } + + this.matcher = ['.//*']; + while (e && le != e && (/\S/).test(e)) { + le = e; + for (var i in ps) { + if (m = e.match(ps[i])) { + this.matcher.push(typeof x[i] == 'function' ? x[i](m) : + new Template(x[i]).evaluate(m)); + e = e.replace(m[0], ''); + break; + } + } + } + + this.xpath = this.matcher.join(''); + Selector._cache[this.expression] = this.xpath; + }, + + findElements: function(root) { + root = root || document; + if (this.xpath) return document._getElementsByXPath(this.xpath, root); + return this.matcher(root); + }, + + match: function(element) { + return this.findElements(document).include(element); + }, + + toString: function() { + return this.expression; + }, + + inspect: function() { + return "#"; + } +}; + +Object.extend(Selector, { + _cache: {}, + + xpath: { + descendant: "//*", + child: "/*", + adjacent: "/following-sibling::*[1]", + laterSibling: '/following-sibling::*', + tagName: function(m) { + if (m[1] == '*') return ''; + return "[local-name()='" + m[1].toLowerCase() + + "' or local-name()='" + m[1].toUpperCase() + "']"; + }, + className: "[contains(concat(' ', @class, ' '), ' #{1} ')]", + id: "[@id='#{1}']", + attrPresence: "[@#{1}]", + attr: function(m) { + m[3] = m[5] || m[6]; + return new Template(Selector.xpath.operators[m[2]]).evaluate(m); + }, + pseudo: function(m) { + var h = Selector.xpath.pseudos[m[1]]; + if (!h) return ''; + if (typeof h === 'function') return h(m); + return new Template(Selector.xpath.pseudos[m[1]]).evaluate(m); + }, + operators: { + '=': "[@#{1}='#{3}']", + '!=': "[@#{1}!='#{3}']", + '^=': "[starts-with(@#{1}, '#{3}')]", + '$=': "[substring(@#{1}, (string-length(@#{1}) - string-length('#{3}') + 1))='#{3}']", + '*=': "[contains(@#{1}, '#{3}')]", + '~=': "[contains(concat(' ', @#{1}, ' '), ' #{3} ')]", + '|=': "[contains(concat('-', @#{1}, '-'), '-#{3}-')]" + }, + pseudos: { + 'first-child': '[not(preceding-sibling::*)]', + 'last-child': '[not(following-sibling::*)]', + 'only-child': '[not(preceding-sibling::* or following-sibling::*)]', + 'empty': "[count(*) = 0 and (count(text()) = 0 or translate(text(), ' \t\r\n', '') = '')]", + 'checked': "[@checked]", + 'disabled': "[@disabled]", + 'enabled': "[not(@disabled)]", + 'not': function(m) { + var e = m[6], p = Selector.patterns, + x = Selector.xpath, le, m, v; + + var exclusion = []; + while (e && le != e && (/\S/).test(e)) { + le = e; + for (var i in p) { + if (m = e.match(p[i])) { + v = typeof x[i] == 'function' ? x[i](m) : new Template(x[i]).evaluate(m); + exclusion.push("(" + v.substring(1, v.length - 1) + ")"); + e = e.replace(m[0], ''); + break; + } + } + } + return "[not(" + exclusion.join(" and ") + ")]"; + }, + 'nth-child': function(m) { + return Selector.xpath.pseudos.nth("(count(./preceding-sibling::*) + 1) ", m); + }, + 'nth-last-child': function(m) { + return Selector.xpath.pseudos.nth("(count(./following-sibling::*) + 1) ", m); + }, + 'nth-of-type': function(m) { + return Selector.xpath.pseudos.nth("position() ", m); + }, + 'nth-last-of-type': function(m) { + return Selector.xpath.pseudos.nth("(last() + 1 - position()) ", m); + }, + 'first-of-type': function(m) { + m[6] = "1"; return Selector.xpath.pseudos['nth-of-type'](m); + }, + 'last-of-type': function(m) { + m[6] = "1"; return Selector.xpath.pseudos['nth-last-of-type'](m); + }, + 'only-of-type': function(m) { + var p = Selector.xpath.pseudos; return p['first-of-type'](m) + p['last-of-type'](m); + }, + nth: function(fragment, m) { + var mm, formula = m[6], predicate; + if (formula == 'even') formula = '2n+0'; + if (formula == 'odd') formula = '2n+1'; + if (mm = formula.match(/^(\d+)$/)) // digit only + return '[' + fragment + "= " + mm[1] + ']'; + if (mm = formula.match(/^(-?\d*)?n(([+-])(\d+))?/)) { // an+b + if (mm[1] == "-") mm[1] = -1; + var a = mm[1] ? Number(mm[1]) : 1; + var b = mm[2] ? Number(mm[2]) : 0; + predicate = "[((#{fragment} - #{b}) mod #{a} = 0) and " + + "((#{fragment} - #{b}) div #{a} >= 0)]"; + return new Template(predicate).evaluate({ + fragment: fragment, a: a, b: b }); + } + } + } + }, + + criteria: { + tagName: 'n = h.tagName(n, r, "#{1}", c); c = false;', + className: 'n = h.className(n, r, "#{1}", c); c = false;', + id: 'n = h.id(n, r, "#{1}", c); c = false;', + attrPresence: 'n = h.attrPresence(n, r, "#{1}"); c = false;', + attr: function(m) { + m[3] = (m[5] || m[6]); + return new Template('n = h.attr(n, r, "#{1}", "#{3}", "#{2}"); c = false;').evaluate(m); + }, + pseudo: function(m) { + if (m[6]) m[6] = m[6].replace(/"/g, '\\"'); + return new Template('n = h.pseudo(n, "#{1}", "#{6}", r, c); c = false;').evaluate(m); + }, + descendant: 'c = "descendant";', + child: 'c = "child";', + adjacent: 'c = "adjacent";', + laterSibling: 'c = "laterSibling";' + }, + + patterns: { + // combinators must be listed first + // (and descendant needs to be last combinator) + laterSibling: /^\s*~\s*/, + child: /^\s*>\s*/, + adjacent: /^\s*\+\s*/, + descendant: /^\s/, + + // selectors follow + tagName: /^\s*(\*|[\w\-]+)(\b|$)?/, + id: /^#([\w\-\*]+)(\b|$)/, + className: /^\.([\w\-\*]+)(\b|$)/, + pseudo: /^:((first|last|nth|nth-last|only)(-child|-of-type)|empty|checked|(en|dis)abled|not)(\((.*?)\))?(\b|$|\s)/, + attrPresence: /^\[([\w]+)\]/, + attr: /\[((?:[\w-]*:)?[\w-]+)\s*(?:([!^$*~|]?=)\s*((['"])([^\]]*?)\4|([^'"][^\]]*?)))?\]/ + }, + + handlers: { + // UTILITY FUNCTIONS + // joins two collections + concat: function(a, b) { + for (var i = 0, node; node = b[i]; i++) + a.push(node); + return a; + }, + + // marks an array of nodes for counting + mark: function(nodes) { + for (var i = 0, node; node = nodes[i]; i++) + node._counted = true; + return nodes; + }, + + unmark: function(nodes) { + for (var i = 0, node; node = nodes[i]; i++) + node._counted = undefined; + return nodes; + }, + + // mark each child node with its position (for nth calls) + // "ofType" flag indicates whether we're indexing for nth-of-type + // rather than nth-child + index: function(parentNode, reverse, ofType) { + parentNode._counted = true; + if (reverse) { + for (var nodes = parentNode.childNodes, i = nodes.length - 1, j = 1; i >= 0; i--) { + node = nodes[i]; + if (node.nodeType == 1 && (!ofType || node._counted)) node.nodeIndex = j++; + } + } else { + for (var i = 0, j = 1, nodes = parentNode.childNodes; node = nodes[i]; i++) + if (node.nodeType == 1 && (!ofType || node._counted)) node.nodeIndex = j++; + } + }, + + // filters out duplicates and extends all nodes + unique: function(nodes) { + if (nodes.length == 0) return nodes; + var results = [], n; + for (var i = 0, l = nodes.length; i < l; i++) + if (!(n = nodes[i])._counted) { + n._counted = true; + results.push(Element.extend(n)); + } + return Selector.handlers.unmark(results); + }, + + // COMBINATOR FUNCTIONS + descendant: function(nodes) { + var h = Selector.handlers; + for (var i = 0, results = [], node; node = nodes[i]; i++) + h.concat(results, node.getElementsByTagName('*')); + return results; + }, + + child: function(nodes) { + var h = Selector.handlers; + for (var i = 0, results = [], node; node = nodes[i]; i++) { + for (var j = 0, children = [], child; child = node.childNodes[j]; j++) + if (child.nodeType == 1 && child.tagName != '!') results.push(child); + } + return results; + }, + + adjacent: function(nodes) { + for (var i = 0, results = [], node; node = nodes[i]; i++) { + var next = this.nextElementSibling(node); + if (next) results.push(next); + } + return results; + }, + + laterSibling: function(nodes) { + var h = Selector.handlers; + for (var i = 0, results = [], node; node = nodes[i]; i++) + h.concat(results, Element.nextSiblings(node)); + return results; + }, + + nextElementSibling: function(node) { + while (node = node.nextSibling) + if (node.nodeType == 1) return node; + return null; + }, + + previousElementSibling: function(node) { + while (node = node.previousSibling) + if (node.nodeType == 1) return node; + return null; + }, + + // TOKEN FUNCTIONS + tagName: function(nodes, root, tagName, combinator) { + tagName = tagName.toUpperCase(); + var results = [], h = Selector.handlers; + if (nodes) { + if (combinator) { + // fastlane for ordinary descendant combinators + if (combinator == "descendant") { + for (var i = 0, node; node = nodes[i]; i++) + h.concat(results, node.getElementsByTagName(tagName)); + return results; + } else nodes = this[combinator](nodes); + if (tagName == "*") return nodes; + } + for (var i = 0, node; node = nodes[i]; i++) + if (node.tagName.toUpperCase() == tagName) results.push(node); + return results; + } else return root.getElementsByTagName(tagName); + }, + + id: function(nodes, root, id, combinator) { + var targetNode = $(id), h = Selector.handlers; + if (!nodes && root == document) return targetNode ? [targetNode] : []; + if (nodes) { + if (combinator) { + if (combinator == 'child') { + for (var i = 0, node; node = nodes[i]; i++) + if (targetNode.parentNode == node) return [targetNode]; + } else if (combinator == 'descendant') { + for (var i = 0, node; node = nodes[i]; i++) + if (Element.descendantOf(targetNode, node)) return [targetNode]; + } else if (combinator == 'adjacent') { + for (var i = 0, node; node = nodes[i]; i++) + if (Selector.handlers.previousElementSibling(targetNode) == node) + return [targetNode]; + } else nodes = h[combinator](nodes); + } + for (var i = 0, node; node = nodes[i]; i++) + if (node == targetNode) return [targetNode]; + return []; + } + return (targetNode && Element.descendantOf(targetNode, root)) ? [targetNode] : []; + }, + + className: function(nodes, root, className, combinator) { + if (nodes && combinator) nodes = this[combinator](nodes); + return Selector.handlers.byClassName(nodes, root, className); + }, + + byClassName: function(nodes, root, className) { + if (!nodes) nodes = Selector.handlers.descendant([root]); + var needle = ' ' + className + ' '; + for (var i = 0, results = [], node, nodeClassName; node = nodes[i]; i++) { + nodeClassName = node.className; + if (nodeClassName.length == 0) continue; + if (nodeClassName == className || (' ' + nodeClassName + ' ').include(needle)) + results.push(node); + } + return results; + }, + + attrPresence: function(nodes, root, attr) { + var results = []; + for (var i = 0, node; node = nodes[i]; i++) + if (Element.hasAttribute(node, attr)) results.push(node); + return results; + }, + + attr: function(nodes, root, attr, value, operator) { + if (!nodes) nodes = root.getElementsByTagName("*"); + var handler = Selector.operators[operator], results = []; + for (var i = 0, node; node = nodes[i]; i++) { + var nodeValue = Element.readAttribute(node, attr); + if (nodeValue === null) continue; + if (handler(nodeValue, value)) results.push(node); + } + return results; + }, + + pseudo: function(nodes, name, value, root, combinator) { + if (nodes && combinator) nodes = this[combinator](nodes); + if (!nodes) nodes = root.getElementsByTagName("*"); + return Selector.pseudos[name](nodes, value, root); + } + }, + + pseudos: { + 'first-child': function(nodes, value, root) { + for (var i = 0, results = [], node; node = nodes[i]; i++) { + if (Selector.handlers.previousElementSibling(node)) continue; + results.push(node); + } + return results; + }, + 'last-child': function(nodes, value, root) { + for (var i = 0, results = [], node; node = nodes[i]; i++) { + if (Selector.handlers.nextElementSibling(node)) continue; + results.push(node); + } + return results; + }, + 'only-child': function(nodes, value, root) { + var h = Selector.handlers; + for (var i = 0, results = [], node; node = nodes[i]; i++) + if (!h.previousElementSibling(node) && !h.nextElementSibling(node)) + results.push(node); + return results; + }, + 'nth-child': function(nodes, formula, root) { + return Selector.pseudos.nth(nodes, formula, root); + }, + 'nth-last-child': function(nodes, formula, root) { + return Selector.pseudos.nth(nodes, formula, root, true); + }, + 'nth-of-type': function(nodes, formula, root) { + return Selector.pseudos.nth(nodes, formula, root, false, true); + }, + 'nth-last-of-type': function(nodes, formula, root) { + return Selector.pseudos.nth(nodes, formula, root, true, true); + }, + 'first-of-type': function(nodes, formula, root) { + return Selector.pseudos.nth(nodes, "1", root, false, true); + }, + 'last-of-type': function(nodes, formula, root) { + return Selector.pseudos.nth(nodes, "1", root, true, true); + }, + 'only-of-type': function(nodes, formula, root) { + var p = Selector.pseudos; + return p['last-of-type'](p['first-of-type'](nodes, formula, root), formula, root); + }, + + // handles the an+b logic + getIndices: function(a, b, total) { + if (a == 0) return b > 0 ? [b] : []; + return $R(1, total).inject([], function(memo, i) { + if (0 == (i - b) % a && (i - b) / a >= 0) memo.push(i); + return memo; + }); + }, + + // handles nth(-last)-child, nth(-last)-of-type, and (first|last)-of-type + nth: function(nodes, formula, root, reverse, ofType) { + if (nodes.length == 0) return []; + if (formula == 'even') formula = '2n+0'; + if (formula == 'odd') formula = '2n+1'; + var h = Selector.handlers, results = [], indexed = [], m; + h.mark(nodes); + for (var i = 0, node; node = nodes[i]; i++) { + if (!node.parentNode._counted) { + h.index(node.parentNode, reverse, ofType); + indexed.push(node.parentNode); + } + } + if (formula.match(/^\d+$/)) { // just a number + formula = Number(formula); + for (var i = 0, node; node = nodes[i]; i++) + if (node.nodeIndex == formula) results.push(node); + } else if (m = formula.match(/^(-?\d*)?n(([+-])(\d+))?/)) { // an+b + if (m[1] == "-") m[1] = -1; + var a = m[1] ? Number(m[1]) : 1; + var b = m[2] ? Number(m[2]) : 0; + var indices = Selector.pseudos.getIndices(a, b, nodes.length); + for (var i = 0, node, l = indices.length; node = nodes[i]; i++) { + for (var j = 0; j < l; j++) + if (node.nodeIndex == indices[j]) results.push(node); + } + } + h.unmark(nodes); + h.unmark(indexed); + return results; + }, + + 'empty': function(nodes, value, root) { + for (var i = 0, results = [], node; node = nodes[i]; i++) { + // IE treats comments as element nodes + if (node.tagName == '!' || (node.firstChild && !node.innerHTML.match(/^\s*$/))) continue; + results.push(node); + } + return results; + }, + + 'not': function(nodes, selector, root) { + var h = Selector.handlers, selectorType, m; + var exclusions = new Selector(selector).findElements(root); + h.mark(exclusions); + for (var i = 0, results = [], node; node = nodes[i]; i++) + if (!node._counted) results.push(node); + h.unmark(exclusions); + return results; + }, + + 'enabled': function(nodes, value, root) { + for (var i = 0, results = [], node; node = nodes[i]; i++) + if (!node.disabled) results.push(node); + return results; + }, + + 'disabled': function(nodes, value, root) { + for (var i = 0, results = [], node; node = nodes[i]; i++) + if (node.disabled) results.push(node); + return results; + }, + + 'checked': function(nodes, value, root) { + for (var i = 0, results = [], node; node = nodes[i]; i++) + if (node.checked) results.push(node); + return results; + } + }, + + operators: { + '=': function(nv, v) { return nv == v; }, + '!=': function(nv, v) { return nv != v; }, + '^=': function(nv, v) { return nv.startsWith(v); }, + '$=': function(nv, v) { return nv.endsWith(v); }, + '*=': function(nv, v) { return nv.include(v); }, + '~=': function(nv, v) { return (' ' + nv + ' ').include(' ' + v + ' '); }, + '|=': function(nv, v) { return ('-' + nv.toUpperCase() + '-').include('-' + v.toUpperCase() + '-'); } + }, + + matchElements: function(elements, expression) { + var matches = new Selector(expression).findElements(), h = Selector.handlers; + h.mark(matches); + for (var i = 0, results = [], element; element = elements[i]; i++) + if (element._counted) results.push(element); + h.unmark(matches); + return results; + }, + + findElement: function(elements, expression, index) { + if (typeof expression == 'number') { + index = expression; expression = false; + } + return Selector.matchElements(elements, expression || '*')[index || 0]; + }, + + findChildElements: function(element, expressions) { + var exprs = expressions.join(','), expressions = []; + exprs.scan(/(([\w#:.~>+()\s-]+|\*|\[.*?\])+)\s*(,|$)/, function(m) { + expressions.push(m[1].strip()); + }); + var results = [], h = Selector.handlers; + for (var i = 0, l = expressions.length, selector; i < l; i++) { + selector = new Selector(expressions[i].strip()); + h.concat(results, selector.findElements(element)); + } + return (l > 1) ? h.unique(results) : results; + } +}); + +function $$() { + return Selector.findChildElements(document, $A(arguments)); +} +var Form = { + reset: function(form) { + $(form).reset(); + return form; + }, + + serializeElements: function(elements, getHash) { + var data = elements.inject({}, function(result, element) { + if (!element.disabled && element.name) { + var key = element.name, value = $(element).getValue(); + if (value != null) { + if (key in result) { + if (result[key].constructor != Array) result[key] = [result[key]]; + result[key].push(value); + } + else result[key] = value; + } + } + return result; + }); + + return getHash ? data : Hash.toQueryString(data); + } +}; + +Form.Methods = { + serialize: function(form, getHash) { + return Form.serializeElements(Form.getElements(form), getHash); + }, + + getElements: function(form) { + return $A($(form).getElementsByTagName('*')).inject([], + function(elements, child) { + if (Form.Element.Serializers[child.tagName.toLowerCase()]) + elements.push(Element.extend(child)); + return elements; + } + ); + }, + + getInputs: function(form, typeName, name) { + form = $(form); + var inputs = form.getElementsByTagName('input'); + + if (!typeName && !name) return $A(inputs).map(Element.extend); + + for (var i = 0, matchingInputs = [], length = inputs.length; i < length; i++) { + var input = inputs[i]; + if ((typeName && input.type != typeName) || (name && input.name != name)) + continue; + matchingInputs.push(Element.extend(input)); + } + + return matchingInputs; + }, + + disable: function(form) { + form = $(form); + Form.getElements(form).invoke('disable'); + return form; + }, + + enable: function(form) { + form = $(form); + Form.getElements(form).invoke('enable'); + return form; + }, + + findFirstElement: function(form) { + return $(form).getElements().find(function(element) { + return element.type != 'hidden' && !element.disabled && + ['input', 'select', 'textarea'].include(element.tagName.toLowerCase()); + }); + }, + + focusFirstElement: function(form) { + form = $(form); + form.findFirstElement().activate(); + return form; + }, + + request: function(form, options) { + form = $(form), options = Object.clone(options || {}); + + var params = options.parameters; + options.parameters = form.serialize(true); + + if (params) { + if (typeof params == 'string') params = params.toQueryParams(); + Object.extend(options.parameters, params); + } + + if (form.hasAttribute('method') && !options.method) + options.method = form.method; + + return new Ajax.Request(form.readAttribute('action'), options); + } +} + +Object.extend(Form, Form.Methods); + +/*--------------------------------------------------------------------------*/ + +Form.Element = { + focus: function(element) { + $(element).focus(); + return element; + }, + + select: function(element) { + $(element).select(); + return element; + } +} + +Form.Element.Methods = { + serialize: function(element) { + element = $(element); + if (!element.disabled && element.name) { + var value = element.getValue(); + if (value != undefined) { + var pair = {}; + pair[element.name] = value; + return Hash.toQueryString(pair); + } + } + return ''; + }, + + getValue: function(element) { + element = $(element); + var method = element.tagName.toLowerCase(); + return Form.Element.Serializers[method](element); + }, + + clear: function(element) { + $(element).value = ''; + return element; + }, + + present: function(element) { + return $(element).value != ''; + }, + + activate: function(element) { + element = $(element); + try { + element.focus(); + if (element.select && (element.tagName.toLowerCase() != 'input' || + !['button', 'reset', 'submit'].include(element.type))) + element.select(); + } catch (e) {} + return element; + }, + + disable: function(element) { + element = $(element); + element.blur(); + element.disabled = true; + return element; + }, + + enable: function(element) { + element = $(element); + element.disabled = false; + return element; + } +} + +Object.extend(Form.Element, Form.Element.Methods); +Object.extend(Element.Methods.ByTag, { + "FORM": Object.clone(Form.Methods), + "INPUT": Object.clone(Form.Element.Methods), + "SELECT": Object.clone(Form.Element.Methods), + "TEXTAREA": Object.clone(Form.Element.Methods) +}); + +/*--------------------------------------------------------------------------*/ + +var Field = Form.Element; +var $F = Form.Element.getValue; + +/*--------------------------------------------------------------------------*/ + +Form.Element.Serializers = { + input: function(element) { + switch (element.type.toLowerCase()) { + case 'checkbox': + case 'radio': + return Form.Element.Serializers.inputSelector(element); + default: + return Form.Element.Serializers.textarea(element); + } + }, + + inputSelector: function(element) { + return element.checked ? element.value : null; + }, + + textarea: function(element) { + return element.value; + }, + + select: function(element) { + return this[element.type == 'select-one' ? + 'selectOne' : 'selectMany'](element); + }, + + selectOne: function(element) { + var index = element.selectedIndex; + return index >= 0 ? this.optionValue(element.options[index]) : null; + }, + + selectMany: function(element) { + var values, length = element.length; + if (!length) return null; + + for (var i = 0, values = []; i < length; i++) { + var opt = element.options[i]; + if (opt.selected) values.push(this.optionValue(opt)); + } + return values; + }, + + optionValue: function(opt) { + // extend element because hasAttribute may not be native + return Element.extend(opt).hasAttribute('value') ? opt.value : opt.text; + } +} + +/*--------------------------------------------------------------------------*/ + +Abstract.TimedObserver = function() {} +Abstract.TimedObserver.prototype = { + initialize: function(element, frequency, callback) { + this.frequency = frequency; + this.element = $(element); + this.callback = callback; + + this.lastValue = this.getValue(); + this.registerCallback(); + }, + + registerCallback: function() { + setInterval(this.onTimerEvent.bind(this), this.frequency * 1000); + }, + + onTimerEvent: function() { + var value = this.getValue(); + var changed = ('string' == typeof this.lastValue && 'string' == typeof value + ? this.lastValue != value : String(this.lastValue) != String(value)); + if (changed) { + this.callback(this.element, value); + this.lastValue = value; + } + } +} + +Form.Element.Observer = Class.create(); +Form.Element.Observer.prototype = Object.extend(new Abstract.TimedObserver(), { + getValue: function() { + return Form.Element.getValue(this.element); + } +}); + +Form.Observer = Class.create(); +Form.Observer.prototype = Object.extend(new Abstract.TimedObserver(), { + getValue: function() { + return Form.serialize(this.element); + } +}); + +/*--------------------------------------------------------------------------*/ + +Abstract.EventObserver = function() {} +Abstract.EventObserver.prototype = { + initialize: function(element, callback) { + this.element = $(element); + this.callback = callback; + + this.lastValue = this.getValue(); + if (this.element.tagName.toLowerCase() == 'form') + this.registerFormCallbacks(); + else + this.registerCallback(this.element); + }, + + onElementEvent: function() { + var value = this.getValue(); + if (this.lastValue != value) { + this.callback(this.element, value); + this.lastValue = value; + } + }, + + registerFormCallbacks: function() { + Form.getElements(this.element).each(this.registerCallback.bind(this)); + }, + + registerCallback: function(element) { + if (element.type) { + switch (element.type.toLowerCase()) { + case 'checkbox': + case 'radio': + Event.observe(element, 'click', this.onElementEvent.bind(this)); + break; + default: + Event.observe(element, 'change', this.onElementEvent.bind(this)); + break; + } + } + } +} + +Form.Element.EventObserver = Class.create(); +Form.Element.EventObserver.prototype = Object.extend(new Abstract.EventObserver(), { + getValue: function() { + return Form.Element.getValue(this.element); + } +}); + +Form.EventObserver = Class.create(); +Form.EventObserver.prototype = Object.extend(new Abstract.EventObserver(), { + getValue: function() { + return Form.serialize(this.element); + } +}); +if (!window.Event) { + var Event = new Object(); +} + +Object.extend(Event, { + KEY_BACKSPACE: 8, + KEY_TAB: 9, + KEY_RETURN: 13, + KEY_ESC: 27, + KEY_LEFT: 37, + KEY_UP: 38, + KEY_RIGHT: 39, + KEY_DOWN: 40, + KEY_DELETE: 46, + KEY_HOME: 36, + KEY_END: 35, + KEY_PAGEUP: 33, + KEY_PAGEDOWN: 34, + + element: function(event) { + return $(event.target || event.srcElement); + }, + + isLeftClick: function(event) { + return (((event.which) && (event.which == 1)) || + ((event.button) && (event.button == 1))); + }, + + pointerX: function(event) { + return event.pageX || (event.clientX + + (document.documentElement.scrollLeft || document.body.scrollLeft)); + }, + + pointerY: function(event) { + return event.pageY || (event.clientY + + (document.documentElement.scrollTop || document.body.scrollTop)); + }, + + stop: function(event) { + if (event.preventDefault) { + event.preventDefault(); + event.stopPropagation(); + } else { + event.returnValue = false; + event.cancelBubble = true; + } + }, + + // find the first node with the given tagName, starting from the + // node the event was triggered on; traverses the DOM upwards + findElement: function(event, tagName) { + var element = Event.element(event); + while (element.parentNode && (!element.tagName || + (element.tagName.toUpperCase() != tagName.toUpperCase()))) + element = element.parentNode; + return element; + }, + + observers: false, + + _observeAndCache: function(element, name, observer, useCapture) { + if (!this.observers) this.observers = []; + if (element.addEventListener) { + this.observers.push([element, name, observer, useCapture]); + element.addEventListener(name, observer, useCapture); + } else if (element.attachEvent) { + this.observers.push([element, name, observer, useCapture]); + element.attachEvent('on' + name, observer); + } + }, + + unloadCache: function() { + if (!Event.observers) return; + for (var i = 0, length = Event.observers.length; i < length; i++) { + Event.stopObserving.apply(this, Event.observers[i]); + Event.observers[i][0] = null; + } + Event.observers = false; + }, + + observe: function(element, name, observer, useCapture) { + element = $(element); + useCapture = useCapture || false; + + if (name == 'keypress' && + (Prototype.BrowserFeatures.WebKit || element.attachEvent)) + name = 'keydown'; + + Event._observeAndCache(element, name, observer, useCapture); + }, + + stopObserving: function(element, name, observer, useCapture) { + element = $(element); + useCapture = useCapture || false; + + if (name == 'keypress' && + (Prototype.BrowserFeatures.WebKit || element.attachEvent)) + name = 'keydown'; + + if (element.removeEventListener) { + element.removeEventListener(name, observer, useCapture); + } else if (element.detachEvent) { + try { + element.detachEvent('on' + name, observer); + } catch (e) {} + } + } +}); + +/* prevent memory leaks in IE */ +if (Prototype.BrowserFeatures.IE) + Event.observe(window, 'unload', Event.unloadCache, false); +var Position = { + // set to true if needed, warning: firefox performance problems + // NOT neeeded for page scrolling, only if draggable contained in + // scrollable elements + includeScrollOffsets: false, + + // must be called before calling withinIncludingScrolloffset, every time the + // page is scrolled + prepare: function() { + this.deltaX = window.pageXOffset + || document.documentElement.scrollLeft + || document.body.scrollLeft + || 0; + this.deltaY = window.pageYOffset + || document.documentElement.scrollTop + || document.body.scrollTop + || 0; + }, + + realOffset: function(element) { + var valueT = 0, valueL = 0; + do { + valueT += element.scrollTop || 0; + valueL += element.scrollLeft || 0; + element = element.parentNode; + } while (element); + return [valueL, valueT]; + }, + + cumulativeOffset: function(element) { + var valueT = 0, valueL = 0; + do { + valueT += element.offsetTop || 0; + valueL += element.offsetLeft || 0; + element = element.offsetParent; + } while (element); + return [valueL, valueT]; + }, + + positionedOffset: function(element) { + var valueT = 0, valueL = 0; + do { + valueT += element.offsetTop || 0; + valueL += element.offsetLeft || 0; + element = element.offsetParent; + if (element) { + if(element.tagName=='BODY') break; + var p = Element.getStyle(element, 'position'); + if (p == 'relative' || p == 'absolute') break; + } + } while (element); + return [valueL, valueT]; + }, + + 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; + }, + + // caches x/y coordinate pair to use with overlap + within: function(element, x, y) { + if (this.includeScrollOffsets) + return this.withinIncludingScrolloffsets(element, x, y); + this.xcomp = x; + this.ycomp = y; + this.offset = this.cumulativeOffset(element); + + return (y >= this.offset[1] && + y < this.offset[1] + element.offsetHeight && + x >= this.offset[0] && + x < this.offset[0] + element.offsetWidth); + }, + + withinIncludingScrolloffsets: function(element, x, y) { + var offsetcache = this.realOffset(element); + + this.xcomp = x + offsetcache[0] - this.deltaX; + this.ycomp = y + offsetcache[1] - this.deltaY; + this.offset = this.cumulativeOffset(element); + + return (this.ycomp >= this.offset[1] && + this.ycomp < this.offset[1] + element.offsetHeight && + this.xcomp >= this.offset[0] && + this.xcomp < this.offset[0] + element.offsetWidth); + }, + + // within must be called directly before + overlap: function(mode, element) { + if (!mode) return 0; + if (mode == 'vertical') + return ((this.offset[1] + element.offsetHeight) - this.ycomp) / + element.offsetHeight; + if (mode == 'horizontal') + return ((this.offset[0] + element.offsetWidth) - this.xcomp) / + element.offsetWidth; + }, + + 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 { + if (!window.opera || element.tagName=='BODY') { + valueT -= element.scrollTop || 0; + valueL -= element.scrollLeft || 0; + } + } while (element = element.parentNode); + + return [valueL, valueT]; + }, + + 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'; + }, + + 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'; + }, + + 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; + } +} + +// Safari returns margins on body which is incorrect if the child is absolutely +// positioned. For performance reasons, redefine Position.cumulativeOffset for +// KHTML/WebKit only. +if (Prototype.BrowserFeatures.WebKit) { + 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]; + } +} + +Element.addMethods(); \ No newline at end of file diff --git a/xCAT-web/js/window.js b/xCAT-web/js/window.js new file mode 100644 index 000000000..76d37933c --- /dev/null +++ b/xCAT-web/js/window.js @@ -0,0 +1,1843 @@ +// Copyright (c) 2006 Sébastien Gruhier (http://xilinus.com, http://itseb.com) +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// +// VERSION 1.3 + +var Window = Class.create(); + +Window.keepMultiModalWindow = false; +Window.hasEffectLib = (typeof Effect != 'undefined'); +Window.resizeEffectDuration = 0.4; + +Window.prototype = { + // Constructor + // Available parameters : className, blurClassName, title, minWidth, minHeight, maxWidth, maxHeight, width, height, top, left, bottom, right, resizable, zIndex, opacity, recenterAuto, wiredDrag + // hideEffect, showEffect, showEffectOptions, hideEffectOptions, effectOptions, url, draggable, closable, minimizable, maximizable, parent, onload + // add all callbacks (if you do not use an observer) + // onDestroy onStartResize onStartMove onResize onMove onEndResize onEndMove onFocus onBlur onBeforeShow onShow onHide onMinimize onMaximize onClose + + initialize: function() { + var id; + var optionIndex = 0; + // For backward compatibility like win= new Window("id", {...}) instead of win = new Window({id: "id", ...}) + if (arguments.length > 0) { + if (typeof arguments[0] == "string" ) { + id = arguments[0]; + optionIndex = 1; + } + else + id = arguments[0] ? arguments[0].id : null; + } + + // Generate unique ID if not specified + if (!id) + id = "window_" + new Date().getTime(); + + if ($(id)) + alert("Window " + id + " is already registered in the DOM! Make sure you use setDestroyOnClose() or destroyOnClose: true in the constructor"); + + this.options = Object.extend({ + className: "dialog", + blurClassName: null, + minWidth: 100, + minHeight: 20, + resizable: true, + closable: true, + minimizable: true, + maximizable: true, + draggable: true, + userData: null, + showEffect: (Window.hasEffectLib ? Effect.Appear : Element.show), + hideEffect: (Window.hasEffectLib ? Effect.Fade : Element.hide), + showEffectOptions: {}, + hideEffectOptions: {}, + effectOptions: null, + parent: document.body, + title: " ", + url: null, + onload: Prototype.emptyFunction, + width: 200, + height: 300, + opacity: 1, + recenterAuto: true, + wiredDrag: false, + closeCallback: null, + destroyOnClose: false, + gridX: 1, + gridY: 1 + }, arguments[optionIndex] || {}); + if (this.options.blurClassName) + this.options.focusClassName = this.options.className; + + if (typeof this.options.top == "undefined" && typeof this.options.bottom == "undefined") + this.options.top = this._round(Math.random()*500, this.options.gridY); + if (typeof this.options.left == "undefined" && typeof this.options.right == "undefined") + this.options.left = this._round(Math.random()*500, this.options.gridX); + + if (this.options.effectOptions) { + Object.extend(this.options.hideEffectOptions, this.options.effectOptions); + Object.extend(this.options.showEffectOptions, this.options.effectOptions); + if (this.options.showEffect == Element.Appear) + this.options.showEffectOptions.to = this.options.opacity; + } + if (Window.hasEffectLib) { + if (this.options.showEffect == Effect.Appear) + this.options.showEffectOptions.to = this.options.opacity; + + if (this.options.hideEffect == Effect.Fade) + this.options.hideEffectOptions.from = this.options.opacity; + } + if (this.options.hideEffect == Element.hide) + this.options.hideEffect = function(){ Element.hide(this.element); if (this.options.destroyOnClose) this.destroy(); }.bind(this) + + if (this.options.parent != document.body) + this.options.parent = $(this.options.parent); + + this.element = this._createWindow(id); + this.element.win = this; + + // Bind event listener + this.eventMouseDown = this._initDrag.bindAsEventListener(this); + this.eventMouseUp = this._endDrag.bindAsEventListener(this); + this.eventMouseMove = this._updateDrag.bindAsEventListener(this); + this.eventOnLoad = this._getWindowBorderSize.bindAsEventListener(this); + this.eventMouseDownContent = this.toFront.bindAsEventListener(this); + this.eventResize = this._recenter.bindAsEventListener(this); + + this.topbar = $(this.element.id + "_top"); + this.bottombar = $(this.element.id + "_bottom"); + this.content = $(this.element.id + "_content"); + + Event.observe(this.topbar, "mousedown", this.eventMouseDown); + Event.observe(this.bottombar, "mousedown", this.eventMouseDown); + Event.observe(this.content, "mousedown", this.eventMouseDownContent); + Event.observe(window, "load", this.eventOnLoad); + Event.observe(window, "resize", this.eventResize); + Event.observe(window, "scroll", this.eventResize); + Event.observe(this.options.parent, "scroll", this.eventResize); + + if (this.options.draggable) { + var that = this; + [this.topbar, this.topbar.up().previous(), this.topbar.up().next()].each(function(element) { + element.observe("mousedown", that.eventMouseDown); + element.addClassName("top_draggable"); + }); + [this.bottombar.up(), this.bottombar.up().previous(), this.bottombar.up().next()].each(function(element) { + element.observe("mousedown", that.eventMouseDown); + element.addClassName("bottom_draggable"); + }); + + } + + if (this.options.resizable) { + this.sizer = $(this.element.id + "_sizer"); + Event.observe(this.sizer, "mousedown", this.eventMouseDown); + } + + this.useLeft = null; + this.useTop = null; + if (typeof this.options.left != "undefined") { + this.element.setStyle({left: parseFloat(this.options.left) + 'px'}); + this.useLeft = true; + } + else { + this.element.setStyle({right: parseFloat(this.options.right) + 'px'}); + this.useLeft = false; + } + + if (typeof this.options.top != "undefined") { + this.element.setStyle({top: parseFloat(this.options.top) + 'px'}); + this.useTop = true; + } + else { + this.element.setStyle({bottom: parseFloat(this.options.bottom) + 'px'}); + this.useTop = false; + } + + this.storedLocation = null; + + this.setOpacity(this.options.opacity); + if (this.options.zIndex) + this.setZIndex(this.options.zIndex) + + if (this.options.destroyOnClose) + this.setDestroyOnClose(true); + + this._getWindowBorderSize(); + this.width = this.options.width; + this.height = this.options.height; + this.visible = false; + + this.constraint = false; + this.constraintPad = {top: 0, left:0, bottom:0, right:0}; + + if (this.width && this.height) + this.setSize(this.options.width, this.options.height); + this.setTitle(this.options.title) + Windows.register(this); + }, + + // Destructor + destroy: function() { + this._notify("onDestroy"); + Event.stopObserving(this.topbar, "mousedown", this.eventMouseDown); + Event.stopObserving(this.bottombar, "mousedown", this.eventMouseDown); + Event.stopObserving(this.content, "mousedown", this.eventMouseDownContent); + + Event.stopObserving(window, "load", this.eventOnLoad); + Event.stopObserving(window, "resize", this.eventResize); + Event.stopObserving(window, "scroll", this.eventResize); + + Event.stopObserving(this.content, "load", this.options.onload); + + if (this._oldParent) { + var content = this.getContent(); + var originalContent = null; + for(var i = 0; i < content.childNodes.length; i++) { + originalContent = content.childNodes[i]; + if (originalContent.nodeType == 1) + break; + originalContent = null; + } + if (originalContent) + this._oldParent.appendChild(originalContent); + this._oldParent = null; + } + + if (this.sizer) + Event.stopObserving(this.sizer, "mousedown", this.eventMouseDown); + + if (this.options.url) + this.content.src = null + + if(this.iefix) + Element.remove(this.iefix); + + Element.remove(this.element); + Windows.unregister(this); + }, + + // Sets close callback, if it sets, it should return true to be able to close the window. + setCloseCallback: function(callback) { + this.options.closeCallback = callback; + }, + + // Gets window content + getContent: function () { + return this.content; + }, + + // Sets the content with an element id + setContent: function(id, autoresize, autoposition) { + var element = $(id); + if (null == element) throw "Unable to find element '" + id + "' in DOM"; + this._oldParent = element.parentNode; + + var d = null; + var p = null; + + if (autoresize) + d = Element.getDimensions(element); + if (autoposition) + p = Position.cumulativeOffset(element); + + var content = this.getContent(); + // Clear HTML (and even iframe) + this.setHTMLContent(""); + content = this.getContent(); + + content.appendChild(element); + element.show(); + if (autoresize) + this.setSize(d.width, d.height); + if (autoposition) + this.setLocation(p[1] - this.heightN, p[0] - this.widthW); + }, + + setHTMLContent: function(html) { + // It was an url (iframe), recreate a div content instead of iframe content + if (this.options.url) { + this.content.src = null; + this.options.url = null; + + var content ="
"; + $(this.getId() +"_table_content").innerHTML = content; + + this.content = $(this.element.id + "_content"); + } + + this.getContent().innerHTML = html; + }, + + setAjaxContent: function(url, options, showCentered, showModal) { + this.showFunction = showCentered ? "showCenter" : "show"; + this.showModal = showModal || false; + + options = options || {}; + + // Clear HTML (and even iframe) + this.setHTMLContent(""); + + this.onComplete = options.onComplete; + if (! this._onCompleteHandler) + this._onCompleteHandler = this._setAjaxContent.bind(this); + options.onComplete = this._onCompleteHandler; + + new Ajax.Request(url, options); + options.onComplete = this.onComplete; + }, + + _setAjaxContent: function(originalRequest) { + Element.update(this.getContent(), originalRequest.responseText); + if (this.onComplete) + this.onComplete(originalRequest); + this.onComplete = null; + this[this.showFunction](this.showModal) + }, + + setURL: function(url) { + // Not an url content, change div to iframe + if (this.options.url) + this.content.src = null; + this.options.url = url; + var content= ""; + $(this.getId() +"_table_content").innerHTML = content; + + this.content = $(this.element.id + "_content"); + }, + + getURL: function() { + return this.options.url ? this.options.url : null; + }, + + refresh: function() { + if (this.options.url) + $(this.element.getAttribute('id') + '_content').src = this.options.url; + }, + + // Stores position/size in a cookie, by default named with window id + setCookie: function(name, expires, path, domain, secure) { + name = name || this.element.id; + this.cookie = [name, expires, path, domain, secure]; + + // Get cookie + var value = WindowUtilities.getCookie(name) + // If exists + if (value) { + var values = value.split(','); + var x = values[0].split(':'); + var y = values[1].split(':'); + + var w = parseFloat(values[2]), h = parseFloat(values[3]); + var mini = values[4]; + var maxi = values[5]; + + this.setSize(w, h); + if (mini == "true") + this.doMinimize = true; // Minimize will be done at onload window event + else if (maxi == "true") + this.doMaximize = true; // Maximize will be done at onload window event + + this.useLeft = x[0] == "l"; + this.useTop = y[0] == "t"; + + this.element.setStyle(this.useLeft ? {left: x[1]} : {right: x[1]}); + this.element.setStyle(this.useTop ? {top: y[1]} : {bottom: y[1]}); + } + }, + + // Gets window ID + getId: function() { + return this.element.id; + }, + + // Detroys itself when closing + setDestroyOnClose: function() { + this.options.destroyOnClose = true; + }, + + setConstraint: function(bool, padding) { + this.constraint = bool; + this.constraintPad = Object.extend(this.constraintPad, padding || {}); + // Reset location to apply constraint + if (this.useTop && this.useLeft) + this.setLocation(parseFloat(this.element.style.top), parseFloat(this.element.style.left)); + }, + + // initDrag event + + _initDrag: function(event) { + // No resize on minimized window + if (Event.element(event) == this.sizer && this.isMinimized()) + return; + + // No move on maximzed window + if (Event.element(event) != this.sizer && this.isMaximized()) + return; + + if (Prototype.BrowserFeatures.IE && this.heightN == 0) + this._getWindowBorderSize(); + + // Get pointer X,Y + this.pointer = [this._round(Event.pointerX(event), this.options.gridX), this._round(Event.pointerY(event), this.options.gridY)]; + if (this.options.wiredDrag) + this.currentDrag = this._createWiredElement(); + else + this.currentDrag = this.element; + + // Resize + if (Event.element(event) == this.sizer) { + this.doResize = true; + this.widthOrg = this.width; + this.heightOrg = this.height; + this.bottomOrg = parseFloat(this.element.getStyle('bottom')); + this.rightOrg = parseFloat(this.element.getStyle('right')); + this._notify("onStartResize"); + } + else { + this.doResize = false; + + // Check if click on close button, + var closeButton = $(this.getId() + '_close'); + if (closeButton && Position.within(closeButton, this.pointer[0], this.pointer[1])) { + this.currentDrag = null; + return; + } + + this.toFront(); + + if (! this.options.draggable) + return; + this._notify("onStartMove"); + } + // Register global event to capture mouseUp and mouseMove + Event.observe(document, "mouseup", this.eventMouseUp, false); + Event.observe(document, "mousemove", this.eventMouseMove, false); + + // Add an invisible div to keep catching mouse event over iframes + WindowUtilities.disableScreen('__invisible__', '__invisible__', this.overlayOpacity); + + // Stop selection while dragging + document.body.ondrag = function () { return false; }; + document.body.onselectstart = function () { return false; }; + + this.currentDrag.show(); + Event.stop(event); + }, + + _round: function(val, round) { + return round == 1 ? val : val = Math.floor(val / round) * round; + }, + + // updateDrag event + _updateDrag: function(event) { + var pointer = [this._round(Event.pointerX(event), this.options.gridX), this._round(Event.pointerY(event), this.options.gridY)]; + var dx = pointer[0] - this.pointer[0]; + var dy = pointer[1] - this.pointer[1]; + + // Resize case, update width/height + if (this.doResize) { + var w = this.widthOrg + dx; + var h = this.heightOrg + dy; + + dx = this.width - this.widthOrg + dy = this.height - this.heightOrg + + // Check if it's a right position, update it to keep upper-left corner at the same position + if (this.useLeft) + w = this._updateWidthConstraint(w) + else + this.currentDrag.setStyle({right: (this.rightOrg -dx) + 'px'}); + // Check if it's a bottom position, update it to keep upper-left corner at the same position + if (this.useTop) + h = this._updateHeightConstraint(h) + else + this.currentDrag.setStyle({bottom: (this.bottomOrg -dy) + 'px'}); + + this.setSize(w , h); + this._notify("onResize"); + } + // Move case, update top/left + else { + this.pointer = pointer; + + if (this.useLeft) { + var left = parseFloat(this.currentDrag.getStyle('left')) + dx; + var newLeft = this._updateLeftConstraint(left); + // Keep mouse pointer correct + this.pointer[0] += newLeft-left; + this.currentDrag.setStyle({left: newLeft + 'px'}); + } + else + this.currentDrag.setStyle({right: parseFloat(this.currentDrag.getStyle('right')) - dx + 'px'}); + + if (this.useTop) { + var top = parseFloat(this.currentDrag.getStyle('top')) + dy; + var newTop = this._updateTopConstraint(top); + // Keep mouse pointer correct + this.pointer[1] += newTop - top; + this.currentDrag.setStyle({top: newTop + 'px'}); + } + else + this.currentDrag.setStyle({bottom: parseFloat(this.currentDrag.getStyle('bottom')) - dy + 'px'}); + + this._notify("onMove"); + } + if (this.iefix) + this._fixIEOverlapping(); + + this._removeStoreLocation(); + Event.stop(event); + }, + + // endDrag callback + _endDrag: function(event) { + // Remove temporary div over iframes + WindowUtilities.enableScreen('__invisible__'); + + if (this.doResize) + this._notify("onEndResize"); + else + this._notify("onEndMove"); + + // Release event observing + Event.stopObserving(document, "mouseup", this.eventMouseUp,false); + Event.stopObserving(document, "mousemove", this.eventMouseMove, false); + + Event.stop(event); + + this._hideWiredElement(); + + // Store new location/size if need be + this._saveCookie() + + // Restore selection + document.body.ondrag = null; + document.body.onselectstart = null; + }, + + _updateLeftConstraint: function(left) { + if (this.constraint && this.useLeft && this.useTop) { + var width = this.options.parent == document.body ? WindowUtilities.getPageSize().windowWidth : this.options.parent.getDimensions().width; + + if (left < this.constraintPad.left) + left = this.constraintPad.left; + if (left + this.width + this.widthE + this.widthW > width - this.constraintPad.right) + left = width - this.constraintPad.right - this.width - this.widthE - this.widthW; + } + return left; + }, + + _updateTopConstraint: function(top) { + if (this.constraint && this.useLeft && this.useTop) { + var height = this.options.parent == document.body ? WindowUtilities.getPageSize().windowHeight : this.options.parent.getDimensions().height; + + var h = this.height + this.heightN + this.heightS; + + if (top < this.constraintPad.top) + top = this.constraintPad.top; + if (top + h > height - this.constraintPad.bottom) + top = height - this.constraintPad.bottom - h; + } + return top; + }, + + _updateWidthConstraint: function(w) { + if (this.constraint && this.useLeft && this.useTop) { + var width = this.options.parent == document.body ? WindowUtilities.getPageSize().windowWidth : this.options.parent.getDimensions().width; + var left = parseFloat(this.element.getStyle("left")); + + if (left + w + this.widthE + this.widthW > width - this.constraintPad.right) + w = width - this.constraintPad.right - left - this.widthE - this.widthW; + } + return w; + }, + + _updateHeightConstraint: function(h) { + if (this.constraint && this.useLeft && this.useTop) { + var height = this.options.parent == document.body ? WindowUtilities.getPageSize().windowHeight : this.options.parent.getDimensions().height; + var top = parseFloat(this.element.getStyle("top")); + + if (top + h + this.heightN + this.heightS > height - this.constraintPad.bottom) + h = height - this.constraintPad.bottom - top - this.heightN - this.heightS; + } + return h; + }, + + + // Creates HTML window code + _createWindow: function(id) { + var className = this.options.className; + var win = document.createElement("div"); + win.setAttribute('id', id); + win.className = "dialog"; + + var content; + if (this.options.url) + content= ""; + else + content ="
"; + + var closeDiv = this.options.closable ? "
" : ""; + var minDiv = this.options.minimizable ? "
" : ""; + var maxDiv = this.options.maximizable ? "
" : ""; + var seAttributes = this.options.resizable ? "class='" + className + "_sizer' id='" + id + "_sizer'" : "class='" + className + "_se'"; + var blank = "../themes/default/blank.gif"; + + win.innerHTML = closeDiv + minDiv + maxDiv + "\ + \ + \ + \ + \ + \ + \ +
"+ this.options.title +"
\ + \ + \ + \ + \ + \ + \ +
" + content + "
\ + \ + \ + \ + \ + \ + \ +
\ + "; + Element.hide(win); + this.options.parent.insertBefore(win, this.options.parent.firstChild); + Event.observe($(id + "_content"), "load", this.options.onload); + return win; + }, + + + changeClassName: function(newClassName) { + var className = this.options.className; + var id = this.getId(); + $A(["_close", "_minimize", "_maximize", "_sizer", "_content"]).each(function(value) { this._toggleClassName($(id + value), className + value, newClassName + value) }.bind(this)); + this._toggleClassName($(id + "_top"), className + "_title", newClassName + "_title"); + $$("#" + id + " td").each(function(td) {td.className = td.className.sub(className,newClassName); }); + this.options.className = newClassName; + }, + + _toggleClassName: function(element, oldClassName, newClassName) { + if (element) { + element.removeClassName(oldClassName); + element.addClassName(newClassName); + } + }, + + // Sets window location + setLocation: function(top, left) { + top = this._updateTopConstraint(top); + left = this._updateLeftConstraint(left); + + var e = this.currentDrag || this.element; + e.setStyle({top: top + 'px'}); + e.setStyle({left: left + 'px'}); + + this.useLeft = true; + this.useTop = true; + }, + + getLocation: function() { + var location = {}; + if (this.useTop) + location = Object.extend(location, {top: this.element.getStyle("top")}); + else + location = Object.extend(location, {bottom: this.element.getStyle("bottom")}); + if (this.useLeft) + location = Object.extend(location, {left: this.element.getStyle("left")}); + else + location = Object.extend(location, {right: this.element.getStyle("right")}); + + return location; + }, + + // Gets window size + getSize: function() { + return {width: this.width, height: this.height}; + }, + + // Sets window size + setSize: function(width, height, useEffect) { + width = parseFloat(width); + height = parseFloat(height); + + // Check min and max size + if (!this.minimized && width < this.options.minWidth) + width = this.options.minWidth; + + if (!this.minimized && height < this.options.minHeight) + height = this.options.minHeight; + + if (this.options. maxHeight && height > this.options. maxHeight) + height = this.options. maxHeight; + + if (this.options. maxWidth && width > this.options. maxWidth) + width = this.options. maxWidth; + + + if (this.useTop && this.useLeft && Window.hasEffectLib && Effect.ResizeWindow && useEffect) { + new Effect.ResizeWindow(this, null, null, width, height, {duration: Window.resizeEffectDuration}); + } else { + this.width = width; + this.height = height; + var e = this.currentDrag ? this.currentDrag : this.element; + + e.setStyle({width: width + this.widthW + this.widthE + "px"}) + e.setStyle({height: height + this.heightN + this.heightS + "px"}) + + // Update content size + if (!this.currentDrag || this.currentDrag == this.element) { + var content = $(this.element.id + '_content'); + content.setStyle({height: height + 'px'}); + content.setStyle({width: width + 'px'}); + } + } + }, + + updateHeight: function() { + this.setSize(this.width, this.content.scrollHeight, true); + }, + + updateWidth: function() { + this.setSize(this.content.scrollWidth, this.height, true); + }, + + // Brings window to front + toFront: function() { + if (this.element.style.zIndex < Windows.maxZIndex) + this.setZIndex(Windows.maxZIndex + 1); + if (this.iefix) + this._fixIEOverlapping(); + }, + + getBounds: function(insideOnly) { + if (! this.width || !this.height || !this.visible) + this.computeBounds(); + var w = this.width; + var h = this.height; + + if (!insideOnly) { + w += this.widthW + this.widthE; + h += this.heightN + this.heightS; + } + var bounds = Object.extend(this.getLocation(), {width: w + "px", height: h + "px"}); + return bounds; + }, + + computeBounds: function() { + if (! this.width || !this.height) { + var size = WindowUtilities._computeSize(this.content.innerHTML, this.content.id, this.width, this.height, 0, this.options.className) + if (this.height) + this.width = size + 5 + else + this.height = size + 5 + } + + this.setSize(this.width, this.height); + if (this.centered) + this._center(this.centerTop, this.centerLeft); + }, + + // Displays window modal state or not + show: function(modal) { + this.visible = true; + if (modal) { + // Hack for Safari !! + if (typeof this.overlayOpacity == "undefined") { + var that = this; + setTimeout(function() {that.show(modal)}, 10); + return; + } + Windows.addModalWindow(this); + + this.modal = true; + this.setZIndex(Windows.maxZIndex + 1); + Windows.unsetOverflow(this); + } + else + if (!this.element.style.zIndex) + this.setZIndex(Windows.maxZIndex + 1); + + // To restore overflow if need be + if (this.oldStyle) + this.getContent().setStyle({overflow: this.oldStyle}); + + this.computeBounds(); + + this._notify("onBeforeShow"); + if (this.options.showEffect != Element.show && this.options.showEffectOptions) + this.options.showEffect(this.element, this.options.showEffectOptions); + else + this.options.showEffect(this.element); + + this._checkIEOverlapping(); + WindowUtilities.focusedWindow = this + this._notify("onShow"); + }, + + // Displays window modal state or not at the center of the page + showCenter: function(modal, top, left) { + this.centered = true; + this.centerTop = top; + this.centerLeft = left; + + this.show(modal); + }, + + isVisible: function() { + return this.visible; + }, + + _center: function(top, left) { + var windowScroll = WindowUtilities.getWindowScroll(this.options.parent); + var pageSize = WindowUtilities.getPageSize(this.options.parent); + if (typeof top == "undefined") + top = (pageSize.windowHeight - (this.height + this.heightN + this.heightS))/2; + top += windowScroll.top + + if (typeof left == "undefined") + left = (pageSize.windowWidth - (this.width + this.widthW + this.widthE))/2; + left += windowScroll.left + this.setLocation(top, left); + this.toFront(); + }, + + _recenter: function(event) { + if (this.centered) { + var pageSize = WindowUtilities.getPageSize(this.options.parent); + var windowScroll = WindowUtilities.getWindowScroll(this.options.parent); + + // Check for this stupid IE that sends dumb events + if (this.pageSize && this.pageSize.windowWidth == pageSize.windowWidth && this.pageSize.windowHeight == pageSize.windowHeight && + this.windowScroll.left == windowScroll.left && this.windowScroll.top == windowScroll.top) + return; + this.pageSize = pageSize; + this.windowScroll = windowScroll; + // set height of Overlay to take up whole page and show + if ($('overlay_modal')) + $('overlay_modal').setStyle({height: (pageSize.pageHeight + 'px')}); + + if (this.options.recenterAuto) + this._center(this.centerTop, this.centerLeft); + } + }, + + // Hides window + hide: function() { + this.visible = false; + if (this.modal) { + Windows.removeModalWindow(this); + Windows.resetOverflow(); + } + // To avoid bug on scrolling bar + this.oldStyle = this.getContent().getStyle('overflow') || "auto" + this.getContent().setStyle({overflow: "hidden"}); + + this.options.hideEffect(this.element, this.options.hideEffectOptions); + + if(this.iefix) + this.iefix.hide(); + + if (!this.doNotNotifyHide) + this._notify("onHide"); + }, + + close: function() { + // Asks closeCallback if exists + if (this.visible) { + if (this.options.closeCallback && ! this.options.closeCallback(this)) + return; + + if (this.options.destroyOnClose) { + var destroyFunc = this.destroy.bind(this); + if (this.options.hideEffectOptions.afterFinish) { + var func = this.options.hideEffectOptions.afterFinish; + this.options.hideEffectOptions.afterFinish = function() {func();destroyFunc() } + } + else + this.options.hideEffectOptions.afterFinish = function() {destroyFunc() } + } + Windows.updateFocusedWindow(); + + this.doNotNotifyHide = true; + this.hide(); + this.doNotNotifyHide = false; + this._notify("onClose"); + } + }, + + minimize: function() { + if (this.resizing) + return; + + var r2 = $(this.getId() + "_row2"); + + if (!this.minimized) { + this.minimized = true; + + var dh = r2.getDimensions().height; + this.r2Height = dh; + var h = this.element.getHeight() - dh; + + if (this.useLeft && this.useTop && Window.hasEffectLib && Effect.ResizeWindow) { + new Effect.ResizeWindow(this, null, null, null, this.height -dh, {duration: Window.resizeEffectDuration}); + } else { + this.height -= dh; + this.element.setStyle({height: h + "px"}); + r2.hide(); + } + + if (! this.useTop) { + var bottom = parseFloat(this.element.getStyle('bottom')); + this.element.setStyle({bottom: (bottom + dh) + 'px'}); + } + } + else { + this.minimized = false; + + var dh = this.r2Height; + this.r2Height = null; + if (this.useLeft && this.useTop && Window.hasEffectLib && Effect.ResizeWindow) { + new Effect.ResizeWindow(this, null, null, null, this.height + dh, {duration: Window.resizeEffectDuration}); + } + else { + var h = this.element.getHeight() + dh; + this.height += dh; + this.element.setStyle({height: h + "px"}) + r2.show(); + } + if (! this.useTop) { + var bottom = parseFloat(this.element.getStyle('bottom')); + this.element.setStyle({bottom: (bottom - dh) + 'px'}); + } + this.toFront(); + } + this._notify("onMinimize"); + + // Store new location/size if need be + this._saveCookie() + }, + + maximize: function() { + if (this.isMinimized() || this.resizing) + return; + + if (Prototype.BrowserFeatures.IE && this.heightN == 0) + this._getWindowBorderSize(); + + if (this.storedLocation != null) { + this._restoreLocation(); + if(this.iefix) + this.iefix.hide(); + } + else { + this._storeLocation(); + Windows.unsetOverflow(this); + + var windowScroll = WindowUtilities.getWindowScroll(this.options.parent); + var pageSize = WindowUtilities.getPageSize(this.options.parent); + var left = windowScroll.left; + var top = windowScroll.top; + + if (this.options.parent != document.body) { + windowScroll = {top:0, left:0, bottom:0, right:0}; + var dim = this.options.parent.getDimensions(); + pageSize.windowWidth = dim.width; + pageSize.windowHeight = dim.height; + top = 0; + left = 0; + } + + if (this.constraint) { + pageSize.windowWidth -= Math.max(0, this.constraintPad.left) + Math.max(0, this.constraintPad.right); + pageSize.windowHeight -= Math.max(0, this.constraintPad.top) + Math.max(0, this.constraintPad.bottom); + left += Math.max(0, this.constraintPad.left); + top += Math.max(0, this.constraintPad.top); + } + + var width = pageSize.windowWidth - this.widthW - this.widthE; + var height= pageSize.windowHeight - this.heightN - this.heightS; + + if (this.useLeft && this.useTop && Window.hasEffectLib && Effect.ResizeWindow) { + new Effect.ResizeWindow(this, top, left, width, height, {duration: Window.resizeEffectDuration}); + } + else { + this.setSize(width, height); + this.element.setStyle(this.useLeft ? {left: left} : {right: left}); + this.element.setStyle(this.useTop ? {top: top} : {bottom: top}); + } + + this.toFront(); + if (this.iefix) + this._fixIEOverlapping(); + } + this._notify("onMaximize"); + + // Store new location/size if need be + this._saveCookie() + }, + + isMinimized: function() { + return this.minimized; + }, + + isMaximized: function() { + return (this.storedLocation != null); + }, + + setOpacity: function(opacity) { + if (Element.setOpacity) + Element.setOpacity(this.element, opacity); + }, + + setZIndex: function(zindex) { + this.element.setStyle({zIndex: zindex}); + Windows.updateZindex(zindex, this); + }, + + setTitle: function(newTitle) { + if (!newTitle || newTitle == "") + newTitle = " "; + + Element.update(this.element.id + '_top', newTitle); + }, + + getTitle: function() { + return $(this.element.id + '_top').innerHTML; + }, + + setStatusBar: function(element) { + var statusBar = $(this.getId() + "_bottom"); + + if (typeof(element) == "object") { + if (this.bottombar.firstChild) + this.bottombar.replaceChild(element, this.bottombar.firstChild); + else + this.bottombar.appendChild(element); + } + else + this.bottombar.innerHTML = element; + }, + + _checkIEOverlapping: function() { + if(!this.iefix && (navigator.appVersion.indexOf('MSIE')>0) && (navigator.userAgent.indexOf('Opera')<0) && (this.element.getStyle('position')=='absolute')) { + new Insertion.After(this.element.id, ''); + this.iefix = $(this.element.id+'_iefix'); + } + if(this.iefix) + setTimeout(this._fixIEOverlapping.bind(this), 50); + }, + + _fixIEOverlapping: function() { + Position.clone(this.element, this.iefix); + this.iefix.style.zIndex = this.element.style.zIndex - 1; + this.iefix.show(); + }, + + _getWindowBorderSize: function(event) { + // Hack to get real window border size!! + var div = this._createHiddenDiv(this.options.className + "_n") + this.heightN = Element.getDimensions(div).height; + div.parentNode.removeChild(div) + + var div = this._createHiddenDiv(this.options.className + "_s") + this.heightS = Element.getDimensions(div).height; + div.parentNode.removeChild(div) + + var div = this._createHiddenDiv(this.options.className + "_e") + this.widthE = Element.getDimensions(div).width; + div.parentNode.removeChild(div) + + var div = this._createHiddenDiv(this.options.className + "_w") + this.widthW = Element.getDimensions(div).width; + div.parentNode.removeChild(div); + + var div = document.createElement("div"); + div.className = "overlay_" + this.options.className ; + document.body.appendChild(div); + //alert("no timeout:\nopacity: " + div.getStyle("opacity") + "\nwidth: " + document.defaultView.getComputedStyle(div, null).width); + var that = this; + + // Workaround for Safari!! + setTimeout(function() {that.overlayOpacity = ($(div).getStyle("opacity")); div.parentNode.removeChild(div);}, 10); + + // Workaround for IE!! + if (Prototype.BrowserFeatures.IE) { + this.heightS = $(this.getId() +"_row3").getDimensions().height; + this.heightN = $(this.getId() +"_row1").getDimensions().height; + } + + // Safari size fix + if (Prototype.BrowserFeatures.WebKit && Prototype.BrowserFeatures.WebKitVersion < 420) + this.setSize(this.width, this.height); + if (this.doMaximize) + this.maximize(); + if (this.doMinimize) + this.minimize(); + }, + + _createHiddenDiv: function(className) { + var objBody = document.body; + var win = document.createElement("div"); + win.setAttribute('id', this.element.id+ "_tmp"); + win.className = className; + win.style.display = 'none'; + win.innerHTML = ''; + objBody.insertBefore(win, objBody.firstChild); + return win; + }, + + _storeLocation: function() { + if (this.storedLocation == null) { + this.storedLocation = {useTop: this.useTop, useLeft: this.useLeft, + top: this.element.getStyle('top'), bottom: this.element.getStyle('bottom'), + left: this.element.getStyle('left'), right: this.element.getStyle('right'), + width: this.width, height: this.height }; + } + }, + + _restoreLocation: function() { + if (this.storedLocation != null) { + this.useLeft = this.storedLocation.useLeft; + this.useTop = this.storedLocation.useTop; + + if (this.useLeft && this.useTop && Window.hasEffectLib && Effect.ResizeWindow) + new Effect.ResizeWindow(this, this.storedLocation.top, this.storedLocation.left, this.storedLocation.width, this.storedLocation.height, {duration: Window.resizeEffectDuration}); + else { + this.element.setStyle(this.useLeft ? {left: this.storedLocation.left} : {right: this.storedLocation.right}); + this.element.setStyle(this.useTop ? {top: this.storedLocation.top} : {bottom: this.storedLocation.bottom}); + this.setSize(this.storedLocation.width, this.storedLocation.height); + } + + Windows.resetOverflow(); + this._removeStoreLocation(); + } + }, + + _removeStoreLocation: function() { + this.storedLocation = null; + }, + + _saveCookie: function() { + if (this.cookie) { + var value = ""; + if (this.useLeft) + value += "l:" + (this.storedLocation ? this.storedLocation.left : this.element.getStyle('left')) + else + value += "r:" + (this.storedLocation ? this.storedLocation.right : this.element.getStyle('right')) + if (this.useTop) + value += ",t:" + (this.storedLocation ? this.storedLocation.top : this.element.getStyle('top')) + else + value += ",b:" + (this.storedLocation ? this.storedLocation.bottom :this.element.getStyle('bottom')) + + value += "," + (this.storedLocation ? this.storedLocation.width : this.width); + value += "," + (this.storedLocation ? this.storedLocation.height : this.height); + value += "," + this.isMinimized(); + value += "," + this.isMaximized(); + WindowUtilities.setCookie(value, this.cookie) + } + }, + + _createWiredElement: function() { + if (! this.wiredElement) { + if (Prototype.BrowserFeatures.IE) + this._getWindowBorderSize(); + var div = document.createElement("div"); + div.className = "wired_frame " + this.options.className + "_wired_frame"; + + div.style.position = 'absolute'; + this.options.parent.insertBefore(div, this.options.parent.firstChild); + this.wiredElement = $(div); + } + if (this.useLeft) + this.wiredElement.setStyle({left: this.element.getStyle('left')}); + else + this.wiredElement.setStyle({right: this.element.getStyle('right')}); + + if (this.useTop) + this.wiredElement.setStyle({top: this.element.getStyle('top')}); + else + this.wiredElement.setStyle({bottom: this.element.getStyle('bottom')}); + + var dim = this.element.getDimensions(); + this.wiredElement.setStyle({width: dim.width + "px", height: dim.height +"px"}); + + this.wiredElement.setStyle({zIndex: Windows.maxZIndex+30}); + return this.wiredElement; + }, + + _hideWiredElement: function() { + if (! this.wiredElement || ! this.currentDrag) + return; + if (this.currentDrag == this.element) + this.currentDrag = null; + else { + if (this.useLeft) + this.element.setStyle({left: this.currentDrag.getStyle('left')}); + else + this.element.setStyle({right: this.currentDrag.getStyle('right')}); + + if (this.useTop) + this.element.setStyle({top: this.currentDrag.getStyle('top')}); + else + this.element.setStyle({bottom: this.currentDrag.getStyle('bottom')}); + + this.currentDrag.hide(); + this.currentDrag = null; + if (this.doResize) + this.setSize(this.width, this.height); + } + }, + + _notify: function(eventName) { + if (this.options[eventName]) + this.options[eventName](this); + else + Windows.notify(eventName, this); + } +}; + +// Windows containers, register all page windows +var Windows = { + windows: [], + modalWindows: [], + observers: [], + focusedWindow: null, + maxZIndex: 0, + overlayShowEffectOptions: {duration: 0.5}, + overlayHideEffectOptions: {duration: 0.5}, + + addObserver: function(observer) { + this.removeObserver(observer); + this.observers.push(observer); + }, + + removeObserver: function(observer) { + this.observers = this.observers.reject( function(o) { return o==observer }); + }, + + // onDestroy onStartResize onStartMove onResize onMove onEndResize onEndMove onFocus onBlur onBeforeShow onShow onHide onMinimize onMaximize onClose + notify: function(eventName, win) { + this.observers.each( function(o) {if(o[eventName]) o[eventName](eventName, win);}); + }, + + // Gets window from its id + getWindow: function(id) { + return this.windows.detect(function(d) { return d.getId() ==id }); + }, + + // Gets the last focused window + getFocusedWindow: function() { + return this.focusedWindow; + }, + + updateFocusedWindow: function() { + this.focusedWindow = this.windows.length >=2 ? this.windows[this.windows.length-2] : null; + }, + + // Registers a new window (called by Windows constructor) + register: function(win) { + this.windows.push(win); + }, + + // Add a modal window in the stack + addModalWindow: function(win) { + // Disable screen if first modal window + if (this.modalWindows.length == 0) { + WindowUtilities.disableScreen(win.options.className, 'overlay_modal', win.overlayOpacity, win.getId(), win.options.parent); + } + else { + // Move overlay over all windows + if (Window.keepMultiModalWindow) { + $('overlay_modal').style.zIndex = Windows.maxZIndex + 1; + Windows.maxZIndex += 1; + WindowUtilities._hideSelect(this.modalWindows.last().getId()); + } + // Hide current modal window + else + this.modalWindows.last().element.hide(); + // Fucking IE select issue + WindowUtilities._showSelect(win.getId()); + } + this.modalWindows.push(win); + }, + + removeModalWindow: function(win) { + this.modalWindows.pop(); + + // No more modal windows + if (this.modalWindows.length == 0) + WindowUtilities.enableScreen(); + else { + if (Window.keepMultiModalWindow) { + this.modalWindows.last().toFront(); + WindowUtilities._showSelect(this.modalWindows.last().getId()); + } + else + this.modalWindows.last().element.show(); + } + }, + + // Registers a new window (called by Windows constructor) + register: function(win) { + this.windows.push(win); + }, + + // Unregisters a window (called by Windows destructor) + unregister: function(win) { + this.windows = this.windows.reject(function(d) { return d==win }); + }, + + // Closes all windows + closeAll: function() { + this.windows.each( function(w) {Windows.close(w.getId())} ); + }, + + closeAllModalWindows: function() { + WindowUtilities.enableScreen(); + this.modalWindows.each( function(win) {if (win) win.close()}); + }, + + // Minimizes a window with its id + minimize: function(id, event) { + var win = this.getWindow(id) + if (win && win.visible) + win.minimize(); + Event.stop(event); + }, + + // Maximizes a window with its id + maximize: function(id, event) { + var win = this.getWindow(id) + if (win && win.visible) + win.maximize(); + Event.stop(event); + }, + + // Closes a window with its id + close: function(id, event) { + var win = this.getWindow(id); + if (win) + win.close(); + if (event) + Event.stop(event); + }, + + blur: function(id) { + var win = this.getWindow(id); + if (!win) + return; + if (win.options.blurClassName) + win.changeClassName(win.options.blurClassName); + if (this.focusedWindow == win) + this.focusedWindow = null; + win._notify("onBlur"); + }, + + focus: function(id) { + var win = this.getWindow(id); + if (!win) + return; + if (this.focusedWindow) + this.blur(this.focusedWindow.getId()) + + if (win.options.focusClassName) + win.changeClassName(win.options.focusClassName); + this.focusedWindow = win; + win._notify("onFocus"); + }, + + unsetOverflow: function(except) { + this.windows.each(function(d) { d.oldOverflow = d.getContent().getStyle("overflow") || "auto" ; d.getContent().setStyle({overflow: "hidden"}) }); + if (except && except.oldOverflow) + except.getContent().setStyle({overflow: except.oldOverflow}); + }, + + resetOverflow: function() { + this.windows.each(function(d) { if (d.oldOverflow) d.getContent().setStyle({overflow: d.oldOverflow}) }); + }, + + updateZindex: function(zindex, win) { + if (zindex > this.maxZIndex) { + this.maxZIndex = zindex; + if (this.focusedWindow) + this.blur(this.focusedWindow.getId()) + } + this.focusedWindow = win; + if (this.focusedWindow) + this.focus(this.focusedWindow.getId()) + } +}; + +var Dialog = { + dialogId: null, + onCompleteFunc: null, + callFunc: null, + parameters: null, + + confirm: function(content, parameters) { + // Get Ajax return before + if (content && typeof content != "string") { + Dialog._runAjaxRequest(content, parameters, Dialog.confirm); + return + } + content = content || ""; + + parameters = parameters || {}; + var okLabel = parameters.okLabel ? parameters.okLabel : "Ok"; + var cancelLabel = parameters.cancelLabel ? parameters.cancelLabel : "Cancel"; + + // Backward compatibility + parameters = Object.extend(parameters, parameters.windowParameters || {}); + parameters.windowParameters = parameters.windowParameters || {}; + + parameters.className = parameters.className || "alert"; + + var okButtonClass = "class ='" + (parameters.buttonClass ? parameters.buttonClass + " " : "") + " ok_button'" + var cancelButtonClass = "class ='" + (parameters.buttonClass ? parameters.buttonClass + " " : "") + " cancel_button'" + var content = "\ +
" + content + "
\ +
\ + \ + \ +
\ + "; + return this._openDialog(content, parameters) + }, + + alert: function(content, parameters) { + // Get Ajax return before + if (content && typeof content != "string") { + Dialog._runAjaxRequest(content, parameters, Dialog.alert); + return + } + content = content || ""; + + parameters = parameters || {}; + var okLabel = parameters.okLabel ? parameters.okLabel : "Ok"; + + // Backward compatibility + parameters = Object.extend(parameters, parameters.windowParameters || {}); + parameters.windowParameters = parameters.windowParameters || {}; + + parameters.className = parameters.className || "alert"; + + var okButtonClass = "class ='" + (parameters.buttonClass ? parameters.buttonClass + " " : "") + " ok_button'" + var content = "\ +
" + content + "
\ +
\ + \ +
"; + return this._openDialog(content, parameters) + }, + + info: function(content, parameters) { + // Get Ajax return before + if (content && typeof content != "string") { + Dialog._runAjaxRequest(content, parameters, Dialog.info); + return + } + content = content || ""; + + // Backward compatibility + parameters = parameters || {}; + parameters = Object.extend(parameters, parameters.windowParameters || {}); + parameters.windowParameters = parameters.windowParameters || {}; + + parameters.className = parameters.className || "alert"; + + var content = ""; + if (parameters.showProgress) + content += ""; + + parameters.ok = null; + parameters.cancel = null; + + return this._openDialog(content, parameters) + }, + + setInfoMessage: function(message) { + $('modal_dialog_message').update(message); + }, + + closeInfo: function() { + Windows.close(this.dialogId); + }, + + _openDialog: function(content, parameters) { + var className = parameters.className; + + if (! parameters.height && ! parameters.width) { + parameters.width = WindowUtilities.getPageSize(parameters.options.parent || document.body).pageWidth / 2; + } + if (parameters.id) + this.dialogId = parameters.id; + else { + var t = new Date(); + this.dialogId = 'modal_dialog_' + t.getTime(); + parameters.id = this.dialogId; + } + + // compute height or width if need be + if (! parameters.height || ! parameters.width) { + var size = WindowUtilities._computeSize(content, this.dialogId, parameters.width, parameters.height, 5, className) + if (parameters.height) + parameters.width = size + 5 + else + parameters.height = size + 5 + } + parameters.effectOptions = parameters.effectOptions ; + parameters.resizable = parameters.resizable || false; + parameters.minimizable = parameters.minimizable || false; + parameters.maximizable = parameters.maximizable || false; + parameters.draggable = parameters.draggable || false; + parameters.closable = parameters.closable || false; + + var win = new Window(parameters); + win.getContent().innerHTML = content; + + win.showCenter(true, parameters.top, parameters.left); + win.setDestroyOnClose(); + + win.cancelCallback = parameters.onCancel || parameters.cancel; + win.okCallback = parameters.onOk || parameters.ok; + + return win; + }, + + _getAjaxContent: function(originalRequest) { + Dialog.callFunc(originalRequest.responseText, Dialog.parameters) + }, + + _runAjaxRequest: function(message, parameters, callFunc) { + if (message.options == null) + message.options = {} + Dialog.onCompleteFunc = message.options.onComplete; + Dialog.parameters = parameters; + Dialog.callFunc = callFunc; + + message.options.onComplete = Dialog._getAjaxContent; + new Ajax.Request(message.url, message.options); + }, + + okCallback: function() { + var win = Windows.focusedWindow; + if (!win.okCallback || win.okCallback(win)) { + // Remove onclick on button + $$("#" + win.getId()+" input").each(function(element) {element.onclick=null;}) + win.close(); + } + }, + + cancelCallback: function() { + var win = Windows.focusedWindow; + // Remove onclick on button + $$("#" + win.getId()+" input").each(function(element) {element.onclick=null}) + win.close(); + if (win.cancelCallback) + win.cancelCallback(win); + } +} +/* + Based on Lightbox JS: Fullsize Image Overlays + by Lokesh Dhakar - http://www.huddletogether.com + + For more information on this script, visit: + http://huddletogether.com/projects/lightbox/ + + Licensed under the Creative Commons Attribution 2.5 License - http://creativecommons.org/licenses/by/2.5/ + (basically, do anything you want, just leave my name and link) +*/ + +if (Prototype.BrowserFeatures.WebKit) { + var array = navigator.userAgent.match(new RegExp(/AppleWebKit\/([\d\.\+]*)/)); + Prototype.BrowserFeatures.WebKitVersion = parseFloat(array[1]); +} + +var WindowUtilities = { + // From dragdrop.js + getWindowScroll: function(parent) { + var T, L, W, H; + parent = parent || document.body; + if (parent != document.body) { + T = parent.scrollTop; + L = parent.scrollLeft; + W = parent.scrollWidth; + H = parent.scrollHeight; + } + else { + var w = window; + with (w.document) { + if (w.document.documentElement && documentElement.scrollTop) { + T = documentElement.scrollTop; + L = documentElement.scrollLeft; + } else if (w.document.body) { + T = body.scrollTop; + L = body.scrollLeft; + } + if (w.innerWidth) { + W = w.innerWidth; + H = w.innerHeight; + } else if (w.document.documentElement && documentElement.clientWidth) { + W = documentElement.clientWidth; + H = documentElement.clientHeight; + } else { + W = body.offsetWidth; + H = body.offsetHeight + } + } + } + return { top: T, left: L, width: W, height: H }; + }, + // + // getPageSize() + // Returns array with page width, height and window width, height + // Core code from - quirksmode.org + // Edit for Firefox by pHaez + // + getPageSize: function(parent){ + parent = parent || document.body; + var windowWidth, windowHeight; + var pageHeight, pageWidth; + if (parent != document.body) { + windowWidth = parent.getWidth(); + windowHeight = parent.getHeight(); + pageWidth = parent.scrollWidth; + pageHeight = parent.scrollHeight; + } + else { + var xScroll, yScroll; + + if (window.innerHeight && window.scrollMaxY) { + xScroll = document.body.scrollWidth; + yScroll = window.innerHeight + window.scrollMaxY; + } else if (document.body.scrollHeight > document.body.offsetHeight){ // all but Explorer Mac + xScroll = document.body.scrollWidth; + yScroll = document.body.scrollHeight; + } else { // Explorer Mac...would also work in Explorer 6 Strict, Mozilla and Safari + xScroll = document.body.offsetWidth; + yScroll = document.body.offsetHeight; + } + + + if (self.innerHeight) { // all except Explorer + windowWidth = self.innerWidth; + windowHeight = self.innerHeight; + } else if (document.documentElement && document.documentElement.clientHeight) { // Explorer 6 Strict Mode + windowWidth = document.documentElement.clientWidth; + windowHeight = document.documentElement.clientHeight; + } else if (document.body) { // other Explorers + windowWidth = document.body.clientWidth; + windowHeight = document.body.clientHeight; + } + + // for small pages with total height less then height of the viewport + if(yScroll < windowHeight){ + pageHeight = windowHeight; + } else { + pageHeight = yScroll; + } + + // for small pages with total width less then width of the viewport + if(xScroll < windowWidth){ + pageWidth = windowWidth; + } else { + pageWidth = xScroll; + } + } + return {pageWidth: pageWidth ,pageHeight: pageHeight , windowWidth: windowWidth, windowHeight: windowHeight}; + }, + + disableScreen: function(className, overlayId, overlayOpacity, contentId, parent) { + WindowUtilities.initLightbox(overlayId, className, function() {this._disableScreen(className, overlayId, overlayOpacity, contentId)}.bind(this), parent || document.body); + }, + + _disableScreen: function(className, overlayId, overlayOpacity, contentId) { + // prep objects + var objOverlay = $(overlayId); + + var pageSize = WindowUtilities.getPageSize(objOverlay.parentNode); + + // Hide select boxes as they will 'peek' through the image in IE, store old value + if (contentId && Prototype.BrowserFeatures.IE) { + WindowUtilities._hideSelect(); + WindowUtilities._showSelect(contentId); + } + + // set height of Overlay to take up whole page and show + objOverlay.style.height = (pageSize.pageHeight + 'px'); + objOverlay.style.display = 'none'; + if (overlayId == "overlay_modal" && Window.hasEffectLib && Windows.overlayShowEffectOptions) { + objOverlay.overlayOpacity = overlayOpacity; + new Effect.Appear(objOverlay, Object.extend({from: 0, to: overlayOpacity}, Windows.overlayShowEffectOptions)); + } + else + objOverlay.style.display = "block"; + }, + + enableScreen: function(id) { + id = id || 'overlay_modal'; + var objOverlay = $(id); + if (objOverlay) { + // hide lightbox and overlay + if (id == "overlay_modal" && Window.hasEffectLib && Windows.overlayHideEffectOptions) + new Effect.Fade(objOverlay, Object.extend({from: objOverlay.overlayOpacity, to:0}, Windows.overlayHideEffectOptions)); + else { + objOverlay.style.display = 'none'; + objOverlay.parentNode.removeChild(objOverlay); + } + + // make select boxes visible using old value + if (id != "__invisible__") + WindowUtilities._showSelect(); + } + }, + + _hideSelect: function(id) { + if (Prototype.BrowserFeatures.IE) { + id = id == null ? "" : "#" + id + " "; + $$(id + 'select').each(function(element) { + if (! WindowUtilities.isDefined(element.oldVisibility)) { + element.oldVisibility = element.style.visibility ? element.style.visibility : "visible"; + element.style.visibility = "hidden"; + } + }); + } + }, + + _showSelect: function(id) { + if (Prototype.BrowserFeatures.IE) { + id = id == null ? "" : "#" + id + " "; + $$(id + 'select').each(function(element) { + if (WindowUtilities.isDefined(element.oldVisibility)) { + // Why?? Ask IE + try { + element.style.visibility = element.oldVisibility; + } catch(e) { + element.style.visibility = "visible"; + } + element.oldVisibility = null; + } + else { + if (element.style.visibility) + element.style.visibility = "visible"; + } + }); + } + }, + + isDefined: function(object) { + return typeof(object) != "undefined" && object != null; + }, + + // initLightbox() + // Function runs on window load, going through link tags looking for rel="lightbox". + // These links receive onclick events that enable the lightbox display for their targets. + // The function also inserts html markup at the top of the page which will be used as a + // container for the overlay pattern and the inline image. + initLightbox: function(id, className, doneHandler, parent) { + // Already done, just update zIndex + if ($(id)) { + Element.setStyle(id, {zIndex: Windows.maxZIndex + 1}); + Windows.maxZIndex++; + doneHandler(); + } + // create overlay div and hardcode some functional styles (aesthetic styles are in CSS file) + else { + var objOverlay = document.createElement("div"); + objOverlay.setAttribute('id', id); + objOverlay.className = "overlay_" + className + objOverlay.style.display = 'none'; + objOverlay.style.position = 'absolute'; + objOverlay.style.top = '0'; + objOverlay.style.left = '0'; + objOverlay.style.zIndex = Windows.maxZIndex + 1; + Windows.maxZIndex++; + objOverlay.style.width = '100%'; + parent.insertBefore(objOverlay, parent.firstChild); + if (Prototype.BrowserFeatures.WebKit && id == "overlay_modal") { + setTimeout(function() {doneHandler()}, 10); + } + else + doneHandler(); + } + }, + + setCookie: function(value, parameters) { + document.cookie= parameters[0] + "=" + escape(value) + + ((parameters[1]) ? "; expires=" + parameters[1].toGMTString() : "") + + ((parameters[2]) ? "; path=" + parameters[2] : "") + + ((parameters[3]) ? "; domain=" + parameters[3] : "") + + ((parameters[4]) ? "; secure" : ""); + }, + + getCookie: function(name) { + var dc = document.cookie; + var prefix = name + "="; + var begin = dc.indexOf("; " + prefix); + if (begin == -1) { + begin = dc.indexOf(prefix); + if (begin != 0) return null; + } else { + begin += 2; + } + var end = document.cookie.indexOf(";", begin); + if (end == -1) { + end = dc.length; + } + return unescape(dc.substring(begin + prefix.length, end)); + }, + + _computeSize: function(content, id, width, height, margin, className) { + var objBody = document.body; + var tmpObj = document.createElement("div"); + tmpObj.setAttribute('id', id); + tmpObj.className = className + "_content"; + + if (height) + tmpObj.style.height = height + "px" + else + tmpObj.style.width = width + "px" + + tmpObj.style.position = 'absolute'; + tmpObj.style.top = '0'; + tmpObj.style.left = '0'; + tmpObj.style.display = 'none'; + + tmpObj.innerHTML = content; + objBody.insertBefore(tmpObj, objBody.firstChild); + + var size; + if (height) + size = $(tmpObj).getDimensions().width + margin; + else + size = $(tmpObj).getDimensions().height + margin; + objBody.removeChild(tmpObj); + return size; + } +} +