From f9b9ad7f4257c99f5f2f8cc7bc96cd9add3fcaea Mon Sep 17 00:00:00 2001 From: Michael McGrath Date: Sat, 1 Mar 2008 11:16:22 -0600 Subject: [PATCH] I'm not totally sold on this, and I haven't figured out how to include links in the actual bubbles. Take a look, let me know what you guys think. I'd like to use this ajax method to keep all the help items in one spot but I'm worried about how to get it translated. --- fas/fas/controllers.py | 2 + fas/fas/help.py | 24 + fas/fas/static/images/balloons/balloon-0.png | Bin 0 -> 24548 bytes fas/fas/static/images/balloons/balloon-1.png | Bin 0 -> 24435 bytes fas/fas/static/images/balloons/balloon-2.png | Bin 0 -> 35557 bytes fas/fas/static/images/balloons/balloon-3.png | Bin 0 -> 28383 bytes fas/fas/static/images/balloons/button.png | Bin 0 -> 502 bytes fas/fas/static/images/balloons/icon.gif | Bin 0 -> 346 bytes fas/fas/static/js/HelpBalloon.js | 874 ++++ fas/fas/static/js/effects.js | 1122 +++++ fas/fas/static/js/prototype.improvements.js | 91 + fas/fas/static/js/prototype.js | 4221 ++++++++++++++++++ fas/fas/static/js/scriptaculous.js | 58 + fas/fas/templates/help.html | 14 + fas/fas/templates/master.html | 6 + fas/fas/templates/user/edit.html | 7 +- 16 files changed, 6418 insertions(+), 1 deletion(-) create mode 100644 fas/fas/help.py create mode 100644 fas/fas/static/images/balloons/balloon-0.png create mode 100644 fas/fas/static/images/balloons/balloon-1.png create mode 100644 fas/fas/static/images/balloons/balloon-2.png create mode 100644 fas/fas/static/images/balloons/balloon-3.png create mode 100644 fas/fas/static/images/balloons/button.png create mode 100644 fas/fas/static/images/balloons/icon.gif create mode 100644 fas/fas/static/js/HelpBalloon.js create mode 100644 fas/fas/static/js/effects.js create mode 100644 fas/fas/static/js/prototype.improvements.js create mode 100644 fas/fas/static/js/prototype.js create mode 100644 fas/fas/static/js/scriptaculous.js create mode 100644 fas/fas/templates/help.html diff --git a/fas/fas/controllers.py b/fas/fas/controllers.py index fc9b8c5..d58e3bd 100644 --- a/fas/fas/controllers.py +++ b/fas/fas/controllers.py @@ -11,6 +11,7 @@ from fas.user import User from fas.group import Group from fas.cla import CLA from fas.json_request import JsonRequest +from fas.help import Help #from fas.openid_fas import OpenID import os @@ -36,6 +37,7 @@ class Root(controllers.RootController): group = Group() cla = CLA() json = JsonRequest() + help = Help() # openid = OpenID() # TODO: Find a better place for this. diff --git a/fas/fas/help.py b/fas/fas/help.py new file mode 100644 index 0000000..6a8188f --- /dev/null +++ b/fas/fas/help.py @@ -0,0 +1,24 @@ +import turbogears +from turbogears import controllers, expose, paginate, identity, redirect, widgets, validate, validators, error_handler +from turbogears.database import session + +from fas.auth import * + +class Help(controllers.Controller): + def __init__(self): + '''Create a JsonRequest Controller.''' + + @expose(template="fas.templates.help") + def get_help(self, id='none'): + try: + help = { 'none' : ['Error', 'We could not find that help item'], + 'user_ircnick' : ['IRC Nick (Optional)', 'IRC Nick is used to identify yourself on irc.freenode.net. Please register your nick on irc.freenode.net first, then fill this in so people can find you online when they need to'], + 'user_primary_email' : ['Primary Email (Required)', 'This email address should be your prefered email contact and will be used to send various official emails to. This is also where your @fedoraproject.org email will get forwarded'], + 'user_human_name' : ['Full Name (Required)', 'Your Human Name or "real life" name'], + 'user_gpg_keyid' : ['GPG Key', 'Only required for users signing the CLA. It is generally used to prove that a message or email came from you or to encrypt information so that only the recipients can read it. See http://fedoraproject.org/wiki/Infrastructure/AccountSystem/CLAHowTo for more information'], + 'user_telephone' : ['Telephone', 'Only required for users signing the CLA. Sometimes during a time of emergency someone from the Fedora Project may need to contact you. For more information see our Privacy Policy'], + } + except KeyError: + return dict(title='Error', help='We could not find that help item') + return dict(help=help[id]) + diff --git a/fas/fas/static/images/balloons/balloon-0.png b/fas/fas/static/images/balloons/balloon-0.png new file mode 100644 index 0000000000000000000000000000000000000000..acf8e6aa61555bf1a7e26574cab3b83a5c23d64c GIT binary patch literal 24548 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 RPXGV_TsZ55=Aiz#^FJwKg_8gP literal 0 HcmV?d00001 diff --git a/fas/fas/static/images/balloons/balloon-2.png b/fas/fas/static/images/balloons/balloon-2.png new file mode 100644 index 0000000000000000000000000000000000000000..79de8680faff1e5563f5ce4871b5fbb191540171 GIT binary patch literal 35557 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≶5CP{2C39>&rYYn@if2LrSNA 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 literal 0 HcmV?d00001 diff --git a/fas/fas/static/images/balloons/icon.gif b/fas/fas/static/images/balloons/icon.gif new file mode 100644 index 0000000000000000000000000000000000000000..79df28506ba454f8c689f1e2273f47a2d27912c8 GIT binary patch literal 346 zcmZ?wbhEHb4sHTTh?A{TX(&6?X}i**E%*_@7j2y zXY_MUsQ_S~Jf@BaM# z_ZJ>`u;}1}WrrWGIQnSy@yBb9KVEn8$%a!;H=cgF`OLE|XP<39|9ruL`-=}fSa#&$ z@}rMd9(%Or#N+j+p6t8w^7if9|Ns97yNf8G_>+Z^fx(bL2c#P0CkD3i12+;pbog4= zFDd#ti-T=_@JZGr12gH=dRs54ICM)0U(N)_5c6? literal 0 HcmV?d00001 diff --git a/fas/fas/static/js/HelpBalloon.js b/fas/fas/static/js/HelpBalloon.js new file mode 100644 index 0000000..4f19892 --- /dev/null +++ b/fas/fas/static/js/HelpBalloon.js @@ -0,0 +1,874 @@ +// +// Copyright (c) 2008 Beau D. Scott | http://www.beauscott.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. +// + +/** + * HelpBalloon.js + * Prototype/Scriptaculous based help balloons / dialog balloons + * @version 1.5 + * @requires prototype.js + * @requires scriptaculous.js + * @author Beau D. Scott + */ +var HelpBalloon = Class.create(); +HelpBalloon.prototype = { + /** + * Instantiates the object + * @param {Object} options + * @see HelpBalloonOptions + * @constructor + */ + initialize: function(options) + { + /** + * Display and behavioral options + * @see HelpBalloonOptions + */ + this.options = new HelpBalloonOptions(); + Object.extend(this.options, options || {}); + + /** + * The local store of 'title'. Will change if the balloon is making a remote call + * unless options.title is specified + * @var {String} + * @private + */ + this.title = this.options.title; + + /** + * Display elements + * @var {Object} + * @private + */ + this._elements = new HelpBalloonElements(); + + /** + * The balloons visibility state. + * @var {Boolean} + * @private + */ + this.visible = false; + + /** + * Rendering status + * @var {Boolean} + * @private + */ + this.drawn = false; + + /** + * X/Y coordinate of icon at time of render + * @var {Array} + * @private + */ + this.renderXY = [0,0]; + + /** + * Stores the balloon coordinates + * @private + * @var {Object} + */ + this.balloonCoords = null; + + /** + * Balloon styling + * @private + * @var {Object} + */ + this.balloonStyle = { + 'position': 'absolute', + 'border': 'none', + 'display': 'none' + } + + /** + * Title Bar style + * @var {Object} + * @private + */ + this.titleStyle = { + 'color': 'black', + 'fontSize': '16px', + 'fontWeight': 'bold', + 'fontFamily': 'Verdana' + } + + /** + * Width,height of the balloons + * @private + * @var {Array} + */ + this.balloonDimensions = [0,0]; + + /** + * ID for object and Icon, Requires prototype.improvements.js + * @var {String} + */ + this.id = "HelpBalloon_" + Object.genGUID(); + + // + // Preload the balloon and button images so they're ready + // at render time + // + // 0 1 + // X + // 2 3 + // + for(var i = 0; i < 4; i++) + { + var balloon = new Element('img', { + src: this.options.balloonPrefix + i + this.options.balloonSuffix + }); + this._elements.balloons.push(balloon.src); + } + /** + * @private + */ + this.lastBalloon = balloon; + + this._elements.button = new Element('img', { + src: this.options.button + }); + + // + // Create the anchoring icon, or attach the balloon to the given icon element + // If a string is passed in, assume it's a URL, if it's an object, assume it's + // a DOM member. + // + if(typeof this.options.icon == 'string') + { + this._elements.icon = new Element('img', { + src: this.options.icon, + id: this.id + "_icon" + }); + this._elements.icon.setStyle('cursor', 'pointer'); + } + else + { + // Not a string given (most likely an object. Do not append the element + // Kind of a hack for now, but I'll fix it in the next version. + + this._elements.icon = this.options.icon; + this.options.returnElement = true; + } + + this._elements.icon._HelpBalloon = this; + + // + // Attach rendering events + // + + for(i = 0; i < this.options.useEvent.length; i++) + { + Event.observe(this._elements.icon, this.options.useEvent[i], this.toggle.bindAsEventListener(this)); + } + + this._elements.container = new Element('div', { + '_HelpBalloon': this + }); + + // + // If we are not relying on other javascript to attach the anchoring icon + // to the DOM, we'll just do where the script is called from. Default behavior. + // + // If you want to use external JavaScript to attach it to the DOM, attach this._elements.icon + // + if(!this.options.returnElement) + { + document.write(''); + var te = $(this.id); + var p = te.parentNode; + p.insertBefore(this._elements.icon, te); + p.removeChild(te); + } + }, + + /** + * Toggles the help balloon + * @param {Object} e Event + */ + toggle: function(e) + { + if(!e) e = window.event || {type: this.options.useEvent, target: this._elements.icon}; + var icon = Event.element(e); + + if(e.type == this.options.useEvent && !this.visible && icon == this._elements.icon) + this.show(); + else + this.hide(); + }, + + /** + * Triggers the balloon to appear + */ + show: function() + { + if(!this.drawn) this._draw(); + this._reposition(); + this._hideOtherHelps(); + + Effect.Appear(this._elements.container, { + duration: this.options.duration, + afterFinish: function(e){ + this._elements.container.setStyle('display', 'block'); + this._hideLowerElements(); + }.bindAsEventListener(this) + }); + this.visible = true; + Event.observe(window, 'resize', this._reposition.bindAsEventListener(this)); + }, + + /** + * Hides the balloon + */ + hide: function() + { + this._showLowerElements(); + Effect.Fade(this._elements.container, {duration: this.options.duration}); + + setTimeout(function(){ + this._elements.container.setStyle('display', 'none'); + }.bind(this), this.options.duration * 1000); + + this.visible = false; + Event.stopObserving(window, 'resize', this._reposition.bindAsEventListener(this)); + }, + + /** + * Redraws the balloon based on the current coordinates of the icon. + * @private + */ + _reposition: function() + { + this.balloonCoords = this._getXY(this._elements.icon); + //Horizontal and vertical offsets in relation to the icon's 0,0 position. + // Default is the middle of the object + var ho = this._elements.icon.offsetWidth / 2; + var vo = this._elements.icon.offsetHeight / 2; + + var offsets = this.options.anchorPosition.split(/\s+/gi); + for(var i = 0; i < offsets.length; i++) + { + switch(offsets[i].toLowerCase()) + { + case 'left': + ho = 0; + break; + case 'right': + ho = this._elements.icon.offsetWidth; + break; + case 'center': + ho = this._elements.icon.offsetWidth / 2; + break; + case 'top': + vo = 0; + break; + case 'middle': + vo = this._elements.icon.offsetHeight / 2; + break; + case 'bottom': + vo = this._elements.icon.offsetHeight; + break; + } + } + + this.balloonCoords.y += vo; + this.balloonCoords.x += ho; + + // + // Figure out what position to show based on available realestate + // 0 1 + // X + // 2 3 + // Number indicates position of corner opposite anchor + // + var pos = 1; + var offsetHeight = this.balloonCoords.y - this.balloonDimensions[1]; + if(offsetHeight < 0) + pos += 2; + + var offsetWidth = this.balloonCoords.x + this.balloonDimensions[0]; + var ww = Browser.isMSIE() ? document.body.clientWidth : window.outerWidth; + if(offsetWidth > ww) + pos -- ; + + var zx = 0; + var zy = 0; + + // + // 0 1 + // X + // 2 3 + // + switch(pos) + { + case 0: + zx = this.balloonCoords.x - this.balloonDimensions[0]; + zy = this.balloonCoords.y - this.balloonDimensions[1]; + break; + + case 1: + zx = this.balloonCoords.x; + zy = this.balloonCoords.y - this.balloonDimensions[1]; + break; + + case 2: + zx = this.balloonCoords.x - this.balloonDimensions[0]; + zy = this.balloonCoords.y; + break; + + case 3: + zx = this.balloonCoords.x; + zy = this.balloonCoords.y; + break; + } + var containerStyle = { + /*'backgroundRepeat': 'no-repeat', + 'backgroundColor': 'transparent', + 'backgroundPosition': 'top left',*/ + 'left' : zx + "px", + 'top' : zy + "px", + 'width' : this.balloonDimensions[0] + 'px', + 'height' : this.balloonDimensions[1] + 'px' + } + if(Browser.isMSIE()) + { + // + // Fix for IE alpha transparencies + // + if(this._elements.balloons[pos].toLowerCase().indexOf('.png') > -1) + { + Element.setStyle(this._elements.bgContainer, { + 'left' : '0px', + 'top' : '0px', + 'filter' : "progid:DXImageTransform.Microsoft.AlphaImageLoader(src='" + this._elements.balloons[pos] + "', sizingMethod='scale')", + 'width' : this.balloonDimensions[0] + 'px', + 'height' : this.balloonDimensions[1] + 'px', + 'position' : 'absolute' + }); + } + else + containerStyle['background'] = 'transparent url(' + this._elements.balloons[pos] + ') top left no-repeat'; + } + else + { + containerStyle['background'] = 'transparent url(' + this._elements.balloons[pos] + ') top left no-repeat'; + } + Element.setStyle(this._elements.container, containerStyle); + }, + + /** + * Render's the Balloon + * @private + */ + _draw: function() + { + Element.setStyle(this._elements.container, this.balloonStyle); + if(this.options.dataURL && (!this.drawn || !this.options.cacheRemoteContent)) + { + var cont = new Ajax.Request(this.options.dataURL, {asynchronous: false, method: this.options.method}); + // + // Expects the following XML format: + // + // My Title + // My content + // + // + var doHTML = false; + if(cont.transport.responseXML) + { + var xml = cont.transport.responseXML.getElementsByTagName('HelpBalloon')[0]; + + if(xml) + { + if(!this.options.title) + { + xmlTitle = xml.getElementsByTagName('title')[0]; + if(xmlTitle) this.title = xmlTitle.firstChild.nodeValue; + } + + xmlContent = xml.getElementsByTagName('content')[0]; + if(xmlContent) this.options.content = xmlContent.firstChild.nodeValue; + } + else + doHTML = true; + } + else + doHTML = true; + + if(doHTML) + { + // Attempt to get the title from a HTML tag, unless the title option has been set. If so, use that. + if(!this.options.title) + { + var htmlTitle = cont.transport.responseText.match(/\<title\>([^\<]+)\<\/title\>/gi); + if(htmlTitle) + { + htmlTitle = htmlTitle.toString().replace(/\<title\>|\<\/title\>/gi, ''); + this.title = htmlTitle; + } + } + this.options.content = cont.transport.responseText; + } + } + + this.balloonDimensions[0] = this.lastBalloon.width; + this.balloonDimensions[1] = this.lastBalloon.height; + + var contentDimensions = [ + this.balloonDimensions[0] - (2 * this.options.contentMargin), + this.balloonDimensions[1] - (2 * this.options.contentMargin) + ]; + + var buttonDimensions = [ + this._elements.button.width, + this._elements.button.height + ]; + + // + // Create all the elements on demand if they haven't been created yet + // + if(!this.drawn) + { + this._elements.inner = new Element('div'); + + this._elements.title = new Element('div'); + this._elements.inner.appendChild(this._elements.title); + + // PNG fix for IE + if(Browser.isMSIE() && this.options.button.toLowerCase().indexOf('.png') > -1) + { + this._elements.bgContainer = new Element('div'); + + // Have to create yet-another-child of container to house the background for IE... when it was set in + // the main container, it for some odd reason prevents child components from being clickable. + this._elements.container.appendChild(this._elements.bgContainer); + + this._elements.closer = new Element('div'); + this._elements.closer.setStyle('filter', + "progid:DXImageTransform.Microsoft.AlphaImageLoader(src='" + this.options.button + "', sizingMethod='scale')"); + } + else + { + this._elements.closer = this._elements.button; + } + + Event.observe(this._elements.closer, 'click', this.toggle.bindAsEventListener(this)); + this._elements.inner.appendChild(this._elements.closer); + + this._elements.content = new Element('div'); + this._elements.inner.appendChild(this._elements.content); + + this._elements.container.appendChild(this._elements.inner); + + document.getElementsByTagName('body')[0].appendChild(this._elements.container); + + this.drawn = true; + } + + // Reset the title element and reappend the title value (could have changed with a new URL) + this._elements.title.innerHTML = ''; + this._elements.title.appendChild(document.createTextNode(this.title)); + + // Reset content value: + this._elements.content.innerHTML = this.options.content; + + // + // Reapply styling to components as values might have changed + // + + Element.setStyle(this._elements.inner, { + 'position': 'absolute', + 'top': this.options.contentMargin + 'px', + 'left': this.options.contentMargin + 'px', + 'width': contentDimensions[0] + 'px', + 'height': contentDimensions[1] + 'px' + }); + + Element.setStyle(this._elements.title, { + 'width': (contentDimensions[0] - buttonDimensions[0]) + 'px', + 'height': buttonDimensions[1] + 'px', + 'position': 'absolute', + 'overflow': 'hidden', + 'top': '0px', + 'left': '0px' + }); + + Element.setStyle(this._elements.title, this.titleStyle); + + Element.setStyle(this._elements.closer, { + 'width': buttonDimensions[0] + 'px', + 'height': buttonDimensions[1] + 'px', + 'cursor': 'pointer', + 'position': 'absolute', + 'top': '0px', + 'right': '0px' + }); + + Element.setStyle(this._elements.content, { + 'width': contentDimensions[0] + 'px', + 'height': (contentDimensions[1] - this._elements.button.height) + 'px', + 'overflow': 'auto', + 'position': 'absolute', + 'top': buttonDimensions[1] + 'px', + 'left': '0px', + 'fontFamily': 'verdana', + 'fontSize': '11px', + 'fontWeight': 'normal', + 'color': 'black' + }); + + }, + + /** + * Gets the current position of the obj + * @param {Element} element to get position of + * @return Object of (x, y, x2, y2) + */ + _getXY: function(obj) + { + var pos = Position.cumulativeOffset(obj) + var y = pos[1]; + var x = pos[0]; + var x2 = x + parseInt(obj.offsetWidth); + var y2 = y + parseInt(obj.offsetHeight); + return {'x':x, 'y':y, 'x2':x2, 'y2':y2}; + + }, + + /** + * Determins if the object is a child of the balloon element + * @param {Element} Element to check parentage + * @return {Boolean} + * @private + */ + _isChild: function(obj) + { + var i = 15; + do{ + if(obj == this._elements.container) + return true; + obj = obj.parentNode; + }while(obj && i--); + return false + }, + + /** + * Determines if the balloon is over this_obj object + * @param {Element} Object to look under + * @return {Boolean} + * @private + */ + _isOver: function(this_obj) + { + if(!this.visible) return false; + if(this_obj == this._elements.container || this._isChild(this_obj)) return false; + var this_coords = this._getXY(this_obj); + var that_coords = this._getXY(this._elements.container); + if( + ( + ( + (this_coords.x >= that_coords.x && this_coords.x <= that_coords.x2) + || + (this_coords.x2 >= that_coords.x && this_coords.x2 <= that_coords.x2) + ) + && + ( + (this_coords.y >= that_coords.y && this_coords.y <= that_coords.y2) + || + (this_coords.y2 >= that_coords.y && this_coords.y2 <= that_coords.y2) + ) + ) + + ){ + return true; + } + else + return false; + }, + + /** + * Restores visibility of elements under the balloon + * (For IE) + * TODO: suck yourself + * @private + */ + _showLowerElements: function() + { + if(this.options.hideUnderElementsInIE) + { + var elements = this._getWeirdAPIElements(); + for(var i = 0; i < elements.length; i++) + { + if(this._isOver(elements[i])) + { + if(elements[i].style.visibility != 'visible' && elements[i].hiddenBy == this) + { + elements[i].style.visibility = 'visible'; + elements[i].hiddenBy = null; + } + } + } + } + }, + + /** + * Hides elements below the balloon + * (For IE) + * @private + */ + _hideLowerElements: function() + { + if(this.options.hideUnderElementsInIE) + { + var elements = this._getWeirdAPIElements(); + for(var i = 0; i < elements.length; i++) + { + if(this._isOver(elements[i])) + { + if(elements[i].style.visibility != 'hidden') + { + elements[i].style.visibility = 'hidden'; + elements[i].hiddenBy = this; + } + } + } + } + }, + + /** + * Determines which elements need to be hidden + * (For IE) + * @return {Array} array of elements + */ + _getWeirdAPIElements: function() + { + if(!Browser.isMSIE()) return []; + var objs = ['select', 'input', 'object']; + var elements = []; + for(var i = 0; i < objs.length; i++) + { + var e = document.getElementsByTagName(objs[i]); + for(var j = 0; j < e.length; j++) + { + elements.push(e[j]); + } + } + return elements; + }, + + /** + * Hides the other visible help balloons + * @param {Event} e + */ + _hideOtherHelps: function(e) + { + if(!e) e = window.event; + var divs = document.getElementsByTagName('div'); + for(var i = 0; i < divs.length; i++) + { + if(divs[i]._HelpBalloon && divs[i]._HelpBalloon.visible && (divs[i] != this._elements.container)) + divs[i]._HelpBalloon.toggle(e); + } + } +}; + +/** + * HelpBalloonOptions + * Helper class for defining options for the HelpBalloon object + * @author Beau D. Scott <beau_scott@hotmail.com> + */ +var HelpBalloonOptions = Class.create(); +HelpBalloonOptions.prototype = { + /** + * @constructor + */ + initialize: function(){}, + /** + * For use with embedding this object into another. If true, the icon is not created + * and not appeneded to the DOM at construction. + * Default is false + * @var {Boolean} + */ + returnElement: false, + /** + * URL to the anchoring icon image file to use. This can also be a direct reference + * to an existing element if you're using that as your anchoring icon. + * @var {Object} + */ + icon: '/static/images/balloons/icon.gif', + /** + * Alt text of the help icon + * @var {String} + */ + altText: 'Click here for help with this topic.', + /** + * URL to pull the title/content XML + * @var {String} + */ + dataURL: null, + /** + * Static title of the balloon + * @var {String} + */ + title: null, + /** + * Static content of the balloon + * @var {String} + */ + content: null, + /** + * Show/Hide effect duration + * @var {Number} + */ + duration: 0.2, + /** + * The event type to listen for on the icon to show the balloon. + * Default 'click' + * @var {String} + */ + useEvent: ['click'], + /** + * Request method for dynamic content. (get, post) + * Default 'get' + * @var {String} + */ + method: 'get', + /** + * Flag indicating cache the request result. If this is false, every + * time the balloon is shown, it will retrieve the remote url and parse it + * before the balloon appears, updating the content. Otherwise, it will make + * the call once and use the same content with each subsequent showing. + * Default true + * @var {Boolean} + */ + cacheRemoteContent: true, + /** + * Vertical and horizontal margin of the content pane + * @var {Number} + */ + contentMargin: 35, + /** + * X coordinate of the closing button + * @var {Number} + */ + buttonX: 246, + /** + * Y coordinate of the closing button + * @var {Number} + */ + buttonY: 35, + /** + * Clossing button image path + * @var {String} + */ + button: '/static/images/balloons/button.png', + /** + * Balloon image path prefix. There are 4 button images, numerically named, starting with 0. + * 0, 1 + * 2, 3 + * (the number indicates the corner opposite the anchor (the pointing direction) + * @var {String} + */ + balloonPrefix: '/static/images/balloons/balloon-', + /** + * The image filename suffix, including the file extension + * @var {String} + */ + balloonSuffix: '.png', + /** + * Position of the balloon's anchor relative to the icon element. + * Combine one horizontal indicator (left, center, right) and one vertical indicator (top, middle, bottom). + * Default is 'center middle' + * @var {String} + */ + anchorPosition: 'center middle', + /** + * Flag indicating whether to hide the elements under the balloon in IE. + * Setting this to false can cause rendering issues in Internet Explorer + * as some elements appear on top of the balloon if they're not hidden. + * Default is true. + * @var {Boolean} + */ + hideUnderElementsInIE: true +}; + +/** + * HelpBalloonElements + * Helper class for defining elements for the HelpBalloon object + * @author Beau D. Scott <beau_scott@hotmail.com> + */ +var HelpBalloonElements = Class.create(); +HelpBalloonElements.prototype = { + /** + * @constructor + */ + initialize: function(){}, + /** + * Containing element of the balloon + * @var {Element} + */ + container: null, + /** + * Inner content container + * @var {Element} + */ + inner: null, + /** + * A reference to the anchoring element/icon + * @var {Element} + */ + icon: null, + /** + * Content container + * @var {Element} + */ + content: null, + /** + * Closing button element + * @var {Element} + */ + button: null, + /** + * The closer object. This can be the same as button, but could + * also be a div with a png loaded as the back ground, browser dependent. + * @var {Element} + */ + closer: null, + /** + * Title container + * @var {Element} + */ + title: null, + /** + * Background container (houses the balloon images + * @var {Element} + */ + bgContainer: null, + /** + * Array of balloon image references + * @var {Array} + */ + balloons: [] +}; diff --git a/fas/fas/static/js/effects.js b/fas/fas/static/js/effects.js new file mode 100644 index 0000000..b8c0259 --- /dev/null +++ b/fas/fas/static/js/effects.js @@ -0,0 +1,1122 @@ +// script.aculo.us effects.js v1.8.1, Thu Jan 03 22:07:12 -0500 2008 + +// 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/ + +// 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)); +}; + +/*--------------------------------------------------------------------------*/ + +Element.collectTextNodes = function(element) { + return $A($(element).childNodes).collect( function(node) { + return (node.nodeType==3 ? node.nodeValue : + (node.hasChildNodes() ? Element.collectTextNodes(node) : '')); + }).flatten().join(''); +}; + +Element.collectTextNodesIgnoreClass = function(element, className) { + return $A($(element).childNodes).collect( function(node) { + 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); + return element; +}; + +Element.getInlineOpacity = function(element){ + return $(element).style.opacity || ''; +}; + +Element.forceRerendering = function(element) { + try { + element = $(element); + var n = document.createTextNode(' '); + element.appendChild(n); + element.removeChild(n); + } catch(e) { } +}; + +/*--------------------------------------------------------------------------*/ + +var Effect = { + _elementDoesNotExistError: { + name: 'ElementDoesNotExistError', + message: 'The specified DOM element does not exist, but is required for this effect to operate' + }, + Transitions: { + linear: Prototype.K, + sinoidal: function(pos) { + return (-Math.cos(pos*Math.PI)/2) + 0.5; + }, + reverse: function(pos) { + return 1-pos; + }, + flicker: function(pos) { + var pos = ((-Math.cos(pos*Math.PI)/4) + 0.75) + Math.random()/4; + return pos > 1 ? 1 : pos; + }, + wobble: function(pos) { + return (-Math.cos(pos*Math.PI*(9*pos))/2) + 0.5; + }, + pulse: function(pos, pulses) { + pulses = pulses || 5; + return ( + ((pos % (1/pulses)) * pulses).round() == 0 ? + ((pos * pulses * 2) - (pos * pulses * 2).floor()) : + 1 - ((pos * pulses * 2) - (pos * pulses * 2).floor()) + ); + }, + spring: function(pos) { + return 1 - (Math.cos(pos * 4.5 * Math.PI) * Math.exp(-pos * 6)); + }, + none: function(pos) { + return 0; + }, + full: function(pos) { + return 1; + } + }, + DefaultOptions: { + duration: 1.0, // seconds + fps: 100, // 100= assume 66fps max. + sync: false, // true for combining + from: 0.0, + to: 1.0, + delay: 0.0, + queue: 'parallel' + }, + tagifyText: function(element) { + 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( + new Element('span', {style: tagifyStyle}).update( + character == ' ' ? String.fromCharCode(160) : character), + child); + }); + Element.remove(child); + } + }); + }, + multiple: function(element, effect) { + var elements; + if (((typeof element == 'object') || + Object.isFunction(element)) && + (element.length)) + elements = element; + else + elements = $(element).childNodes; + + var options = Object.extend({ + speed: 0.1, + delay: 0.0 + }, arguments[2] || { }); + var masterDelay = options.delay; + + $A(elements).each( function(element, index) { + new effect(element, Object.extend(options, { delay: index * options.speed + masterDelay })); + }); + }, + PAIRS: { + 'slide': ['SlideDown','SlideUp'], + 'blind': ['BlindDown','BlindUp'], + 'appear': ['Appear','Fade'] + }, + toggle: function(element, effect) { + element = $(element); + effect = (effect || 'appear').toLowerCase(); + var options = Object.extend({ + queue: { position:'end', scope:(element.id || 'global'), limit: 1 } + }, arguments[2] || { }); + Effect[element.visible() ? + Effect.PAIRS[effect][1] : Effect.PAIRS[effect][0]](element, options); + } +}; + +Effect.DefaultOptions.transition = Effect.Transitions.sinoidal; + +/* ------------- core effects ------------- */ + +Effect.ScopedQueue = Class.create(Enumerable, { + initialize: function() { + this.effects = []; + this.interval = null; + }, + _each: function(iterator) { + this.effects._each(iterator); + }, + add: function(effect) { + var timestamp = new Date().getTime(); + + var position = Object.isString(effect.options.queue) ? + effect.options.queue : effect.options.queue.position; + + switch(position) { + case 'front': + // move unstarted effects after this effect + this.effects.findAll(function(e){ return e.state=='idle' }).each( function(e) { + e.startOn += effect.finishOn; + e.finishOn += effect.finishOn; + }); + break; + case 'with-last': + timestamp = this.effects.pluck('startOn').max() || timestamp; + break; + case 'end': + // start effect after last queued effect has finished + timestamp = this.effects.pluck('finishOn').max() || timestamp; + break; + } + + effect.startOn += timestamp; + effect.finishOn += timestamp; + + 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); + }, + remove: function(effect) { + this.effects = this.effects.reject(function(e) { return e==effect }); + if (this.effects.length == 0) { + clearInterval(this.interval); + this.interval = null; + } + }, + loop: function() { + var timePos = new Date().getTime(); + for(var i=0, len=this.effects.length;i<len;i++) + this.effects[i] && this.effects[i].loop(timePos); + } +}); + +Effect.Queues = { + instances: $H(), + get: function(queueName) { + if (!Object.isString(queueName)) return queueName; + + return this.instances.get(queueName) || + this.instances.set(queueName, new Effect.ScopedQueue()); + } +}; +Effect.Queue = Effect.Queues.get('global'); + +Effect.Base = Class.create({ + position: null, + start: function(options) { + function codeForEvent(options,eventName){ + return ( + (options[eventName+'Internal'] ? 'this.options.'+eventName+'Internal(this);' : '') + + (options[eventName] ? 'this.options.'+eventName+'(this);' : '') + ); + } + if (options && options.transition === false) options.transition = Effect.Transitions.linear; + this.options = Object.extend(Object.extend({ },Effect.DefaultOptions), options || { }); + this.currentFrame = 0; + this.state = 'idle'; + this.startOn = this.options.delay*1000; + this.finishOn = this.startOn+(this.options.duration*1000); + this.fromToDelta = this.options.to-this.options.from; + this.totalTime = this.finishOn-this.startOn; + this.totalFrames = this.options.fps*this.options.duration; + + eval('this.render = function(pos){ '+ + 'if (this.state=="idle"){this.state="running";'+ + codeForEvent(this.options,'beforeSetup')+ + (this.setup ? 'this.setup();':'')+ + codeForEvent(this.options,'afterSetup')+ + '};if (this.state=="running"){'+ + 'pos=this.options.transition(pos)*'+this.fromToDelta+'+'+this.options.from+';'+ + 'this.position=pos;'+ + codeForEvent(this.options,'beforeUpdate')+ + (this.update ? 'this.update(pos);':'')+ + codeForEvent(this.options,'afterUpdate')+ + '}}'); + + this.event('beforeStart'); + if (!this.options.sync) + Effect.Queues.get(Object.isString(this.options.queue) ? + 'global' : this.options.queue.scope).add(this); + }, + loop: function(timePos) { + if (timePos >= this.startOn) { + if (timePos >= this.finishOn) { + this.render(1.0); + this.cancel(); + this.event('beforeFinish'); + if (this.finish) this.finish(); + this.event('afterFinish'); + return; + } + var pos = (timePos - this.startOn) / this.totalTime, + frame = (pos * this.totalFrames).round(); + if (frame > this.currentFrame) { + this.render(pos); + this.currentFrame = frame; + } + } + }, + cancel: function() { + if (!this.options.sync) + Effect.Queues.get(Object.isString(this.options.queue) ? + 'global' : this.options.queue.scope).remove(this); + this.state = 'finished'; + }, + event: function(eventName) { + if (this.options[eventName + 'Internal']) this.options[eventName + 'Internal'](this); + if (this.options[eventName]) this.options[eventName](this); + }, + inspect: function() { + var data = $H(); + for(property in this) + if (!Object.isFunction(this[property])) data.set(property, this[property]); + return '#<Effect:' + data.inspect() + ',options:' + $H(this.options).inspect() + '>'; + } +}); + +Effect.Parallel = Class.create(Effect.Base, { + initialize: function(effects) { + this.effects = effects || []; + this.start(arguments[1]); + }, + update: function(position) { + this.effects.invoke('render', position); + }, + finish: function(position) { + this.effects.each( function(effect) { + effect.render(1.0); + effect.cancel(); + effect.event('beforeFinish'); + if (effect.finish) effect.finish(position); + effect.event('afterFinish'); + }); + } +}); + +Effect.Tween = Class.create(Effect.Base, { + initialize: function(object, from, to) { + object = Object.isString(object) ? $(object) : object; + var args = $A(arguments), method = args.last(), + options = args.length == 5 ? args[3] : null; + this.method = Object.isFunction(method) ? method.bind(object) : + Object.isFunction(object[method]) ? object[method].bind(object) : + function(value) { object[method] = value }; + this.start(Object.extend({ from: from, to: to }, options || { })); + }, + update: function(position) { + this.method(position); + } +}); + +Effect.Event = Class.create(Effect.Base, { + initialize: function() { + this.start(Object.extend({ duration: 0 }, arguments[0] || { })); + }, + update: Prototype.emptyFunction +}); + +Effect.Opacity = Class.create(Effect.Base, { + initialize: function(element) { + this.element = $(element); + if (!this.element) throw(Effect._elementDoesNotExistError); + // make this work on IE on elements without 'layout' + if (Prototype.Browser.IE && (!this.element.currentStyle.hasLayout)) + this.element.setStyle({zoom: 1}); + var options = Object.extend({ + from: this.element.getOpacity() || 0.0, + to: 1.0 + }, arguments[1] || { }); + this.start(options); + }, + update: function(position) { + this.element.setOpacity(position); + } +}); + +Effect.Move = Class.create(Effect.Base, { + initialize: function(element) { + this.element = $(element); + if (!this.element) throw(Effect._elementDoesNotExistError); + var options = Object.extend({ + x: 0, + y: 0, + mode: 'relative' + }, arguments[1] || { }); + this.start(options); + }, + setup: function() { + this.element.makePositioned(); + this.originalLeft = parseFloat(this.element.getStyle('left') || '0'); + this.originalTop = parseFloat(this.element.getStyle('top') || '0'); + if (this.options.mode == 'absolute') { + this.options.x = this.options.x - this.originalLeft; + this.options.y = this.options.y - this.originalTop; + } + }, + update: function(position) { + this.element.setStyle({ + left: (this.options.x * position + this.originalLeft).round() + 'px', + top: (this.options.y * position + this.originalTop).round() + 'px' + }); + } +}); + +// for backwards compatibility +Effect.MoveBy = function(element, toTop, toLeft) { + return new Effect.Move(element, + Object.extend({ x: toLeft, y: toTop }, arguments[3] || { })); +}; + +Effect.Scale = Class.create(Effect.Base, { + initialize: function(element, percent) { + this.element = $(element); + if (!this.element) throw(Effect._elementDoesNotExistError); + var options = Object.extend({ + scaleX: true, + scaleY: true, + scaleContent: true, + scaleFromCenter: false, + scaleMode: 'box', // 'box' or 'contents' or { } with provided values + scaleFrom: 100.0, + scaleTo: percent + }, arguments[2] || { }); + this.start(options); + }, + setup: function() { + 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) { + this.fontSize = parseFloat(fontSize); + 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]; + if (/^content/.test(this.options.scaleMode)) + this.dims = [this.element.scrollHeight, this.element.scrollWidth]; + if (!this.dims) + this.dims = [this.options.scaleMode.originalHeight, + this.options.scaleMode.originalWidth]; + }, + update: function(position) { + var currentScale = (this.options.scaleFrom/100.0) + (this.factor * position); + if (this.options.scaleContent && this.fontSize) + this.element.setStyle({fontSize: this.fontSize * currentScale + this.fontSizeType }); + this.setDimensions(this.dims[0] * currentScale, this.dims[1] * currentScale); + }, + finish: function(position) { + if (this.restoreAfterFinish) this.element.setStyle(this.originalStyle); + }, + setDimensions: function(height, width) { + var d = { }; + if (this.options.scaleX) d.width = width.round() + 'px'; + if (this.options.scaleY) d.height = height.round() + 'px'; + if (this.options.scaleFromCenter) { + var topd = (height - this.dims[0])/2; + var leftd = (width - this.dims[1])/2; + if (this.elementPositioning == 'absolute') { + if (this.options.scaleY) d.top = this.originalTop-topd + 'px'; + if (this.options.scaleX) d.left = this.originalLeft-leftd + 'px'; + } else { + if (this.options.scaleY) d.top = -topd + 'px'; + if (this.options.scaleX) d.left = -leftd + 'px'; + } + } + this.element.setStyle(d); + } +}); + +Effect.Highlight = Class.create(Effect.Base, { + initialize: function(element) { + this.element = $(element); + if (!this.element) throw(Effect._elementDoesNotExistError); + var options = Object.extend({ startcolor: '#ffff99' }, arguments[1] || { }); + this.start(options); + }, + setup: function() { + // Prevent executing on elements not in the layout flow + if (this.element.getStyle('display')=='none') { this.cancel(); return; } + // Disable background image during the effect + this.oldStyle = { }; + if (!this.options.keepBackgroundImage) { + this.oldStyle.backgroundImage = this.element.getStyle('background-image'); + this.element.setStyle({backgroundImage: 'none'}); + } + if (!this.options.endcolor) + this.options.endcolor = this.element.getStyle('background-color').parseColor('#ffffff'); + if (!this.options.restorecolor) + this.options.restorecolor = this.element.getStyle('background-color'); + // init color calculations + this._base = $R(0,2).map(function(i){ return parseInt(this.options.startcolor.slice(i*2+1,i*2+3),16) }.bind(this)); + this._delta = $R(0,2).map(function(i){ return parseInt(this.options.endcolor.slice(i*2+1,i*2+3),16)-this._base[i] }.bind(this)); + }, + update: function(position) { + this.element.setStyle({backgroundColor: $R(0,2).inject('#',function(m,v,i){ + return m+((this._base[i]+(this._delta[i]*position)).round().toColorPart()); }.bind(this)) }); + }, + finish: function() { + this.element.setStyle(Object.extend(this.oldStyle, { + backgroundColor: this.options.restorecolor + })); + } +}); + +Effect.ScrollTo = function(element) { + var options = arguments[1] || { }, + scrollOffsets = document.viewport.getScrollOffsets(), + elementOffsets = $(element).cumulativeOffset(), + max = (window.height || document.body.scrollHeight) - document.viewport.getHeight(); + + if (options.offset) elementOffsets[1] += options.offset; + + return new Effect.Tween(null, + scrollOffsets.top, + elementOffsets[1] > max ? max : elementOffsets[1], + options, + function(p){ scrollTo(scrollOffsets.left, p.round()) } + ); +}; + +/* ------------- combination effects ------------- */ + +Effect.Fade = function(element) { + element = $(element); + var oldOpacity = element.getInlineOpacity(); + var options = Object.extend({ + from: element.getOpacity() || 1.0, + to: 0.0, + afterFinishInternal: function(effect) { + if (effect.options.to!=0) return; + effect.element.hide().setStyle({opacity: oldOpacity}); + } + }, arguments[1] || { }); + return new Effect.Opacity(element,options); +}; + +Effect.Appear = function(element) { + element = $(element); + var options = Object.extend({ + from: (element.getStyle('display') == 'none' ? 0.0 : element.getOpacity() || 0.0), + to: 1.0, + // force Safari to render floated elements properly + afterFinishInternal: function(effect) { + effect.element.forceRerendering(); + }, + beforeSetup: function(effect) { + 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(), + position: element.getStyle('position'), + top: element.style.top, + left: element.style.left, + width: element.style.width, + 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, + beforeSetupInternal: function(effect) { + Position.absolutize(effect.effects[0].element) + }, + afterFinishInternal: function(effect) { + effect.effects[0].element.hide().setStyle(oldStyle); } + }, arguments[1] || { }) + ); +}; + +Effect.BlindUp = function(element) { + element = $(element); + element.makeClipping(); + return new Effect.Scale(element, 0, + Object.extend({ scaleContent: false, + scaleX: false, + restoreAfterFinish: true, + afterFinishInternal: function(effect) { + effect.element.hide().undoClipping(); + } + }, arguments[1] || { }) + ); +}; + +Effect.BlindDown = function(element) { + element = $(element); + var elementDimensions = element.getDimensions(); + 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(); + }, + afterFinishInternal: function(effect) { + effect.element.undoClipping(); + } + }, arguments[1] || { })); +}; + +Effect.SwitchOff = function(element) { + element = $(element); + var oldOpacity = element.getInlineOpacity(); + return new Effect.Appear(element, Object.extend({ + duration: 0.4, + from: 0, + transition: Effect.Transitions.flicker, + afterFinishInternal: function(effect) { + new Effect.Scale(effect.element, 1, { + duration: 0.3, scaleFromCenter: true, + scaleX: false, scaleContent: false, restoreAfterFinish: true, + beforeSetup: function(effect) { + effect.element.makePositioned().makeClipping(); + }, + afterFinishInternal: function(effect) { + effect.element.hide().undoClipping().undoPositioned().setStyle({opacity: oldOpacity}); + } + }) + } + }, arguments[1] || { })); +}; + +Effect.DropOut = function(element) { + element = $(element); + var oldStyle = { + top: element.getStyle('top'), + left: element.getStyle('left'), + opacity: element.getInlineOpacity() }; + return new Effect.Parallel( + [ 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(); + }, + afterFinishInternal: function(effect) { + effect.effects[0].element.hide().undoPositioned().setStyle(oldStyle); + } + }, arguments[1] || { })); +}; + +Effect.Shake = function(element) { + element = $(element); + var options = Object.extend({ + distance: 20, + duration: 0.5 + }, arguments[1] || {}); + var distance = parseFloat(options.distance); + var split = parseFloat(options.duration) / 10.0; + var oldStyle = { + top: element.getStyle('top'), + left: element.getStyle('left') }; + return new Effect.Move(element, + { x: distance, y: 0, duration: split, afterFinishInternal: function(effect) { + new Effect.Move(effect.element, + { x: -distance*2, y: 0, duration: split*2, afterFinishInternal: function(effect) { + new Effect.Move(effect.element, + { x: distance*2, y: 0, duration: split*2, afterFinishInternal: function(effect) { + new Effect.Move(effect.element, + { x: -distance*2, y: 0, duration: split*2, afterFinishInternal: function(effect) { + new Effect.Move(effect.element, + { x: distance*2, y: 0, duration: split*2, afterFinishInternal: function(effect) { + new Effect.Move(effect.element, + { x: -distance, y: 0, duration: split, afterFinishInternal: function(effect) { + effect.element.undoPositioned().setStyle(oldStyle); + }}) }}) }}) }}) }}) }}); +}; + +Effect.SlideDown = function(element) { + element = $(element).cleanWhitespace(); + // 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, + scaleFrom: window.opera ? 0 : 1, + scaleMode: {originalHeight: elementDimensions.height, originalWidth: elementDimensions.width}, + restoreAfterFinish: true, + afterSetup: function(effect) { + effect.element.makePositioned(); + effect.element.down().makePositioned(); + if (window.opera) effect.element.setStyle({top: ''}); + effect.element.makeClipping().setStyle({height: '0px'}).show(); + }, + afterUpdateInternal: function(effect) { + effect.element.down().setStyle({bottom: + (effect.dims[0] - effect.element.clientHeight) + 'px' }); + }, + afterFinishInternal: function(effect) { + effect.element.undoClipping().undoPositioned(); + effect.element.down().undoPositioned().setStyle({bottom: oldInnerBottom}); } + }, arguments[1] || { }) + ); +}; + +Effect.SlideUp = function(element) { + element = $(element).cleanWhitespace(); + var oldInnerBottom = element.down().getStyle('bottom'); + var elementDimensions = element.getDimensions(); + return new Effect.Scale(element, window.opera ? 0 : 1, + Object.extend({ scaleContent: false, + scaleX: false, + scaleMode: 'box', + scaleFrom: 100, + scaleMode: {originalHeight: elementDimensions.height, originalWidth: elementDimensions.width}, + restoreAfterFinish: true, + afterSetup: function(effect) { + effect.element.makePositioned(); + 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' }); + }, + afterFinishInternal: function(effect) { + effect.element.hide().undoClipping().undoPositioned(); + effect.element.down().undoPositioned().setStyle({bottom: oldInnerBottom}); + } + }, arguments[1] || { }) + ); +}; + +// 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, { + restoreAfterFinish: true, + beforeSetup: function(effect) { + effect.element.makeClipping(); + }, + afterFinishInternal: function(effect) { + effect.element.hide().undoClipping(); + } + }); +}; + +Effect.Grow = function(element) { + element = $(element); + var options = Object.extend({ + direction: 'center', + moveTransition: Effect.Transitions.sinoidal, + scaleTransition: Effect.Transitions.sinoidal, + opacityTransition: Effect.Transitions.full + }, arguments[1] || { }); + var oldStyle = { + top: element.style.top, + left: element.style.left, + height: element.style.height, + width: element.style.width, + opacity: element.getInlineOpacity() }; + + var dims = element.getDimensions(); + var initialMoveX, initialMoveY; + var moveX, moveY; + + switch (options.direction) { + case 'top-left': + initialMoveX = initialMoveY = moveX = moveY = 0; + break; + case 'top-right': + initialMoveX = dims.width; + initialMoveY = moveY = 0; + moveX = -dims.width; + break; + case 'bottom-left': + initialMoveX = moveX = 0; + initialMoveY = dims.height; + moveY = -dims.height; + break; + case 'bottom-right': + initialMoveX = dims.width; + initialMoveY = dims.height; + moveX = -dims.width; + moveY = -dims.height; + break; + case 'center': + initialMoveX = dims.width / 2; + initialMoveY = dims.height / 2; + moveX = -dims.width / 2; + moveY = -dims.height / 2; + break; + } + + return new Effect.Move(element, { + x: initialMoveX, + y: initialMoveY, + duration: 0.01, + beforeSetup: function(effect) { + effect.element.hide().makeClipping().makePositioned(); + }, + afterFinishInternal: function(effect) { + new Effect.Parallel( + [ 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 }, + 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(); + }, + afterFinishInternal: function(effect) { + effect.effects[0].element.undoClipping().undoPositioned().setStyle(oldStyle); + } + }, options) + ) + } + }); +}; + +Effect.Shrink = function(element) { + element = $(element); + var options = Object.extend({ + direction: 'center', + moveTransition: Effect.Transitions.sinoidal, + scaleTransition: Effect.Transitions.sinoidal, + opacityTransition: Effect.Transitions.none + }, arguments[1] || { }); + var oldStyle = { + top: element.style.top, + left: element.style.left, + height: element.style.height, + width: element.style.width, + opacity: element.getInlineOpacity() }; + + var dims = element.getDimensions(); + var moveX, moveY; + + switch (options.direction) { + case 'top-left': + moveX = moveY = 0; + break; + case 'top-right': + moveX = dims.width; + moveY = 0; + break; + case 'bottom-left': + moveX = 0; + moveY = dims.height; + break; + case 'bottom-right': + moveX = dims.width; + moveY = dims.height; + break; + 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({ + beforeStartInternal: function(effect) { + effect.effects[0].element.makePositioned().makeClipping(); + }, + afterFinishInternal: function(effect) { + effect.effects[0].element.hide().undoClipping().undoPositioned().setStyle(oldStyle); } + }, options) + ); +}; + +Effect.Pulsate = function(element) { + element = $(element); + var options = arguments[1] || { }; + var oldOpacity = element.getInlineOpacity(); + 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, + Object.extend(Object.extend({ duration: 2.0, from: 0, + afterFinishInternal: function(effect) { effect.element.setStyle({opacity: oldOpacity}); } + }, options), {transition: reverser})); +}; + +Effect.Fold = function(element) { + element = $(element); + var oldStyle = { + top: element.style.top, + left: element.style.left, + width: element.style.width, + height: element.style.height }; + element.makeClipping(); + return new Effect.Scale(element, 5, Object.extend({ + scaleContent: false, + scaleX: false, + afterFinishInternal: function(effect) { + new Effect.Scale(element, 1, { + scaleContent: false, + scaleY: false, + afterFinishInternal: function(effect) { + effect.element.hide().undoClipping().setStyle(oldStyle); + } }); + }}, arguments[1] || { })); +}; + +Effect.Morph = Class.create(Effect.Base, { + initialize: function(element) { + this.element = $(element); + if (!this.element) throw(Effect._elementDoesNotExistError); + var options = Object.extend({ + style: { } + }, arguments[1] || { }); + + if (!Object.isString(options.style)) this.style = $H(options.style); + else { + if (options.style.include(':')) + this.style = options.style.parseStyle(); + else { + this.element.addClassName(options.style); + this.style = $H(this.element.getStyles()); + this.element.removeClassName(options.style); + var css = this.element.getStyles(); + this.style = this.style.reject(function(style) { + return style.value == css[style.key]; + }); + options.afterFinishInternal = function(effect) { + effect.element.addClassName(effect.options.style); + effect.transforms.each(function(transform) { + effect.element.style[transform.style] = ''; + }); + } + } + } + this.start(options); + }, + + setup: function(){ + function parseColor(color){ + 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 ) + }); + } + this.transforms = this.style.map(function(pair){ + var property = pair[0], value = pair[1], unit = null; + + if (value.parseColor('#zzzzzz') != '#zzzzzz') { + value = value.parseColor(); + unit = 'color'; + } else if (property == 'opacity') { + value = parseFloat(value); + if (Prototype.Browser.IE && (!this.element.currentStyle.hasLayout)) + this.element.setStyle({zoom: 1}); + } else if (Element.CSS_LENGTH.test(value)) { + var components = value.match(/^([\+\-]?[0-9\.]+)(.*)$/); + value = parseFloat(components[1]); + unit = (components.length == 3) ? components[2] : null; + } + + var originalValue = this.element.getStyle(property); + return { + style: property.camelize(), + originalValue: unit=='color' ? parseColor(originalValue) : parseFloat(originalValue || 0), + targetValue: unit=='color' ? parseColor(value) : value, + unit: unit + }; + }.bind(this)).reject(function(transform){ + return ( + (transform.originalValue == transform.targetValue) || + ( + transform.unit != 'color' && + (isNaN(transform.originalValue) || isNaN(transform.targetValue)) + ) + ) + }); + }, + update: function(position) { + var style = { }, transform, i = this.transforms.length; + while(i--) + style[(transform = this.transforms[i]).style] = + transform.unit=='color' ? '#'+ + (Math.round(transform.originalValue[0]+ + (transform.targetValue[0]-transform.originalValue[0])*position)).toColorPart() + + (Math.round(transform.originalValue[1]+ + (transform.targetValue[1]-transform.originalValue[1])*position)).toColorPart() + + (Math.round(transform.originalValue[2]+ + (transform.targetValue[2]-transform.originalValue[2])*position)).toColorPart() : + (transform.originalValue + + (transform.targetValue - transform.originalValue) * position).toFixed(3) + + (transform.unit === null ? '' : transform.unit); + this.element.setStyle(style, true); + } +}); + +Effect.Transform = Class.create({ + initialize: function(tracks){ + this.tracks = []; + this.options = arguments[1] || { }; + this.addTracks(tracks); + }, + addTracks: function(tracks){ + tracks.each(function(track){ + track = $H(track); + var data = track.values().first(); + this.tracks.push($H({ + ids: track.keys().first(), + effect: Effect.Morph, + options: { style: data } + })); + }.bind(this)); + return this; + }, + play: function(){ + return new Effect.Parallel( + this.tracks.map(function(track){ + var ids = track.get('ids'), effect = track.get('effect'), options = track.get('options'); + var elements = [$(ids) || $$(ids)].flatten(); + return elements.map(function(e){ return new effect(e, Object.extend({ sync:true }, options)) }); + }).flatten(), + this.options + ); + } +}); + +Element.CSS_PROPERTIES = $w( + 'backgroundColor backgroundPosition borderBottomColor borderBottomStyle ' + + 'borderBottomWidth borderLeftColor borderLeftStyle borderLeftWidth ' + + 'borderRightColor borderRightStyle borderRightWidth borderSpacing ' + + 'borderTopColor borderTopStyle borderTopWidth bottom clip color ' + + 'fontSize fontWeight height left letterSpacing lineHeight ' + + 'marginBottom marginLeft marginRight marginTop markerOffset maxHeight '+ + '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.__parseStyleElement = document.createElement('div'); +String.prototype.parseStyle = function(){ + var style, styleRules = $H(); + if (Prototype.Browser.WebKit) + style = new Element('div',{style:this}).style; + else { + String.__parseStyleElement.innerHTML = '<div style="' + this + '"></div>'; + style = String.__parseStyleElement.childNodes[0].style; + } + + Element.CSS_PROPERTIES.each(function(property){ + if (style[property]) styleRules.set(property, style[property]); + }); + + if (Prototype.Browser.IE && this.include('opacity')) + styleRules.set('opacity', this.match(/opacity:\s*((?:0|1)?(?:\.\d*)?)/)[1]); + + return styleRules; +}; + +if (document.defaultView && document.defaultView.getComputedStyle) { + Element.getStyles = function(element) { + var css = document.defaultView.getComputedStyle($(element), null); + return Element.CSS_PROPERTIES.inject({ }, function(styles, property) { + styles[property] = css[property]; + return styles; + }); + }; +} else { + Element.getStyles = function(element) { + element = $(element); + var css = element.currentStyle, styles; + styles = Element.CSS_PROPERTIES.inject({ }, function(results, property) { + results[property] = css[property]; + return results; + }); + if (!styles.opacity) styles.opacity = element.getOpacity(); + return styles; + }; +}; + +Effect.Methods = { + morph: function(element, style) { + element = $(element); + new Effect.Morph(element, Object.extend({ style: style }, arguments[2] || { })); + return element; + }, + visualEffect: function(element, effect, options) { + element = $(element) + var s = effect.dasherize().camelize(), klass = s.charAt(0).toUpperCase() + s.substring(1); + new Effect[klass](element, options); + return element; + }, + highlight: function(element, options) { + element = $(element); + new Effect.Highlight(element, options); + return element; + } +}; + +$w('fade appear grow shrink fold blindUp blindDown slideUp slideDown '+ + 'pulsate shake puff squish switchOff dropOut').each( + function(effect) { + Effect.Methods[effect] = function(element, options){ + element = $(element); + Effect[effect.charAt(0).toUpperCase() + effect.substring(1)](element, options); + return element; + } + } +); + +$w('getInlineOpacity forceRerendering setContentZoom collectTextNodes collectTextNodesIgnoreClass getStyles').each( + function(f) { Effect.Methods[f] = Element[f]; } +); + +Element.addMethods(Effect.Methods); diff --git a/fas/fas/static/js/prototype.improvements.js b/fas/fas/static/js/prototype.improvements.js new file mode 100644 index 0000000..8d83921 --- /dev/null +++ b/fas/fas/static/js/prototype.improvements.js @@ -0,0 +1,91 @@ +// +// Copyright (c) 2008 Beau D. Scott | http://www.beauscott.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. +// + +/** + * Prototype Improvements v0.1 + * Various additions to the prototype.js + */ + +Object.extend(Event, { + KEY_SHIFT: 16, + KEY_CONTROL: 17, + KEY_CAPSLOCK: 20, + KEY_SPACE: 32, + keyPressed: function(event) + { + return Browser.isMSIE() ? window.event.keyCode : event.which; + } +}); + +Browser = { + + /** + * Returns the user agent + * @param {bool} useAlert + */ + inspect: function(useAlert) + { + if(useAlert) + alert(navigator.userAgent); + else + return navigator.userAgent; + }, + /** + * Returns true if browser is MS Internet Explorer + */ + isMSIE: function() + { + return (navigator.userAgent.toLowerCase().indexOf("msie") > -1) && !this.isOpera(); + }, + /** + * Returns true if browser is Opera + */ + isOpera: function() + { + return navigator.userAgent.toLowerCase().indexOf("opera") > -1; + }, + /** + * Returns true if browzer is Mozilla + */ + isMozilla: function() + { + return (navigator.userAgent.toLowerCase().indexOf("mozilla") > -1) && !this.isOpera() && !this.isMSIE(); + } +} + + +Object.genGUID = function() +{ + var len = 8; + if(!isNaN(parseInt(arguments[0]))) len = parseInt(arguments[0]); + var chars = "abcdef0123456789"; + var output = ""; + while(output.length < len) + { + var rnd = Math.floor(Math.random() * (chars.length - 1)); + output += chars.charAt(rnd); + } + return output; +} \ No newline at end of file diff --git a/fas/fas/static/js/prototype.js b/fas/fas/static/js/prototype.js new file mode 100644 index 0000000..6385503 --- /dev/null +++ b/fas/fas/static/js/prototype.js @@ -0,0 +1,4221 @@ +/* Prototype JavaScript framework, version 1.6.0.2 + * (c) 2005-2008 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.6.0.2', + + 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, + MobileSafari: !!navigator.userAgent.match(/Apple.*Mobile.*Safari/) + }, + + BrowserFeatures: { + XPath: !!document.evaluate, + ElementExtensions: !!window.HTMLElement, + SpecificElementExtensions: + document.createElement('div').__proto__ && + document.createElement('div').__proto__ !== + document.createElement('form').__proto__ + }, + + ScriptFragment: '<script[^>]*>([\\S\\s]*?)<\/script>', + JSONFilter: /^\/\*-secure-([\s\S]*)\*\/\s*$/, + + emptyFunction: function() { }, + K: function(x) { return x } +}; + +if (Prototype.Browser.MobileSafari) + Prototype.BrowserFeatures.SpecificElementExtensions = false; + + +/* Based on Alex Arnell's inheritance implementation. */ +var Class = { + create: function() { + var parent = null, properties = $A(arguments); + if (Object.isFunction(properties[0])) + parent = properties.shift(); + + function klass() { + this.initialize.apply(this, arguments); + } + + Object.extend(klass, Class.Methods); + klass.superclass = parent; + klass.subclasses = []; + + if (parent) { + var subclass = function() { }; + subclass.prototype = parent.prototype; + klass.prototype = new subclass; + parent.subclasses.push(klass); + } + + for (var i = 0; i < properties.length; i++) + klass.addMethods(properties[i]); + + if (!klass.prototype.initialize) + klass.prototype.initialize = Prototype.emptyFunction; + + klass.prototype.constructor = klass; + + return klass; + } +}; + +Class.Methods = { + addMethods: function(source) { + var ancestor = this.superclass && this.superclass.prototype; + var properties = Object.keys(source); + + if (!Object.keys({ toString: true }).length) + properties.push("toString", "valueOf"); + + for (var i = 0, length = properties.length; i < length; i++) { + var property = properties[i], value = source[property]; + if (ancestor && Object.isFunction(value) && + value.argumentNames().first() == "$super") { + var method = value, value = Object.extend((function(m) { + return function() { return ancestor[m].apply(this, arguments) }; + })(property).wrap(method), { + valueOf: function() { return method }, + toString: function() { return method.toString() } + }); + } + this.prototype[property] = value; + } + + return this; + } +}; + +var Abstract = { }; + +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.isUndefined(object)) return 'undefined'; + if (object === null) return 'null'; + return object.inspect ? object.inspect() : String(object); + } 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.isElement(object)) return; + + var results = []; + for (var property in object) { + var value = Object.toJSON(object[property]); + if (!Object.isUndefined(value)) + results.push(property.toJSON() + ': ' + value); + } + + return '{' + results.join(', ') + '}'; + }, + + toQueryString: function(object) { + return $H(object).toQueryString(); + }, + + toHTML: function(object) { + return object && object.toHTML ? object.toHTML() : String.interpret(object); + }, + + 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); + }, + + isElement: function(object) { + return object && object.nodeType == 1; + }, + + isArray: function(object) { + return object != null && typeof object == "object" && + 'splice' in object && 'join' in object; + }, + + isHash: function(object) { + return object instanceof Hash; + }, + + isFunction: function(object) { + return typeof object == "function"; + }, + + isString: function(object) { + return typeof object == "string"; + }, + + isNumber: function(object) { + return typeof object == "number"; + }, + + isUndefined: function(object) { + return typeof object == "undefined"; + } +}); + +Object.extend(Function.prototype, { + argumentNames: function() { + var names = this.toString().match(/^[\s\(]*function[^(]*\((.*?)\)/)[1].split(",").invoke("strip"); + return names.length == 1 && !names[0] ? [] : names; + }, + + bind: function() { + if (arguments.length < 2 && Object.isUndefined(arguments[0])) return this; + var __method = this, args = $A(arguments), object = args.shift(); + return function() { + return __method.apply(object, args.concat($A(arguments))); + } + }, + + bindAsEventListener: function() { + var __method = this, args = $A(arguments), object = args.shift(); + return function(event) { + return __method.apply(object, [event || window.event].concat(args)); + } + }, + + curry: function() { + if (!arguments.length) return this; + var __method = this, args = $A(arguments); + return function() { + return __method.apply(this, args.concat($A(arguments))); + } + }, + + delay: function() { + var __method = this, args = $A(arguments), timeout = args.shift() * 1000; + return window.setTimeout(function() { + return __method.apply(__method, args); + }, timeout); + }, + + wrap: function(wrapper) { + var __method = this; + return function() { + return wrapper.apply(this, [__method.bind(this)].concat($A(arguments))); + } + }, + + methodize: function() { + if (this._methodized) return this._methodized; + var __method = this; + return this._methodized = function() { + return __method.apply(null, [this].concat($A(arguments))); + }; + } +}); + +Function.prototype.defer = Function.prototype.delay.curry(0.01); + +Date.prototype.toJSON = function() { + return '"' + this.getUTCFullYear() + '-' + + (this.getUTCMonth() + 1).toPaddedString(2) + '-' + + this.getUTCDate().toPaddedString(2) + 'T' + + this.getUTCHours().toPaddedString(2) + ':' + + this.getUTCMinutes().toPaddedString(2) + ':' + + this.getUTCSeconds().toPaddedString(2) + 'Z"'; +}; + +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; + } +}; + +RegExp.prototype.match = RegExp.prototype.test; + +RegExp.escape = function(str) { + return String(str).replace(/([.*+?^=!:${}()|[\]\/\\])/g, '\\$1'); +}; + +/*--------------------------------------------------------------------------*/ + +var PeriodicalExecuter = Class.create({ + 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); + }, + + execute: function() { + this.callback(this); + }, + + stop: function() { + if (!this.timer) return; + clearInterval(this.timer); + this.timer = null; + }, + + onTimerEvent: function() { + if (!this.currentlyExecuting) { + try { + this.currentlyExecuting = true; + this.execute(); + } 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 = Object.isUndefined(count) ? 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 String(this); + }, + + truncate: function(length, truncation) { + length = length || 30; + truncation = Object.isUndefined(truncation) ? '...' : truncation; + return this.length > length ? + this.slice(0, length - truncation.length) + truncation : String(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 = new Element('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 (!Object.isArray(hash[key])) 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) { + return count < 1 ? '' : new Array(count + 1).join(this); + }, + + 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}'); + }, + + isJSON: function() { + var str = this; + if (str.blank()) return false; + str = this.replace(/\\./g, '@').replace(/"[^"\\\n\r]*"/g, ''); + return (/^[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t]*$/).test(str); + }, + + evalJSON: function(sanitize) { + var json = this.unfilterJSON(); + try { + if (!sanitize || json.isJSON()) 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); + }, + + interpolate: function(object, pattern) { + return new Template(this, pattern).evaluate(object); + } +}); + +if (Prototype.Browser.WebKit || Prototype.Browser.IE) Object.extend(String.prototype, { + escapeHTML: function() { + return this.replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>'); + }, + unescapeHTML: function() { + return this.replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>'); + } +}); + +String.prototype.gsub.prepareReplacement = function(replacement) { + if (Object.isFunction(replacement)) 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({ + initialize: function(template, pattern) { + this.template = template.toString(); + this.pattern = pattern || Template.Pattern; + }, + + evaluate: function(object) { + if (Object.isFunction(object.toTemplateReplacements)) + object = object.toTemplateReplacements(); + + return this.template.gsub(this.pattern, function(match) { + if (object == null) return ''; + + var before = match[1] || ''; + if (before == '\\') return match[2]; + + var ctx = object, expr = match[3]; + var pattern = /^([^.[]+|\[((?:.*?[^\\])?)\])(\.|\[|$)/; + match = pattern.exec(expr); + if (match == null) return before; + + while (match != null) { + var comp = match[1].startsWith('[') ? match[2].gsub('\\\\]', ']') : match[1]; + ctx = ctx[comp]; + if (null == ctx || '' == match[3]) break; + expr = expr.substring('[' == match[3] ? match[1].length : match[0].length); + match = pattern.exec(expr); + } + + return before + String.interpret(ctx); + }); + } +}); +Template.Pattern = /(^|.|\r|\n)(#\{(.*?)\})/; + +var $break = { }; + +var Enumerable = { + each: function(iterator, context) { + var index = 0; + iterator = iterator.bind(context); + try { + this._each(function(value) { + iterator(value, index++); + }); + } catch (e) { + if (e != $break) throw e; + } + return this; + }, + + eachSlice: function(number, iterator, context) { + iterator = iterator ? iterator.bind(context) : Prototype.K; + var index = -number, slices = [], array = this.toArray(); + while ((index += number) < array.length) + slices.push(array.slice(index, index+number)); + return slices.collect(iterator, context); + }, + + all: function(iterator, context) { + iterator = iterator ? iterator.bind(context) : Prototype.K; + var result = true; + this.each(function(value, index) { + result = result && !!iterator(value, index); + if (!result) throw $break; + }); + return result; + }, + + any: function(iterator, context) { + iterator = iterator ? iterator.bind(context) : Prototype.K; + var result = false; + this.each(function(value, index) { + if (result = !!iterator(value, index)) + throw $break; + }); + return result; + }, + + collect: function(iterator, context) { + iterator = iterator ? iterator.bind(context) : Prototype.K; + var results = []; + this.each(function(value, index) { + results.push(iterator(value, index)); + }); + return results; + }, + + detect: function(iterator, context) { + iterator = iterator.bind(context); + var result; + this.each(function(value, index) { + if (iterator(value, index)) { + result = value; + throw $break; + } + }); + return result; + }, + + findAll: function(iterator, context) { + iterator = iterator.bind(context); + var results = []; + this.each(function(value, index) { + if (iterator(value, index)) + results.push(value); + }); + return results; + }, + + grep: function(filter, iterator, context) { + iterator = iterator ? iterator.bind(context) : Prototype.K; + var results = []; + + if (Object.isString(filter)) + filter = new RegExp(filter); + + this.each(function(value, index) { + if (filter.match(value)) + results.push(iterator(value, index)); + }); + return results; + }, + + include: function(object) { + if (Object.isFunction(this.indexOf)) + if (this.indexOf(object) != -1) return true; + + var found = false; + this.each(function(value) { + if (value == object) { + found = true; + throw $break; + } + }); + return found; + }, + + inGroupsOf: function(number, fillWith) { + fillWith = Object.isUndefined(fillWith) ? null : fillWith; + return this.eachSlice(number, function(slice) { + while(slice.length < number) slice.push(fillWith); + return slice; + }); + }, + + inject: function(memo, iterator, context) { + iterator = iterator.bind(context); + 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, context) { + iterator = iterator ? iterator.bind(context) : Prototype.K; + var result; + this.each(function(value, index) { + value = iterator(value, index); + if (result == null || value >= result) + result = value; + }); + return result; + }, + + min: function(iterator, context) { + iterator = iterator ? iterator.bind(context) : Prototype.K; + var result; + this.each(function(value, index) { + value = iterator(value, index); + if (result == null || value < result) + result = value; + }); + return result; + }, + + partition: function(iterator, context) { + iterator = iterator ? iterator.bind(context) : Prototype.K; + var trues = [], falses = []; + this.each(function(value, index) { + (iterator(value, index) ? + trues : falses).push(value); + }); + return [trues, falses]; + }, + + pluck: function(property) { + var results = []; + this.each(function(value) { + results.push(value[property]); + }); + return results; + }, + + reject: function(iterator, context) { + iterator = iterator.bind(context); + var results = []; + this.each(function(value, index) { + if (!iterator(value, index)) + results.push(value); + }); + return results; + }, + + sortBy: function(iterator, context) { + iterator = iterator.bind(context); + 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 (Object.isFunction(args.last())) + 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 '#<Enumerable:' + this.toArray().inspect() + '>'; + } +}; + +Object.extend(Enumerable, { + map: Enumerable.collect, + find: Enumerable.detect, + select: Enumerable.findAll, + filter: Enumerable.findAll, + member: Enumerable.include, + entries: Enumerable.toArray, + every: Enumerable.all, + some: Enumerable.any +}); +function $A(iterable) { + if (!iterable) return []; + if (iterable.toArray) return iterable.toArray(); + var length = iterable.length || 0, results = new Array(length); + while (length--) results[length] = iterable[length]; + return results; +} + +if (Prototype.Browser.WebKit) { + $A = function(iterable) { + if (!iterable) return []; + if (!(Object.isFunction(iterable) && iterable == '[object NodeList]') && + iterable.toArray) return iterable.toArray(); + var length = iterable.length || 0, results = new Array(length); + while (length--) results[length] = iterable[length]; + return results; + }; +} + +Array.from = $A; + +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(Object.isArray(value) ? + value.flatten() : [value]); + }); + }, + + without: function() { + var values = $A(arguments); + return this.select(function(value) { + return !values.include(value); + }); + }, + + 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; + }); + }, + + intersect: function(array) { + return this.uniq().findAll(function(item) { + return array.detect(function(value) { return item === value }); + }); + }, + + 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 (!Object.isUndefined(value)) results.push(value); + }); + return '[' + results.join(', ') + ']'; + } +}); + +// use native browser JS 1.6 implementation if available +if (Object.isFunction(Array.prototype.forEach)) + Array.prototype._each = Array.prototype.forEach; + +if (!Array.prototype.indexOf) Array.prototype.indexOf = function(item, i) { + i || (i = 0); + var length = this.length; + if (i < 0) i = length + i; + for (; i < length; i++) + if (this[i] === item) return i; + return -1; +}; + +if (!Array.prototype.lastIndexOf) Array.prototype.lastIndexOf = function(item, i) { + i = isNaN(i) ? this.length : (i < 0 ? this.length + i : i) + 1; + var n = this.slice(0, i).reverse().indexOf(item); + return (n < 0) ? n : i - n - 1; +}; + +Array.prototype.toArray = Array.prototype.clone; + +function $w(string) { + if (!Object.isString(string)) return []; + string = string.strip(); + return string ? string.split(/\s+/) : []; +} + +if (Prototype.Browser.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 (Object.isArray(arguments[i])) { + for (var j = 0, arrayLength = arguments[i].length; j < arrayLength; j++) + array.push(arguments[i][j]); + } else { + array.push(arguments[i]); + } + } + return array; + }; +} +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'; + } +}); + +$w('abs round ceil floor').each(function(method){ + Number.prototype[method] = Math[method].methodize(); +}); +function $H(object) { + return new Hash(object); +}; + +var Hash = Class.create(Enumerable, (function() { + + function toQueryPair(key, value) { + if (Object.isUndefined(value)) return key; + return key + '=' + encodeURIComponent(String.interpret(value)); + } + + return { + initialize: function(object) { + this._object = Object.isHash(object) ? object.toObject() : Object.clone(object); + }, + + _each: function(iterator) { + for (var key in this._object) { + var value = this._object[key], pair = [key, value]; + pair.key = key; + pair.value = value; + iterator(pair); + } + }, + + set: function(key, value) { + return this._object[key] = value; + }, + + get: function(key) { + return this._object[key]; + }, + + unset: function(key) { + var value = this._object[key]; + delete this._object[key]; + return value; + }, + + toObject: function() { + return Object.clone(this._object); + }, + + keys: function() { + return this.pluck('key'); + }, + + values: function() { + return this.pluck('value'); + }, + + index: function(value) { + var match = this.detect(function(pair) { + return pair.value === value; + }); + return match && match.key; + }, + + merge: function(object) { + return this.clone().update(object); + }, + + update: function(object) { + return new Hash(object).inject(this, function(result, pair) { + result.set(pair.key, pair.value); + return result; + }); + }, + + toQueryString: function() { + return this.map(function(pair) { + var key = encodeURIComponent(pair.key), values = pair.value; + + if (values && typeof values == 'object') { + if (Object.isArray(values)) + return values.map(toQueryPair.curry(key)).join('&'); + } + return toQueryPair(key, values); + }).join('&'); + }, + + inspect: function() { + return '#<Hash:{' + this.map(function(pair) { + return pair.map(Object.inspect).join(': '); + }).join(', ') + '}>'; + }, + + toJSON: function() { + return Object.toJSON(this.toObject()); + }, + + clone: function() { + return new Hash(this); + } + } +})()); + +Hash.prototype.toTemplateReplacements = Hash.prototype.toObject; +Hash.from = $H; +var ObjectRange = Class.create(Enumerable, { + 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 (Object.isFunction(responder[callback])) { + 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 = Class.create({ + initialize: function(options) { + this.options = { + method: 'post', + asynchronous: true, + contentType: 'application/x-www-form-urlencoded', + encoding: 'UTF-8', + parameters: '', + evalJSON: true, + evalJS: true + }; + Object.extend(this.options, options || { }); + + this.options.method = this.options.method.toLowerCase(); + + if (Object.isString(this.options.parameters)) + this.options.parameters = this.options.parameters.toQueryParams(); + else if (Object.isHash(this.options.parameters)) + this.options.parameters = this.options.parameters.toObject(); + } +}); + +Ajax.Request = Class.create(Ajax.Base, { + _complete: false, + + initialize: function($super, url, options) { + $super(options); + this.transport = Ajax.getTransport(); + 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 = Object.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 { + var response = new Ajax.Response(this); + if (this.options.onCreate) this.options.onCreate(response); + Ajax.Responders.dispatch('onCreate', this, response); + + this.transport.open(this.method.toUpperCase(), this.url, + this.options.asynchronous); + + if (this.options.asynchronous) this.respondToReadyState.bind(this).defer(1); + + 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 (Object.isFunction(extras.push)) + 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() { + var status = this.getStatus(); + return !status || (status >= 200 && status < 300); + }, + + getStatus: function() { + try { + return this.transport.status || 0; + } catch (e) { return 0 } + }, + + respondToReadyState: function(readyState) { + var state = Ajax.Request.Events[readyState], response = new Ajax.Response(this); + + if (state == 'Complete') { + try { + this._complete = true; + (this.options['on' + response.status] + || this.options['on' + (this.success() ? 'Success' : 'Failure')] + || Prototype.emptyFunction)(response, response.headerJSON); + } catch (e) { + this.dispatchException(e); + } + + var contentType = response.getHeader('Content-type'); + if (this.options.evalJS == 'force' + || (this.options.evalJS && this.isSameOrigin() && contentType + && contentType.match(/^\s*(text|application)\/(x-)?(java|ecma)script(;.*)?\s*$/i))) + this.evalResponse(); + } + + try { + (this.options['on' + state] || Prototype.emptyFunction)(response, response.headerJSON); + Ajax.Responders.dispatch('on' + state, this, response, response.headerJSON); + } catch (e) { + this.dispatchException(e); + } + + if (state == 'Complete') { + // avoid memory leak in MSIE: clean up + this.transport.onreadystatechange = Prototype.emptyFunction; + } + }, + + isSameOrigin: function() { + var m = this.url.match(/^\s*https?:\/\/[^\/]*/); + return !m || (m[0] == '#{protocol}//#{domain}#{port}'.interpolate({ + protocol: location.protocol, + domain: document.domain, + port: location.port ? ':' + location.port : '' + })); + }, + + getHeader: function(name) { + try { + return this.transport.getResponseHeader(name) || 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.Request.Events = + ['Uninitialized', 'Loading', 'Loaded', 'Interactive', 'Complete']; + +Ajax.Response = Class.create({ + initialize: function(request){ + this.request = request; + var transport = this.transport = request.transport, + readyState = this.readyState = transport.readyState; + + if((readyState > 2 && !Prototype.Browser.IE) || readyState == 4) { + this.status = this.getStatus(); + this.statusText = this.getStatusText(); + this.responseText = String.interpret(transport.responseText); + this.headerJSON = this._getHeaderJSON(); + } + + if(readyState == 4) { + var xml = transport.responseXML; + this.responseXML = Object.isUndefined(xml) ? null : xml; + this.responseJSON = this._getResponseJSON(); + } + }, + + status: 0, + statusText: '', + + getStatus: Ajax.Request.prototype.getStatus, + + getStatusText: function() { + try { + return this.transport.statusText || ''; + } catch (e) { return '' } + }, + + getHeader: Ajax.Request.prototype.getHeader, + + getAllHeaders: function() { + try { + return this.getAllResponseHeaders(); + } catch (e) { return null } + }, + + getResponseHeader: function(name) { + return this.transport.getResponseHeader(name); + }, + + getAllResponseHeaders: function() { + return this.transport.getAllResponseHeaders(); + }, + + _getHeaderJSON: function() { + var json = this.getHeader('X-JSON'); + if (!json) return null; + json = decodeURIComponent(escape(json)); + try { + return json.evalJSON(this.request.options.sanitizeJSON || + !this.request.isSameOrigin()); + } catch (e) { + this.request.dispatchException(e); + } + }, + + _getResponseJSON: function() { + var options = this.request.options; + if (!options.evalJSON || (options.evalJSON != 'force' && + !(this.getHeader('Content-type') || '').include('application/json')) || + this.responseText.blank()) + return null; + try { + return this.responseText.evalJSON(options.sanitizeJSON || + !this.request.isSameOrigin()); + } catch (e) { + this.request.dispatchException(e); + } + } +}); + +Ajax.Updater = Class.create(Ajax.Request, { + initialize: function($super, container, url, options) { + this.container = { + success: (container.success || container), + failure: (container.failure || (container.success ? null : container)) + }; + + options = Object.clone(options); + var onComplete = options.onComplete; + options.onComplete = (function(response, json) { + this.updateContent(response.responseText); + if (Object.isFunction(onComplete)) onComplete(response, json); + }).bind(this); + + $super(url, options); + }, + + updateContent: function(responseText) { + var receiver = this.container[this.success() ? 'success' : 'failure'], + options = this.options; + + if (!options.evalScripts) responseText = responseText.stripScripts(); + + if (receiver = $(receiver)) { + if (options.insertion) { + if (Object.isString(options.insertion)) { + var insertion = { }; insertion[options.insertion] = responseText; + receiver.insert(insertion); + } + else options.insertion(receiver, responseText); + } + else receiver.update(responseText); + } + } +}); + +Ajax.PeriodicalUpdater = Class.create(Ajax.Base, { + initialize: function($super, container, url, options) { + $super(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(response) { + if (this.options.decay) { + this.decay = (response.responseText == this.lastText ? + this.decay * this.options.decay : 1); + + this.lastText = response.responseText; + } + this.timer = this.onTimerEvent.bind(this).delay(this.decay * this.frequency); + }, + + 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 (Object.isString(element)) + 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(Element.extend(query.snapshotItem(i))); + return results; + }; +} + +/*--------------------------------------------------------------------------*/ + +if (!window.Node) var Node = { }; + +if (!Node.ELEMENT_NODE) { + // DOM level 2 ECMAScript Language Binding + Object.extend(Node, { + ELEMENT_NODE: 1, + ATTRIBUTE_NODE: 2, + TEXT_NODE: 3, + CDATA_SECTION_NODE: 4, + ENTITY_REFERENCE_NODE: 5, + ENTITY_NODE: 6, + PROCESSING_INSTRUCTION_NODE: 7, + COMMENT_NODE: 8, + DOCUMENT_NODE: 9, + DOCUMENT_TYPE_NODE: 10, + DOCUMENT_FRAGMENT_NODE: 11, + NOTATION_NODE: 12 + }); +} + +(function() { + var element = this.Element; + this.Element = function(tagName, attributes) { + attributes = attributes || { }; + tagName = tagName.toLowerCase(); + var cache = Element.cache; + if (Prototype.Browser.IE && attributes.name) { + tagName = '<' + tagName + ' name="' + attributes.name + '">'; + delete attributes.name; + return Element.writeAttribute(document.createElement(tagName), attributes); + } + if (!cache[tagName]) cache[tagName] = Element.extend(document.createElement(tagName)); + return Element.writeAttribute(cache[tagName].cloneNode(false), attributes); + }; + Object.extend(this.Element, element || { }); +}).call(window); + +Element.cache = { }; + +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, content) { + element = $(element); + if (content && content.toElement) content = content.toElement(); + if (Object.isElement(content)) return element.update().insert(content); + content = Object.toHTML(content); + element.innerHTML = content.stripScripts(); + content.evalScripts.bind(content).defer(); + return element; + }, + + replace: function(element, content) { + element = $(element); + if (content && content.toElement) content = content.toElement(); + else if (!Object.isElement(content)) { + content = Object.toHTML(content); + var range = element.ownerDocument.createRange(); + range.selectNode(element); + content.evalScripts.bind(content).defer(); + content = range.createContextualFragment(content.stripScripts()); + } + element.parentNode.replaceChild(content, element); + return element; + }, + + insert: function(element, insertions) { + element = $(element); + + if (Object.isString(insertions) || Object.isNumber(insertions) || + Object.isElement(insertions) || (insertions && (insertions.toElement || insertions.toHTML))) + insertions = {bottom:insertions}; + + var content, insert, tagName, childNodes; + + for (var position in insertions) { + content = insertions[position]; + position = position.toLowerCase(); + insert = Element._insertionTranslations[position]; + + if (content && content.toElement) content = content.toElement(); + if (Object.isElement(content)) { + insert(element, content); + continue; + } + + content = Object.toHTML(content); + + tagName = ((position == 'before' || position == 'after') + ? element.parentNode : element).tagName.toUpperCase(); + + childNodes = Element._getContentFromAnonymousElement(tagName, content.stripScripts()); + + if (position == 'top' || position == 'after') childNodes.reverse(); + childNodes.each(insert.curry(element)); + + content.evalScripts.bind(content).defer(); + } + + return element; + }, + + wrap: function(element, wrapper, attributes) { + element = $(element); + if (Object.isElement(wrapper)) + $(wrapper).writeAttribute(attributes || { }); + else if (Object.isString(wrapper)) wrapper = new Element(wrapper, attributes); + else wrapper = new Element('div', wrapper); + if (element.parentNode) + element.parentNode.replaceChild(wrapper, element); + wrapper.appendChild(element); + return wrapper; + }, + + 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 $(element).select("*"); + }, + + 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 (Object.isString(selector)) + 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 Object.isNumber(expression) ? ancestors[expression] : + Selector.findElement(ancestors, expression, index); + }, + + down: function(element, expression, index) { + element = $(element); + if (arguments.length == 1) return element.firstDescendant(); + return Object.isNumber(expression) ? element.descendants()[expression] : + element.select(expression)[index || 0]; + }, + + previous: function(element, expression, index) { + element = $(element); + if (arguments.length == 1) return $(Selector.handlers.previousElementSibling(element)); + var previousSiblings = element.previousSiblings(); + return Object.isNumber(expression) ? previousSiblings[expression] : + Selector.findElement(previousSiblings, expression, index); + }, + + next: function(element, expression, index) { + element = $(element); + if (arguments.length == 1) return $(Selector.handlers.nextElementSibling(element)); + var nextSiblings = element.nextSiblings(); + return Object.isNumber(expression) ? nextSiblings[expression] : + Selector.findElement(nextSiblings, expression, index); + }, + + select: function() { + var args = $A(arguments), element = $(args.shift()); + return Selector.findChildElements(element, args); + }, + + adjacent: function() { + var args = $A(arguments), element = $(args.shift()); + return Selector.findChildElements(element.parentNode, args).without(element); + }, + + identify: function(element) { + element = $(element); + var id = element.readAttribute('id'), self = arguments.callee; + if (id) return id; + do { id = 'anonymous_element_' + self.counter++ } while ($(id)); + element.writeAttribute('id', id); + return id; + }, + + readAttribute: function(element, name) { + element = $(element); + if (Prototype.Browser.IE) { + var t = Element._attributeTranslations.read; + if (t.values[name]) return t.values[name](element, name); + if (t.names[name]) name = t.names[name]; + if (name.include(':')) { + return (!element.attributes || !element.attributes[name]) ? null : + element.attributes[name].value; + } + } + return element.getAttribute(name); + }, + + writeAttribute: function(element, name, value) { + element = $(element); + var attributes = { }, t = Element._attributeTranslations.write; + + if (typeof name == 'object') attributes = name; + else attributes[name] = Object.isUndefined(value) ? true : value; + + for (var attr in attributes) { + name = t.names[attr] || attr; + value = attributes[attr]; + if (t.values[attr]) name = t.values[attr](element, value); + if (value === false || value === null) + element.removeAttribute(name); + else if (value === true) + element.setAttribute(name, name); + else element.setAttribute(name, value); + } + return element; + }, + + 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; + return (elementClassName.length > 0 && (elementClassName == className || + new RegExp("(^|\\s)" + className + "(\\s|$)").test(elementClassName))); + }, + + addClassName: function(element, className) { + if (!(element = $(element))) return; + if (!element.hasClassName(className)) + element.className += (element.className ? ' ' : '') + className; + return element; + }, + + removeClassName: function(element, className) { + if (!(element = $(element))) return; + element.className = element.className.replace( + new RegExp("(^|\\s+)" + className + "(\\s+|$)"), ' ').strip(); + return element; + }, + + toggleClassName: function(element, className) { + if (!(element = $(element))) return; + return element[element.hasClassName(className) ? + 'removeClassName' : 'addClassName'](className); + }, + + // 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); + var originalAncestor = ancestor; + + if (element.compareDocumentPosition) + return (element.compareDocumentPosition(ancestor) & 8) === 8; + + if (element.sourceIndex && !Prototype.Browser.Opera) { + var e = element.sourceIndex, a = ancestor.sourceIndex, + nextAncestor = ancestor.nextSibling; + if (!nextAncestor) { + do { ancestor = ancestor.parentNode; } + while (!(nextAncestor = ancestor.nextSibling) && ancestor.parentNode); + } + if (nextAncestor && nextAncestor.sourceIndex) + return (e > a && e < nextAncestor.sourceIndex); + } + + while (element = element.parentNode) + if (element == originalAncestor) return true; + return false; + }, + + scrollTo: function(element) { + element = $(element); + var pos = element.cumulativeOffset(); + 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) { + element = $(element); + var elementStyle = element.style, match; + if (Object.isString(styles)) { + element.style.cssText += ';' + styles; + return styles.include('opacity') ? + element.setOpacity(styles.match(/opacity:\s*(\d?\.?\d*)/)[1]) : element; + } + for (var property in styles) + if (property == 'opacity') element.setOpacity(styles[property]); + else + elementStyle[(property == 'float' || property == 'cssFloat') ? + (Object.isUndefined(elementStyle.styleFloat) ? 'cssFloat' : 'styleFloat') : + property] = 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.getStyle(element, 'overflow') || 'auto'; + if (element._overflow !== '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; + }, + + cumulativeOffset: function(element) { + var valueT = 0, valueL = 0; + do { + valueT += element.offsetTop || 0; + valueL += element.offsetLeft || 0; + element = element.offsetParent; + } while (element); + return Element._returnOffset(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 !== 'static') break; + } + } while (element); + return Element._returnOffset(valueL, valueT); + }, + + absolutize: function(element) { + element = $(element); + if (element.getStyle('position') == 'absolute') return; + // Position.prepare(); // To be done manually by Scripty when it needs it. + + var offsets = element.positionedOffset(); + 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'; + return element; + }, + + relativize: function(element) { + element = $(element); + if (element.getStyle('position') == 'relative') return; + // Position.prepare(); // To be done manually by Scripty when it needs it. + + 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; + return element; + }, + + cumulativeScrollOffset: function(element) { + var valueT = 0, valueL = 0; + do { + valueT += element.scrollTop || 0; + valueL += element.scrollLeft || 0; + element = element.parentNode; + } while (element); + return Element._returnOffset(valueL, valueT); + }, + + getOffsetParent: 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); + }, + + viewportOffset: 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 && + Element.getStyle(element, 'position') == 'absolute') break; + + } while (element = element.offsetParent); + + element = forElement; + do { + if (!Prototype.Browser.Opera || element.tagName == 'BODY') { + valueT -= element.scrollTop || 0; + valueL -= element.scrollLeft || 0; + } + } while (element = element.parentNode); + + return Element._returnOffset(valueL, valueT); + }, + + clonePosition: function(element, source) { + 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 = source.viewportOffset(); + + // find coordinate system to use + element = $(element); + 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(element, 'position') == 'absolute') { + parent = element.getOffsetParent(); + delta = parent.viewportOffset(); + } + + // 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) element.style.left = (p[0] - delta[0] + options.offsetLeft) + 'px'; + if (options.setTop) element.style.top = (p[1] - delta[1] + options.offsetTop) + 'px'; + if (options.setWidth) element.style.width = source.offsetWidth + 'px'; + if (options.setHeight) element.style.height = source.offsetHeight + 'px'; + return element; + } +}; + +Element.Methods.identify.counter = 1; + +Object.extend(Element.Methods, { + getElementsBySelector: Element.Methods.select, + childElements: Element.Methods.immediateDescendants +}); + +Element._attributeTranslations = { + write: { + names: { + className: 'class', + htmlFor: 'for' + }, + values: { } + } +}; + +if (Prototype.Browser.Opera) { + Element.Methods.getStyle = Element.Methods.getStyle.wrap( + function(proceed, element, style) { + switch (style) { + case 'left': case 'top': case 'right': case 'bottom': + if (proceed(element, 'position') === 'static') return null; + case 'height': case 'width': + // returns '0px' for hidden elements; we want it to return null + if (!Element.visible(element)) return null; + + // returns the border-box dimensions rather than the content-box + // dimensions, so we subtract padding and borders from the value + var dim = parseInt(proceed(element, style), 10); + + if (dim !== element['offset' + style.capitalize()]) + return dim + 'px'; + + var properties; + if (style === 'height') { + properties = ['border-top-width', 'padding-top', + 'padding-bottom', 'border-bottom-width']; + } + else { + properties = ['border-left-width', 'padding-left', + 'padding-right', 'border-right-width']; + } + return properties.inject(dim, function(memo, property) { + var val = proceed(element, property); + return val === null ? memo : memo - parseInt(val, 10); + }) + 'px'; + default: return proceed(element, style); + } + } + ); + + Element.Methods.readAttribute = Element.Methods.readAttribute.wrap( + function(proceed, element, attribute) { + if (attribute === 'title') return element.title; + return proceed(element, attribute); + } + ); +} + +else if (Prototype.Browser.IE) { + // IE doesn't report offsets correctly for static elements, so we change them + // to "relative" to get the values, then change them back. + Element.Methods.getOffsetParent = Element.Methods.getOffsetParent.wrap( + function(proceed, element) { + element = $(element); + var position = element.getStyle('position'); + if (position !== 'static') return proceed(element); + element.setStyle({ position: 'relative' }); + var value = proceed(element); + element.setStyle({ position: position }); + return value; + } + ); + + $w('positionedOffset viewportOffset').each(function(method) { + Element.Methods[method] = Element.Methods[method].wrap( + function(proceed, element) { + element = $(element); + var position = element.getStyle('position'); + if (position !== 'static') return proceed(element); + // Trigger hasLayout on the offset parent so that IE6 reports + // accurate offsetTop and offsetLeft values for position: fixed. + var offsetParent = element.getOffsetParent(); + if (offsetParent && offsetParent.getStyle('position') === 'fixed') + offsetParent.setStyle({ zoom: 1 }); + element.setStyle({ position: 'relative' }); + var value = proceed(element); + element.setStyle({ position: position }); + return value; + } + ); + }); + + 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) { + function stripAlpha(filter){ + return filter.replace(/alpha\([^\)]*\)/gi,''); + } + element = $(element); + var currentStyle = element.currentStyle; + if ((currentStyle && !currentStyle.hasLayout) || + (!currentStyle && element.style.zoom == 'normal')) + element.style.zoom = 1; + + var filter = element.getStyle('filter'), style = element.style; + if (value == 1 || value === '') { + (filter = stripAlpha(filter)) ? + style.filter = filter : style.removeAttribute('filter'); + return element; + } else if (value < 0.00001) value = 0; + style.filter = stripAlpha(filter) + + 'alpha(opacity=' + (value * 100) + ')'; + return element; + }; + + Element._attributeTranslations = { + read: { + names: { + 'class': 'className', + 'for': 'htmlFor' + }, + values: { + _getAttr: function(element, attribute) { + return element.getAttribute(attribute, 2); + }, + _getAttrNode: function(element, attribute) { + var node = element.getAttributeNode(attribute); + return node ? node.value : ""; + }, + _getEv: function(element, attribute) { + attribute = element.getAttribute(attribute); + return attribute ? attribute.toString().slice(23, -2) : null; + }, + _flag: function(element, attribute) { + return $(element).hasAttribute(attribute) ? attribute : null; + }, + style: function(element) { + return element.style.cssText.toLowerCase(); + }, + title: function(element) { + return element.title; + } + } + } + }; + + Element._attributeTranslations.write = { + names: Object.extend({ + cellpadding: 'cellPadding', + cellspacing: 'cellSpacing' + }, Element._attributeTranslations.read.names), + values: { + checked: function(element, value) { + element.checked = !!value; + }, + + style: function(element, value) { + element.style.cssText = value ? value : ''; + } + } + }; + + Element._attributeTranslations.has = {}; + + $w('colSpan rowSpan vAlign dateTime accessKey tabIndex ' + + 'encType maxLength readOnly longDesc').each(function(attr) { + Element._attributeTranslations.write.names[attr.toLowerCase()] = attr; + Element._attributeTranslations.has[attr.toLowerCase()] = attr; + }); + + (function(v) { + Object.extend(v, { + href: v._getAttr, + src: v._getAttr, + type: v._getAttr, + action: v._getAttrNode, + disabled: v._flag, + checked: v._flag, + readonly: v._flag, + multiple: v._flag, + onload: v._getEv, + onunload: v._getEv, + onclick: v._getEv, + ondblclick: v._getEv, + onmousedown: v._getEv, + onmouseup: v._getEv, + onmouseover: v._getEv, + onmousemove: v._getEv, + onmouseout: v._getEv, + onfocus: v._getEv, + onblur: v._getEv, + onkeypress: v._getEv, + onkeydown: v._getEv, + onkeyup: v._getEv, + onsubmit: v._getEv, + onreset: v._getEv, + onselect: v._getEv, + onchange: v._getEv + }); + })(Element._attributeTranslations.read.values); +} + +else if (Prototype.Browser.Gecko && /rv:1\.8\.0/.test(navigator.userAgent)) { + Element.Methods.setOpacity = function(element, value) { + element = $(element); + element.style.opacity = (value == 1) ? 0.999999 : + (value === '') ? '' : (value < 0.00001) ? 0 : value; + return element; + }; +} + +else if (Prototype.Browser.WebKit) { + Element.Methods.setOpacity = function(element, value) { + element = $(element); + element.style.opacity = (value == 1 || value === '') ? '' : + (value < 0.00001) ? 0 : value; + + if (value == 1) + if(element.tagName == 'IMG' && element.width) { + element.width++; element.width--; + } else try { + var n = document.createTextNode(' '); + element.appendChild(n); + element.removeChild(n); + } catch (e) { } + + return element; + }; + + // Safari returns margins on body which is incorrect if the child is absolutely + // positioned. For performance reasons, redefine Element#cumulativeOffset for + // KHTML/WebKit only. + Element.Methods.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 Element._returnOffset(valueL, valueT); + }; +} + +if (Prototype.Browser.IE || Prototype.Browser.Opera) { + // IE and Opera are missing .innerHTML support for TABLE-related and SELECT elements + Element.Methods.update = function(element, content) { + element = $(element); + + if (content && content.toElement) content = content.toElement(); + if (Object.isElement(content)) return element.update().insert(content); + + content = Object.toHTML(content); + var tagName = element.tagName.toUpperCase(); + + if (tagName in Element._insertionTranslations.tags) { + $A(element.childNodes).each(function(node) { element.removeChild(node) }); + Element._getContentFromAnonymousElement(tagName, content.stripScripts()) + .each(function(node) { element.appendChild(node) }); + } + else element.innerHTML = content.stripScripts(); + + content.evalScripts.bind(content).defer(); + return element; + }; +} + +if ('outerHTML' in document.createElement('div')) { + Element.Methods.replace = function(element, content) { + element = $(element); + + if (content && content.toElement) content = content.toElement(); + if (Object.isElement(content)) { + element.parentNode.replaceChild(content, element); + return element; + } + + content = Object.toHTML(content); + var parent = element.parentNode, tagName = parent.tagName.toUpperCase(); + + if (Element._insertionTranslations.tags[tagName]) { + var nextSibling = element.next(); + var fragments = Element._getContentFromAnonymousElement(tagName, content.stripScripts()); + parent.removeChild(element); + if (nextSibling) + fragments.each(function(node) { parent.insertBefore(node, nextSibling) }); + else + fragments.each(function(node) { parent.appendChild(node) }); + } + else element.outerHTML = content.stripScripts(); + + content.evalScripts.bind(content).defer(); + return element; + }; +} + +Element._returnOffset = function(l, t) { + var result = [l, t]; + result.left = l; + result.top = t; + return result; +}; + +Element._getContentFromAnonymousElement = function(tagName, html) { + var div = new Element('div'), t = Element._insertionTranslations.tags[tagName]; + if (t) { + div.innerHTML = t[0] + html + t[1]; + t[2].times(function() { div = div.firstChild }); + } else div.innerHTML = html; + return $A(div.childNodes); +}; + +Element._insertionTranslations = { + before: function(element, node) { + element.parentNode.insertBefore(node, element); + }, + top: function(element, node) { + element.insertBefore(node, element.firstChild); + }, + bottom: function(element, node) { + element.appendChild(node); + }, + after: function(element, node) { + element.parentNode.insertBefore(node, element.nextSibling); + }, + tags: { + TABLE: ['<table>', '</table>', 1], + TBODY: ['<table><tbody>', '</tbody></table>', 2], + TR: ['<table><tbody><tr>', '</tr></tbody></table>', 3], + TD: ['<table><tbody><tr><td>', '</td></tr></tbody></table>', 4], + SELECT: ['<select>', '</select>', 1] + } +}; + +(function() { + Object.extend(this.tags, { + THEAD: this.tags.TBODY, + TFOOT: this.tags.TBODY, + TH: this.tags.TD + }); +}).call(Element._insertionTranslations); + +Element.Methods.Simulated = { + hasAttribute: function(element, attribute) { + attribute = Element._attributeTranslations.has[attribute] || attribute; + var 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.extend = (function() { + if (Prototype.BrowserFeatures.SpecificElementExtensions) + return Prototype.K; + + var Methods = { }, ByTag = Element.Methods.ByTag; + + var extend = Object.extend(function(element) { + if (!element || element._extendedByPrototype || + element.nodeType != 1 || element == window) return element; + + var methods = Object.clone(Methods), + tagName = element.tagName, property, value; + + // extend methods for specific tags + if (ByTag[tagName]) Object.extend(methods, ByTag[tagName]); + + for (property in methods) { + value = methods[property]; + if (Object.isFunction(value) && !(property in element)) + element[property] = value.methodize(); + } + + element._extendedByPrototype = Prototype.emptyFunction; + return element; + + }, { + refresh: function() { + // extend methods for all tags (Safari doesn't need this) + if (!Prototype.BrowserFeatures.ElementExtensions) { + Object.extend(Methods, Element.Methods); + Object.extend(Methods, Element.Methods.Simulated); + } + } + }); + + extend.refresh(); + return extend; +})(); + +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 (!methods) { + Object.extend(Form, Form.Methods); + 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) + }); + } + + if (arguments.length == 2) { + var tagName = methods; + methods = arguments[1]; + } + + if (!tagName) Object.extend(Element.Methods, methods || { }); + else { + if (Object.isArray(tagName)) 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; + for (var property in methods) { + var value = methods[property]; + if (!Object.isFunction(value)) continue; + if (!onlyIfAbsent || !(property in destination)) + destination[property] = value.methodize(); + } + } + + 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 (Object.isUndefined(klass)) continue; + copy(T[tag], klass.prototype); + } + } + + Object.extend(Element, Element.Methods); + delete Element.ByTag; + + if (Element.extend.refresh) Element.extend.refresh(); + Element.cache = { }; +}; + +document.viewport = { + getDimensions: function() { + var dimensions = { }; + var B = Prototype.Browser; + $w('width height').each(function(d) { + var D = d.capitalize(); + dimensions[d] = (B.WebKit && !document.evaluate) ? self['inner' + D] : + (B.Opera) ? document.body['client' + D] : document.documentElement['client' + D]; + }); + return dimensions; + }, + + getWidth: function() { + return this.getDimensions().width; + }, + + getHeight: function() { + return this.getDimensions().height; + }, + + getScrollOffsets: function() { + return Element._returnOffset( + window.pageXOffset || document.documentElement.scrollLeft || document.body.scrollLeft, + window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop); + } +}; +/* 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({ + initialize: function(expression) { + this.expression = expression.strip(); + this.compileMatcher(); + }, + + shouldUseXPath: function() { + if (!Prototype.BrowserFeatures.XPath) return false; + + var e = this.expression; + + // Safari 3 chokes on :*-of-type and :empty + if (Prototype.Browser.WebKit && + (e.include("-of-type") || e.include(":empty"))) + return false; + + // XPath can't do namespaced attributes, nor can it read + // the "checked" property from DOM nodes + if ((/(\[[\w-]*?:|:checked)/).test(this.expression)) + return false; + + return true; + }, + + compileMatcher: function() { + if (this.shouldUseXPath()) + 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(Object.isFunction(c[i]) ? 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(Object.isFunction(x[i]) ? 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) { + this.tokens = []; + + var e = this.expression, ps = Selector.patterns, as = Selector.assertions; + var le, p, m; + + while (e && le !== e && (/\S/).test(e)) { + le = e; + for (var i in ps) { + p = ps[i]; + if (m = e.match(p)) { + // use the Selector.assertions methods unless the selector + // is too complex. + if (as[i]) { + this.tokens.push([i, Object.clone(m)]); + e = e.replace(m[0], ''); + } else { + // reluctantly do a document-wide search + // and look for a match in the array + return this.findElements(document).include(element); + } + } + } + } + + var match = true, name, matches; + for (var i = 0, token; token = this.tokens[i]; i++) { + name = token[0], matches = token[1]; + if (!Selector.assertions[name](element, matches)) { + match = false; break; + } + } + + return match; + }, + + toString: function() { + return this.expression; + }, + + inspect: function() { + return "#<Selector:" + this.expression.inspect() + ">"; + } +}); + +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: function(m) { + m[1] = m[1].toLowerCase(); + return new Template("[@#{1}]").evaluate(m); + }, + attr: function(m) { + m[1] = m[1].toLowerCase(); + 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 (Object.isFunction(h)) 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, v; + + var exclusion = []; + while (e && le != e && (/\S/).test(e)) { + le = e; + for (var i in p) { + if (m = e.match(p[i])) { + v = Object.isFunction(x[i]) ? 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); c = false;', + attr: function(m) { + m[3] = (m[5] || m[6]); + return new Template('n = h.attr(n, r, "#{1}", "#{3}", "#{2}", c); 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]*?)\4|([^'"][^\]]*?)))?\]/ + }, + + // for Selector.match and Element#match + assertions: { + tagName: function(element, matches) { + return matches[1].toUpperCase() == element.tagName.toUpperCase(); + }, + + className: function(element, matches) { + return Element.hasClassName(element, matches[1]); + }, + + id: function(element, matches) { + return element.id === matches[1]; + }, + + attrPresence: function(element, matches) { + return Element.hasAttribute(element, matches[1]); + }, + + attr: function(element, matches) { + var nodeValue = Element.readAttribute(element, matches[1]); + return nodeValue && Selector.operators[matches[2]](nodeValue, matches[5] || matches[6]); + } + }, + + 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) { + var _true = Prototype.emptyFunction; + for (var i = 0, node; node = nodes[i]; i++) + node._countedByPrototype = _true; + return nodes; + }, + + unmark: function(nodes) { + for (var i = 0, node; node = nodes[i]; i++) + node._countedByPrototype = 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._countedByPrototype = Prototype.emptyFunction; + if (reverse) { + for (var nodes = parentNode.childNodes, i = nodes.length - 1, j = 1; i >= 0; i--) { + var node = nodes[i]; + if (node.nodeType == 1 && (!ofType || node._countedByPrototype)) node.nodeIndex = j++; + } + } else { + for (var i = 0, j = 1, nodes = parentNode.childNodes; node = nodes[i]; i++) + if (node.nodeType == 1 && (!ofType || node._countedByPrototype)) 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])._countedByPrototype) { + n._countedByPrototype = Prototype.emptyFunction; + 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, 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) { + var uTagName = 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() === uTagName) results.push(node); + return results; + } else return root.getElementsByTagName(tagName); + }, + + id: function(nodes, root, id, combinator) { + var targetNode = $(id), h = Selector.handlers; + if (!targetNode) return []; + if (!nodes && root == document) return [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, combinator) { + if (!nodes) nodes = root.getElementsByTagName("*"); + if (nodes && combinator) nodes = this[combinator](nodes); + 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, combinator) { + if (!nodes) nodes = root.getElementsByTagName("*"); + if (nodes && combinator) nodes = this[combinator](nodes); + 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._countedByPrototype) { + 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._countedByPrototype) 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() + '-'); } + }, + + split: function(expression) { + var expressions = []; + expression.scan(/(([\w#:.~>+()\s-]+|\*|\[.*?\])+)\s*(,|$)/, function(m) { + expressions.push(m[1].strip()); + }); + return expressions; + }, + + matchElements: function(elements, expression) { + var matches = $$(expression), h = Selector.handlers; + h.mark(matches); + for (var i = 0, results = [], element; element = elements[i]; i++) + if (element._countedByPrototype) results.push(element); + h.unmark(matches); + return results; + }, + + findElement: function(elements, expression, index) { + if (Object.isNumber(expression)) { + index = expression; expression = false; + } + return Selector.matchElements(elements, expression || '*')[index || 0]; + }, + + findChildElements: function(element, expressions) { + expressions = Selector.split(expressions.join(',')); + 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; + } +}); + +if (Prototype.Browser.IE) { + Object.extend(Selector.handlers, { + // IE returns comment nodes on getElementsByTagName("*"). + // Filter them out. + concat: function(a, b) { + for (var i = 0, node; node = b[i]; i++) + if (node.tagName !== "!") a.push(node); + return a; + }, + + // IE improperly serializes _countedByPrototype in (inner|outer)HTML. + unmark: function(nodes) { + for (var i = 0, node; node = nodes[i]; i++) + node.removeAttribute('_countedByPrototype'); + return nodes; + } + }); +} + +function $$() { + return Selector.findChildElements(document, $A(arguments)); +} +var Form = { + reset: function(form) { + $(form).reset(); + return form; + }, + + serializeElements: function(elements, options) { + if (typeof options != 'object') options = { hash: !!options }; + else if (Object.isUndefined(options.hash)) options.hash = true; + var key, value, submitted = false, submit = options.submit; + + var data = elements.inject({ }, function(result, element) { + if (!element.disabled && element.name) { + key = element.name; value = $(element).getValue(); + if (value != null && (element.type != 'submit' || (!submitted && + submit !== false && (!submit || key == submit) && (submitted = true)))) { + if (key in result) { + // a key is already present; construct an array of values + if (!Object.isArray(result[key])) result[key] = [result[key]]; + result[key].push(value); + } + else result[key] = value; + } + } + return result; + }); + + return options.hash ? data : Object.toQueryString(data); + } +}; + +Form.Methods = { + serialize: function(form, options) { + return Form.serializeElements(Form.getElements(form), options); + }, + + 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) { + var elements = $(form).getElements().findAll(function(element) { + return 'hidden' != element.type && !element.disabled; + }); + var firstByIndex = elements.findAll(function(element) { + return element.hasAttribute('tabIndex') && element.tabIndex >= 0; + }).sortBy(function(element) { return element.tabIndex }).first(); + + return firstByIndex ? firstByIndex : elements.find(function(element) { + return ['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, action = form.readAttribute('action') || ''; + if (action.blank()) action = window.location.href; + options.parameters = form.serialize(true); + + if (params) { + if (Object.isString(params)) params = params.toQueryParams(); + Object.extend(options.parameters, params); + } + + if (form.hasAttribute('method') && !options.method) + options.method = form.method; + + return new Ajax.Request(action, options); + } +}; + +/*--------------------------------------------------------------------------*/ + +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 Object.toQueryString(pair); + } + } + return ''; + }, + + getValue: function(element) { + element = $(element); + var method = element.tagName.toLowerCase(); + return Form.Element.Serializers[method](element); + }, + + setValue: function(element, value) { + element = $(element); + var method = element.tagName.toLowerCase(); + Form.Element.Serializers[method](element, value); + return 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; + } +}; + +/*--------------------------------------------------------------------------*/ + +var Field = Form.Element; +var $F = Form.Element.Methods.getValue; + +/*--------------------------------------------------------------------------*/ + +Form.Element.Serializers = { + input: function(element, value) { + switch (element.type.toLowerCase()) { + case 'checkbox': + case 'radio': + return Form.Element.Serializers.inputSelector(element, value); + default: + return Form.Element.Serializers.textarea(element, value); + } + }, + + inputSelector: function(element, value) { + if (Object.isUndefined(value)) return element.checked ? element.value : null; + else element.checked = !!value; + }, + + textarea: function(element, value) { + if (Object.isUndefined(value)) return element.value; + else element.value = value; + }, + + select: function(element, index) { + if (Object.isUndefined(index)) + return this[element.type == 'select-one' ? + 'selectOne' : 'selectMany'](element); + else { + var opt, value, single = !Object.isArray(index); + for (var i = 0, length = element.length; i < length; i++) { + opt = element.options[i]; + value = this.optionValue(opt); + if (single) { + if (value == index) { + opt.selected = true; + return; + } + } + else opt.selected = index.include(value); + } + } + }, + + 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 = Class.create(PeriodicalExecuter, { + initialize: function($super, element, frequency, callback) { + $super(callback, frequency); + this.element = $(element); + this.lastValue = this.getValue(); + }, + + execute: function() { + var value = this.getValue(); + if (Object.isString(this.lastValue) && Object.isString(value) ? + this.lastValue != value : String(this.lastValue) != String(value)) { + this.callback(this.element, value); + this.lastValue = value; + } + } +}); + +Form.Element.Observer = Class.create(Abstract.TimedObserver, { + getValue: function() { + return Form.Element.getValue(this.element); + } +}); + +Form.Observer = Class.create(Abstract.TimedObserver, { + getValue: function() { + return Form.serialize(this.element); + } +}); + +/*--------------------------------------------------------------------------*/ + +Abstract.EventObserver = Class.create({ + 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, 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(Abstract.EventObserver, { + getValue: function() { + return Form.Element.getValue(this.element); + } +}); + +Form.EventObserver = Class.create(Abstract.EventObserver, { + getValue: function() { + return Form.serialize(this.element); + } +}); +if (!window.Event) var Event = { }; + +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, + KEY_INSERT: 45, + + cache: { }, + + relatedTarget: function(event) { + var element; + switch(event.type) { + case 'mouseover': element = event.fromElement; break; + case 'mouseout': element = event.toElement; break; + default: return null; + } + return Element.extend(element); + } +}); + +Event.Methods = (function() { + var isButton; + + if (Prototype.Browser.IE) { + var buttonMap = { 0: 1, 1: 4, 2: 2 }; + isButton = function(event, code) { + return event.button == buttonMap[code]; + }; + + } else if (Prototype.Browser.WebKit) { + isButton = function(event, code) { + switch (code) { + case 0: return event.which == 1 && !event.metaKey; + case 1: return event.which == 1 && event.metaKey; + default: return false; + } + }; + + } else { + isButton = function(event, code) { + return event.which ? (event.which === code + 1) : (event.button === code); + }; + } + + return { + isLeftClick: function(event) { return isButton(event, 0) }, + isMiddleClick: function(event) { return isButton(event, 1) }, + isRightClick: function(event) { return isButton(event, 2) }, + + element: function(event) { + var node = Event.extend(event).target; + return Element.extend(node.nodeType == Node.TEXT_NODE ? node.parentNode : node); + }, + + findElement: function(event, expression) { + var element = Event.element(event); + if (!expression) return element; + var elements = [element].concat(element.ancestors()); + return Selector.findElement(elements, expression, 0); + }, + + pointer: function(event) { + return { + x: event.pageX || (event.clientX + + (document.documentElement.scrollLeft || document.body.scrollLeft)), + y: event.pageY || (event.clientY + + (document.documentElement.scrollTop || document.body.scrollTop)) + }; + }, + + pointerX: function(event) { return Event.pointer(event).x }, + pointerY: function(event) { return Event.pointer(event).y }, + + stop: function(event) { + Event.extend(event); + event.preventDefault(); + event.stopPropagation(); + event.stopped = true; + } + }; +})(); + +Event.extend = (function() { + var methods = Object.keys(Event.Methods).inject({ }, function(m, name) { + m[name] = Event.Methods[name].methodize(); + return m; + }); + + if (Prototype.Browser.IE) { + Object.extend(methods, { + stopPropagation: function() { this.cancelBubble = true }, + preventDefault: function() { this.returnValue = false }, + inspect: function() { return "[object Event]" } + }); + + return function(event) { + if (!event) return false; + if (event._extendedByPrototype) return event; + + event._extendedByPrototype = Prototype.emptyFunction; + var pointer = Event.pointer(event); + Object.extend(event, { + target: event.srcElement, + relatedTarget: Event.relatedTarget(event), + pageX: pointer.x, + pageY: pointer.y + }); + return Object.extend(event, methods); + }; + + } else { + Event.prototype = Event.prototype || document.createEvent("HTMLEvents").__proto__; + Object.extend(Event.prototype, methods); + return Prototype.K; + } +})(); + +Object.extend(Event, (function() { + var cache = Event.cache; + + function getEventID(element) { + if (element._prototypeEventID) return element._prototypeEventID[0]; + arguments.callee.id = arguments.callee.id || 1; + return element._prototypeEventID = [++arguments.callee.id]; + } + + function getDOMEventName(eventName) { + if (eventName && eventName.include(':')) return "dataavailable"; + return eventName; + } + + function getCacheForID(id) { + return cache[id] = cache[id] || { }; + } + + function getWrappersForEventName(id, eventName) { + var c = getCacheForID(id); + return c[eventName] = c[eventName] || []; + } + + function createWrapper(element, eventName, handler) { + var id = getEventID(element); + var c = getWrappersForEventName(id, eventName); + if (c.pluck("handler").include(handler)) return false; + + var wrapper = function(event) { + if (!Event || !Event.extend || + (event.eventName && event.eventName != eventName)) + return false; + + Event.extend(event); + handler.call(element, event); + }; + + wrapper.handler = handler; + c.push(wrapper); + return wrapper; + } + + function findWrapper(id, eventName, handler) { + var c = getWrappersForEventName(id, eventName); + return c.find(function(wrapper) { return wrapper.handler == handler }); + } + + function destroyWrapper(id, eventName, handler) { + var c = getCacheForID(id); + if (!c[eventName]) return false; + c[eventName] = c[eventName].without(findWrapper(id, eventName, handler)); + } + + function destroyCache() { + for (var id in cache) + for (var eventName in cache[id]) + cache[id][eventName] = null; + } + + if (window.attachEvent) { + window.attachEvent("onunload", destroyCache); + } + + return { + observe: function(element, eventName, handler) { + element = $(element); + var name = getDOMEventName(eventName); + + var wrapper = createWrapper(element, eventName, handler); + if (!wrapper) return element; + + if (element.addEventListener) { + element.addEventListener(name, wrapper, false); + } else { + element.attachEvent("on" + name, wrapper); + } + + return element; + }, + + stopObserving: function(element, eventName, handler) { + element = $(element); + var id = getEventID(element), name = getDOMEventName(eventName); + + if (!handler && eventName) { + getWrappersForEventName(id, eventName).each(function(wrapper) { + element.stopObserving(eventName, wrapper.handler); + }); + return element; + + } else if (!eventName) { + Object.keys(getCacheForID(id)).each(function(eventName) { + element.stopObserving(eventName); + }); + return element; + } + + var wrapper = findWrapper(id, eventName, handler); + if (!wrapper) return element; + + if (element.removeEventListener) { + element.removeEventListener(name, wrapper, false); + } else { + element.detachEvent("on" + name, wrapper); + } + + destroyWrapper(id, eventName, handler); + + return element; + }, + + fire: function(element, eventName, memo) { + element = $(element); + if (element == document && document.createEvent && !element.dispatchEvent) + element = document.documentElement; + + var event; + if (document.createEvent) { + event = document.createEvent("HTMLEvents"); + event.initEvent("dataavailable", true, true); + } else { + event = document.createEventObject(); + event.eventType = "ondataavailable"; + } + + event.eventName = eventName; + event.memo = memo || { }; + + if (document.createEvent) { + element.dispatchEvent(event); + } else { + element.fireEvent(event.eventType, event); + } + + return Event.extend(event); + } + }; +})()); + +Object.extend(Event, Event.Methods); + +Element.addMethods({ + fire: Event.fire, + observe: Event.observe, + stopObserving: Event.stopObserving +}); + +Object.extend(document, { + fire: Element.Methods.fire.methodize(), + observe: Element.Methods.observe.methodize(), + stopObserving: Element.Methods.stopObserving.methodize(), + loaded: false +}); + +(function() { + /* Support for the DOMContentLoaded event is based on work by Dan Webb, + Matthias Miller, Dean Edwards and John Resig. */ + + var timer; + + function fireContentLoadedEvent() { + if (document.loaded) return; + if (timer) window.clearInterval(timer); + document.fire("dom:loaded"); + document.loaded = true; + } + + if (document.addEventListener) { + if (Prototype.Browser.WebKit) { + timer = window.setInterval(function() { + if (/loaded|complete/.test(document.readyState)) + fireContentLoadedEvent(); + }, 0); + + Event.observe(window, "load", fireContentLoadedEvent); + + } else { + document.addEventListener("DOMContentLoaded", + fireContentLoadedEvent, false); + } + + } else { + document.write("<script id=__onDOMContentLoaded defer src=//:><\/script>"); + $("__onDOMContentLoaded").onreadystatechange = function() { + if (this.readyState == "complete") { + this.onreadystatechange = null; + fireContentLoadedEvent(); + } + }; + } +})(); +/*------------------------------- DEPRECATED -------------------------------*/ + +Hash.toQueryString = Object.toQueryString; + +var Toggle = { display: Element.toggle }; + +Element.Methods.childOf = Element.Methods.descendantOf; + +var Insertion = { + Before: function(element, content) { + return Element.insert(element, {before:content}); + }, + + Top: function(element, content) { + return Element.insert(element, {top:content}); + }, + + Bottom: function(element, content) { + return Element.insert(element, {bottom:content}); + }, + + After: function(element, content) { + return Element.insert(element, {after:content}); + } +}; + +var $continue = new Error('"throw $continue" is deprecated, use "return" instead'); + +// This should be moved to script.aculo.us; notice the deprecated methods +// further below, that map to the newer Element methods. +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; + }, + + // 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 = Element.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 = Element.cumulativeScrollOffset(element); + + this.xcomp = x + offsetcache[0] - this.deltaX; + this.ycomp = y + offsetcache[1] - this.deltaY; + this.offset = Element.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; + }, + + // Deprecation layer -- use newer Element methods now (1.5.2). + + cumulativeOffset: Element.Methods.cumulativeOffset, + + positionedOffset: Element.Methods.positionedOffset, + + absolutize: function(element) { + Position.prepare(); + return Element.absolutize(element); + }, + + relativize: function(element) { + Position.prepare(); + return Element.relativize(element); + }, + + realOffset: Element.Methods.cumulativeScrollOffset, + + offsetParent: Element.Methods.getOffsetParent, + + page: Element.Methods.viewportOffset, + + clone: function(source, target, options) { + options = options || { }; + return Element.clonePosition(target, source, options); + } +}; + +/*--------------------------------------------------------------------------*/ + +if (!document.getElementsByClassName) document.getElementsByClassName = function(instanceMethods){ + function iter(name) { + return name.blank() ? null : "[contains(concat(' ', @class, ' '), ' " + name + " ')]"; + } + + instanceMethods.getElementsByClassName = Prototype.BrowserFeatures.XPath ? + function(element, className) { + className = className.toString().strip(); + var cond = /\s/.test(className) ? $w(className).map(iter).join('') : iter(className); + return cond ? document._getElementsByXPath('.//*' + cond, element) : []; + } : function(element, className) { + className = className.toString().strip(); + var elements = [], classNames = (/\s/.test(className) ? $w(className) : null); + if (!classNames && !className) return elements; + + var nodes = $(element).getElementsByTagName('*'); + className = ' ' + className + ' '; + + for (var i = 0, child, cn; child = nodes[i]; i++) { + if (child.className && (cn = ' ' + child.className + ' ') && (cn.include(className) || + (classNames && classNames.all(function(name) { + return !name.toString().blank() && cn.include(' ' + name + ' '); + })))) + elements.push(Element.extend(child)); + } + return elements; + }; + + return function(className, parentElement) { + return $(parentElement || document.body).getElementsByClassName(className); + }; +}(Element.Methods); + +/*--------------------------------------------------------------------------*/ + +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); + +/*--------------------------------------------------------------------------*/ + +Element.addMethods(); \ No newline at end of file diff --git a/fas/fas/static/js/scriptaculous.js b/fas/fas/static/js/scriptaculous.js new file mode 100644 index 0000000..6cfe36e --- /dev/null +++ b/fas/fas/static/js/scriptaculous.js @@ -0,0 +1,58 @@ +// script.aculo.us scriptaculous.js v1.8.1, Thu Jan 03 22:07:12 -0500 2008 + +// Copyright (c) 2005-2007 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us) +// +// 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. +// +// For details, see the script.aculo.us web site: http://script.aculo.us/ + +var Scriptaculous = { + Version: '1.8.1', + require: function(libraryName) { + // inserting via DOM fails in Safari 2.0, so brute force approach + document.write('<script type="text/javascript" src="'+libraryName+'"><\/script>'); + }, + REQUIRED_PROTOTYPE: '1.6.0', + load: function() { + function convertVersionString(versionString){ + var r = versionString.split('.'); + return parseInt(r[0])*100000 + parseInt(r[1])*1000 + parseInt(r[2]); + } + + if((typeof Prototype=='undefined') || + (typeof Element == 'undefined') || + (typeof Element.Methods=='undefined') || + (convertVersionString(Prototype.Version) < + convertVersionString(Scriptaculous.REQUIRED_PROTOTYPE))) + throw("script.aculo.us requires the Prototype JavaScript framework >= " + + Scriptaculous.REQUIRED_PROTOTYPE); + + $A(document.getElementsByTagName("script")).findAll( function(s) { + return (s.src && s.src.match(/scriptaculous\.js(\?.*)?$/)) + }).each( function(s) { + var path = s.src.replace(/scriptaculous\.js(\?.*)?$/,''); + var includes = s.src.match(/\?.*load=([a-z,]*)/); + (includes ? includes[1] : 'builder,effects,dragdrop,controls,slider,sound').split(',').each( + function(include) { Scriptaculous.require(path+include+'.js') }); + }); + } +} + +Scriptaculous.load(); \ No newline at end of file diff --git a/fas/fas/templates/help.html b/fas/fas/templates/help.html new file mode 100644 index 0000000..1c862e5 --- /dev/null +++ b/fas/fas/templates/help.html @@ -0,0 +1,14 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml" + xmlns:py="http://genshi.edgewall.org/" + xmlns:xi="http://www.w3.org/2001/XInclude"> + + <head> + <title>${help[0]} + + +

+ ${help[1]} +

+ + diff --git a/fas/fas/templates/master.html b/fas/fas/templates/master.html index 9c634d5..1aec2f0 100644 --- a/fas/fas/templates/master.html +++ b/fas/fas/templates/master.html @@ -9,6 +9,12 @@ + + + + + +
diff --git a/fas/fas/templates/user/edit.html b/fas/fas/templates/user/edit.html index 94ea966..f5ffb80 100644 --- a/fas/fas/templates/user/edit.html +++ b/fas/fas/templates/user/edit.html @@ -10,13 +10,15 @@

${_('Edit Account (%s)') % target.username}

- + +
+