// script.aculo.us scriptaculous.js v1.9.0, Thu Dec 23 16:54:48 -0500 2010

// Copyright (c) 2005-2010 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.9.0',
  require: function(libraryName) {
    try{
      // inserting via DOM fails in Safari 2.0, so brute force approach
      document.write('<script type="text/javascript" src="'+libraryName+'"><\/script>');
    } catch(e) {
      // for xhtml+xml served content, fall back to DOM methods
      var script = document.createElement('script');
      script.type = 'text/javascript';
      script.src = libraryName;
      document.getElementsByTagName('head')[0].appendChild(script);
    }
  },
  REQUIRED_PROTOTYPE: '1.6.0.3',
  load: function() {
    function convertVersionString(versionString) {
      var v = versionString.replace(/_.*|\./g, '');
      v = parseInt(v + '0'.times(4-v.length));
      return versionString.indexOf('_') > -1 ? v-1 : v;
    }

    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);

    var js = /scriptaculous\.js(\?.*)?$/;
    $$('script[src]').findAll(function(s) {
      return s.src.match(js);
    }).each(function(s) {
      var path = s.src.replace(js, ''),
      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();
// script.aculo.us builder.js v1.9.0, Thu Dec 23 16:54:48 -0500 2010

// Copyright (c) 2005-2010 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
//
// script.aculo.us is freely distributable under the terms of an MIT-style license.
// For details, see the script.aculo.us web site: http://script.aculo.us/

var Builder = {
  NODEMAP: {
    AREA: 'map',
    CAPTION: 'table',
    COL: 'table',
    COLGROUP: 'table',
    LEGEND: 'fieldset',
    OPTGROUP: 'select',
    OPTION: 'select',
    PARAM: 'object',
    TBODY: 'table',
    TD: 'table',
    TFOOT: 'table',
    TH: 'table',
    THEAD: 'table',
    TR: 'table'
  },
  // note: For Firefox < 1.5, OPTION and OPTGROUP tags are currently broken,
  //       due to a Firefox bug
  node: function(elementName) {
    elementName = elementName.toUpperCase();

    // try innerHTML approach
    var parentTag = this.NODEMAP[elementName] || 'div';
    var parentElement = document.createElement(parentTag);
    try { // prevent IE "feature": http://dev.rubyonrails.org/ticket/2707
      parentElement.innerHTML = "<" + elementName + "></" + elementName + ">";
    } catch(e) {}
    var element = parentElement.firstChild || null;

    // see if browser added wrapping tags
    if(element && (element.tagName.toUpperCase() != elementName))
      element = element.getElementsByTagName(elementName)[0];

    // fallback to createElement approach
    if(!element) element = document.createElement(elementName);

    // abort if nothing could be created
    if(!element) return;

    // attributes (or text)
    if(arguments[1])
      if(this._isStringOrNumber(arguments[1]) ||
        (arguments[1] instanceof Array) ||
        arguments[1].tagName) {
          this._children(element, arguments[1]);
        } else {
          var attrs = this._attributes(arguments[1]);
          if(attrs.length) {
            try { // prevent IE "feature": http://dev.rubyonrails.org/ticket/2707
              parentElement.innerHTML = "<" +elementName + " " +
                attrs + "></" + elementName + ">";
            } catch(e) {}
            element = parentElement.firstChild || null;
            // workaround firefox 1.0.X bug
            if(!element) {
              element = document.createElement(elementName);
              for(attr in arguments[1])
                element[attr == 'class' ? 'className' : attr] = arguments[1][attr];
            }
            if(element.tagName.toUpperCase() != elementName)
              element = parentElement.getElementsByTagName(elementName)[0];
          }
        }

    // text, or array of children
    if(arguments[2])
      this._children(element, arguments[2]);

     return $(element);
  },
  _text: function(text) {
     return document.createTextNode(text);
  },

  ATTR_MAP: {
    'className': 'class',
    'htmlFor': 'for'
  },

  _attributes: function(attributes) {
    var attrs = [];
    for(attribute in attributes)
      attrs.push((attribute in this.ATTR_MAP ? this.ATTR_MAP[attribute] : attribute) +
          '="' + attributes[attribute].toString().escapeHTML().gsub(/"/,'&quot;') + '"');
    return attrs.join(" ");
  },
  _children: function(element, children) {
    if(children.tagName) {
      element.appendChild(children);
      return;
    }
    if(typeof children=='object') { // array can hold nodes and text
      children.flatten().each( function(e) {
        if(typeof e=='object')
          element.appendChild(e);
        else
          if(Builder._isStringOrNumber(e))
            element.appendChild(Builder._text(e));
      });
    } else
      if(Builder._isStringOrNumber(children))
        element.appendChild(Builder._text(children));
  },
  _isStringOrNumber: function(param) {
    return(typeof param=='string' || typeof param=='number');
  },
  build: function(html) {
    var element = this.node('div');
    $(element).update(html.strip());
    return element.down();
  },
  dump: function(scope) {
    if(typeof scope != 'object' && typeof scope != 'function') scope = window; //global scope

    var tags = ("A ABBR ACRONYM ADDRESS APPLET AREA B BASE BASEFONT BDO BIG BLOCKQUOTE BODY " +
      "BR BUTTON CAPTION CENTER CITE CODE COL COLGROUP DD DEL DFN DIR DIV DL DT EM FIELDSET " +
      "FONT FORM FRAME FRAMESET H1 H2 H3 H4 H5 H6 HEAD HR HTML I IFRAME IMG INPUT INS ISINDEX "+
      "KBD LABEL LEGEND LI LINK MAP MENU META NOFRAMES NOSCRIPT OBJECT OL OPTGROUP OPTION P "+
      "PARAM PRE Q S SAMP SCRIPT SELECT SMALL SPAN STRIKE STRONG STYLE SUB SUP TABLE TBODY TD "+
      "TEXTAREA TFOOT TH THEAD TITLE TR TT U UL VAR").split(/\s+/);

    tags.each( function(tag){
      scope[tag] = function() {
        return Builder.node.apply(Builder, [tag].concat($A(arguments)));
      };
    });
  }
};
// script.aculo.us effects.js v1.9.0, Thu Dec 23 16:54:48 -0500 2010

// Copyright (c) 2005-2010 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) + .5;
    },
    reverse: function(pos) {
      return 1-pos;
    },
    flicker: function(pos) {
      var pos = ((-Math.cos(pos*Math.PI)/4) + .75) + Math.random()/4;
      return pos > 1 ? 1 : pos;
    },
    wobble: function(pos) {
      return (-Math.cos(pos*Math.PI*(9*pos))/2) + .5;
    },
    pulse: function(pos, pulses) {
      return (-Math.cos((pos*((pulses||5)-.5)*2)*Math.PI)/2) + .5;
    },
    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, options) {
    element = $(element);
    effect  = (effect || 'appear').toLowerCase();
    
    return Effect[ Effect.PAIRS[ effect ][ element.visible() ? 1 : 0 ] ](element, Object.extend({
      queue: { position:'end', scope:(element.id || 'global'), limit: 1 }
    }, 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) {
    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;

    this.render = (function() {
      function dispatch(effect, eventName) {
        if (effect.options[eventName + 'Internal'])
          effect.options[eventName + 'Internal'](effect);
        if (effect.options[eventName])
          effect.options[eventName](effect);
      }

      return function(pos) {
        if (this.state === "idle") {
          this.state = "running";
          dispatch(this, 'beforeSetup');
          if (this.setup) this.setup();
          dispatch(this, 'afterSetup');
        }
        if (this.state === "running") {
          pos = (this.options.transition(pos) * this.fromToDelta) + this.options.from;
          this.position = pos;
          dispatch(this, 'beforeUpdate');
          if (this.update) this.update(pos);
          dispatch(this, '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();

  if (options.offset) elementOffsets[1] += options.offset;

  return new Effect.Tween(null,
    scrollOffsets.top,
    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] || { },
    oldOpacity = element.getInlineOpacity(),
    transition = options.transition || Effect.Transitions.linear,
    reverser   = function(pos){
      return 1 - transition((-Math.cos((pos*(options.pulses||5)*2)*Math.PI)/2) + .5);
    };

  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);
// script.aculo.us dragdrop.js v1.9.0, Thu Dec 23 16:54:48 -0500 2010

// Copyright (c) 2005-2010 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
//
// script.aculo.us is freely distributable under the terms of an MIT-style license.
// For details, see the script.aculo.us web site: http://script.aculo.us/

if(Object.isUndefined(Effect))
  throw("dragdrop.js requires including script.aculo.us' effects.js library");

var Droppables = {
  drops: [],

  remove: function(element) {
    this.drops = this.drops.reject(function(d) { return d.element==$(element) });
  },

  add: function(element) {
    element = $(element);
    var options = Object.extend({
      greedy:     true,
      hoverclass: null,
      tree:       false
    }, arguments[1] || { });

    // cache containers
    if(options.containment) {
      options._containers = [];
      var containment = options.containment;
      if(Object.isArray(containment)) {
        containment.each( function(c) { options._containers.push($(c)) });
      } else {
        options._containers.push($(containment));
      }
    }

    if(options.accept) options.accept = [options.accept].flatten();

    Element.makePositioned(element); // fix IE
    options.element = element;

    this.drops.push(options);
  },

  findDeepestChild: function(drops) {
    deepest = drops[0];

    for (i = 1; i < drops.length; ++i)
      if (Element.isParent(drops[i].element, deepest.element))
        deepest = drops[i];

    return deepest;
  },

  isContained: function(element, drop) {
    var containmentNode;
    if(drop.tree) {
      containmentNode = element.treeNode;
    } else {
      containmentNode = element.parentNode;
    }
    return drop._containers.detect(function(c) { return containmentNode == c });
  },

  isAffected: function(point, element, drop) {
    return (
      (drop.element!=element) &&
      ((!drop._containers) ||
        this.isContained(element, drop)) &&
      ((!drop.accept) ||
        (Element.classNames(element).detect(
          function(v) { return drop.accept.include(v) } ) )) &&
      Position.within(drop.element, point[0], point[1]) );
  },

  deactivate: function(drop) {
    if(drop.hoverclass)
      Element.removeClassName(drop.element, drop.hoverclass);
    this.last_active = null;
  },

  activate: function(drop) {
    if(drop.hoverclass)
      Element.addClassName(drop.element, drop.hoverclass);
    this.last_active = drop;
  },

  show: function(point, element) {
    if(!this.drops.length) return;
    var drop, affected = [];

    this.drops.each( function(drop) {
      if(Droppables.isAffected(point, element, drop))
        affected.push(drop);
    });

    if(affected.length>0)
      drop = Droppables.findDeepestChild(affected);

    if(this.last_active && this.last_active != drop) this.deactivate(this.last_active);
    if (drop) {
      Position.within(drop.element, point[0], point[1]);
      if(drop.onHover)
        drop.onHover(element, drop.element, Position.overlap(drop.overlap, drop.element));

      if (drop != this.last_active) Droppables.activate(drop);
    }
  },

  fire: function(event, element) {
    if(!this.last_active) return;
    Position.prepare();

    if (this.isAffected([Event.pointerX(event), Event.pointerY(event)], element, this.last_active))
      if (this.last_active.onDrop) {
        this.last_active.onDrop(element, this.last_active.element, event);
        return true;
      }
  },

  reset: function() {
    if(this.last_active)
      this.deactivate(this.last_active);
  }
};

var Draggables = {
  drags: [],
  observers: [],

  register: function(draggable) {
    if(this.drags.length == 0) {
      this.eventMouseUp   = this.endDrag.bindAsEventListener(this);
      this.eventMouseMove = this.updateDrag.bindAsEventListener(this);
      this.eventKeypress  = this.keyPress.bindAsEventListener(this);

      Event.observe(document, "mouseup", this.eventMouseUp);
      Event.observe(document, "mousemove", this.eventMouseMove);
      Event.observe(document, "keypress", this.eventKeypress);
    }
    this.drags.push(draggable);
  },

  unregister: function(draggable) {
    this.drags = this.drags.reject(function(d) { return d==draggable });
    if(this.drags.length == 0) {
      Event.stopObserving(document, "mouseup", this.eventMouseUp);
      Event.stopObserving(document, "mousemove", this.eventMouseMove);
      Event.stopObserving(document, "keypress", this.eventKeypress);
    }
  },

  activate: function(draggable) {
    if(draggable.options.delay) {
      this._timeout = setTimeout(function() {
        Draggables._timeout = null;
        window.focus();
        Draggables.activeDraggable = draggable;
      }.bind(this), draggable.options.delay);
    } else {
      window.focus(); // allows keypress events if window isn't currently focused, fails for Safari
      this.activeDraggable = draggable;
    }
  },

  deactivate: function() {
    this.activeDraggable = null;
  },

  updateDrag: function(event) {
    if(!this.activeDraggable) return;
    var pointer = [Event.pointerX(event), Event.pointerY(event)];
    // Mozilla-based browsers fire successive mousemove events with
    // the same coordinates, prevent needless redrawing (moz bug?)
    if(this._lastPointer && (this._lastPointer.inspect() == pointer.inspect())) return;
    this._lastPointer = pointer;

    this.activeDraggable.updateDrag(event, pointer);
  },

  endDrag: function(event) {
    if(this._timeout) {
      clearTimeout(this._timeout);
      this._timeout = null;
    }
    if(!this.activeDraggable) return;
    this._lastPointer = null;
    this.activeDraggable.endDrag(event);
    this.activeDraggable = null;
  },

  keyPress: function(event) {
    if(this.activeDraggable)
      this.activeDraggable.keyPress(event);
  },

  addObserver: function(observer) {
    this.observers.push(observer);
    this._cacheObserverCallbacks();
  },

  removeObserver: function(element) {  // element instead of observer fixes mem leaks
    this.observers = this.observers.reject( function(o) { return o.element==element });
    this._cacheObserverCallbacks();
  },

  notify: function(eventName, draggable, event) {  // 'onStart', 'onEnd', 'onDrag'
    if(this[eventName+'Count'] > 0)
      this.observers.each( function(o) {
        if(o[eventName]) o[eventName](eventName, draggable, event);
      });
    if(draggable.options[eventName]) draggable.options[eventName](draggable, event);
  },

  _cacheObserverCallbacks: function() {
    ['onStart','onEnd','onDrag'].each( function(eventName) {
      Draggables[eventName+'Count'] = Draggables.observers.select(
        function(o) { return o[eventName]; }
      ).length;
    });
  }
};

/*--------------------------------------------------------------------------*/

var Draggable = Class.create({
  initialize: function(element) {
    var defaults = {
      handle: false,
      reverteffect: function(element, top_offset, left_offset) {
        var dur = Math.sqrt(Math.abs(top_offset^2)+Math.abs(left_offset^2))*0.02;
        new Effect.Move(element, { x: -left_offset, y: -top_offset, duration: dur,
          queue: {scope:'_draggable', position:'end'}
        });
      },
      endeffect: function(element) {
        var toOpacity = Object.isNumber(element._opacity) ? element._opacity : 1.0;
        new Effect.Opacity(element, {duration:0.2, from:0.7, to:toOpacity,
          queue: {scope:'_draggable', position:'end'},
          afterFinish: function(){
            Draggable._dragging[element] = false
          }
        });
      },
      zindex: 1000,
      revert: false,
      quiet: false,
      scroll: false,
      scrollSensitivity: 20,
      scrollSpeed: 15,
      snap: false,  // false, or xy or [x,y] or function(x,y){ return [x,y] }
      delay: 0
    };

    if(!arguments[1] || Object.isUndefined(arguments[1].endeffect))
      Object.extend(defaults, {
        starteffect: function(element) {
          element._opacity = Element.getOpacity(element);
          Draggable._dragging[element] = true;
          new Effect.Opacity(element, {duration:0.2, from:element._opacity, to:0.7});
        }
      });

    var options = Object.extend(defaults, arguments[1] || { });

    this.element = $(element);

    if(options.handle && Object.isString(options.handle))
      this.handle = this.element.down('.'+options.handle, 0);

    if(!this.handle) this.handle = $(options.handle);
    if(!this.handle) this.handle = this.element;

    if(options.scroll && !options.scroll.scrollTo && !options.scroll.outerHTML) {
      options.scroll = $(options.scroll);
      this._isScrollChild = Element.childOf(this.element, options.scroll);
    }

    Element.makePositioned(this.element); // fix IE

    this.options  = options;
    this.dragging = false;

    this.eventMouseDown = this.initDrag.bindAsEventListener(this);
    Event.observe(this.handle, "mousedown", this.eventMouseDown);

    Draggables.register(this);
  },

  destroy: function() {
    Event.stopObserving(this.handle, "mousedown", this.eventMouseDown);
    Draggables.unregister(this);
  },

  currentDelta: function() {
    return([
      parseInt(Element.getStyle(this.element,'left') || '0'),
      parseInt(Element.getStyle(this.element,'top') || '0')]);
  },

  initDrag: function(event) {
    if(!Object.isUndefined(Draggable._dragging[this.element]) &&
      Draggable._dragging[this.element]) return;
    if(Event.isLeftClick(event)) {
      // abort on form elements, fixes a Firefox issue
      var src = Event.element(event);
      if((tag_name = src.tagName.toUpperCase()) && (
        tag_name=='INPUT' ||
        tag_name=='SELECT' ||
        tag_name=='OPTION' ||
        tag_name=='BUTTON' ||
        tag_name=='TEXTAREA')) return;

      var pointer = [Event.pointerX(event), Event.pointerY(event)];
      var pos     = this.element.cumulativeOffset();
      this.offset = [0,1].map( function(i) { return (pointer[i] - pos[i]) });

      Draggables.activate(this);
      Event.stop(event);
    }
  },

  startDrag: function(event) {
    this.dragging = true;
    if(!this.delta)
      this.delta = this.currentDelta();

    if(this.options.zindex) {
      this.originalZ = parseInt(Element.getStyle(this.element,'z-index') || 0);
      this.element.style.zIndex = this.options.zindex;
    }

    if(this.options.ghosting) {
      this._clone = this.element.cloneNode(true);
      this._originallyAbsolute = (this.element.getStyle('position') == 'absolute');
      if (!this._originallyAbsolute)
        Position.absolutize(this.element);
      this.element.parentNode.insertBefore(this._clone, this.element);
    }

    if(this.options.scroll) {
      if (this.options.scroll == window) {
        var where = this._getWindowScroll(this.options.scroll);
        this.originalScrollLeft = where.left;
        this.originalScrollTop = where.top;
      } else {
        this.originalScrollLeft = this.options.scroll.scrollLeft;
        this.originalScrollTop = this.options.scroll.scrollTop;
      }
    }

    Draggables.notify('onStart', this, event);

    if(this.options.starteffect) this.options.starteffect(this.element);
  },

  updateDrag: function(event, pointer) {
    if(!this.dragging) this.startDrag(event);

    if(!this.options.quiet){
      Position.prepare();
      Droppables.show(pointer, this.element);
    }

    Draggables.notify('onDrag', this, event);

    this.draw(pointer);
    if(this.options.change) this.options.change(this);

    if(this.options.scroll) {
      this.stopScrolling();

      var p;
      if (this.options.scroll == window) {
        with(this._getWindowScroll(this.options.scroll)) { p = [ left, top, left+width, top+height ]; }
      } else {
        p = Position.page(this.options.scroll).toArray();
        p[0] += this.options.scroll.scrollLeft + Position.deltaX;
        p[1] += this.options.scroll.scrollTop + Position.deltaY;
        p.push(p[0]+this.options.scroll.offsetWidth);
        p.push(p[1]+this.options.scroll.offsetHeight);
      }
      var speed = [0,0];
      if(pointer[0] < (p[0]+this.options.scrollSensitivity)) speed[0] = pointer[0]-(p[0]+this.options.scrollSensitivity);
      if(pointer[1] < (p[1]+this.options.scrollSensitivity)) speed[1] = pointer[1]-(p[1]+this.options.scrollSensitivity);
      if(pointer[0] > (p[2]-this.options.scrollSensitivity)) speed[0] = pointer[0]-(p[2]-this.options.scrollSensitivity);
      if(pointer[1] > (p[3]-this.options.scrollSensitivity)) speed[1] = pointer[1]-(p[3]-this.options.scrollSensitivity);
      this.startScrolling(speed);
    }

    // fix AppleWebKit rendering
    if(Prototype.Browser.WebKit) window.scrollBy(0,0);

    Event.stop(event);
  },

  finishDrag: function(event, success) {
    this.dragging = false;

    if(this.options.quiet){
      Position.prepare();
      var pointer = [Event.pointerX(event), Event.pointerY(event)];
      Droppables.show(pointer, this.element);
    }

    if(this.options.ghosting) {
      if (!this._originallyAbsolute)
        Position.relativize(this.element);
      delete this._originallyAbsolute;
      Element.remove(this._clone);
      this._clone = null;
    }

    var dropped = false;
    if(success) {
      dropped = Droppables.fire(event, this.element);
      if (!dropped) dropped = false;
    }
    if(dropped && this.options.onDropped) this.options.onDropped(this.element);
    Draggables.notify('onEnd', this, event);

    var revert = this.options.revert;
    if(revert && Object.isFunction(revert)) revert = revert(this.element);

    var d = this.currentDelta();
    if(revert && this.options.reverteffect) {
      if (dropped == 0 || revert != 'failure')
        this.options.reverteffect(this.element,
          d[1]-this.delta[1], d[0]-this.delta[0]);
    } else {
      this.delta = d;
    }

    if(this.options.zindex)
      this.element.style.zIndex = this.originalZ;

    if(this.options.endeffect)
      this.options.endeffect(this.element);

    Draggables.deactivate(this);
    Droppables.reset();
  },

  keyPress: function(event) {
    if(event.keyCode!=Event.KEY_ESC) return;
    this.finishDrag(event, false);
    Event.stop(event);
  },

  endDrag: function(event) {
    if(!this.dragging) return;
    this.stopScrolling();
    this.finishDrag(event, true);
    Event.stop(event);
  },

  draw: function(point) {
    var pos = this.element.cumulativeOffset();
    if(this.options.ghosting) {
      var r   = Position.realOffset(this.element);
      pos[0] += r[0] - Position.deltaX; pos[1] += r[1] - Position.deltaY;
    }

    var d = this.currentDelta();
    pos[0] -= d[0]; pos[1] -= d[1];

    if(this.options.scroll && (this.options.scroll != window && this._isScrollChild)) {
      pos[0] -= this.options.scroll.scrollLeft-this.originalScrollLeft;
      pos[1] -= this.options.scroll.scrollTop-this.originalScrollTop;
    }

    var p = [0,1].map(function(i){
      return (point[i]-pos[i]-this.offset[i])
    }.bind(this));

    if(this.options.snap) {
      if(Object.isFunction(this.options.snap)) {
        p = this.options.snap(p[0],p[1],this);
      } else {
      if(Object.isArray(this.options.snap)) {
        p = p.map( function(v, i) {
          return (v/this.options.snap[i]).round()*this.options.snap[i] }.bind(this));
      } else {
        p = p.map( function(v) {
          return (v/this.options.snap).round()*this.options.snap }.bind(this));
      }
    }}

    var style = this.element.style;
    if((!this.options.constraint) || (this.options.constraint=='horizontal'))
      style.left = p[0] + "px";
    if((!this.options.constraint) || (this.options.constraint=='vertical'))
      style.top  = p[1] + "px";

    if(style.visibility=="hidden") style.visibility = ""; // fix gecko rendering
  },

  stopScrolling: function() {
    if(this.scrollInterval) {
      clearInterval(this.scrollInterval);
      this.scrollInterval = null;
      Draggables._lastScrollPointer = null;
    }
  },

  startScrolling: function(speed) {
    if(!(speed[0] || speed[1])) return;
    this.scrollSpeed = [speed[0]*this.options.scrollSpeed,speed[1]*this.options.scrollSpeed];
    this.lastScrolled = new Date();
    this.scrollInterval = setInterval(this.scroll.bind(this), 10);
  },

  scroll: function() {
    var current = new Date();
    var delta = current - this.lastScrolled;
    this.lastScrolled = current;
    if(this.options.scroll == window) {
      with (this._getWindowScroll(this.options.scroll)) {
        if (this.scrollSpeed[0] || this.scrollSpeed[1]) {
          var d = delta / 1000;
          this.options.scroll.scrollTo( left + d*this.scrollSpeed[0], top + d*this.scrollSpeed[1] );
        }
      }
    } else {
      this.options.scroll.scrollLeft += this.scrollSpeed[0] * delta / 1000;
      this.options.scroll.scrollTop  += this.scrollSpeed[1] * delta / 1000;
    }

    Position.prepare();
    Droppables.show(Draggables._lastPointer, this.element);
    Draggables.notify('onDrag', this);
    if (this._isScrollChild) {
      Draggables._lastScrollPointer = Draggables._lastScrollPointer || $A(Draggables._lastPointer);
      Draggables._lastScrollPointer[0] += this.scrollSpeed[0] * delta / 1000;
      Draggables._lastScrollPointer[1] += this.scrollSpeed[1] * delta / 1000;
      if (Draggables._lastScrollPointer[0] < 0)
        Draggables._lastScrollPointer[0] = 0;
      if (Draggables._lastScrollPointer[1] < 0)
        Draggables._lastScrollPointer[1] = 0;
      this.draw(Draggables._lastScrollPointer);
    }

    if(this.options.change) this.options.change(this);
  },

  _getWindowScroll: function(w) {
    var T, L, W, H;
    with (w.document) {
      if (w.document.documentElement && documentElement.scrollTop) {
        T = documentElement.scrollTop;
        L = documentElement.scrollLeft;
      } else if (w.document.body) {
        T = body.scrollTop;
        L = body.scrollLeft;
      }
      if (w.innerWidth) {
        W = w.innerWidth;
        H = w.innerHeight;
      } else if (w.document.documentElement && documentElement.clientWidth) {
        W = documentElement.clientWidth;
        H = documentElement.clientHeight;
      } else {
        W = body.offsetWidth;
        H = body.offsetHeight;
      }
    }
    return { top: T, left: L, width: W, height: H };
  }
});

Draggable._dragging = { };

/*--------------------------------------------------------------------------*/

var SortableObserver = Class.create({
  initialize: function(element, observer) {
    this.element   = $(element);
    this.observer  = observer;
    this.lastValue = Sortable.serialize(this.element);
  },

  onStart: function() {
    this.lastValue = Sortable.serialize(this.element);
  },

  onEnd: function() {
    Sortable.unmark();
    if(this.lastValue != Sortable.serialize(this.element))
      this.observer(this.element)
  }
});

var Sortable = {
  SERIALIZE_RULE: /^[^_\-](?:[A-Za-z0-9\-\_]*)[_](.*)$/,

  sortables: { },

  _findRootElement: function(element) {
    while (element.tagName.toUpperCase() != "BODY") {
      if(element.id && Sortable.sortables[element.id]) return element;
      element = element.parentNode;
    }
  },

  options: function(element) {
    element = Sortable._findRootElement($(element));
    if(!element) return;
    return Sortable.sortables[element.id];
  },

  destroy: function(element){
    element = $(element);
    var s = Sortable.sortables[element.id];

    if(s) {
      Draggables.removeObserver(s.element);
      s.droppables.each(function(d){ Droppables.remove(d) });
      s.draggables.invoke('destroy');

      delete Sortable.sortables[s.element.id];
    }
  },

  create: function(element) {
    element = $(element);
    var options = Object.extend({
      element:     element,
      tag:         'li',       // assumes li children, override with tag: 'tagname'
      dropOnEmpty: false,
      tree:        false,
      treeTag:     'ul',
      overlap:     'vertical', // one of 'vertical', 'horizontal'
      constraint:  'vertical', // one of 'vertical', 'horizontal', false
      containment: element,    // also takes array of elements (or id's); or false
      handle:      false,      // or a CSS class
      only:        false,
      delay:       0,
      hoverclass:  null,
      ghosting:    false,
      quiet:       false,
      scroll:      false,
      scrollSensitivity: 20,
      scrollSpeed: 15,
      format:      this.SERIALIZE_RULE,

      // these take arrays of elements or ids and can be
      // used for better initialization performance
      elements:    false,
      handles:     false,

      onChange:    Prototype.emptyFunction,
      onUpdate:    Prototype.emptyFunction
    }, arguments[1] || { });

    // clear any old sortable with same element
    this.destroy(element);

    // build options for the draggables
    var options_for_draggable = {
      revert:      true,
      quiet:       options.quiet,
      scroll:      options.scroll,
      scrollSpeed: options.scrollSpeed,
      scrollSensitivity: options.scrollSensitivity,
      delay:       options.delay,
      ghosting:    options.ghosting,
      constraint:  options.constraint,
      handle:      options.handle };

    if(options.starteffect)
      options_for_draggable.starteffect = options.starteffect;

    if(options.reverteffect)
      options_for_draggable.reverteffect = options.reverteffect;
    else
      if(options.ghosting) options_for_draggable.reverteffect = function(element) {
        element.style.top  = 0;
        element.style.left = 0;
      };

    if(options.endeffect)
      options_for_draggable.endeffect = options.endeffect;

    if(options.zindex)
      options_for_draggable.zindex = options.zindex;

    // build options for the droppables
    var options_for_droppable = {
      overlap:     options.overlap,
      containment: options.containment,
      tree:        options.tree,
      hoverclass:  options.hoverclass,
      onHover:     Sortable.onHover
    };

    var options_for_tree = {
      onHover:      Sortable.onEmptyHover,
      overlap:      options.overlap,
      containment:  options.containment,
      hoverclass:   options.hoverclass
    };

    // fix for gecko engine
    Element.cleanWhitespace(element);

    options.draggables = [];
    options.droppables = [];

    // drop on empty handling
    if(options.dropOnEmpty || options.tree) {
      Droppables.add(element, options_for_tree);
      options.droppables.push(element);
    }

    (options.elements || this.findElements(element, options) || []).each( function(e,i) {
      var handle = options.handles ? $(options.handles[i]) :
        (options.handle ? $(e).select('.' + options.handle)[0] : e);
      options.draggables.push(
        new Draggable(e, Object.extend(options_for_draggable, { handle: handle })));
      Droppables.add(e, options_for_droppable);
      if(options.tree) e.treeNode = element;
      options.droppables.push(e);
    });

    if(options.tree) {
      (Sortable.findTreeElements(element, options) || []).each( function(e) {
        Droppables.add(e, options_for_tree);
        e.treeNode = element;
        options.droppables.push(e);
      });
    }

    // keep reference
    this.sortables[element.identify()] = options;

    // for onupdate
    Draggables.addObserver(new SortableObserver(element, options.onUpdate));

  },

  // return all suitable-for-sortable elements in a guaranteed order
  findElements: function(element, options) {
    return Element.findChildren(
      element, options.only, options.tree ? true : false, options.tag);
  },

  findTreeElements: function(element, options) {
    return Element.findChildren(
      element, options.only, options.tree ? true : false, options.treeTag);
  },

  onHover: function(element, dropon, overlap) {
    if(Element.isParent(dropon, element)) return;

    if(overlap > .33 && overlap < .66 && Sortable.options(dropon).tree) {
      return;
    } else if(overlap>0.5) {
      Sortable.mark(dropon, 'before');
      if(dropon.previousSibling != element) {
        var oldParentNode = element.parentNode;
        element.style.visibility = "hidden"; // fix gecko rendering
        dropon.parentNode.insertBefore(element, dropon);
        if(dropon.parentNode!=oldParentNode)
          Sortable.options(oldParentNode).onChange(element);
        Sortable.options(dropon.parentNode).onChange(element);
      }
    } else {
      Sortable.mark(dropon, 'after');
      var nextElement = dropon.nextSibling || null;
      if(nextElement != element) {
        var oldParentNode = element.parentNode;
        element.style.visibility = "hidden"; // fix gecko rendering
        dropon.parentNode.insertBefore(element, nextElement);
        if(dropon.parentNode!=oldParentNode)
          Sortable.options(oldParentNode).onChange(element);
        Sortable.options(dropon.parentNode).onChange(element);
      }
    }
  },

  onEmptyHover: function(element, dropon, overlap) {
    var oldParentNode = element.parentNode;
    var droponOptions = Sortable.options(dropon);

    if(!Element.isParent(dropon, element)) {
      var index;

      var children = Sortable.findElements(dropon, {tag: droponOptions.tag, only: droponOptions.only});
      var child = null;

      if(children) {
        var offset = Element.offsetSize(dropon, droponOptions.overlap) * (1.0 - overlap);

        for (index = 0; index < children.length; index += 1) {
          if (offset - Element.offsetSize (children[index], droponOptions.overlap) >= 0) {
            offset -= Element.offsetSize (children[index], droponOptions.overlap);
          } else if (offset - (Element.offsetSize (children[index], droponOptions.overlap) / 2) >= 0) {
            child = index + 1 < children.length ? children[index + 1] : null;
            break;
          } else {
            child = children[index];
            break;
          }
        }
      }

      dropon.insertBefore(element, child);

      Sortable.options(oldParentNode).onChange(element);
      droponOptions.onChange(element);
    }
  },

  unmark: function() {
    if(Sortable._marker) Sortable._marker.hide();
  },

  mark: function(dropon, position) {
    // mark on ghosting only
    var sortable = Sortable.options(dropon.parentNode);
    if(sortable && !sortable.ghosting) return;

    if(!Sortable._marker) {
      Sortable._marker =
        ($('dropmarker') || Element.extend(document.createElement('DIV'))).
          hide().addClassName('dropmarker').setStyle({position:'absolute'});
      document.getElementsByTagName("body").item(0).appendChild(Sortable._marker);
    }
    var offsets = dropon.cumulativeOffset();
    Sortable._marker.setStyle({left: offsets[0]+'px', top: offsets[1] + 'px'});

    if(position=='after')
      if(sortable.overlap == 'horizontal')
        Sortable._marker.setStyle({left: (offsets[0]+dropon.clientWidth) + 'px'});
      else
        Sortable._marker.setStyle({top: (offsets[1]+dropon.clientHeight) + 'px'});

    Sortable._marker.show();
  },

  _tree: function(element, options, parent) {
    var children = Sortable.findElements(element, options) || [];

    for (var i = 0; i < children.length; ++i) {
      var match = children[i].id.match(options.format);

      if (!match) continue;

      var child = {
        id: encodeURIComponent(match ? match[1] : null),
        element: element,
        parent: parent,
        children: [],
        position: parent.children.length,
        container: $(children[i]).down(options.treeTag)
      };

      /* Get the element containing the children and recurse over it */
      if (child.container)
        this._tree(child.container, options, child);

      parent.children.push (child);
    }

    return parent;
  },

  tree: function(element) {
    element = $(element);
    var sortableOptions = this.options(element);
    var options = Object.extend({
      tag: sortableOptions.tag,
      treeTag: sortableOptions.treeTag,
      only: sortableOptions.only,
      name: element.id,
      format: sortableOptions.format
    }, arguments[1] || { });

    var root = {
      id: null,
      parent: null,
      children: [],
      container: element,
      position: 0
    };

    return Sortable._tree(element, options, root);
  },

  /* Construct a [i] index for a particular node */
  _constructIndex: function(node) {
    var index = '';
    do {
      if (node.id) index = '[' + node.position + ']' + index;
    } while ((node = node.parent) != null);
    return index;
  },

  sequence: function(element) {
    element = $(element);
    var options = Object.extend(this.options(element), arguments[1] || { });

    return $(this.findElements(element, options) || []).map( function(item) {
      return item.id.match(options.format) ? item.id.match(options.format)[1] : '';
    });
  },

  setSequence: function(element, new_sequence) {
    element = $(element);
    var options = Object.extend(this.options(element), arguments[2] || { });

    var nodeMap = { };
    this.findElements(element, options).each( function(n) {
        if (n.id.match(options.format))
            nodeMap[n.id.match(options.format)[1]] = [n, n.parentNode];
        n.parentNode.removeChild(n);
    });

    new_sequence.each(function(ident) {
      var n = nodeMap[ident];
      if (n) {
        n[1].appendChild(n[0]);
        delete nodeMap[ident];
      }
    });
  },

  serialize: function(element) {
    element = $(element);
    var options = Object.extend(Sortable.options(element), arguments[1] || { });
    var name = encodeURIComponent(
      (arguments[1] && arguments[1].name) ? arguments[1].name : element.id);

    if (options.tree) {
      return Sortable.tree(element, arguments[1]).children.map( function (item) {
        return [name + Sortable._constructIndex(item) + "[id]=" +
                encodeURIComponent(item.id)].concat(item.children.map(arguments.callee));
      }).flatten().join('&');
    } else {
      return Sortable.sequence(element, arguments[1]).map( function(item) {
        return name + "[]=" + encodeURIComponent(item);
      }).join('&');
    }
  }
};

// Returns true if child is contained within element
Element.isParent = function(child, element) {
  if (!child.parentNode || child == element) return false;
  if (child.parentNode == element) return true;
  return Element.isParent(child.parentNode, element);
};

Element.findChildren = function(element, only, recursive, tagName) {
  if(!element.hasChildNodes()) return null;
  tagName = tagName.toUpperCase();
  if(only) only = [only].flatten();
  var elements = [];
  $A(element.childNodes).each( function(e) {
    if(e.tagName && e.tagName.toUpperCase()==tagName &&
      (!only || (Element.classNames(e).detect(function(v) { return only.include(v) }))))
        elements.push(e);
    if(recursive) {
      var grandchildren = Element.findChildren(e, only, recursive, tagName);
      if(grandchildren) elements.push(grandchildren);
    }
  });

  return (elements.length>0 ? elements.flatten() : []);
};

Element.offsetSize = function (element, type) {
  return element['offset' + ((type=='vertical' || type=='height') ? 'Height' : 'Width')];
};
// script.aculo.us controls.js v1.9.0, Thu Dec 23 16:54:48 -0500 2010

// Copyright (c) 2005-2010 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
//           (c) 2005-2010 Ivan Krstic (http://blogs.law.harvard.edu/ivan)
//           (c) 2005-2010 Jon Tirsen (http://www.tirsen.com)
// Contributors:
//  Richard Livsey
//  Rahul Bhargava
//  Rob Wills
//
// script.aculo.us is freely distributable under the terms of an MIT-style license.
// For details, see the script.aculo.us web site: http://script.aculo.us/

// Autocompleter.Base handles all the autocompletion functionality
// that's independent of the data source for autocompletion. This
// includes drawing the autocompletion menu, observing keyboard
// and mouse events, and similar.
//
// Specific autocompleters need to provide, at the very least,
// a getUpdatedChoices function that will be invoked every time
// the text inside the monitored textbox changes. This method
// should get the text for which to provide autocompletion by
// invoking this.getToken(), NOT by directly accessing
// this.element.value. This is to allow incremental tokenized
// autocompletion. Specific auto-completion logic (AJAX, etc)
// belongs in getUpdatedChoices.
//
// Tokenized incremental autocompletion is enabled automatically
// when an autocompleter is instantiated with the 'tokens' option
// in the options parameter, e.g.:
// new Ajax.Autocompleter('id','upd', '/url/', { tokens: ',' });
// will incrementally autocomplete with a comma as the token.
// Additionally, ',' in the above example can be replaced with
// a token array, e.g. { tokens: [',', '\n'] } which
// enables autocompletion on multiple tokens. This is most
// useful when one of the tokens is \n (a newline), as it
// allows smart autocompletion after linebreaks.

if(typeof Effect == 'undefined')
  throw("controls.js requires including script.aculo.us' effects.js library");

var Autocompleter = { };
Autocompleter.Base = Class.create({
  baseInitialize: function(element, update, options) {
    element          = $(element);
    this.element     = element;
    this.update      = $(update);
    this.hasFocus    = false;
    this.changed     = false;
    this.active      = false;
    this.index       = 0;
    this.entryCount  = 0;
    this.oldElementValue = this.element.value;

    if(this.setOptions)
      this.setOptions(options);
    else
      this.options = options || { };

    this.options.paramName    = this.options.paramName || this.element.name;
    this.options.tokens       = this.options.tokens || [];
    this.options.frequency    = this.options.frequency || 0.4;
    this.options.minChars     = this.options.minChars || 1;
    this.options.onShow       = this.options.onShow ||
      function(element, update){
        if(!update.style.position || update.style.position=='absolute') {
          update.style.display = (Prototype.Browser.IE) ? 'inline' : 'absolute'; // IE8 fix for autocomplete
          Position.clone(element, update, {
            setHeight: false,
            offsetTop: element.offsetHeight
          });
        }
        Effect.Appear(update,{duration:0.15});
      };
    this.options.onHide = this.options.onHide ||
      function(element, update){ new Effect.Fade(update,{duration:0.15}) };

    if(typeof(this.options.tokens) == 'string')
      this.options.tokens = new Array(this.options.tokens);
    // Force carriage returns as token delimiters anyway
    if (!this.options.tokens.include('\n'))
      this.options.tokens.push('\n');

    this.observer = null;

    this.element.setAttribute('autocomplete','off');

    Element.hide(this.update);

    Event.observe(this.element, 'blur', this.onBlur.bindAsEventListener(this));
    Event.observe(this.element, 'keydown', this.onKeyPress.bindAsEventListener(this));
  },

  show: function() {
    if(Element.getStyle(this.update, 'display')=='none') this.options.onShow(this.element, this.update);
    if(!this.iefix &&
      (Prototype.Browser.IE) &&
      (Element.getStyle(this.update, 'position')=='absolute')) {
      new Insertion.After(this.update,
       '<iframe id="' + this.update.id + '_iefix" '+
       'style="display:none;position:absolute;filter:progid:DXImageTransform.Microsoft.Alpha(opacity=0);" ' +
       'src="javascript:false;" frameborder="0" scrolling="no"></iframe>');
      this.iefix = $(this.update.id+'_iefix');
    }
    if(this.iefix) setTimeout(this.fixIEOverlapping.bind(this), 50);
  },

  fixIEOverlapping: function() {
    Position.clone(this.update, this.iefix, {setTop:(!this.update.style.height)});
    this.iefix.style.zIndex = 1;
    this.update.style.zIndex = 2;
    Element.show(this.iefix);
  },

  hide: function() {
    this.stopIndicator();
    if(Element.getStyle(this.update, 'display')!='none') this.options.onHide(this.element, this.update);
    if(this.iefix) Element.hide(this.iefix);
  },

  startIndicator: function() {
    if(this.options.indicator) Element.show(this.options.indicator);
  },

  stopIndicator: function() {
    if(this.options.indicator) Element.hide(this.options.indicator);
  },

  onKeyPress: function(event) {
    if(this.active)
      switch(event.keyCode) {
       case Event.KEY_TAB:
       case Event.KEY_RETURN:
         this.selectEntry();
         Event.stop(event);
       case Event.KEY_ESC:
         this.hide();
         this.active = false;
         Event.stop(event);
         return;
       case Event.KEY_LEFT:
       case Event.KEY_RIGHT:
         return;
       case Event.KEY_UP:
         this.markPrevious();
         this.render();
         Event.stop(event);
         return;
       case Event.KEY_DOWN:
         this.markNext();
         this.render();
         Event.stop(event);
         return;
      }
     else
       if(event.keyCode==Event.KEY_TAB || event.keyCode==Event.KEY_RETURN ||
         (Prototype.Browser.WebKit > 0 && event.keyCode == 0)) return;

    this.changed = true;
    this.hasFocus = true;

    if(this.observer) clearTimeout(this.observer);
      this.observer =
        setTimeout(this.onObserverEvent.bind(this), this.options.frequency*1000);
  },

  activate: function() {
    this.changed = false;
    this.hasFocus = true;
    this.getUpdatedChoices();
  },

  onHover: function(event) {
    var element = Event.findElement(event, 'LI');
    if(this.index != element.autocompleteIndex)
    {
        this.index = element.autocompleteIndex;
        this.render();
    }
    Event.stop(event);
  },

  onClick: function(event) {
    var element = Event.findElement(event, 'LI');
    this.index = element.autocompleteIndex;
    this.selectEntry();
    this.hide();
  },

  onBlur: function(event) {
    // needed to make click events working
    setTimeout(this.hide.bind(this), 250);
    this.hasFocus = false;
    this.active = false;
  },

  render: function() {
    if(this.entryCount > 0) {
      for (var i = 0; i < this.entryCount; i++)
        this.index==i ?
          Element.addClassName(this.getEntry(i),"selected") :
          Element.removeClassName(this.getEntry(i),"selected");
      if(this.hasFocus) {
        this.show();
        this.active = true;
      }
    } else {
      this.active = false;
      this.hide();
    }
  },

  markPrevious: function() {
    if(this.index > 0) this.index--;
      else this.index = this.entryCount-1;
    this.getEntry(this.index).scrollIntoView(true);
  },

  markNext: function() {
    if(this.index < this.entryCount-1) this.index++;
      else this.index = 0;
    this.getEntry(this.index).scrollIntoView(false);
  },

  getEntry: function(index) {
    return this.update.firstChild.childNodes[index];
  },

  getCurrentEntry: function() {
    return this.getEntry(this.index);
  },

  selectEntry: function() {
    this.active = false;
    this.updateElement(this.getCurrentEntry());
  },

  updateElement: function(selectedElement) {
    if (this.options.updateElement) {
      this.options.updateElement(selectedElement);
      return;
    }
    var value = '';
    if (this.options.select) {
      var nodes = $(selectedElement).select('.' + this.options.select) || [];
      if(nodes.length>0) value = Element.collectTextNodes(nodes[0], this.options.select);
    } else
      value = Element.collectTextNodesIgnoreClass(selectedElement, 'informal');

    var bounds = this.getTokenBounds();
    if (bounds[0] != -1) {
      var newValue = this.element.value.substr(0, bounds[0]);
      var whitespace = this.element.value.substr(bounds[0]).match(/^\s+/);
      if (whitespace)
        newValue += whitespace[0];
      this.element.value = newValue + value + this.element.value.substr(bounds[1]);
    } else {
      this.element.value = value;
    }
    this.oldElementValue = this.element.value;
    this.element.focus();

    if (this.options.afterUpdateElement)
      this.options.afterUpdateElement(this.element, selectedElement);
  },

  updateChoices: function(choices) {
    if(!this.changed && this.hasFocus) {
      this.update.innerHTML = choices;
      Element.cleanWhitespace(this.update);
      Element.cleanWhitespace(this.update.down());

      if(this.update.firstChild && this.update.down().childNodes) {
        this.entryCount =
          this.update.down().childNodes.length;
        for (var i = 0; i < this.entryCount; i++) {
          var entry = this.getEntry(i);
          entry.autocompleteIndex = i;
          this.addObservers(entry);
        }
      } else {
        this.entryCount = 0;
      }

      this.stopIndicator();
      this.index = 0;

      if(this.entryCount==1 && this.options.autoSelect) {
        this.selectEntry();
        this.hide();
      } else {
        this.render();
      }
    }
  },

  addObservers: function(element) {
    Event.observe(element, "mouseover", this.onHover.bindAsEventListener(this));
    Event.observe(element, "click", this.onClick.bindAsEventListener(this));
  },

  onObserverEvent: function() {
    this.changed = false;
    this.tokenBounds = null;
    if(this.getToken().length>=this.options.minChars) {
      this.getUpdatedChoices();
    } else {
      this.active = false;
      this.hide();
    }
    this.oldElementValue = this.element.value;
  },

  getToken: function() {
    var bounds = this.getTokenBounds();
    return this.element.value.substring(bounds[0], bounds[1]).strip();
  },

  getTokenBounds: function() {
    if (null != this.tokenBounds) return this.tokenBounds;
    var value = this.element.value;
    if (value.strip().empty()) return [-1, 0];
    var diff = arguments.callee.getFirstDifferencePos(value, this.oldElementValue);
    var offset = (diff == this.oldElementValue.length ? 1 : 0);
    var prevTokenPos = -1, nextTokenPos = value.length;
    var tp;
    for (var index = 0, l = this.options.tokens.length; index < l; ++index) {
      tp = value.lastIndexOf(this.options.tokens[index], diff + offset - 1);
      if (tp > prevTokenPos) prevTokenPos = tp;
      tp = value.indexOf(this.options.tokens[index], diff + offset);
      if (-1 != tp && tp < nextTokenPos) nextTokenPos = tp;
    }
    return (this.tokenBounds = [prevTokenPos + 1, nextTokenPos]);
  }
});

Autocompleter.Base.prototype.getTokenBounds.getFirstDifferencePos = function(newS, oldS) {
  var boundary = Math.min(newS.length, oldS.length);
  for (var index = 0; index < boundary; ++index)
    if (newS[index] != oldS[index])
      return index;
  return boundary;
};

Ajax.Autocompleter = Class.create(Autocompleter.Base, {
  initialize: function(element, update, url, options) {
    this.baseInitialize(element, update, options);
    this.options.asynchronous  = true;
    this.options.onComplete    = this.onComplete.bind(this);
    this.options.defaultParams = this.options.parameters || null;
    this.url                   = url;
  },

  getUpdatedChoices: function() {
    this.startIndicator();

    var entry = encodeURIComponent(this.options.paramName) + '=' +
      encodeURIComponent(this.getToken());

    this.options.parameters = this.options.callback ?
      this.options.callback(this.element, entry) : entry;

    if(this.options.defaultParams)
      this.options.parameters += '&' + this.options.defaultParams;

    new Ajax.Request(this.url, this.options);
  },

  onComplete: function(request) {
    this.updateChoices(request.responseText);
  }
});

// The local array autocompleter. Used when you'd prefer to
// inject an array of autocompletion options into the page, rather
// than sending out Ajax queries, which can be quite slow sometimes.
//
// The constructor takes four parameters. The first two are, as usual,
// the id of the monitored textbox, and id of the autocompletion menu.
// The third is the array you want to autocomplete from, and the fourth
// is the options block.
//
// Extra local autocompletion options:
// - choices - How many autocompletion choices to offer
//
// - partialSearch - If false, the autocompleter will match entered
//                    text only at the beginning of strings in the
//                    autocomplete array. Defaults to true, which will
//                    match text at the beginning of any *word* in the
//                    strings in the autocomplete array. If you want to
//                    search anywhere in the string, additionally set
//                    the option fullSearch to true (default: off).
//
// - fullSsearch - Search anywhere in autocomplete array strings.
//
// - partialChars - How many characters to enter before triggering
//                   a partial match (unlike minChars, which defines
//                   how many characters are required to do any match
//                   at all). Defaults to 2.
//
// - ignoreCase - Whether to ignore case when autocompleting.
//                 Defaults to true.
//
// It's possible to pass in a custom function as the 'selector'
// option, if you prefer to write your own autocompletion logic.
// In that case, the other options above will not apply unless
// you support them.

Autocompleter.Local = Class.create(Autocompleter.Base, {
  initialize: function(element, update, array, options) {
    this.baseInitialize(element, update, options);
    this.options.array = array;
  },

  getUpdatedChoices: function() {
    this.updateChoices(this.options.selector(this));
  },

  setOptions: function(options) {
    this.options = Object.extend({
      choices: 10,
      partialSearch: true,
      partialChars: 2,
      ignoreCase: true,
      fullSearch: false,
      selector: function(instance) {
        var ret       = []; // Beginning matches
        var partial   = []; // Inside matches
        var entry     = instance.getToken();
        var count     = 0;

        for (var i = 0; i < instance.options.array.length &&
          ret.length < instance.options.choices ; i++) {

          var elem = instance.options.array[i];
          var foundPos = instance.options.ignoreCase ?
            elem.toLowerCase().indexOf(entry.toLowerCase()) :
            elem.indexOf(entry);

          while (foundPos != -1) {
            if (foundPos == 0 && elem.length != entry.length) {
              ret.push("<li><strong>" + elem.substr(0, entry.length) + "</strong>" +
                elem.substr(entry.length) + "</li>");
              break;
            } else if (entry.length >= instance.options.partialChars &&
              instance.options.partialSearch && foundPos != -1) {
              if (instance.options.fullSearch || /\s/.test(elem.substr(foundPos-1,1))) {
                partial.push("<li>" + elem.substr(0, foundPos) + "<strong>" +
                  elem.substr(foundPos, entry.length) + "</strong>" + elem.substr(
                  foundPos + entry.length) + "</li>");
                break;
              }
            }

            foundPos = instance.options.ignoreCase ?
              elem.toLowerCase().indexOf(entry.toLowerCase(), foundPos + 1) :
              elem.indexOf(entry, foundPos + 1);

          }
        }
        if (partial.length)
          ret = ret.concat(partial.slice(0, instance.options.choices - ret.length));
        return "<ul>" + ret.join('') + "</ul>";
      }
    }, options || { });
  }
});

// AJAX in-place editor and collection editor
// Full rewrite by Christophe Porteneuve <tdd@tddsworld.com> (April 2007).

// Use this if you notice weird scrolling problems on some browsers,
// the DOM might be a bit confused when this gets called so do this
// waits 1 ms (with setTimeout) until it does the activation
Field.scrollFreeActivate = function(field) {
  setTimeout(function() {
    Field.activate(field);
  }, 1);
};

Ajax.InPlaceEditor = Class.create({
  initialize: function(element, url, options) {
    this.url = url;
    this.element = element = $(element);
    this.prepareOptions();
    this._controls = { };
    arguments.callee.dealWithDeprecatedOptions(options); // DEPRECATION LAYER!!!
    Object.extend(this.options, options || { });
    if (!this.options.formId && this.element.id) {
      this.options.formId = this.element.id + '-inplaceeditor';
      if ($(this.options.formId))
        this.options.formId = '';
    }
    if (this.options.externalControl)
      this.options.externalControl = $(this.options.externalControl);
    if (!this.options.externalControl)
      this.options.externalControlOnly = false;
    this._originalBackground = this.element.getStyle('background-color') || 'transparent';
    this.element.title = this.options.clickToEditText;
    this._boundCancelHandler = this.handleFormCancellation.bind(this);
    this._boundComplete = (this.options.onComplete || Prototype.emptyFunction).bind(this);
    this._boundFailureHandler = this.handleAJAXFailure.bind(this);
    this._boundSubmitHandler = this.handleFormSubmission.bind(this);
    this._boundWrapperHandler = this.wrapUp.bind(this);
    this.registerListeners();
  },
  checkForEscapeOrReturn: function(e) {
    if (!this._editing || e.ctrlKey || e.altKey || e.shiftKey) return;
    if (Event.KEY_ESC == e.keyCode)
      this.handleFormCancellation(e);
    else if (Event.KEY_RETURN == e.keyCode)
      this.handleFormSubmission(e);
  },
  createControl: function(mode, handler, extraClasses) {
    var control = this.options[mode + 'Control'];
    var text = this.options[mode + 'Text'];
    if ('button' == control) {
      var btn = document.createElement('input');
      btn.type = 'submit';
      btn.value = text;
      btn.className = 'editor_' + mode + '_button';
      if ('cancel' == mode)
        btn.onclick = this._boundCancelHandler;
      this._form.appendChild(btn);
      this._controls[mode] = btn;
    } else if ('link' == control) {
      var link = document.createElement('a');
      link.href = '#';
      link.appendChild(document.createTextNode(text));
      link.onclick = 'cancel' == mode ? this._boundCancelHandler : this._boundSubmitHandler;
      link.className = 'editor_' + mode + '_link';
      if (extraClasses)
        link.className += ' ' + extraClasses;
      this._form.appendChild(link);
      this._controls[mode] = link;
    }
  },
  createEditField: function() {
    var text = (this.options.loadTextURL ? this.options.loadingText : this.getText());
    var fld;
    if (1 >= this.options.rows && !/\r|\n/.test(this.getText())) {
      fld = document.createElement('input');
      fld.type = 'text';
      var size = this.options.size || this.options.cols || 0;
      if (0 < size) fld.size = size;
    } else {
      fld = document.createElement('textarea');
      fld.rows = (1 >= this.options.rows ? this.options.autoRows : this.options.rows);
      fld.cols = this.options.cols || 40;
    }
    fld.name = this.options.paramName;
    fld.value = text; // No HTML breaks conversion anymore
    fld.className = 'editor_field';
    if (this.options.submitOnBlur)
      fld.onblur = this._boundSubmitHandler;
    this._controls.editor = fld;
    if (this.options.loadTextURL)
      this.loadExternalText();
    this._form.appendChild(this._controls.editor);
  },
  createForm: function() {
    var ipe = this;
    function addText(mode, condition) {
      var text = ipe.options['text' + mode + 'Controls'];
      if (!text || condition === false) return;
      ipe._form.appendChild(document.createTextNode(text));
    };
    this._form = $(document.createElement('form'));
    this._form.id = this.options.formId;
    this._form.addClassName(this.options.formClassName);
    this._form.onsubmit = this._boundSubmitHandler;
    this.createEditField();
    if ('textarea' == this._controls.editor.tagName.toLowerCase())
      this._form.appendChild(document.createElement('br'));
    if (this.options.onFormCustomization)
      this.options.onFormCustomization(this, this._form);
    addText('Before', this.options.okControl || this.options.cancelControl);
    this.createControl('ok', this._boundSubmitHandler);
    addText('Between', this.options.okControl && this.options.cancelControl);
    this.createControl('cancel', this._boundCancelHandler, 'editor_cancel');
    addText('After', this.options.okControl || this.options.cancelControl);
  },
  destroy: function() {
    if (this._oldInnerHTML)
      this.element.innerHTML = this._oldInnerHTML;
    this.leaveEditMode();
    this.unregisterListeners();
  },
  enterEditMode: function(e) {
    if (this._saving || this._editing) return;
    this._editing = true;
    this.triggerCallback('onEnterEditMode');
    if (this.options.externalControl)
      this.options.externalControl.hide();
    this.element.hide();
    this.createForm();
    this.element.parentNode.insertBefore(this._form, this.element);
    if (!this.options.loadTextURL)
      this.postProcessEditField();
    if (e) Event.stop(e);
  },
  enterHover: function(e) {
    if (this.options.hoverClassName)
      this.element.addClassName(this.options.hoverClassName);
    if (this._saving) return;
    this.triggerCallback('onEnterHover');
  },
  getText: function() {
    return this.element.innerHTML.unescapeHTML();
  },
  handleAJAXFailure: function(transport) {
    this.triggerCallback('onFailure', transport);
    if (this._oldInnerHTML) {
      this.element.innerHTML = this._oldInnerHTML;
      this._oldInnerHTML = null;
    }
  },
  handleFormCancellation: function(e) {
    this.wrapUp();
    if (e) Event.stop(e);
  },
  handleFormSubmission: function(e) {
    var form = this._form;
    var value = $F(this._controls.editor);
    this.prepareSubmission();
    var params = this.options.callback(form, value) || '';
    if (Object.isString(params))
      params = params.toQueryParams();
    params.editorId = this.element.id;
    if (this.options.htmlResponse) {
      var options = Object.extend({ evalScripts: true }, this.options.ajaxOptions);
      Object.extend(options, {
        parameters: params,
        onComplete: this._boundWrapperHandler,
        onFailure: this._boundFailureHandler
      });
      new Ajax.Updater({ success: this.element }, this.url, options);
    } else {
      var options = Object.extend({ method: 'get' }, this.options.ajaxOptions);
      Object.extend(options, {
        parameters: params,
        onComplete: this._boundWrapperHandler,
        onFailure: this._boundFailureHandler
      });
      new Ajax.Request(this.url, options);
    }
    if (e) Event.stop(e);
  },
  leaveEditMode: function() {
    this.element.removeClassName(this.options.savingClassName);
    this.removeForm();
    this.leaveHover();
    this.element.style.backgroundColor = this._originalBackground;
    this.element.show();
    if (this.options.externalControl)
      this.options.externalControl.show();
    this._saving = false;
    this._editing = false;
    this._oldInnerHTML = null;
    this.triggerCallback('onLeaveEditMode');
  },
  leaveHover: function(e) {
    if (this.options.hoverClassName)
      this.element.removeClassName(this.options.hoverClassName);
    if (this._saving) return;
    this.triggerCallback('onLeaveHover');
  },
  loadExternalText: function() {
    this._form.addClassName(this.options.loadingClassName);
    this._controls.editor.disabled = true;
    var options = Object.extend({ method: 'get' }, this.options.ajaxOptions);
    Object.extend(options, {
      parameters: 'editorId=' + encodeURIComponent(this.element.id),
      onComplete: Prototype.emptyFunction,
      onSuccess: function(transport) {
        this._form.removeClassName(this.options.loadingClassName);
        var text = transport.responseText;
        if (this.options.stripLoadedTextTags)
          text = text.stripTags();
        this._controls.editor.value = text;
        this._controls.editor.disabled = false;
        this.postProcessEditField();
      }.bind(this),
      onFailure: this._boundFailureHandler
    });
    new Ajax.Request(this.options.loadTextURL, options);
  },
  postProcessEditField: function() {
    var fpc = this.options.fieldPostCreation;
    if (fpc)
      $(this._controls.editor)['focus' == fpc ? 'focus' : 'activate']();
  },
  prepareOptions: function() {
    this.options = Object.clone(Ajax.InPlaceEditor.DefaultOptions);
    Object.extend(this.options, Ajax.InPlaceEditor.DefaultCallbacks);
    [this._extraDefaultOptions].flatten().compact().each(function(defs) {
      Object.extend(this.options, defs);
    }.bind(this));
  },
  prepareSubmission: function() {
    this._saving = true;
    this.removeForm();
    this.leaveHover();
    this.showSaving();
  },
  registerListeners: function() {
    this._listeners = { };
    var listener;
    $H(Ajax.InPlaceEditor.Listeners).each(function(pair) {
      listener = this[pair.value].bind(this);
      this._listeners[pair.key] = listener;
      if (!this.options.externalControlOnly)
        this.element.observe(pair.key, listener);
      if (this.options.externalControl)
        this.options.externalControl.observe(pair.key, listener);
    }.bind(this));
  },
  removeForm: function() {
    if (!this._form) return;
    this._form.remove();
    this._form = null;
    this._controls = { };
  },
  showSaving: function() {
    this._oldInnerHTML = this.element.innerHTML;
    this.element.innerHTML = this.options.savingText;
    this.element.addClassName(this.options.savingClassName);
    this.element.style.backgroundColor = this._originalBackground;
    this.element.show();
  },
  triggerCallback: function(cbName, arg) {
    if ('function' == typeof this.options[cbName]) {
      this.options[cbName](this, arg);
    }
  },
  unregisterListeners: function() {
    $H(this._listeners).each(function(pair) {
      if (!this.options.externalControlOnly)
        this.element.stopObserving(pair.key, pair.value);
      if (this.options.externalControl)
        this.options.externalControl.stopObserving(pair.key, pair.value);
    }.bind(this));
  },
  wrapUp: function(transport) {
    this.leaveEditMode();
    // Can't use triggerCallback due to backward compatibility: requires
    // binding + direct element
    this._boundComplete(transport, this.element);
  }
});

Object.extend(Ajax.InPlaceEditor.prototype, {
  dispose: Ajax.InPlaceEditor.prototype.destroy
});

Ajax.InPlaceCollectionEditor = Class.create(Ajax.InPlaceEditor, {
  initialize: function($super, element, url, options) {
    this._extraDefaultOptions = Ajax.InPlaceCollectionEditor.DefaultOptions;
    $super(element, url, options);
  },

  createEditField: function() {
    var list = document.createElement('select');
    list.name = this.options.paramName;
    list.size = 1;
    this._controls.editor = list;
    this._collection = this.options.collection || [];
    if (this.options.loadCollectionURL)
      this.loadCollection();
    else
      this.checkForExternalText();
    this._form.appendChild(this._controls.editor);
  },

  loadCollection: function() {
    this._form.addClassName(this.options.loadingClassName);
    this.showLoadingText(this.options.loadingCollectionText);
    var options = Object.extend({ method: 'get' }, this.options.ajaxOptions);
    Object.extend(options, {
      parameters: 'editorId=' + encodeURIComponent(this.element.id),
      onComplete: Prototype.emptyFunction,
      onSuccess: function(transport) {
        var js = transport.responseText.strip();
        if (!/^\[.*\]$/.test(js)) // TODO: improve sanity check
          throw('Server returned an invalid collection representation.');
        this._collection = eval(js);
        this.checkForExternalText();
      }.bind(this),
      onFailure: this.onFailure
    });
    new Ajax.Request(this.options.loadCollectionURL, options);
  },

  showLoadingText: function(text) {
    this._controls.editor.disabled = true;
    var tempOption = this._controls.editor.firstChild;
    if (!tempOption) {
      tempOption = document.createElement('option');
      tempOption.value = '';
      this._controls.editor.appendChild(tempOption);
      tempOption.selected = true;
    }
    tempOption.update((text || '').stripScripts().stripTags());
  },

  checkForExternalText: function() {
    this._text = this.getText();
    if (this.options.loadTextURL)
      this.loadExternalText();
    else
      this.buildOptionList();
  },

  loadExternalText: function() {
    this.showLoadingText(this.options.loadingText);
    var options = Object.extend({ method: 'get' }, this.options.ajaxOptions);
    Object.extend(options, {
      parameters: 'editorId=' + encodeURIComponent(this.element.id),
      onComplete: Prototype.emptyFunction,
      onSuccess: function(transport) {
        this._text = transport.responseText.strip();
        this.buildOptionList();
      }.bind(this),
      onFailure: this.onFailure
    });
    new Ajax.Request(this.options.loadTextURL, options);
  },

  buildOptionList: function() {
    this._form.removeClassName(this.options.loadingClassName);
    this._collection = this._collection.map(function(entry) {
      return 2 === entry.length ? entry : [entry, entry].flatten();
    });
    var marker = ('value' in this.options) ? this.options.value : this._text;
    var textFound = this._collection.any(function(entry) {
      return entry[0] == marker;
    }.bind(this));
    this._controls.editor.update('');
    var option;
    this._collection.each(function(entry, index) {
      option = document.createElement('option');
      option.value = entry[0];
      option.selected = textFound ? entry[0] == marker : 0 == index;
      option.appendChild(document.createTextNode(entry[1]));
      this._controls.editor.appendChild(option);
    }.bind(this));
    this._controls.editor.disabled = false;
    Field.scrollFreeActivate(this._controls.editor);
  }
});

//**** DEPRECATION LAYER FOR InPlace[Collection]Editor! ****
//**** This only  exists for a while,  in order to  let ****
//**** users adapt to  the new API.  Read up on the new ****
//**** API and convert your code to it ASAP!            ****

Ajax.InPlaceEditor.prototype.initialize.dealWithDeprecatedOptions = function(options) {
  if (!options) return;
  function fallback(name, expr) {
    if (name in options || expr === undefined) return;
    options[name] = expr;
  };
  fallback('cancelControl', (options.cancelLink ? 'link' : (options.cancelButton ? 'button' :
    options.cancelLink == options.cancelButton == false ? false : undefined)));
  fallback('okControl', (options.okLink ? 'link' : (options.okButton ? 'button' :
    options.okLink == options.okButton == false ? false : undefined)));
  fallback('highlightColor', options.highlightcolor);
  fallback('highlightEndColor', options.highlightendcolor);
};

Object.extend(Ajax.InPlaceEditor, {
  DefaultOptions: {
    ajaxOptions: { },
    autoRows: 3,                                // Use when multi-line w/ rows == 1
    cancelControl: 'link',                      // 'link'|'button'|false
    cancelText: 'cancel',
    clickToEditText: 'Click to edit',
    externalControl: null,                      // id|elt
    externalControlOnly: false,
    fieldPostCreation: 'activate',              // 'activate'|'focus'|false
    formClassName: 'inplaceeditor-form',
    formId: null,                               // id|elt
    highlightColor: '#ffff99',
    highlightEndColor: '#ffffff',
    hoverClassName: '',
    htmlResponse: true,
    loadingClassName: 'inplaceeditor-loading',
    loadingText: 'Loading...',
    okControl: 'button',                        // 'link'|'button'|false
    okText: 'ok',
    paramName: 'value',
    rows: 1,                                    // If 1 and multi-line, uses autoRows
    savingClassName: 'inplaceeditor-saving',
    savingText: 'Saving...',
    size: 0,
    stripLoadedTextTags: false,
    submitOnBlur: false,
    textAfterControls: '',
    textBeforeControls: '',
    textBetweenControls: ''
  },
  DefaultCallbacks: {
    callback: function(form) {
      return Form.serialize(form);
    },
    onComplete: function(transport, element) {
      // For backward compatibility, this one is bound to the IPE, and passes
      // the element directly.  It was too often customized, so we don't break it.
      new Effect.Highlight(element, {
        startcolor: this.options.highlightColor, keepBackgroundImage: true });
    },
    onEnterEditMode: null,
    onEnterHover: function(ipe) {
      ipe.element.style.backgroundColor = ipe.options.highlightColor;
      if (ipe._effect)
        ipe._effect.cancel();
    },
    onFailure: function(transport, ipe) {
      alert('Error communication with the server: ' + transport.responseText.stripTags());
    },
    onFormCustomization: null, // Takes the IPE and its generated form, after editor, before controls.
    onLeaveEditMode: null,
    onLeaveHover: function(ipe) {
      ipe._effect = new Effect.Highlight(ipe.element, {
        startcolor: ipe.options.highlightColor, endcolor: ipe.options.highlightEndColor,
        restorecolor: ipe._originalBackground, keepBackgroundImage: true
      });
    }
  },
  Listeners: {
    click: 'enterEditMode',
    keydown: 'checkForEscapeOrReturn',
    mouseover: 'enterHover',
    mouseout: 'leaveHover'
  }
});

Ajax.InPlaceCollectionEditor.DefaultOptions = {
  loadingCollectionText: 'Loading options...'
};

// Delayed observer, like Form.Element.Observer,
// but waits for delay after last key input
// Ideal for live-search fields

Form.Element.DelayedObserver = Class.create({
  initialize: function(element, delay, callback) {
    this.delay     = delay || 0.5;
    this.element   = $(element);
    this.callback  = callback;
    this.timer     = null;
    this.lastValue = $F(this.element);
    Event.observe(this.element,'keyup',this.delayedListener.bindAsEventListener(this));
  },
  delayedListener: function(event) {
    if(this.lastValue == $F(this.element)) return;
    if(this.timer) clearTimeout(this.timer);
    this.timer = setTimeout(this.onTimerEvent.bind(this), this.delay * 1000);
    this.lastValue = $F(this.element);
  },
  onTimerEvent: function() {
    this.timer = null;
    this.callback(this.element, $F(this.element));
  }
});
// script.aculo.us slider.js v1.9.0, Thu Dec 23 16:54:48 -0500 2010

// Copyright (c) 2005-2010 Marty Haught, Thomas Fuchs
//
// 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/

if (!Control) var Control = { };

// options:
//  axis: 'vertical', or 'horizontal' (default)
//
// callbacks:
//  onChange(value)
//  onSlide(value)
Control.Slider = Class.create({
  initialize: function(handle, track, options) {
    var slider = this;

    if (Object.isArray(handle)) {
      this.handles = handle.collect( function(e) { return $(e) });
    } else {
      this.handles = [$(handle)];
    }

    this.track   = $(track);
    this.options = options || { };

    this.axis      = this.options.axis || 'horizontal';
    this.increment = this.options.increment || 1;
    this.step      = parseInt(this.options.step || '1');
    this.range     = this.options.range || $R(0,1);

    this.value     = 0; // assure backwards compat
    this.values    = this.handles.map( function() { return 0 });
    this.spans     = this.options.spans ? this.options.spans.map(function(s){ return $(s) }) : false;
    this.options.startSpan = $(this.options.startSpan || null);
    this.options.endSpan   = $(this.options.endSpan || null);

    this.restricted = this.options.restricted || false;

    this.maximum   = this.options.maximum || this.range.end;
    this.minimum   = this.options.minimum || this.range.start;

    // Will be used to align the handle onto the track, if necessary
    this.alignX = parseInt(this.options.alignX || '0');
    this.alignY = parseInt(this.options.alignY || '0');

    this.trackLength = this.maximumOffset() - this.minimumOffset();

    this.handleLength = this.isVertical() ?
      (this.handles[0].offsetHeight != 0 ?
        this.handles[0].offsetHeight : this.handles[0].style.height.replace(/px$/,"")) :
      (this.handles[0].offsetWidth != 0 ? this.handles[0].offsetWidth :
        this.handles[0].style.width.replace(/px$/,""));

    this.active   = false;
    this.dragging = false;
    this.disabled = false;

    if (this.options.disabled) this.setDisabled();

    // Allowed values array
    this.allowedValues = this.options.values ? this.options.values.sortBy(Prototype.K) : false;
    if (this.allowedValues) {
      this.minimum = this.allowedValues.min();
      this.maximum = this.allowedValues.max();
    }

    this.eventMouseDown = this.startDrag.bindAsEventListener(this);
    this.eventMouseUp   = this.endDrag.bindAsEventListener(this);
    this.eventMouseMove = this.update.bindAsEventListener(this);

    // Initialize handles in reverse (make sure first handle is active)
    this.handles.each( function(h,i) {
      i = slider.handles.length-1-i;
      slider.setValue(parseFloat(
        (Object.isArray(slider.options.sliderValue) ?
          slider.options.sliderValue[i] : slider.options.sliderValue) ||
         slider.range.start), i);
      h.makePositioned().observe("mousedown", slider.eventMouseDown);
    });

    this.track.observe("mousedown", this.eventMouseDown);
    document.observe("mouseup", this.eventMouseUp);
    document.observe("mousemove", this.eventMouseMove);

    this.initialized = true;
  },
  dispose: function() {
    var slider = this;
    Event.stopObserving(this.track, "mousedown", this.eventMouseDown);
    Event.stopObserving(document, "mouseup", this.eventMouseUp);
    Event.stopObserving(document, "mousemove", this.eventMouseMove);
    this.handles.each( function(h) {
      Event.stopObserving(h, "mousedown", slider.eventMouseDown);
    });
  },
  setDisabled: function(){
    this.disabled = true;
  },
  setEnabled: function(){
    this.disabled = false;
  },
  getNearestValue: function(value){
    if (this.allowedValues){
      if (value >= this.allowedValues.max()) return(this.allowedValues.max());
      if (value <= this.allowedValues.min()) return(this.allowedValues.min());

      var offset = Math.abs(this.allowedValues[0] - value);
      var newValue = this.allowedValues[0];
      this.allowedValues.each( function(v) {
        var currentOffset = Math.abs(v - value);
        if (currentOffset <= offset){
          newValue = v;
          offset = currentOffset;
        }
      });
      return newValue;
    }
    if (value > this.range.end) return this.range.end;
    if (value < this.range.start) return this.range.start;
    return value;
  },
  setValue: function(sliderValue, handleIdx){
    if (!this.active) {
      this.activeHandleIdx = handleIdx || 0;
      this.activeHandle    = this.handles[this.activeHandleIdx];
      this.updateStyles();
    }
    handleIdx = handleIdx || this.activeHandleIdx || 0;
    if (this.initialized && this.restricted) {
      if ((handleIdx>0) && (sliderValue<this.values[handleIdx-1]))
        sliderValue = this.values[handleIdx-1];
      if ((handleIdx < (this.handles.length-1)) && (sliderValue>this.values[handleIdx+1]))
        sliderValue = this.values[handleIdx+1];
    }
    sliderValue = this.getNearestValue(sliderValue);
    this.values[handleIdx] = sliderValue;
    this.value = this.values[0]; // assure backwards compat

    this.handles[handleIdx].style[this.isVertical() ? 'top' : 'left'] =
      this.translateToPx(sliderValue);

    this.drawSpans();
    if (!this.dragging || !this.event) this.updateFinished();
  },
  setValueBy: function(delta, handleIdx) {
    this.setValue(this.values[handleIdx || this.activeHandleIdx || 0] + delta,
      handleIdx || this.activeHandleIdx || 0);
  },
  translateToPx: function(value) {
    return Math.round(
      ((this.trackLength-this.handleLength)/(this.range.end-this.range.start)) *
      (value - this.range.start)) + "px";
  },
  translateToValue: function(offset) {
    return ((offset/(this.trackLength-this.handleLength) *
      (this.range.end-this.range.start)) + this.range.start);
  },
  getRange: function(range) {
    var v = this.values.sortBy(Prototype.K);
    range = range || 0;
    return $R(v[range],v[range+1]);
  },
  minimumOffset: function(){
    return(this.isVertical() ? this.alignY : this.alignX);
  },
  maximumOffset: function(){
    return(this.isVertical() ?
      (this.track.offsetHeight != 0 ? this.track.offsetHeight :
        this.track.style.height.replace(/px$/,"")) - this.alignY :
      (this.track.offsetWidth != 0 ? this.track.offsetWidth :
        this.track.style.width.replace(/px$/,"")) - this.alignX);
  },
  isVertical:  function(){
    return (this.axis == 'vertical');
  },
  drawSpans: function() {
    var slider = this;
    if (this.spans)
      $R(0, this.spans.length-1).each(function(r) { slider.setSpan(slider.spans[r], slider.getRange(r)) });
    if (this.options.startSpan)
      this.setSpan(this.options.startSpan,
        $R(0, this.values.length>1 ? this.getRange(0).min() : this.value ));
    if (this.options.endSpan)
      this.setSpan(this.options.endSpan,
        $R(this.values.length>1 ? this.getRange(this.spans.length-1).max() : this.value, this.maximum));
  },
  setSpan: function(span, range) {
    if (this.isVertical()) {
      span.style.top = this.translateToPx(range.start);
      span.style.height = this.translateToPx(range.end - range.start + this.range.start);
    } else {
      span.style.left = this.translateToPx(range.start);
      span.style.width = this.translateToPx(range.end - range.start + this.range.start);
    }
  },
  updateStyles: function() {
    this.handles.each( function(h){ Element.removeClassName(h, 'selected') });
    Element.addClassName(this.activeHandle, 'selected');
  },
  startDrag: function(event) {
    if (Event.isLeftClick(event)) {
      if (!this.disabled){
        this.active = true;

        var handle = Event.element(event);
        var pointer  = [Event.pointerX(event), Event.pointerY(event)];
        var track = handle;
        if (track==this.track) {
          var offsets  = this.track.cumulativeOffset();
          this.event = event;
          this.setValue(this.translateToValue(
           (this.isVertical() ? pointer[1]-offsets[1] : pointer[0]-offsets[0])-(this.handleLength/2)
          ));
          var offsets  = this.activeHandle.cumulativeOffset();
          this.offsetX = (pointer[0] - offsets[0]);
          this.offsetY = (pointer[1] - offsets[1]);
        } else {
          // find the handle (prevents issues with Safari)
          while((this.handles.indexOf(handle) == -1) && handle.parentNode)
            handle = handle.parentNode;

          if (this.handles.indexOf(handle)!=-1) {
            this.activeHandle    = handle;
            this.activeHandleIdx = this.handles.indexOf(this.activeHandle);
            this.updateStyles();

            var offsets  = this.activeHandle.cumulativeOffset();
            this.offsetX = (pointer[0] - offsets[0]);
            this.offsetY = (pointer[1] - offsets[1]);
          }
        }
      }
      Event.stop(event);
    }
  },
  update: function(event) {
   if (this.active) {
      if (!this.dragging) this.dragging = true;
      this.draw(event);
      if (Prototype.Browser.WebKit) window.scrollBy(0,0);
      Event.stop(event);
   }
  },
  draw: function(event) {
    var pointer = [Event.pointerX(event), Event.pointerY(event)];
    var offsets = this.track.cumulativeOffset();
    pointer[0] -= this.offsetX + offsets[0];
    pointer[1] -= this.offsetY + offsets[1];
    this.event = event;
    this.setValue(this.translateToValue( this.isVertical() ? pointer[1] : pointer[0] ));
    if (this.initialized && this.options.onSlide)
      this.options.onSlide(this.values.length>1 ? this.values : this.value, this);
  },
  endDrag: function(event) {
    if (this.active && this.dragging) {
      this.finishDrag(event, true);
      Event.stop(event);
    }
    this.active = false;
    this.dragging = false;
  },
  finishDrag: function(event, success) {
    this.active = false;
    this.dragging = false;
    this.updateFinished();
  },
  updateFinished: function() {
    if (this.initialized && this.options.onChange)
      this.options.onChange(this.values.length>1 ? this.values : this.value, this);
    this.event = null;
  }
});
// script.aculo.us sound.js v1.9.0, Thu Dec 23 16:54:48 -0500 2010

// Copyright (c) 2005-2010 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
//
// Based on code created by Jules Gravinese (http://www.webveteran.com/)
//
// 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/

Sound = {
  tracks: {},
  _enabled: true,
  template:
    new Template('<embed style="height:0" id="sound_#{track}_#{id}" src="#{url}" loop="false" autostart="true" hidden="true"/>'),
  enable: function(){
    Sound._enabled = true;
  },
  disable: function(){
    Sound._enabled = false;
  },
  play: function(url){
    if(!Sound._enabled) return;
    var options = Object.extend({
      track: 'global', url: url, replace: false
    }, arguments[1] || {});

    if(options.replace && this.tracks[options.track]) {
      $R(0, this.tracks[options.track].id).each(function(id){
        var sound = $('sound_'+options.track+'_'+id);
        sound.Stop && sound.Stop();
        sound.remove();
      });
      this.tracks[options.track] = null;
    }

    if(!this.tracks[options.track])
      this.tracks[options.track] = { id: 0 };
    else
      this.tracks[options.track].id++;

    options.id = this.tracks[options.track].id;
    $$('body')[0].insert(
      Prototype.Browser.IE ? new Element('bgsound',{
        id: 'sound_'+options.track+'_'+options.id,
        src: options.url, loop: 1, autostart: true
      }) : Sound.template.evaluate(options));
  }
};

if(Prototype.Browser.Gecko && navigator.userAgent.indexOf("Win") > 0){
  if(navigator.plugins && $A(navigator.plugins).detect(function(p){ return p.name.indexOf('QuickTime') != -1 }))
    Sound.template = new Template('<object id="sound_#{track}_#{id}" width="0" height="0" type="audio/mpeg" data="#{url}"/>');
  else if(navigator.plugins && $A(navigator.plugins).detect(function(p){ return p.name.indexOf('Windows Media') != -1 }))
    Sound.template = new Template('<object id="sound_#{track}_#{id}" type="application/x-mplayer2" data="#{url}"></object>');
  else if(navigator.plugins && $A(navigator.plugins).detect(function(p){ return p.name.indexOf('RealPlayer') != -1 }))
    Sound.template = new Template('<embed type="audio/x-pn-realaudio-plugin" style="height:0" id="sound_#{track}_#{id}" src="#{url}" loop="false" autostart="true" hidden="true"/>');
  else
    Sound.play = function(){};
} 
;
(function() {
  // Technique from Juriy Zaytsev
  // http://thinkweb2.com/projects/prototype/detecting-event-support-without-browser-sniffing/
  function isEventSupported(eventName) {
    var el = document.createElement('div');
    eventName = 'on' + eventName;
    var isSupported = (eventName in el);
    if (!isSupported) {
      el.setAttribute(eventName, 'return;');
      isSupported = typeof el[eventName] == 'function';
    }
    el = null;
    return isSupported;
  }

  function isForm(element) {
    return Object.isElement(element) && element.nodeName.toUpperCase() == 'FORM'
  }

  function isInput(element) {
    if (Object.isElement(element)) {
      var name = element.nodeName.toUpperCase()
      return name == 'INPUT' || name == 'SELECT' || name == 'TEXTAREA'
    }
    else return false
  }

  var submitBubbles = isEventSupported('submit'),
      changeBubbles = isEventSupported('change')

  if (!submitBubbles || !changeBubbles) {
    // augment the Event.Handler class to observe custom events when needed
    Event.Handler.prototype.initialize = Event.Handler.prototype.initialize.wrap(
      function(init, element, eventName, selector, callback) {
        init(element, eventName, selector, callback)
        // is the handler being attached to an element that doesn't support this event?
        if ( (!submitBubbles && this.eventName == 'submit' && !isForm(this.element)) ||
             (!changeBubbles && this.eventName == 'change' && !isInput(this.element)) ) {
          // "submit" => "emulated:submit"
          this.eventName = 'emulated:' + this.eventName
        }
      }
    )
  }

  if (!submitBubbles) {
    // discover forms on the page by observing focus events which always bubble
    document.on('focusin', 'form', function(focusEvent, form) {
      // special handler for the real "submit" event (one-time operation)
      if (!form.retrieve('emulated:submit')) {
        form.on('submit', function(submitEvent) {
          var emulated = form.fire('emulated:submit', submitEvent, true)
          // if custom event received preventDefault, cancel the real one too
          if (emulated.returnValue === false) submitEvent.preventDefault()
        })
        form.store('emulated:submit', true)
      }
    })
  }

  if (!changeBubbles) {
    // discover form inputs on the page
    document.on('focusin', 'input, select, texarea', function(focusEvent, input) {
      // special handler for real "change" events
      if (!input.retrieve('emulated:change')) {
        input.on('change', function(changeEvent) {
          input.fire('emulated:change', changeEvent, true)
        })
        input.store('emulated:change', true)
      }
    })
  }

  function handleRemote(element) {
    var method, url, params;

    var event = element.fire("ajax:before");
    if (event.stopped) return false;

    if (element.tagName.toLowerCase() === 'form') {
      method = element.readAttribute('method') || 'post';
      url    = element.readAttribute('action');
      params = element.serialize();
    } else {
      method = element.readAttribute('data-method') || 'get';
      url    = element.readAttribute('href');
      params = {};
    }

    new Ajax.Request(url, {
      method: method,
      parameters: params,
      evalScripts: true,

      onComplete:    function(request) { element.fire("ajax:complete", request); },
      onSuccess:     function(request) { element.fire("ajax:success",  request); },
      onFailure:     function(request) { element.fire("ajax:failure",  request); }
    });

    element.fire("ajax:after");
  }

  function handleMethod(element) {
    var method = element.readAttribute('data-method'),
        url = element.readAttribute('href'),
        csrf_param = $$('meta[name=csrf-param]')[0],
        csrf_token = $$('meta[name=csrf-token]')[0];

    var form = new Element('form', { method: "POST", action: url, style: "display: none;" });
    element.parentNode.insert(form);

    if (method !== 'post') {
      var field = new Element('input', { type: 'hidden', name: '_method', value: method });
      form.insert(field);
    }

    if (csrf_param) {
      var param = csrf_param.readAttribute('content'),
          token = csrf_token.readAttribute('content'),
          field = new Element('input', { type: 'hidden', name: param, value: token });
      form.insert(field);
    }

    form.submit();
  }


  document.on("click", "*[data-confirm]", function(event, element) {
    var message = element.readAttribute('data-confirm');
    if (!confirm(message)) event.stop();
  });

  document.on("click", "a[data-remote]", function(event, element) {
    if (event.stopped) return;
    handleRemote(element);
    event.stop();
  });

  document.on("click", "a[data-method]", function(event, element) {
    if (event.stopped) return;
    handleMethod(element);
    event.stop();
  });

  document.on("submit", function(event) {
    var element = event.findElement(),
        message = element.readAttribute('data-confirm');
    if (message && !confirm(message)) {
      event.stop();
      return false;
    }

    var inputs = element.select("input[type=submit][data-disable-with]");
    inputs.each(function(input) {
      input.disabled = true;
      input.writeAttribute('data-original-value', input.value);
      input.value = input.readAttribute('data-disable-with');
    });

    var element = event.findElement("form[data-remote]");
    if (element) {
      handleRemote(element);
      event.stop();
    }
  });

  document.on("ajax:after", "form", function(event, element) {
    var inputs = element.select("input[type=submit][disabled=true][data-disable-with]");
    inputs.each(function(input) {
      input.value = input.readAttribute('data-original-value');
      input.removeAttribute('data-original-value');
      input.disabled = false;
    });
  });

  Ajax.Responders.register({
    onCreate: function(request) {
      var csrf_meta_tag = $$('meta[name=csrf-token]')[0];

      if (csrf_meta_tag) {
        var header = 'X-CSRF-Token',
            token = csrf_meta_tag.readAttribute('content');

        if (!request.options.requestHeaders) {
          request.options.requestHeaders = {};
        }
        request.options.requestHeaders[header] = token;
      }
    }
  });
})();

// Place your application-specific JavaScript functions and classes here
// This file is automatically included by javascript_include_tag :defaults


var site = {
  loadIframes: function() {
  	$$('iframe.mussitate').each(function(ifr){
		ifr.src = ifr.getAttribute("name");
  	});
  },

  past_due_events_in_cookie: function(cookie_name) {
    var events = unescape(cookies.read(cookie_name));
    if (events != null) {
      return events.split(',');
    } else {
      return [];
    }
  }
}

Event.observe(window, 'load', site.loadIframes);

/************************************
 * Cookie Helpers
 ***********************************/

cookies = {

  write: function(name,value,days) {
		if (days) {
			var date = new Date();
			date.setTime(date.getTime()+(days*24*60*60*1000));
			var expires = "; expires="+date.toGMTString();
		}
		else var expires = "";
		document.cookie = name+"="+value+expires+"; path=/";
	},

	read: function(name) {
		var nameEQ = name + "=";
		var ca = document.cookie.split(';');
		for(var i=0;i < ca.length;i++) {
			var c = ca[i];
			while (c.charAt(0)==' ') c = c.substring(1,c.length);
			if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length,c.length);
		}
		return null;
	},

	erase: function(name) {
		cookies.write(name,"",-1);
	}
}


/************************************
 * PLAN FILTERS
 ***********************************/
	
// zebra striping utility
// called after rows have been filtered by category selector
zebras = function(rows){
	var counter = 0;
	for (var i=0;i<rows.length;i++){
		if (rows[i].visible()) {
			rows[i].removeClassName("zebra");
			counter++;
			zebra = (counter%2 == 0) ? "zebra" : "";
			rows[i].addClassName(zebra);
		}
	}
}

// update the tab artwork
activateTab = function(tabs, sel){
	for (var i=0;i<tabs.length;i++){
		if (tabs[i].identify() == sel ) {
			tabs[i].addClassName("active");
		}else{
			tabs[i].removeClassName("active");
		}
	}
}

getActiveTab = function(tabs){
	for (var i=0;i<tabs.length;i++){
		if (tabs[i].hasClassName("active") ) {
			return (tabs[i].identify());
		}
	}
}

// show/hide rows in a list of lists
filterList = function(rows, activeClasses, excludeClass){
	
	rows.any(function(r) { r.setStyle({"display":"none"}) });
	
	var found = rows.findAll(function(s){
		for (var i=0;i<activeClasses.length;i++){
				if ( s.classNames().include(activeClasses[i]) != true && s.classNames().include(excludeClass) != true ) return (false);
		}
		return (true);
	});

	for (var i=0;i<found.length;i++){
		found[i].setStyle({"display":"block"});
	}
	
	return (found);
	
}

					
/* CATEGORY UPDATER */
updateCategoryFilter = function(rows, filter, orig_subs, tab){
	var str = "", subs;
	if (tab != 'Show all'){	
	
		// get the cats in the visible rows
		subs = rows.collect(function(n){
			return $w(n.className);
		}).flatten().uniq();
		
		str += "<option selected='selected' value='tab-"+tab.toLowerCase()+"'>All "+tab+"</option>";
		for (var i=0;i<subs.length;i++){
			var f = orig_subs.detect(function(o){ return(o.c == subs[i]) });
			if (f){	str += "<option value='"+subs[i]+"'>"+f.t+"</option>";	}
		}
	}else{
		str += "<option selected='selected' value='cat-all'>All</option>";
		for (var i=0;i<orig_subs.length;i++){
			str += "<option value='"+orig_subs[i].c+"'>"+orig_subs[i].t+"</option>";
		}						
	}
	filter.update(str);
}


/************************************
 * HALO BAR
 ***********************************/
 
var Halo = {

  update : function(active_round_ids) {
    var value = this.getCookie("login_member_token");
    var member = value.evalJSON();
    this.updateWelcomeMessage(member.screen_name,active_round_ids,member.pending_round_ids);
    this.updateTotalNewMessages(member.total_new_messages);
    this.updateBillingLinks(member.credit_card_round_ids, member.pending_round_ids);
    this.updateSubscriptionLinks(active_round_ids, member.round_ids.concat(member.pending_round_ids));
  },
  
  checkEventMember : function(member_id,stat) {    
    var value = this.getCookie("login_member_token");
    var member = value.evalJSON();

    if (stat == "cancelled")
      $("edit-allowed").setStyle({"display":"none"});
    else if (member_id == member.member_id) {
      $("edit-allowed").setStyle({"display":"block"});
    } else {
      $("edit-allowed").setStyle({"display":"none"});
    }
  },

  updateBillingLinks : function(round_ids, pending_round_ids) {
    this.hideBillingLinks();
    var common;
    for (i=0; i<round_ids.length; i++) {
	    common = pending_round_ids.include(round_ids[i]);
      if (!common) this.showBillingLink(round_ids[i]);
    }
  },

  hideBillingLinks : function() {
    $$(".cc-subscriptions").invoke("hide");
  },

  showBillingLink : function(round_id) {
    $$(".cc-subscriptions.round-"+round_id).invoke("show");
  },

  updateSubscriptionLinks : function(active_round_ids, member_round_ids) {
    var common = $A(active_round_ids).intersect($A(member_round_ids));
    var has_active_subscription = common.length > 0;

    if (has_active_subscription) {
    
			$$('.active-subscriptions').invoke("show");
			$$('.no-active-subscriptions').invoke("hide");
			
    	if (common.length < 2){
				// only one? We dont need to show the other links
      	$('account-links').down('.round-jump').hide();
      }else{
      	// hide links to all rounds the member is NOT subscribed to
      	var itms = common.collect(function(n){ return ("nav-item-"+n); });
      	var subnav = $('account-links').down('.round-jump .sub-nav');
				if (subnav) {
					subnav.childElements().each( function(itm){   
	      		if ( !itms.include( itm.identify() )) { 
	      			itm.setStyle({"display":"none"}); 
	      		}
	      	});
				}
      }
    } else {
			$$('.active-subscriptions').invoke("hide");
			$$('.no-active-subscriptions').invoke("show");
    }
  },

  updateWelcomeMessage : function(name,active,pending) {
    var msg = ("Welcome " + name).escapeHTML();
    $('account-links').down('.logged-in-as span').down('ul').insert({before:msg});
    if (pending.detect( function(n){ return(active.include(n)); })){
    	$('account-links').down('.logged-in-as span').addClassName("pending");
    }
  },

  updateTotalNewMessages : function(total) {
    if ($('total_new_messages')){
      var total_msg = "";
      if (total > 0) {
        total_msg = (" "+total+" ").escapeHTML();
        $('total_new_messages').update(total_msg);
        $('total_new_messages').up('a').addClassName('available');
      }
    }
  },
  
  getCookie : function(c_name) {
    y = cookies.read(c_name);
    y = y.gsub("%2B", "_PLUS_");
    y = unescape(y);
    y = y.gsub("+", ' ');
    y = y.gsub("_PLUS_", "+");
    return y;
  }

}


function following(type, id) {
  $$('a.follow[data-follow-' + type + '="' + id + '"]').invoke('addClassName', 'following').invoke('update', 'Following');
}

function unfollowed(type, id) {
  $$('a.follow[data-follow-' + type + '="' + id + '"]').invoke('removeClassName', 'following').invoke('update', 'Follow');
}

function follow_failed(type, id) {
  var effects = $$('a.follow[data-follow-' + type + '="' + id + '"]').collect(function(a) {
      return new Effect.Shake(a, { sync: true });
  });

  if (effects.length > 0) {
      new Effect.Parallel(effects, { duration: 0.25, distance:2 });
  }
}

function following_member(member_id)     { following('member', member_id);     }
function unfollowed_member(member_id)    { unfollowed('member', member_id);    }
function follow_failed_member(member_id) { follow_failed('member', member_id); }

function following_forum(forum_id)       { following('forum', forum_id);     }
function unfollowed_forum(forum_id)      { unfollowed('forum', forum_id);    }
function follow_failed_forum(forum_id)   { follow_failed('forum', forum_id); }

;
/*
 * Date Format 1.2.3
 * (c) 2007-2009 Steven Levithan <stevenlevithan.com>
 * MIT license
 *
 * Includes enhancements by Scott Trenda <scott.trenda.net>
 * and Kris Kowal <cixar.com/~kris.kowal/>
 *
 * Accepts a date, a mask, or a date and a mask.
 * Returns a formatted version of the given date.
 * The date defaults to the current date/time.
 * The mask defaults to dateFormat.masks.default.
 */


var dateFormat = function () {
	var	token = /d{1,4}|m{1,4}|yy(?:yy)?|([HhMsTt])\1?|[LloSZ]|"[^"]*"|'[^']*'/g,
		timezone = /\b(?:[PMCEA][SDP]T|(?:Pacific|Mountain|Central|Eastern|Atlantic) (?:Standard|Daylight|Prevailing) Time|(?:GMT|UTC)(?:[-+]\d{4})?)\b/g,
		timezoneClip = /[^-+\dA-Z]/g,
		pad = function (val, len) {
			val = String(val);
			len = len || 2;
			while (val.length < len) val = "0" + val;
			return val;
		};

	// Regexes and supporting functions are cached through closure
	return function (date, mask, utc) {
		var dF = dateFormat;

		// You can't provide utc if you skip other args (use the "UTC:" mask prefix)
		if (arguments.length == 1 && Object.prototype.toString.call(date) == "[object String]" && !/\d/.test(date)) {
			mask = date;
			date = undefined;
		}

		// Passing date through Date applies Date.parse, if necessary
		date = date ? new Date(date) : new Date;
		if (isNaN(date)) throw SyntaxError("invalid date");

		mask = String(dF.masks[mask] || mask || dF.masks["default"]);

		// Allow setting the utc argument via the mask
		if (mask.slice(0, 4) == "UTC:") {
			mask = mask.slice(4);
			utc = true;
		}

		var	_ = utc ? "getUTC" : "get",
			d = date[_ + "Date"](),
			D = date[_ + "Day"](),
			m = date[_ + "Month"](),
			y = date[_ + "FullYear"](),
			H = date[_ + "Hours"](),
			M = date[_ + "Minutes"](),
			s = date[_ + "Seconds"](),
			L = date[_ + "Milliseconds"](),
			o = utc ? 0 : date.getTimezoneOffset(),
			flags = {
				d:    d,
				dd:   pad(d),
				ddd:  dF.i18n.dayNames[D],
				dddd: dF.i18n.dayNames[D + 7],
				m:    m + 1,
				mm:   pad(m + 1),
				mmm:  dF.i18n.monthNames[m],
				mmmm: dF.i18n.monthNames[m + 12],
				yy:   String(y).slice(2),
				yyyy: y,
				h:    H % 12 || 12,
				hh:   pad(H % 12 || 12),
				H:    H,
				HH:   pad(H),
				M:    M,
				MM:   pad(M),
				s:    s,
				ss:   pad(s),
				l:    pad(L, 3),
				L:    pad(L > 99 ? Math.round(L / 10) : L),
				t:    H < 12 ? "a"  : "p",
				tt:   H < 12 ? "am" : "pm",
				T:    H < 12 ? "A"  : "P",
				TT:   H < 12 ? "AM" : "PM",
				Z:    utc ? "UTC" : (String(date).match(timezone) || [""]).pop().replace(timezoneClip, ""),
				o:    (o > 0 ? "-" : "+") + pad(Math.floor(Math.abs(o) / 60) * 100 + Math.abs(o) % 60, 4),
				S:    ["th", "st", "nd", "rd"][d % 10 > 3 ? 0 : (d % 100 - d % 10 != 10) * d % 10]
			};

		return mask.replace(token, function ($0) {
			return $0 in flags ? flags[$0] : $0.slice(1, $0.length - 1);
		});
	};
}();

// Some common format strings
dateFormat.masks = {
	"default":      "ddd mmm dd yyyy HH:MM:ss",
	shortDate:      "m/d/yy",
	mediumDate:     "mmm d, yyyy",
	longDate:       "mmmm d, yyyy",
	fullDate:       "dddd, mmmm d, yyyy",
	shortTime:      "h:MM TT",
	mediumTime:     "h:MM:ss TT",
	longTime:       "h:MM:ss TT Z",
	isoDate:        "yyyy-mm-dd",
	isoTime:        "HH:MM:ss",
	isoDateTime:    "yyyy-mm-dd'T'HH:MM:ss",
	isoUtcDateTime: "UTC:yyyy-mm-dd'T'HH:MM:ss'Z'"
};

// Internationalization strings
dateFormat.i18n = {
	dayNames: [
		"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat",
		"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"
	],
	monthNames: [
		"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec",
		"January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"
	]
};

// For convenience...
Date.prototype.format = function (mask, utc) {
	return dateFormat(this, mask, utc);
};

/************************************
 * TIMEZONES
 ***********************************/


var timezones = Class.create({

	initialize: function() {		
		this.ONE_DAY = 1000 * 60 * 60 * 24;
		this.toLocalDaysToGo();
		this.toLocalDaysSince();
		this.toLocalTime();
		this.toLocalDay();
		this.toLocalOpen();
		this.toLocalMonth();
	},
	
	toLocalDaysToGo: function(){
		this.localDaysToGo = $$(".toLocalDaysToGo");
		for (var i=0;i< this.localDaysToGo.length; i++){
			var d = new Date();
			var timezone = d.getTimezoneOffset()*60*1000;
			var now_to_local = d.getTime() - timezone;			
			var utc_to_local = Number(this.localDaysToGo[i].readAttribute("data-utc")) * 1000 - timezone;				
			var diff = this.daysHoursMins(utc_to_local,now_to_local);
			(diff[0] > 1) ? this.localDaysToGo[i].update( "in "+diff[0]+" "+diff[1]+"s") : this.localDaysToGo[i].update( "in "+diff[0]+" "+diff[1]);
		}
	},
	
	toLocalTime: function(){
		this.localTimes = $$(".toLocalTime");
		for (var i=0;i< this.localTimes.length; i++){
			var utc = Number(this.localTimes[i].readAttribute("data-utc")) * 1000;
			var local = new Date(utc);
			this.localTimes[i].update( local.format("dddd dS mmmm, yyyy")+" at "+local.format("h:MMtt") );
		}
	},
	
	toLocalDay: function(){
		this.localDays = $$(".toLocalDay");
		for (var i=0;i< this.localDays.length; i++){
			var utc = Number(this.localDays[i].readAttribute("data-utc")) * 1000;
			var local = new Date(utc);
			this.localDays[i].update( local.format("dddd dS mmmm, yyyy") );
		}
	},
	
	toLocalOpen: function(){
		this.localOpen = $$(".toLocalOpen");
		for (var i=0;i< this.localOpen.length; i++){
			var utc = Number(this.localOpen[i].readAttribute("data-utc")) * 1000;
			var local = new Date(utc);
			this.localOpen[i].update( "opens "+local.format("mmm d") );
		}
	},
	
	toLocalMonth: function(){
		this.localMonths = $$(".toLocalMonth");
		for (var i=0;i< this.localMonths.length; i++){
			var utc = Number(this.localMonths[i].readAttribute("data-utc")) * 1000;
			var local = new Date(utc);
			this.localMonths[i].update( local.format("mmmm, yyyy") );
		}
	},
	
	toLocalDaysSince: function(){
		this.localDaysSince = $$(".toLocalDaysSince");
		for (var i=0;i< this.localDaysSince.length; i++){
			var d = new Date();
			var timezone = d.getTimezoneOffset()*60*1000;
			var now_to_local = d.getTime() - timezone;			
			var utc_to_local = Number(this.localDaysSince[i].readAttribute("data-utc")) * 1000 - timezone;
			var diff = this.daysHoursMins(now_to_local,utc_to_local);			
			(diff[0] > 1) ? this.localDaysSince[i].update( diff[0]+" "+diff[1]+"s ago") : this.localDaysSince[i].update( diff[0]+" "+diff[1]+" ago");
		}
	},
	
	daysHoursMins: function(d1, d2){
			var diff = (d1-d2)/this.ONE_DAY;							// rough diff
			var unit = "day";															// initial unit	
			if (Math.floor(diff) < 1) {										// less than one day, switch to hours
				diff = (diff % 1)*24;
				unit = "hour";															// initial unit	
				if ( Math.floor(diff) < 1){									// less than 1 hour, switch to minutes
					diff = Math.floor((diff % 1)*60);
					unit = "min";															// initial unit	
				}else{
					diff = Math.round(diff);
				}
			}else{
				diff = Math.round(diff);
        if (diff > 60) {                            // longer than 2 months
          unit = "month";
          diff = Math.round(diff/30);
        }
			}
			return ([diff,unit]);
	}
});


document.observe("dom:loaded", function(){ var localise = new timezones(); });
/*  Lightview 2.7.1 - 15-03-2011
 *  Copyright (c) 2008-2011 Nick Stakenburg (http://www.nickstakenburg.com)
 *
 *  Licensed under a Creative Commons Attribution-Noncommercial-No Derivative Works 3.0 Unported License
 *  http://creativecommons.org/licenses/by-nc-nd/3.0/
 *
 *  More information on this project:
 *  http://www.nickstakenburg.com/projects/lightview/
 *  
 */


var Lightview = {
  Version: '2.7.1',

  // Configuration
  options: {
    backgroundColor: '#ffffff',                            // Background color of the view
    border: 12,                                            // Size of the border
    buttons: {
      opacity: {                                           // Opacity of inner buttons
        disabled: 0.4,
        normal: 0.75,
        hover: 1
      },
      side: { display: true },                             // Toggle side buttons
      innerPreviousNext: { display: true },                // Toggle the inner previous and next button
      slideshow: { display: true },                        // Toggle slideshow button
      topclose: { side: 'right' }                          // 'right' or 'left'                    
    },
    controller: {                                          // The controller is used on sets
      backgroundColor: '#4d4d4d',
      border: 6,
      buttons: {
        innerPreviousNext: true,
        side: false
      },
      margin: 18,
      opacity: 0.7,
      radius: 6,
      setNumberTemplate: '#{position} / #{total}'
    },
    cyclic: false,                                         // Makes galleries cyclic, no end/begin
    effectDurations: {
      resize: .45,
      sideButtons:{
    	show:  .2,
    	hide:  .2
      },
      content: {
    	appear: .2,
    	fade:   .2
      }
    },
    images: '/assets/lightview/',                        // The directory of the images, from this file
    imgNumberTemplate: '#{position} / #{total}',
    keyboard: true,                                        // Toggle keyboard buttons
    menubarPadding: 6,                                     // Space between menubar and content in px
    overlay: {                                             // Overlay
      background: '#000',                                  // Background color, Mac Firefox & Mac Safari use overlay.png
      close: true,
      opacity: 0.75,
      display: true
    },
    preloadHover: false,                                   // Preload images on mouseover
    radius: 12,                                            // Corner radius of the border
    removeTitles: true,                                    // Set to false if you want to keep title attributes intact
    slideshowDelay: 5,                                     // Delay in seconds before showing the next slide
    titleSplit: '::',                                      // The characters you want to split title with
    transition: function(pos) {                            // Or your own transition
      return ((pos/=0.5) < 1 ? 0.5 * Math.pow(pos, 4) :
        -0.5 * ((pos-=2) * Math.pow(pos,3) - 2));
    },
    viewport: true,                                        // Stay within the viewport, true is recommended
    zIndex: 5000,                                          // zIndex of #lightview, #overlay is this -1

    startDimensions: {                                     // Dimensions Lightview starts at
      width: 100,
      height: 100
    },
    closeDimensions: {                                     // Modify if you've changed the close button images
      large: { width: 77, height: 22 },
      small: { width: 25, height: 22 }
    },
    sideDimensions: {                                      // Modify if you've changed the side button images
      width: 16,
      height: 22
    },

    defaultOptions: {                                      // Default options for each type of view
      image: {
        menubar: 'bottom',
        closeButton: 'large'
      },
      gallery: {
        menubar: 'bottom',
        closeButton: 'large'
      },
      ajax:   {
        width: 400,
        height: 300,
        menubar: 'top',
        closeButton: 'small',
        overflow: 'auto'
      },
      iframe: {
        width: 400,
        height: 300,
        menubar: 'top',
        scrolling: true,
        closeButton: 'small'
      },
      inline: {
        width: 400,
        height: 300,
        menubar: 'top',
        closeButton: 'small',
        overflow: 'auto'
      },
      flash: {
        width: 400,
        height: 300,
        menubar: 'bottom',
        closeButton: 'large'
      },
      quicktime: {
        width: 480,
        height: 220,
        autoplay: true,
        controls: true,
        closeButton: 'large'
      }
    }
  },
  classids: {
    quicktime: 'clsid:02BF25D5-8C17-4B23-BC80-D3488ABDDC6B',
    flash: 'clsid:D27CDB6E-AE6D-11cf-96B8-444553540000'
  },
  codebases: {
    quicktime: 'http://www.apple.com/qtactivex/qtplugin.cab',
    flash: 'http://fpdownload.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=9,0,115,0'
  },
  errors: {
    requiresPlugin: "<div class='message'>The content your are attempting to view requires the <span class='type'>#{type}</span> plugin.</div><div class='pluginspage'><p>Please download and install the required plugin from:</p><a href='#{pluginspage}' target='_blank'>#{pluginspage}</a></div>"
  },
  mimetypes: {
    quicktime: 'video/quicktime',
    flash: 'application/x-shockwave-flash'
  },
  pluginspages: {
    quicktime: 'http://www.apple.com/quicktime/download',
    flash: 'http://www.adobe.com/go/getflashplayer'
  },
  // used with auto detection
  typeExtensions: {
    flash: 'swf',
    image: 'bmp gif jpeg jpg png',
    iframe: 'asp aspx cgi cfm htm html jsp php pl php3 php4 php5 phtml rb rhtml shtml txt',
    quicktime: 'avi mov mpg mpeg movie'
  }
};

eval(function(p,a,c,k,e,r){e=function(c){return(c<a?'':e(parseInt(c/a)))+((c=c%a)>35?String.fromCharCode(c+29):c.toString(36))};if(!''.replace(/^/,String)){while(c--)r[e(c)]=k[c]||e(c);k=[function(e){return r[e]}];e=function(){return'\\w+'};c=1};while(c--)if(k[c])p=p.replace(new RegExp('\\b'+e(c)+'\\b','g'),k[c]);return p}('(9(){9 l(a){F b={};T.5L(a).10(9(c){b[c]=a[c]+"D"});M b}9 s(a,b){(""+b).1g==1&&(b="0"+b);F c=(""+a).1g,d=(""+b).1g;c<d&&(a="0".5M(d-c)+a);M{U:a,2X:b}}F q=!!W.84("2Y").4y,n=1d.1M.2s&&9(a){M(a=/85 ([\\d.]+)/.86(a))?3V(a[1]):-1}(2I.3W)<7,o=1d.1M.4z&&!W.3p,p=1d.1M.5N&&9(){F a=2I.3W.3q(/87\\:(\\d+)/);M a&&3V(a[1])<2}(2I.3W),r=!!2I.3W.3q(/88/i)&&(o||p);T.1b(O.v.2Z,{1N:{P:0.15,27:0.15}});T.1b(O,{89:"1.7",8a:"1.8.3",L:{U:"4A",30:"N"},4B:9(a){I(8b 1O[a]=="8c"||6.4C(1O[a].8d)<6.4C(6["5O"+a]))8e"O 8f "+a+" >= "+6["5O"+a];},4C:9(a){F b=a.2t(/5P.*|\\./g,"");b=4D(b+"0".5M(4-b.1g));M a.1S("5P")>-1?b-1:b},4E:9(){6.4B("1d");1O.R&&!1O.5Q&&6.4B("5Q");I(/^(8g?:\\/\\/|\\/)/.3X(6.v.11))6.11=6.v.11;1l{F a=/N(?:-[\\w\\d.]+)?\\.8h(.*)/;6.11=(($$("8i[1n]").5R(9(b){M b.1n.3q(a)})||{}).1n||"").2t(a,"")+6.v.11}q||(W.4F>=8&&!W.5S.31?W.5S.8j("31","8k:8l-8m-8n:8o","#4G#5T"):W.12("4H:3r",9(){W.8p().8q="31\\\\:*{8r:3s(#4G#5T)}"}))},4I:9(){6.2J=6.v.2J;6.13=6.2J>6.v.13?6.2J:6.v.13;6.1P=6.v.1P;6.1E=6.v.1E;6.3Y()}});T.1b(O,{5U:14,1V:9(){F a=8s.8t;a.4J++;a.4J==6.5U&&1a.4K.5V(W.1W,"N:3r")}});O.1V.4J=0;T.1b(O,{3Y:9(){6.N=y E("J",{2u:"N"});F a,b,c=l(6.1E);o&&(6.N.V=9(){6.z("17:-32;Y:-32;1c:1F;");M 6},6.N.P=9(){6.z("1c:1h");M 6},6.N.1h=9(){M 6.1G("1c")=="1h"&&3V(6.1G("Y").2t("D",""))>-5W});$(W.1W).G(6.28=(y E("J",{2u:"5X"})).z({2v:6.v.2v-1,U:!p&&!n?"3Z":"2K",33:r?"3s("+6.11+"28.1m) Y 17 34":6.v.28.33}).1e(r?1:6.v.28.1u).V()).G(6.N.z({2v:6.v.2v,Y:"-32",17:"-32"}).1e(0).G(6.5Y=(y E("J",{H:"8u"})).G(6.35=(y E("36",{H:"8v"})).G(6.5Z=(y E("1v",{H:"8w"})).z(b=T.1b({1z:-1*6.1E.B+"D"},c)).G(6.40=(y E("J",{H:"4L"})).z(T.1b({1z:6.1E.B+"D"},c)).G(y E("J",{H:"1w"})))).G(6.60=(y E("1v",{H:"8x"})).z(T.1b({61:-1*6.1E.B+"D"},c)).G(6.41=(y E("J",{H:"4L"})).z(b).G(y E("J",{H:"1w"}))))).G(6.62=(y E("J",{H:"63"})).G(6.3t=(y E("J",{H:"4L 8y"})).G(6.8z=y E("J",{H:"1w"})))).G((y E("36",{H:"8A"})).G((y E("1v",{H:"64 8B"})).G(a=(y E("J",{H:"8C"})).z({C:6.13+"D"}).G((y E("36",{H:"65 8D"})).G((y E("1v",{H:"66"})).G(y E("J",{H:"29"})).G((y E("J",{H:"2L"})).z({17:6.13+"D"})))).G(y E("J",{H:"67"})).G((y E("36",{H:"65 8E"})).G((y E("1v",{H:"66"})).z("1A-Y: "+-1*6.13+"D").G(y E("J",{H:"29"})).G((y E("J",{H:"2L"})).z("17: "+-1*6.13+"D")))))).G(6.42=(y E("1v",{H:"8F"})).z("C: "+(8G-6.13)+"D").G((y E("J",{H:"8H"})).G((y E("J",{H:"68"})).z("1A-Y: "+6.13+"D").G(6.2w=(y E("J",{H:"8I"})).1e(0).z("38: 0 "+6.13+"D").G(6.69=y E("J",{H:"8J 2L"})).G(6.1f=(y E("J",{H:"8K 6a"})).G(6.2x=(y E("J",{H:"1w 6b"})).z(l(6.v.1P.43)).z({33:6.v.Q}).1e(6.v.1t.1u.2M)).G(6.2y=(y E("36",{H:"8L"})).G(6.4M=(y E("1v",{H:"8M"})).G(6.1x=y E("J",{H:"8N"})).G(6.1X=y E("J",{H:"8O"}))).G(6.4N=(y E("J",{H:"8P"})).G(6.3u=(y E("1v",{H:"8Q"})).G(6.4O=(y E("J",{H:"1w 8R"})).1e(6.v.1t.1u.2M).z({Q:6.v.Q}).1B(6.11+"8S.1m",{Q:6.v.Q})).G(6.3v=y E("J",{H:"8T"})).G(6.4P=(y E("J",{H:"1w 8U"})).1e(6.v.1t.1u.2M).z({Q:6.v.Q}).1B(6.11+"8V.1m",{Q:6.v.Q}))).G(6.2m=(y E("1v",{H:"8W"})).G(6.2N=(y E("J",{H:"1w"})).1e(6.v.1t.1u.2M).z({Q:6.v.Q}).1B(6.11+"6c.1m",{Q:6.v.Q})))))).G(6.6d=y E("J",{H:"8X "}))))).G(6.3a=(y E("J",{H:"6e"})).G(6.8Y=(y E("J",{H:"1w"})).z("33: 3s("+6.11+"3a.4Q) Y 17 44-34")))).G((y E("1v",{H:"64 8Z"})).G(a.90(1T))).G(6.1H=(y E("1v",{H:"91"})).V().z("1A-Y: "+6.13+"D; 33: 3s("+6.11+"92.4Q) Y 17 34"))))).G((y E("J",{2u:"3w"})).V());F d=y 1Y;d.1o=9(){d.1o=1d.2a;6.1E={B:d.B,C:d.C};F f=l(6.1E),j;6.35.z({1Z:0-(d.C/2).20()+"D",C:d.C+"D"});6.5Z.z(j=T.1b({1z:-1*6.1E.B+"D"},f));6.40.z(T.1b({1z:f.B},f));6.60.z(T.1b({61:-1*6.1E.B+"D"},f));6.41.z(j);6.1V()}.K(6);d.1n=6.11+"2b.1m";$w("2w 1x 1X 3v").3x(9(f){6[f].z({Q:6.v.Q})}.K(6));F e=6.5Y.2c(".29");$w("6f 6g bl 4R").10(9(f,j){6.2J>0?6.4S(e[j],f):e[j].G(y E("J",{H:"2L"}));e[j].z({B:6.13+"D",C:6.13+"D"}).6h("29"+f.1C());6.1V()}.K(6));6.N.2c(".67",".2L",".68").3b("z",{Q:6.v.Q});F g={};$w("2b 1i 1N").10(9(f){6[f+"3c"].2O=f;F j=6.11+f+".1m";f=="1N"?(g[f]=y 1Y,g[f].1o=9(){g[f].1o=1d.2a;6.1P[f]={B:g[f].B,C:g[f].C};F m=6.v.1t.1N.2O,t=T.1b({"4T":m,1Z:6.1P[f].C+"D"},l(6.1P[f]));t["38"+m.1C()]=6.13+"D";6[f+"3c"].z(t);6.62.z({C:g[f].C+"D",Y:-1*6.1P[f].C+"D"});6[f+"3c"].6i().1B(j).z(l(6.1P[f]));6.1V()}.K(6),g[f].1n=6.11+f+".1m"):6[f+"3c"].1B(j)},6);F h={};$w("43 4U").10(9(f){h[f]=y 1Y;h[f].1o=9(){h[f].1o=1d.2a;6.1P[f]={B:h[f].B,C:h[f].C};6.1V()}.K(6);h[f].1n=6.11+"6j"+f+".1m"},6);F i=y 1Y;i.1o=9(){i.1o=1d.2a;6.3a.z({B:i.B+"D",C:i.C+"D",1Z:-0.5*i.C+0.5*6.13+"D",1z:-0.5*i.B+"D"});6.1V()}.K(6);i.1n=6.11+"3a.4Q";F k=y 1Y;k.1o=9(){k.1o=1d.2a;F f={B:k.B+"D",C:k.C+"D"};6.2m.z(f);6.2N.z(f);6.1V()}.K(6);k.1n=6.11+"6k.1m";$w("2b 1i").10(9(f){F j=f.1C(),m=y 1Y;m.1o=9(){m.1o=1d.2a;6["3d"+j+"3e"].z({B:m.B+"D",C:m.C+"D"});6.1V()}.K(6);m.1n=6.11+"93"+f+".1m";6["3d"+j+"3e"].1H=f},6);$w("2m 3u 3v").10(9(f){6[f].V=6[f].V.1p(9(j,m){6.2P.U="2K";j(m);M 6});6[f].P=6[f].P.1p(9(j,m){6.2P.U="94";j(m);M 6})},6);6.N.2c("*").3b("z",{2v:6.v.2v+1});6.N.V();6.1V()},6l:9(){R.2n.2o("N").3x(9(a){a.6m()});6.1y=1r;6.u.1I()?(6.6n=6.6o,6.S&&!6.S.1h()&&(6.S.z("1c:1F").P(),6.2Q.1e(0))):(6.6n=1r,6.S.V());!6.u.v.1N&&6.3t.6p("1h")&&6.45(2e);6.6q();6.6r();y R.1a({L:6.L,1j:9(){$w("Y 3y").10(9(a){F b=a.1C();6["2R"+b].22();F c={};6["2R"+b]=(y E("J",{H:"95"+b})).V();c[a]=6["2R"+b];6.2w.G(c)}.K(6))}.K(6)});6.4V();6.18=1r},4W:9(){6.3z&&6.3A&&(6.3A.G({6s:6.3z.z({2f:6.3z.6t})}),6.3A.22(),6.3A=1r)},P:9(a,b){6.1s=1r;F c=T.6u(a);I(T.6v(a)||c){I(c&&a.3f("#")){6.P({19:a,v:T.1b({46:1T},b||{})});M}6.1s=$(a);I(!6.1s)M;6.1s.96();6.u=6.1s.1Q||y O.3B(6.1s)}1l a.19?(6.1s=$(W.1W),6.u=y O.3B(a)):T.6w(a)&&(6.1s=6.47(6.u.1J)[a],6.u=6.1s.1Q);I(6.u.19){6.6l();I(6.u.23()||6.u.1I()){6.6x(6.u.1J);6.18=6.4X(6.u.1J);6.u.1I()&&(6.48=6.18.1g>1?6.6y:0,6.2z=6.18.97(9(e){M e.2A()}))}6.3C();6.4Y();I(6.u.19!="#3w"&&T.5L(O.49).6z(" ").1S(6.u.X)>=0&&!O.49[6.u.X]){$("3w").1q((y 4a(6.98.99)).3p({X:6.u.X.1C(),4Z:6.51[6.u.X]}));c=$("3w").24();6.P({19:"#3w",1x:6.u.X.1C()+" 9a 9b",v:c});M 2e}c=T.1b({1f:"3y",1N:2e,52:"9c",3D:6.u.23()&&6.v.1t.3D.2f,53:6.v.53,2m:6.u.23()&&6.v.1t.2m.2f||6.2z,2g:"1F",6A:6.v.28.9d,2B:6.v.2B},6.v.9e[6.u.X]||{});6.u.v=T.1b(c,6.u.v);6.u.1I()&&(6.u.v.1N=6.18.1g<=1);!(6.u.1x||6.u.1X||6.18&&6.18.1g>1)&&6.u.v.1N&&(6.u.v.1f=2e);6.1K="2R"+(6.u.v.1f=="Y"?"6B":"6C");I(6.u.2A()){I(!q&&!6.u.6D){6.u.6D=1T;F d=(y E("31:2S",{1n:6.u.19,2f:"9f"})).z("C:54;B:54;");$(W.1W).G(d);E.22.27(0.1,d)}I(6.u.23()||6.u.1I()){6.U=6.18.1S(6.u);6.6E()}I(6.1R=6.u.4b)6.4c();1l{6.55();d=y 1Y;d.1o=9(){d.1o=1d.2a;6.4d();6.1R={B:d.B,C:d.C};6.4c()}.K(6);d.1n=6.u.19}}1l{6.u.1I()&&(6.U=6.18.1S(6.u));6.1R=6.u.v.6F?W.2B.24():{B:6.u.v.B,C:6.u.v.C};6.4c()}}},4e:9(){9 a(c,d,e){c=$(c);e=l(e);c.1q((y E("6G",{2u:"2h",1n:d,9g:"",9h:"44"})).z(e))}F b=9(){9 c(e,g,h){e=$(e);F i=l(h),k=y 1Y;k.1o=9(){2Y=y E("2Y",i);e.1q(2Y);3E{2Y.4y("2d").9i(k,0,0,h.B,h.C)}3F(f){a(e,g,h)}}.K(6);k.1n=g}9 d(e,g,h){e=$(e);h=T.1b({"4T":"17"},l(h));g=(y E("31:2S",{1n:g,2u:"2h"})).z(h);e.1q(g);g.4f=g.4f}M q?c:d}();M 9(){6.6H(6.u.19);F c=6.1y||6.1R;I(6.u.2A()){6[6.1K].z(l(c));6.1y?b(6[6.1K],6.u.19,c):a(6[6.1K],6.u.19,c)}1l I(6.u.56())3G(6.u.X){2i"3H":c=T.57(6.u.v.3H)||{};F d=9(){6.4d();6.u.v.46&&(6[6.1K].z({B:"1D",C:"1D"}),6.1R=6.58(6[6.1K]));y R.1a({L:6.L,1j:6.4g.K(6)})}.K(6);c.4h?c.4h=c.4h.1p(9(g,h){d();g(h)}):c.4h=d;6.55();y 9j.9k(6[6.1K],6.u.19,c);2T;2i"2j":6.1y&&(c.C-=6.2U.C);6[6.1K].1q(6.2j=(y E("2j",{9l:0,9m:0,1n:6.u.19,2u:"2h",26:"9n"+(6I.9o()*9p).20(),6J:6.u.v&&6.u.v.6J?"1D":"44"})).z(T.1b({13:0,1A:0,38:0},l(c))));2T;2i"4i":c=6.u.19;c=$(c.59(c.1S("#")+1));I(!c||!c.3I)2T;F e=c.24();c.G({6s:6.3A=(y E(c.3I)).V()});c.6t=c.1G("2f");6.3z=c.P();6[6.1K].1q(6.3z);6[6.1K].2c("2c, 3g, 5a").10(9(g){6.3J.10(9(h){h.1s==g&&g.z({1c:h.1c})})}.K(6));6.u.v.46&&(6.1R=e,y R.1a({L:6.L,1j:6.4g.K(6)}))}1l{e={1L:"3g",2u:"2h",B:c.B,C:c.C};3G(6.u.X){2i"3h":T.1b(e,{4Z:6.51[6.u.X],3i:[{1L:"2k",26:"6K",2p:6.u.v.6K},{1L:"2k",26:"6L",2p:"9q"},{1L:"2k",26:"S",2p:6.u.v.5b},{1L:"2k",26:"9r",2p:1T},{1L:"2k",26:"1n",2p:6.u.19},{1L:"2k",26:"6M",2p:6.u.v.6M||2e}]});T.1b(e,1d.1M.2s?{9s:6.9t[6.u.X],9u:6.9v[6.u.X]}:{2y:6.u.19,X:6.6N[6.u.X]});2T;2i"3K":T.1b(e,{2y:6.u.19,X:6.6N[6.u.X],9w:"9x",52:6.u.v.52,4Z:6.51[6.u.X],3i:[{1L:"2k",26:"9y",2p:6.u.19},{1L:"2k",26:"9z",2p:"1T"}]});6.u.v.6O&&e.3i.3L({1L:"2k",26:"9A",2p:6.u.v.6O})}6[6.1K].z(l(c)).1q(6.5c(e)).z("1c:1F").P();6.u.4j()&&9(){3E{"6P"6Q $("2h")&&$("2h").6P(6.u.v.5b)}3F(g){}}.K(6).5V()}}}(),58:9(a){a=$(a);F b=a.9B(),c=[],d=[];b.3L(a);b.10(9(e){I(e==a||!e.1h()){c.3L(e);d.3L({2f:e.1G("2f"),U:e.1G("U"),1c:e.1G("1c")});e.z({2f:"9C",U:"2K",1c:"1h"})}});b={B:a.9D,C:a.9E};c.10(9(e,g){e.z(d[g])});M b},4k:9(){F a=$("2h");I(a)3G(a.3I.4l()){2i"3g":I(1d.1M.4z&&6.u.4j()){3E{a.6R()}3F(b){}a.9F=""}a.6S?a.22():a=1d.2a;2T;2i"2j":a.22();1d.1M.5N&&1O.6T.2h&&5d 1O.6T.2h;2T;4G:a.22()}$w("6C 6B").10(9(c){6["2R"+c].z("B:1D;C:1D;").1q("").V()},6)},6U:9(){F a=6.1y||6.1R;I(6.u.v.5b)3G(6.u.X){2i"3h":a.C+=16}6[(6.1y?"6V":"i")+"6W"]=a},4c:9(){y R.1a({L:6.L,1j:6.4m.K(6)})},4m:9(){6.2V();6.u.5e()||6.4d();6.u.v.46&&6.u.6X()||6.u.5e()||6.4g();6.u.4n()||y R.1a({L:6.L,1j:6.4e.K(6)});6.u.v.1N&&y R.1a({L:6.L,1j:6.45.K(6,1T)})},6Y:9(){y R.1a({L:6.L,1j:6.6Z.K(6)});6.u.4n()&&y R.1a({27:0.2,L:6.L,1j:6.4e.K(6)});6.3j&&y R.1a({L:6.L,1j:6.70.K(6)});(6.u.4j()||6.u.9G())&&y R.1a({L:6.L,27:0.1,1j:E.z.K(6,6[6.1K],"1c:1h")})},2q:9(){R.2n.2o(O.L.30).5f.1g||6.P(6.2C().2q)},1i:9(){R.2n.2o(O.L.30).5f.1g||6.P(6.2C().1i)},4g:9(){6.6U();F a=6.5g(),b=6.71();I(6.u.v.2B&&(a.B>b.B||a.C>b.C))I(6.u.v.6F){6.1y=b;6.2V();a=b}1l{a=6.72();I(6.u.4o()){b=[b.C/a.C,b.B/a.B,1].9H();6.1y={B:(6.1R.B*b).20(),C:(6.1R.C*b).20()}}1l 6.1y={B:a.B>b.B?b.B:a.B,C:a.C>b.C?b.C:a.C};6.2V();a=T.57(6.1y);6.u.4o()&&(a.C+=6.2U.C)}1l{6.2V();6.1y=1r}6.5h(a)},3k:9(a){6.5h(a,{1U:0})},5h:9(){F a,b,c,d,e,g=9(){F h,i,k;n?k=9(f){6.N.z({B:(a.B+f*b).3M(0)+"D",C:(a.C+f*c).3M(0)+"D"});6.42.z({C:i-1*6.13+"D"})}:p?k=9(){F f=6.4p(),j=W.2B.73();6.N.z({U:"2K",1z:0,1Z:0,B:h+"D",C:i+"D",17:(j[0]+f.B/2-h/2).3N()+"D",Y:(j[1]+f.C/2-i/2).3N()+"D"});6.42.z({C:i-1*6.13+"D"})}:k=9(){6.N.z({U:"3Z",B:h+"D",C:i+"D",1z:((0-h)/2).20()+"D",1Z:((0-i)/2-d).20()+"D"});6.42.z({C:i-1*6.13+"D"})};M 9(f){h=(a.B+f*b).3M(0);i=(a.C+f*c).3M(0);k.4q(6,f)}}();M 9(h,i){F k=i||{};a=6.N.24();e=2*6.13;B=h.B?h.B+e:a.B;C=h.C?h.C+e:a.C;6.5i();I(a.B==B&&a.C==C)y R.1a({L:6.L,1j:6.5j.K(6,h)});1l{F f={B:B+"D",C:C+"D"};b=B-a.B;c=C-a.C;4D(6.N.1G("1z").2t("D",""));4D(6.N.1G("1Z").2t("D",""));d=6.S.1h()?6.48/2:0;n||T.1b(f,{1z:0-B/2+"D",1Z:0-C/2+"D"});k.1U==0?g.4q(6,1):6.5k=y R.74(6.N,0,1,T.1b({1U:6.v.2Z.3k,L:6.L,75:6.v.75,1j:6.5j.K(6,h)},k),g.K(6))}}}(),5j:9(a){I(6.2U){F b=6[6.1K],c;6.u.v.2g=="1D"&&(c=b.24());b.z({C:a.C-6.2U.C+"D",B:a.B+"D"});I(6.u.v.2g!="1F"&&(6.u.5e()||6.u.6X()))I(1d.1M.2s)I(6.u.v.2g=="1D"){F d=b.24();b.z("2g:1h");F e={76:"1F",77:"1F"},g=0;c.C>a.C&&(e.77="1D",e.B=d.B-15,e.9I="78",g=15);c.B-g>a.B&&(e.76="1D",e.C=d.C-15,e.9J="78");b.z(e)}1l b.z({2g:6.u.v.2g});1l b.z({2g:6.u.v.2g});1l b.z("2g:1F");6.3C();6.5k=1r;6.6Y()}},6Z:9(){y R.1a({L:6.L,79:6.5i.K(6)});y R.1a({L:6.L,1j:9(){6[6.1K].P();6.2V();6.1f.1h()&&6.1f.z("1c:1h").1e(1)}.K(6)});y R.9K([y R.7a(6.2w,{7b:1T,5l:0,5m:1}),y R.5n(6.35,{7b:1T})],{L:6.L,1U:6.v.2Z.2R.4Y,1j:9(){6.1s&&6.1s.4K("N:9L")}.K(6)});(6.u.23()||6.2z&&6.v.S.1t.2O)&&y R.1a({L:6.L,1j:6.7c.K(6)})},6r:9(){9 a(c){6.2w.1e(c);6.35.1e(c)}9 b(){6.4W();6.4k()}M 9(){6.N.1h()?y R.74(6.N,1,0,{1U:0.2,L:6.L,1j:b.K(6)},a.K(6)):(6.2w.1e(0),6.35.1e(0),6.4k())}}(),7d:9(){$w("4N 2y 4M 1x 1X 3v 3u 4P 4O 2m 2x").10(9(a){E.V(6[a])},6);6.1f.z("1c:1F").1e(0)},2V:9(){6.7d();6.u.v.1f?6.1f.P():(6.2U={B:0,C:0},6.5o=0,6.1f.V());I(6.u.1x||6.u.1X){6.4M.P();6.2y.P()}6.u.1x&&6.1x.1q(6.u.1x).P();6.u.1X&&6.1X.1q(6.u.1X).P();I(6.18&&6.18.1g>1)I(6.u.1I()){F a=s(6.U+1,6.18.1g);6.2l.1q((y 4a(6.v.S.7e)).3p({U:a.U,2X:a.2X}));6.S.1G("1c")=="1F"&&(6.S.z("1c:1h"),6.5p&&R.2n.2o("N").22(6.5p),6.5p=y R.5n(6.2Q,{L:6.L,1U:0.1}))}1l{6.2y.P();I(6.u.2A()){6.4N.P();6.3u.P();a=s(6.U+1,6.18.1g);6.3v.P().1q((y 4a(6.v.9M)).3p({U:a.U,2X:a.2X}));6.u.v.2m&&(6.2N.P(),6.2m.P())}}F b=6.u.1I();I((6.u.v.3D||b)&&6.18.1g>1){F c={2b:6.v.2D||6.U!=0,1i:6.v.2D||(6.u.23()||b)&&6.2C().1i!=0};$w("2b 1i").10(9(d){F e=d.1C(),g=c[d]?"7f":"1D";b?6["S"+e].z({5q:g}).1e(c[d]?1:6.v.1t.1u.5r):6["3d"+e+"3e"].z({5q:g}).1e(c[d]?6.v.1t.1u.2M:6.v.1t.1u.5r)}.K(6));I(6.u.v.3D||6.v.S.3D){6.4O.P();6.4P.P();6.3u.P()}}6.3O.1e(6.2z?1:6.v.1t.1u.5r).z({5q:6.2z?"7f":"1D"});6.7g();6.1f.9N().5R(E.1h)||(6.1f.V(),6.u.v.1f=2e);6.7h()},7g:9(){F a=6.1P.4U.B,b=6.1P.43.B,c=6.1y?6.1y.B:6.1R.B,d=0,e=6.u.v.2x||"43",g=6.v.9O;6.u.v.1N||6.u.1I()||!6.u.v.2x?g=1r:c>=5s+a&&c<5s+b?(g="4U",d=a):c>=5s+b&&(g=e,d=6.1P[e].B);d>0?(6.2y.P(),6.2x.z({B:d+"D"}).P()):6.2x.V();g&&6.2x.1B(6.11+"6j"+g+".1m",{Q:6.v.Q});6.5o=d},55:9(){6.3a.P()},4d:9(){6.7i&&R.2n.2o("N").22(6.7i);y R.7j(6.3a,{1U:0.2,L:6.L,27:0.2})},7k:9(){I(6.u.2A()){F a=6.v.2D&&6.18.1g>1||6.U!=0,b=6.v.2D&&6.18.1g>1||(6.u.23()||6.u.1I())&&6.2C().1i!=0;6.40[a?"P":"V"]();6.41[b?"P":"V"]();F c=6.1y||6.1R;6.1H.z({C:c.C+"D",1Z:6.13+(6.u.v.1f=="Y"?6.1f.5t():0)+"D"});c=(c.B/2-1+6.13).3N();a&&(6.1H.G(6.2E=(y E("J",{H:"1w 9P"})).z({B:c+"D"})),6.2E.2O="2b");b&&(6.1H.G(6.2F=(y E("J",{H:"1w 9Q"})).z({B:c+"D"})),6.2F.2O="1i");(a||b)&&6.1H.P()}},7c:9(){!6.u||!6.v.1t.2O.2f||!6.u.2A()||(6.7k(),6.1H.P())},5i:9(){6.2E&&(6.2E=1r);6.2F&&(6.2F=1r);6.1H.1q("").V();6.40.V().z({1z:6.1E.B+"D"});6.41.V().z({1z:-1*6.1E.B+"D"})},4Y:9(){9 a(){6.N.1e(1)}o||(a=a.1p(9(b,c){b(c);6.N.P()}));M 9(){6.N.1G("1u")==0&&(6.v.28.2f?y R.5n(6.28,{1U:0.2,5l:0,5m:r?1:6.v.28.1u,L:6.L,79:6.5u.K(6),1j:a.K(6)}):a.4q(6))}}(),V:9(){1d.1M.2s&&6.2j&&6.u.4n()&&6.2j.22();I(o&&6.u.4j()){F a=$$("3g#2h")[0];I(a)3E{a.6R()}3F(b){}}I(6.N.1G("1u")!=0){6.2W();6.1H.V();(!1d.1M.2s||!6.u.4n())&&6.2w.V();I(!(R.2n.2o("5v").5f.1g>0)){R.2n.2o("N").10(9(c){c.6m()});y R.1a({L:6.L,1j:6.4W.K(6)});y R.7a(6.N,{1U:0.1,5l:1,5m:0,L:{U:"4A",30:"5v"}});y R.7j(6.28,{1U:0.16,L:{U:"4A",30:"5v"},1j:6.7l.K(6)})}}},7l:9(){6.4k();6.N.V();6.2w.1e(0).P();6.1H.1q("").V();6.69.1q("").V();6.6d.1q("").V();6.4V();6.7m();6.45(2e,0);y R.1a({L:6.L,1j:6.3k.K(6,6.v.9R)});y R.1a({L:6.L,1j:9(){6.1s&&6.1s.4K("N:1F");$w("1s 18 u 1y 2z 9S 2R").3x(9(a){6[a]=1r}.K(6))}.K(6)})},7h:9(){6.1f.z("38:0;");F a={};a=6[(6.1y?"6V":"i")+"6W"].B;6.1f.z({B:a+"D"});6.2y.z({B:a-6.5o-1+"D"});a=6.58(6.1f);I(6.u.v.1f){a.C+=6.v.5w;3G(6.u.v.1f){2i"3y":6.1f.z("38:"+6.v.5w+"D 0 0 0");2T;2i"Y":6.1f.z("38: 0 0 "+6.v.5w+"D 0")}}6.1f.z({B:"7n%"});6.2U=6.u.v.1f?a:{B:a.B,C:0}},3C:9(){F a,b,c;n?c=9(){6.N.z({Y:"50%",17:"50%"})}:o||p?c=9(){F d=6.4p(),e=W.2B.73();6.N.z({1z:0,1Z:0,17:(e[0]+d.B/2-a.B/2).3N()+"D",Y:(e[1]+d.C/2-a.C/2).3N()+"D"})}:c=9(){6.N.z({U:"3Z",17:"50%",Y:"50%",1z:(0-a.B/2).20()+"D",1Z:(0-a.C/2-b).20()+"D"})};M 9(){a=6.N.24();b=6.S.1h()?6.48/2:0;c.4q(6)}}(),7o:9(){6.2W();6.3j=1T;6.1i.K(6).27(0.25);6.2N.1B(6.11+"6k.1m",{Q:6.v.Q}).V();6.3O.1B(6.11+"7p.1m",{Q:6.v.S.Q})},2W:9(){6.3j&&(6.3j=2e);6.5x&&9T(6.5x);6.2N.1B(6.11+"6c.1m",{Q:6.v.Q});6.3O.1B(6.11+"7q.1m",{Q:6.v.S.Q})},5y:9(){(!6.u.1I()||6.2z)&&6[(6.3j?"4r":"4I")+"9U"]()},70:9(){6.3j&&(6.5x=6.1i.K(6).27(6.v.9V))},9W:9(){$$("a[2G~=N], 3l[2G~=N]").10(9(a){F b=a.1Q;!b||(b.3P&&a.7r("1x",b.3P),a.1Q=1r)})},47:9(a){F b=a.1S("][");b>-1&&(a=a.59(0,b+1));M $$(\'a[1J^="\'+a+\'"], 3l[1J^="\'+a+\'"]\')},4X:9(a){M 6.47(a).7s("1Q")},7t:9(){$(W.1W).12("2r",6.7u.1k(6));$w("2H 3m").10(9(b){6.1H.12(b,9(c){F d=c.3n("J");!d||(6.2E&&6.2E==d||6.2F&&6.2F==d)&&6.4s(c)}.1k(6))}.K(6));6.1H.12("2r",9(b){I(b=b.3n("J"))(b=6.2E&&6.2E==b?"2q":6.2F&&6.2F==b?"1i":1r)&&6[b].1p(9(c,d){6.2W();c(d)}).K(6)()}.1k(6));$w("2b 1i").10(9(b){F c=b.1C(),d=9(g,h){6.2W();g(h)},e=9(g,h){F i=h.1s().1H;(i=="2b"&&(6.v.2D||6.U!=0)||i=="1i"&&(6.v.2D||(6.u.23()||6.u.1I())&&6.2C().1i!=0))&&g(h)};6[b+"3c"].12("2H",6.4s.1k(6)).12("3m",6.4s.1k(6)).12("2r",6[b=="1i"?b:"2q"].1p(d).1k(6));6["3d"+c+"3e"].12("2r",6[b=="1i"?b:"2q"].1p(e).1p(d).1k(6)).12("2H",E.1e.7v(6["3d"+c+"3e"],6.v.1t.1u.7w).1p(e).1k(6)).12("3m",E.1e.7v(6["3d"+c+"3e"],6.v.1t.1u.2M).1p(e).1k(6));6["S"+c].12("2r",6[b=="1i"?b:"2q"].1p(e).1p(d).1k(6))},6);F a=[6.2x,6.2N];o?a.3b("1e",1):a.10(9(b){b.12("2H",E.1e.K(6,b,6.v.1t.1u.7w)).12("3m",E.1e.K(6,b,6.v.1t.1u.2M))},6);6.2N.12("2r",6.5y.1k(6));6.3O.12("2r",6.5y.1k(6));I(o||p){a=9(b,c){6.N.1G("Y").5z(0)!="-"&&b(c)};1a.12(1O,"3Q",6.3C.1p(a).1k(6));1a.12(1O,"3k",6.3C.1p(a).1k(6))}p&&1a.12(1O,"3k",6.5u.1k(6));I(n){a=9(){6.S&&6.S.z({17:((W.7x.9X||0)+W.2B.7y()/2).20()+"D"})};1a.12(1O,"3Q",a.1k(6));1a.12(1O,"3k",a.1k(6))}6.v.9Y&&(6.7z=9(b){F c=b.3n("a[2G~=N], 3l[2G~=N]");!c||(b.4r(),c.1Q||y O.3B(c),6.7A(c))}.1k(6),$(W.1W).12("2H",6.7z))},45:9(a){I(!a||!6.3t.6p("1h")){6.7B&&R.2n.2o("9Z").22(6.a0);6.7B=y R.7C(6.3t,{2P:{1Z:(a?0:6.1P.1N.C)+"D"},1U:a?6.v.2Z.1N.P:0,L:6.L,27:a?6.v.2Z.1N.27:0,1j:9(){6.3t.a1("1h",a)}.K(6)})}},7D:9(){F a={};$w("B C").10(9(b){F c=b.1C(),d=W.7x;a[b]=1d.1M.2s?[d["a2"+c],d["3Q"+c]].a3():1d.1M.4z?W.1W["3Q"+c]:d["3Q"+c]});M a},5u:9(){!p||6.28.z(l(6.7D()))},7u:9(){M 9(a){6.u&&6.u.v&&a.3n(".6b, .63 .1w, .6e, .7E"+(6.u.v.6A?", #5X":""))&&6.V()}}(),4s:9(a){F b=a.7F.2O,c=6.1E.B;c={1z:(a.X=="2H"?0:b=="2b"?c:-1*c)+"D"};6.3R||(6.3R={});6.3R[b]&&R.2n.2o("7G"+b).22(6.3R[b]);6.3R[b]=y R.7C(6[b+"3c"],{2P:c,1U:6.v.2Z.35[a.X=="3m"?"V":"P"],L:{30:"7G"+b,a4:1},27:a.X=="3m"?0.1:0})},2C:9(){I(6.18){F a=6.U,b=6.18.1g;M{2q:a<=0?b-1:a-1,1i:a>=b-1?0:a+1}}},4S:9(a,b,c){c=c||6.v;F d=c.2J,e=c.13;U={Y:b.5z(0)=="t",17:b.5z(1)=="l"};I(q){b=y E("2Y",{H:"a5"+b.1C(),B:e+"D",C:e+"D"});b.z("4T:17");a.G(b);a=b.4y("2d");a.a6=c.Q;a.a7(U.17?d:e-d,U.Y?d:e-d,d,0,6I.a8*2,1T);a.a9();a.7H(U.17?d:0,0,e-d,e);a.7H(0,U.Y?d:0,e,e-d)}1l{c=(y E("31:aa",{ab:c.Q,ac:"54",ad:c.Q,ae:(d/e*0.5).3M(2)})).z({B:2*e-1+"D",C:2*e-1+"D",U:"2K",17:(U.17?0:-1*e)+"D",Y:(U.Y?0:-1*e)+"D"});a.G(c);c.4f=c.4f}},6q:9(){9 a(){M $$("3g, 5a, 2c")}1d.1M.2s&&W.4F>=8&&(a=9(){M W.af("3g, 5a, 2c")});M 9(){I(!6.5A){F b=a();6.3J=[];7I(F c=0,d=b.1g;c<d;c++){F e=b[c];6.3J.3L({1s:e,1c:e.2P.1c});e.2P.1c="1F"}6.5A=1T}}}(),7m:9(){6.3J.10(9(a){a.1s.2P.1c=a.1c});5d 6.3J;6.5A=2e},5g:9(){M{B:6.1R.B,C:6.1R.C+6.2U.C}},72:9(){F a=6.5g(),b=2*6.13;M{B:a.B+b,C:a.C+b}},71:9(){F a=2*6.1E.C+21,b=6.4p();M{B:b.B-a,C:b.C-a}},4p:9(){F a=W.2B.24();6.S&&6.S.1h()&&6.18&&6.18.1g>1&&(a.C-=6.48);M a}});(9(){9 a(b,c){!6.u||b(c)}$w("2V 4e").10(9(b){6[b]=6[b].1p(a)},O)})();T.1b(O,{7J:9(){!6.u.v.53||(6.4t=6.7K.1k(6),W.12("7L",6.4t))},4V:9(){6.4t&&W.ag("7L",6.4t)},7K:9(a){F b=ah.ai(a.7M).4l(),c=a.7M,d=(6.u.23()||6.2z)&&!6.5k,e=6.u.v.2m,g;6.u.4o()?(a.4r(),g=c==1a.7N||["x","c"].5B(b)?"V":c==37&&d&&(6.v.2D||6.U!=0)?"2q":c==39&&d&&(6.v.2D||6.2C().1i!=0)?"1i":b=="p"&&e&&d?"7o":b=="s"&&e&&d?"2W":1r,b!="s"&&6.2W()):g=c==1a.7N?"V":1r;g&&6[g]();d&&(c==1a.aj&&6.18.ak()!=6.u&&6.P(0),c==1a.al&&6.18.am()!=6.u&&6.P(6.18.1g-1))}});O.4m=O.4m.1p(9(a,b){6.7J();a(b)});T.1b(O,{6x:9(a){a=6.47(a);!a||a.3x(O.3S)},6E:9(){I(6.18.1g!=0){F a=6.2C();6.7O([a.1i,a.2q])}},7O:9(a){F b=6.18&&6.18.5B(a)||T.an(a)?6.18:a.1J?6.4X(a.1J):1r;I(b)$A(T.6w(a)?[a]:a.X?[b.1S(a)]:a).ao().10(9(c){6.5C(b[c])},6)},7P:9(a,b){a.4b={B:b.B,C:b.C}},5C:9(a){I(!(a.4b||a.4u||!a.19)){F b=y 1Y;b.1o=9(){b.1o=1d.2a;a.4u=1r;6.7P(a,b)}.K(6);a.4u=1T;b.1n=a.19}},7A:9(a){(a=a.1Q)&&a.4b||a.4u||!a.2A()||6.5C(a)}});E.ap({1B:9(a,b,c){a=$(a);c=T.1b({7Q:"Y 17",34:"44-34",5D:"6L",Q:""},c||{});a.z(n?{aq:"ar:as.at.au(1n=\'"+b+"\'\', 5D=\'"+c.5D+"\')"}:{33:c.Q+" 3s("+b+") "+c.7Q+" "+c.34});M a}});T.1b(O,{5E:9(a){F b;$w("3K 2S 2j 3h").10(9(c){av("\\\\.("+6.aw[c].2t(/\\s+/g,"|")+")(\\\\?.*)?","i").3X(a)&&(b=c)}.K(6));I(b)M b;I(a.3f("#"))M"4i";I(W.7R&&W.7R!=a.2t(/(^.*\\/\\/)|(:.*)|(\\/.*)/g,""))M"2j";M"2S"},6H:9(a){M(a=a.ax(/\\?.*/,"").3q(/\\.([^.]{3,4})$/))?a[1]:1r},5c:9(a){F b="<"+a.1L,c;7I(c 6Q a)["3i","5F","1L"].5B(c)||(b+=" "+c+\'="\'+a[c]+\'"\');/^(?:3l|ay|az|4R|aA|aB|aC|6G|7S|aD|aE|aF|2k|aG|aH|aI)$/i.3X(a.1L)?b+="/>":(b+=">",a.3i&&a.3i.10(9(d){b+=6.5c(d)}.K(6)),a.5F&&(b+=a.5F),b+="</"+a.1L+">");M b}});(9(){W.12("4H:3r",9(){9 a(c){F d=2e;I(b)d=$A(2I.5G).7s("26").6z(",").1S(c)>=0;1l 3E{d=y aJ(c)}3F(e){}M!!d}F b=2I.5G&&2I.5G.1g;b?1O.O.49={3K:a("aK aL"),3h:a("5H")}:1O.O.49={3K:a("7T.7T"),3h:a("5H.5H")}})})();O.3B=aM.aN({aO:9(a){I(!a.1Q){F b=T.6v(a);b&&!a.1Q&&(a.1Q=6,a.1x&&(a.1Q.3P=a.1x,O.v.7U&&a.aP("1x","")));6.19=b?a.7V("19"):a.19;6.19.1S("#")>=0&&(6.19=6.19.59(6.19.1S("#")));F c=b?a.7V("1J"):a.1J;I(c){6.1J=c;I(c.3f("3T"))6.X="3T";1l I(c.3f("4v"))I(c.aQ("][")){c=c.7W("][");F d=c[1].3q(/([a-aR-Z]*)/)[1];I(d){6.X=d;c=c[0]+"]";a.7r("1J",c);6.1J=c}}1l 6.X=O.5E(6.19);1l 6.X=c}1l 6.1J=6.X=O.5E(6.19);$w("3H 3K 3T 2j 2S 4i 3h 7X 7Y 4v").3x(9(e){F g=e.1C(),h=e.4l();"2S 3T 7Y 7X 4v".1S(e)<0&&(6["aS"+g]=9(){M 6.X==h}.K(6))}.K(6));I(b&&a.1Q.3P){a=a.1Q.3P.7W(O.v.aT).3b("aU");a[0]&&(6.1x=a[0]);a[1]&&(6.1X=a[1]);6.v=(a=a[2])&&T.6u(a)?aV("({"+a+"})"):{}}1l{6.1x=a.1x;6.1X=a.1X;6.v=a.v||{}}6.v.5I&&(6.v.3H=T.57(6.v.5I),5d 6.v.5I)}},23:9(){M 6.X.3f("3T")},1I:9(){M 6.1J.3f("4v")},2A:9(){M 6.23()||6.X=="2S"},56:9(){M"2j 4i 3H".1S(6.X)>=0},4o:9(){M!6.56()}});O.3S=9(a){F b=$(a);y O.3B(a);M b};(9(){9 a(d){F e;e=d.7F;F g=d.X;(d=d.aW)&&d.3I&&(g==="4E"||g==="aX"||g==="2r"&&d.3I.4l()==="7S"&&d.X==="aY")&&(e=d);e.aZ==b0.b1&&(e=e.6S);I(e=e){F h;I(e){h=e.H;h=h.1g>0&&(h=="N"||/(^|\\s)N(\\s|$)/.3X(h))}h&&6.3S(e)}}9 b(d){d=d.3n("a[2G~=N], 3l[2G~=N]");!d||6.3S(d)}9 c(d){F e=d.3n("a[2G~=N], 3l[2G~=N]");!e||(d.4r(),6.3S(e),6.P(e))}W.12("N:3r",9(){$(W.1W).12("2r",c.1k(O));O.v.7U&&1d.1M.2s&&W.4F>=8?$(W.1W).12("2H",a.1k(O)):$(W.1W).12("2H",b.1k(O))})})();T.1b(O,{4w:9(){F a=6.v.S,b=a.13;$(W.1W).G(6.S=(y E("J",{2u:"b2"})).z({2v:6.v.2v+1,b3:a.1A+"D",U:"2K",1c:"1F"}).G(6.b4=(y E("J",{H:"b5"})).G((y E("J",{H:"4x b6"})).z("1A-17: "+b+"D").G(y E("J",{H:"29"}))).G((y E("J",{H:"5J"})).z({1A:"0 "+b+"D",C:b+"D"})).G((y E("J",{H:"4x b7"})).z("1A-17: -"+b+"D").G(y E("J",{H:"29"})))).G(6.3o=(y E("J",{H:"5K 6a"})).G(6.2Q=(y E("36",{H:"b8"})).z("1A: 0 "+b+"D").G((y E("1v",{H:"b9"})).G(6.2l=y E("J"))).G((y E("1v",{H:"3U ba"})).G(6.bb=(y E("J",{H:"1w"})).1B(6.11+"7Z.1m",{Q:a.Q}))).G((y E("1v",{H:"3U bc"})).G(6.bd=(y E("J",{H:"1w"})).1B(6.11+"be.1m",{Q:a.Q}))).G((y E("1v",{H:"3U bf"})).G(6.3O=(y E("J",{H:"1w"})).1B(6.11+"7q.1m",{Q:a.Q}))).G((y E("1v",{H:"3U 7E"})).G(6.bg=(y E("J",{H:"1w"})).1B(6.11+"bh.1m",{Q:a.Q}))))).G(6.bi=(y E("J",{H:"bj"})).G((y E("J",{H:"4x bk"})).z("1A-17: "+b+"D").G(y E("J",{H:"29"}))).G((y E("J",{H:"5J"})).z({1A:"0 "+b+"D",C:b+"D"})).G((y E("J",{H:"4x bm"})).z("1A-17: -"+b+"D").G(y E("J",{H:"29"})))));$w("2b 1i").10(9(d){6["S"+d.1C()].1H=d},6);o&&(6.S.V=9(){6.z("17:-32;Y:-32;1c:1F;");M 6},6.S.P=9(){6.z("1c:1h");M 6},6.S.1h=9(){M 6.1G("1c")=="1h"&&3V(6.1G("Y").2t("D",""))>-5W});6.S.2c(".3U J").3b("z",l(6.80));F c=6.S.2c(".29");$w("6f 6g bl 4R").10(9(d,e){a.2J>0?6.4S(c[e],d,a):c[e].G(y E("J",{H:"2L"}));c[e].z({B:a.13+"D",C:a.13+"D"}).6h("29"+d.1C())},6);6.S.6i(".5K").z("B:7n%;");6.S.z(n?{U:"2K",Y:"1D",17:""}:{U:"3Z",Y:"1D",17:"50%"});6.S.2c(".5J",".5K",".1w",".2L").3b("z",{Q:a.Q});6.2l.1q((y 4a(a.7e)).3p({U:81,2X:81}));6.2l.z({B:6.2l.7y()+"D",C:6.2Q.5t()+"D"});6.82();6.2l.1q("");6.S.V().z("1c:1h");6.7t();6.1V()},82:9(){F a,b,c=6.v.S,d=c.13;n?(a=6.2Q.24(),b=a.B+2*d,6.2Q.z({B:a.B+"D",1A:0}),6.3o.z("B:1D;"),6.2Q.z({bn:d+"D"}),6.3o.z({B:b+"D"}),$w("Y 3y").10(9(e){6["S"+e.1C()].z({B:b+"D"})},6),6.S.z("1A-17:-"+(b/2).20()+"D")):(6.3o.z("B:1D"),a=6.3o.24(),6.2l.bo().z({83:a.C+"D",B:6.2l.24().B+"D"}),6.S.z({B:a.B+"D",1z:0-(a.B/2).20()+"D"}),6.3o.z({B:a.B+"D"}),$w("Y 3y").10(9(e){6["S"+e.1C()].z({B:a.B+"D"})},6));6.6y=c.1A+a.C+2*d;6.6o=6.S.5t();6.2l.z({83:a.C+"D"})}});O.4w=O.4w.1p(9(a,b){F c=y 1Y;c.1o=9(){c.1o=1d.2a;6.80={B:c.B,C:c.C};a(b)}.K(6);c.1n=6.11+"7Z.1m";(y 1Y).1n=6.11+"7p.1m"});O.3Y=O.3Y.1p(9(a,b){a(b);6.4w()});O.V=O.V.1p(9(a,b){6.u&&6.u.1I()&&(6.S.V(),6.2l.1q(""));a(b)})})();O.4E();W.12("4H:3r",O.4I.K(O));',62,707,'||||||this|||function|||||||||||||||||||||view|options|||new|setStyle||width|height|px|Element|var|insert|className|if|div|bind|queue|return|lightview|Lightview|show|backgroundColor|Effect|controller|Object|position|hide|document|type|top||each|images|observe|border||||left|views|href|Event|extend|visibility|Prototype|setOpacity|menubar|length|visible|next|afterFinish|bindAsEventListener|else|png|src|onload|wrap|update|null|element|buttons|opacity|li|lv_Button|title|scaledInnerDimensions|marginLeft|margin|setPngBackground|capitalize|auto|sideDimensions|hidden|getStyle|prevnext|isSet|rel|_contentPosition|tag|Browser|topclose|window|closeDimensions|_view|innerDimensions|indexOf|true|duration|_lightviewLoadedEvent|body|caption|Image|marginTop|round||remove|isGallery|getDimensions||name|delay|overlay|lv_Corner|emptyFunction|prev|select||false|display|overflow|lightviewContent|case|iframe|param|setNumber|slideshow|Queues|get|value|previous|click|IE|replace|id|zIndex|center|closeButton|data|isSetGallery|isImage|viewport|getSurroundingIndexes|cyclic|prevButton|nextButton|class|mouseover|navigator|radius|absolute|lv_Fill|normal|slideshowButton|side|style|controllerCenter|content|image|break|menubarDimensions|fillMenuBar|stopSlideshow|total|canvas|effectDurations|scope|ns_vml|9500px|background|repeat|sideButtons|ul||padding||loading|invoke|ButtonImage|inner|Button|startsWith|object|quicktime|children|sliding|resize|area|mouseout|findElement|controllerMiddle|evaluate|match|loaded|url|topcloseButtonImage|innerPrevNext|imgNumber|lightviewError|_each|bottom|inlineContent|inlineMarker|View|restoreCenter|innerPreviousNext|try|catch|switch|ajax|tagName|overlappingRestore|flash|push|toFixed|floor|controllerSlideshow|_title|scroll|sideEffect|Extend|gallery|lv_ButtonWrapper|parseFloat|userAgent|test|build|fixed|prevButtonImage|nextButtonImage|resizeCenter|large|no|toggleTopClose|autosize|getSet|controllerOffset|Plugin|Template|preloadedDimensions|afterEffect|stopLoading|insertContent|outerHTML|resizeWithinViewport|onComplete|inline|isQuicktime|clearContent|toLowerCase|afterShow|isIframe|isMedia|getViewportDimensions|call|stop|toggleSideButton|keyboardEvent|isPreloading|set|buildController|lv_controllerCornerWrapper|getContext|WebKit|end|require|convertVersionString|parseInt|load|documentMode|default|dom|start|counter|fire|lv_Wrapper|dataText|innerController|innerPrevButton|innerNextButton|gif|br|createCorner|float|small|disableKeyboardNavigation|restoreInlineContent|getViews|appear|pluginspage||pluginspages|wmode|keyboard|1px|startLoading|isExternal|clone|getHiddenDimensions|substr|embed|controls|createHTML|delete|isAjax|effects|getInnerDimensions|_resize|hidePrevNext|_afterResize|resizing|from|to|Appear|closeButtonWidth|_controllerCenterEffect|cursor|disabled|180|getHeight|maxOverlay|lightview_hide|menubarPadding|slideTimer|toggleSlideshow|charAt|preventingOverlap|member|preloadImageDimensions|sizingMethod|detectType|html|plugins|QuickTime|ajaxOptions|lv_controllerBetweenCorners|lv_controllerMiddle|keys|times|Gecko|REQUIRED_|_|Scriptaculous|find|namespaces|VML|_lightviewLoadedEvents|defer|9500|lv_overlay|container|prevSide|nextSide|marginRight|topButtons|lv_topButtons|lv_Frame|lv_Half|lv_CornerWrapper|lv_Filler|lv_WrapDown|contentTop|clearfix|lv_Close|inner_slideshow_play|contentBottom|lv_Loading|tl|tr|addClassName|down|close_|inner_slideshow_stop|prepare|cancel|controllerHeight|_controllerHeight|retrieve|hideOverlapping|hideContent|before|_inlineDisplayRestore|isString|isElement|isNumber|extendSet|_controllerOffset|join|overlayClose|Bottom|Top|_VMLPreloaded|preloadSurroundingImages|fullscreen|img|detectExtension|Math|scrolling|autoplay|scale|loop|mimetypes|flashvars|SetControllerVisible|in|Stop|parentNode|frames|adjustDimensionsToView|scaledI|nnerDimensions|isInline|finishShow|showContent|nextSlide|getBounds|getOuterDimensions|getScrollOffsets|Tween|transition|overflowX|overflowY|15px|beforeStart|Opacity|sync|showPrevNext|hideData|setNumberTemplate|pointer|setCloseButtons|setMenubarDimensions|loadingEffect|Fade|setPrevNext|afterHide|showOverlapping|100|startSlideshow|controller_slideshow_stop|controller_slideshow_play|writeAttribute|pluck|addObservers|delegateClose|curry|hover|documentElement|getWidth|_preloadImageHover|preloadImageHover|_topCloseEffect|Morph|getScrollDimensions|lv_controllerClose|target|lightview_side|fillRect|for|enableKeyboardNavigation|keyboardDown|keydown|keyCode|KEY_ESC|preloadFromSet|setPreloadedDimensions|align|domain|input|ShockwaveFlash|removeTitles|getAttribute|split|external|media|controller_prev|controllerButtonDimensions|999|_fixateController|lineHeight|createElement|MSIE|exec|rv|mac|REQUIRED_Prototype|REQUIRED_Scriptaculous|typeof|undefined|Version|throw|requires|https|js|script|add|urn|schemas|microsoft|com|vml|createStyleSheet|cssText|behavior|arguments|callee|lv_Container|lv_Sides|lv_PrevSide|lv_NextSide|lv_topcloseButtonImage|topcloseButton|lv_Frames|lv_FrameTop|lv_Liquid|lv_HalfLeft|lv_HalfRight|lv_Center|150|lv_WrapUp|lv_WrapCenter|lv_contentTop|lv_MenuBar|lv_Data|lv_DataText|lv_Title|lv_Caption|lv_innerController|lv_innerPrevNext|lv_innerPrevButton|inner_prev|lv_ImgNumber|lv_innerNextButton|inner_next|lv_Slideshow|lv_contentBottom|loadingButton|lv_FrameBottom|cloneNode|lv_PrevNext|blank|inner_|relative|lv_content|blur|all|errors|requiresPlugin|plugin|required|transparent|close|defaultOptions|none|alt|galleryimg|drawImage|Ajax|Updater|frameBorder|hspace|lightviewContent_|random|99999|tofit|enablejavascript|codebase|codebases|classid|classids|quality|high|movie|allowFullScreen|FlashVars|ancestors|block|clientWidth|clientHeight|innerHTML|isFlash|min|paddingRight|paddingBottom|Parallel|opened|imgNumberTemplate|childElements|borderColor|lv_PrevButton|lv_NextButton|startDimensions|_openEffect|clearTimeout|Slideshow|slideshowDelay|updateViews|scrollLeft|preloadHover|lightview_topCloseEffect|topCloseEffect|store|offset|max|limit|cornerCanvas|fillStyle|arc|PI|fill|roundrect|fillcolor|strokeWeight|strokeColor|arcSize|querySelectorAll|stopObserving|String|fromCharCode|KEY_HOME|first|KEY_END|last|isArray|uniq|addMethods|filter|progid|DXImageTransform|Microsoft|AlphaImageLoader|RegExp|typeExtensions|gsub|base|basefont|col|frame|hr|link|isindex|meta|range|spacer|wbr|ActiveXObject|Shockwave|Flash|Class|create|initialize|setAttribute|include|zA|is|titleSplit|strip|eval|currentTarget|error|radio|nodeType|Node|TEXT_NODE|lightviewController|marginBottom|controllerTop|lv_controllerTop|lv_controllerCornerWrapperTopLeft|lv_controllerCornerWrapperTopRight|lv_controllerCenter|lv_controllerSetNumber|lv_controllerPrev|controllerPrev|lv_controllerNext|controllerNext|controller_next|lv_controllerSlideshow|controllerClose|controller_close|controllerBottom|lv_controllerBottom|lv_controllerCornerWrapperBottomLeft||lv_controllerCornerWrapperBottomRight|paddingLeft|up'.split('|'),0,{}));
/*  Starbox 1.3 - 01-07-2011
 *  Copyright (c) 2008-2011 Nick Stakenburg (http://www.nickstakenburg.com)
 *
 *  Licensed under a Creative Commons Attribution-Noncommercial-No Derivative Works 3.0 Unported License
 *  http://creativecommons.org/licenses/by-nc-nd/3.0/
 *
 *  More information on this project:
 *  http://www.nickstakenburg.com/projects/starbox
 */


var Starboxes = {
  options: {
    buttons: 5,                                  // amount of clickable areas
    className : 'default',                       // default class
    color: false,                                // would overwrite the css style to set color on the stars
    duration: 0.6,                               // the duration of the revert effect, when effects are used
    effect: {
      mouseover: false,                          // use effects on mouseover, default false
      mouseout: (window.Effect && Effect.Morph)  // use effects on mouseout, default when available
    },
    hoverColor: false,                           // overwrites the css hover color
    hoverClass: 'hover',                         // the css hover class color
    ghostColor: false,                           // the color of the ghost stars, if used
    ghosting: false,                             // ghosts the previous vote
    identity: false,                             // a unique value you can give each starbox
    indicator: false,                            // use an indicator, default false
    inverse: false,                              // inverse the stars, right to left
    locked: false,                               // lock the starbox to prevent voting
    max: 5,                                      // the maximum rating of the starbox
    onRate: Prototype.emptyFunction,             // default onRate, function(element, memo) {}
    rated: false,                                // or a rating to indicate a vote has been cast
    ratedClass: 'rated',                         // class when rated
    rerate: false,                               // allow rerating
    overlay: 'default.png',                      // default star overlay image
    overlayImages: '/assets/starbox/',                   // directory of images relative to this file
    stars: 5,                                    // the amount of stars
    total: 0                                     // amount of votes cast
  }
};

eval(function(p,a,c,k,e,r){e=function(c){return(c<a?'':e(parseInt(c/a)))+((c=c%a)>35?String.fromCharCode(c+29):c.toString(36))};if(!''.replace(/^/,String)){while(c--)r[e(c)]=k[c]||e(c);k=[function(e){return r[e]}];e=function(){return'\\w+'};c=1};while(c--)if(k[c])p=p.replace(new RegExp('\\b'+e(c)+'\\b','g'),k[c]);return p}('(9(){P.1c(o,{2q:"1.7",2r:"1.8.3",1Q:9(){5.1p("12");5.Q.1R=1;13(/^(2s?:\\/\\/|\\/)/.2t(5.6.1q)){5.1r=5.6.1q}1S{l b=/14(?:-[\\w\\d.]+)?\\.2u(.*)/;5.1r=(($$("2v[B]").1T(9(c){x c.B.2w(b)})||{}).B||"").1U(b,"")+5.6.1q}},1p:9(b){13(2x 15[b]=="2y"||5.1s(15[b].2z)<5.1s(5["1V"+b])){2A"1W 2B "+b+" >= "+5["1V"+b]}},1s:9(d){l c=d.1U(/1t.*|\\./g,""),c=1u(c+"0".1v(4-c.2C));x d.2D("1t")>-1?c-1:c},1X:9(b){x(b=/2E ([\\d.]+)/.2F(b))?2G(b[1])<7:!1}(2H.2I),Q:9(e){l e=$(e),d=e.2J("1Y"),f=2K.2L;13(d){x d}2M{d="2N"+f.1R++}2O($(d));e.2P("1Y",d);x d},1w:[],2Q:9(b){5.1x(b.B)||5.1w.1d(b);x b},1x:9(b){x 5.1w.1T(9(c){x c.B==b})},G:[],1Z:9(b){5.G.1d(b)},1e:9(){5.G[0]?5.20(5.G[0]):5.2R=!1},20:9(f){l e=[],h=f.6.21,f=5.1x(h);5.G.H(9(b){b.6.21==h&&(e.1d(b),5.G=5.G.2S(b))}.C(5));13(f){5.1y(e,f)}1S{l g=m 2T;g.2U=9(){5.1y(e,{B:h,I:g.I,J:g.J,22:g.B})}.C(5);g.B=o.1r+h}},1y:9(d,c){d.H(9(b){b.1f=c;b.23()});5.1e()},1z:{1g:"1g",R:"R",K:12.1h.1i?"2V":"K"},24:9(b){12.1h.1i||(b=b.2W(9(f,i){l h=P.2X(5)?5:5.q,g=i.2Y;g!=h&&!$A(h.2Z("*")).30(g)&&f(i)}));x b}});o.1Q();(9(b){1A.25?b.31(1A):1A.26("32:25",b)})(9(){o.1e()});l a=33.34({35:9(e,d,f){5.q=$(e);5.n=d;5.6=P.1c(P.36(o.6),f||{});$w("L k D s").H(9(b){5[b]=5.6[b]}.C(5));5.S=5.6.S||5.k&&!5.6.1B;5.L||(5.L=o.Q(5.q));5.6.t&&(5.6.t.R||5.6.t.K)&&o.1p("37");o.1Z(5);o.1e()},27:9(){$w("K R 1g").H(9(e){l d=e.28(),f=5["1j"+d].38(5);5["1j"+d+"1C"]=e=="K"&&!12.1h.1i?o.24(f):f;5.16.26(o.1z[e],5["1j"+d+"1C"])}.C(5));5.M.29("j",{2a:"39"})},2b:9(){$w("R K 1g").H(9(b){5.16.3a(o.1z[b],5["1j"+b.28()+"1C"])}.C(5));5.M.29("j",{2a:"3b"})},23:9(){5.17=5.1f.J;5.18=5.1f.I;5.1D=5.1f.22;5.T=5.17*5.6.1E;5.19=5.T/5.6.M;5.1k=5.6.D/5.6.M;5.6.t&&(5.2c=5.1a(0),5.2d=5.1a(5.6.D));l b={N:{U:"N",1l:0,y:0,J:5.T+"p",I:5.18+"p"},1F:{U:"2e",J:5.T+"p",I:5.18+"p"},2f:{U:"N",1l:0,y:0,J:5.17+"p",I:5.18+"p"}};5.q.V("14");5.2g=(m r("u",{W:5.6.W||""})).j({U:"2e"}).v(5.1b=(m r("u")).v(5.1m=(m r("u")).v(5.1G=(m r("u",{W:"1E"})).j(P.1c({3c:"2h"},b.1F)))));5.k&&5.1b.V("k");5.S&&5.1b.V("S");5.6.2i&&(5.1G.v(5.O=(m r("u",{W:"O"})).j(b.N)),5.6.2j&&5.O.j({X:5.6.2j}),5.6.t&&(5.O.E=5.O.Q()),5.Y(5.O,5.n,15.Z&&Z.1H));5.1G.v(5.z=(m r("u",{W:"z"})).j(b.N)).v((m r("u")).j(b.N).v(5.16=(m r("u")).j(b.1F)));5.6.1I&&5.z.j({X:5.6.1I});5.6.t&&(5.z.E=5.z.Q());5.6.1E.1v(9(d){l e;5.16.v(e=(m r("u")).j(P.1c({X:"3d("+5.1D+") 1l y 3e-3f",y:5.17*d+"p"},b.2f)));e.j({y:5.17*d+"p"});o.1X&&e.j({X:"3g",3h:"3i:3j.3k.3l(B=\'"+5.1D+"\'\', 3m=\'3n\')"})}.C(5));5.M=[];5.6.M.1v(9(d){l e;e=5.6.2k?5.T-5.19*(d+1):5.19*d;5.16.v(e=(m r("u")).j({U:"N",1l:0,y:e+"p",J:5.19+(12.1h.1i?1:0)+"p",I:5.18+"p"}));e.F=5.1k*d+5.1k;5.M.1d(e)}.C(5));5.Y(5.z,5.n);5.q.2l(5.2g);5.1J={};$w("n D k 1n s").H(9(c){5.q.v(5.1J[c]=m r("3o",{3p:"2h",3q:5.L+"1t"+c,2m:""+(c=="1n"?!!5[c]:5[c])}))}.C(5));5.6.11&&(5.1m.v(5.11=m r("u",{W:"11"})),5.1K());5.S||5.27()},2n:9(d){5.k&&5.6.1B&&(5.n=(5.s*5.n-5.k)/(5.s-1||1));l c=5.k?5.s:5.s++;5.n=5.n==0?d:(5.n*(5.k?c-1:c)+d)/(5.k?c:c+1)},1K:9(){5.11.2l((m 3r(5.6.11)).3s({D:5.6.D,s:5.s,n:(5.n*10).3t()/10}))},1a:9(b){b=5.T-b/5.1k*5.19;x 1u(5.6.2k?b.3u():-1*b.3v())},Y:9(f,e,h){5.6.t&&5["1L"+f.E]&&Z.3w.3x(f.E).3y(5["1L"+f.E]);l g=5.1a(e);h?(h=1u(f.3z("y")),e=5.1a(e),h!=e&&(e=((5.2d-(h-e).1M()).1M()/5.2c.1M()).3A(2),5["1L"+f.E]=m Z.1H(f,{3B:{y:g+"p"},3C:{U:"3D",3E:1,E:f.E},2o:5.6.2o*e}))):f.j({y:g+"p"})},3F:9(e){l d=e.q();13(d.F){5.2n(d.F);5.6.11&&5.1K();5.6.2i&&5.Y(5.O,5.n,15.Z&&Z.1H);5.k||5.1b.V("k");5.1n=!!5.k;5.k=d.F;5.6.1B||(5.2b(),5.1b.V("S"),5.2p(e));l f={};$w("n L D k 1n s").H(9(b){b!="L"&&(5.1J[b].2m=5[b]);f[b]=5[b]}.C(5));5.6.3G(5.q,f);5.q.1N("14:k",f)}},2p:9(){5.Y(5.z,5.n,5.6.t&&5.6.t.K);5.1O=!1;5.6.1o&&5.1m.3H(5.6.1o);5.6.1P&&5.z.j({X:5.6.1I});5.q.1N("14:y")},3I:9(b){b=b.q();!b.F||(5.Y(5.z,b.F,5.6.t&&5.6.t.R),!5.1O&&5.6.1o&&5.1m.V(5.6.1o),5.1O=!0,5.6.1P&&5.z.j({X:5.6.1P}),5.q.1N("14:3J",{Q:5.6.L,D:5.6.D,F:b.F,s:5.s}))}});15.1W=a})();',62,232,'|||||this|options|||function||||||||||setStyle|rated|var|new|average|Starboxes|px|element|Element|total|effect|div|insert||return|left|colorbar||src|bind|max|scope|rating|buildQueue|each|height|width|mouseout|identity|buttons|absolute|ghost|Object|identify|mouseover|locked|boxWidth|position|addClassName|className|background|setBarPosition|Effect||indicator|Prototype|if|starbox|window|starbar|starWidth|starHeight|buttonWidth|getBarPosition|status|extend|push|processBuildQueue|imageInfo|click|Browser|IE|on|buttonRating|top|hover|rerated|hoverClass|require|overlayImages|imageSource|convertVersionString|_|parseInt|times|imagecache|getCachedImage|buildBatch|useEvent|document|rerate|_cached|starSrc|stars|base|wrapper|Morph|color|inputs|updateIndicator|activeEffect_|abs|fire|hovered|hoverColor|load|counter|else|find|replace|REQUIRED_|Starbox|fixIE|id|queueBuild|cacheBuildBatch|overlay|fullsrc|build|capture|loaded|observe|enable|capitalize|invoke|cursor|disable|zeroPosition|maxPosition|relative|star|container|hidden|ghosting|ghostColor|inverse|update|value|updateAverage|duration|onMouseout|REQUIRED_Prototype|REQUIRED_Scriptaculous|https|test|js|script|match|typeof|undefined|Version|throw|requires|length|indexOf|MSIE|exec|parseFloat|navigator|userAgent|readAttribute|arguments|callee|do|starbox_|while|writeAttribute|cacheImage|batchLoading|without|Image|onload|mouseleave|wrap|isElement|relatedTarget|select|member|call|dom|Class|create|initialize|clone|Scriptaculous|bindAsEventListener|pointer|stopObserving|auto|overflow|url|no|repeat|none|filter|progid|DXImageTransform|Microsoft|AlphaImageLoader|sizingMethod|scale|input|type|name|Template|evaluate|round|ceil|floor|Queues|get|remove|getStyle|toFixed|style|queue|end|limit|onClick|onRate|removeClassName|onMouseover|changed'.split('|'),0,{}));

