[
  {
    "path": ".gitignore",
    "content": ".DS_Store\n.sass-cache\n*.swo\n*.swp\n"
  },
  {
    "path": ".gitmodules",
    "content": "[submodule \"extjs\"]\n\tpath = extjs\n\turl = git://github.com/steelThread/ExtJs-ToggleSlide.git\n[submodule \"compass-extension\"]\n\tpath = compass-extension\n\turl = git@github.com:tdreyno/compass-iphone-style-checkboxes.git\n[submodule \"extensions/iphone-style-checkboxes\"]\n\tpath = extensions/iphone-style-checkboxes\n\turl = git@github.com:tdreyno/compass-iphone-style-checkboxes.git\n[submodule \"libs/jquery.actual\"]\n\tpath = libs/jquery.actual\n\turl = git://github.com/dreamerslab/jquery.actual.git\n"
  },
  {
    "path": "LICENSE",
    "content": "Copyright (c) 2008 Thomas Reynolds\n\nPermission is hereby granted, free of charge, to any person obtaining\na copy of this software and associated documentation files (the\n\"Software\"), to deal in the Software without restriction, including\nwithout limitation the rights to use, copy, modify, merge, publish,\ndistribute, sublicense, and/or sell copies of the Software, and to\npermit persons to whom the Software is furnished to do so, subject to\nthe following conditions:\n\nThe above copyright notice and this permission notice shall be\nincluded in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\nEXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\nMERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\nNONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\nLIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\nOF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\nWITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n"
  },
  {
    "path": "README.rdoc",
    "content": "= iPhone Style Checkboxes\n\nTurn your checkboxes into iPhone-style binary switches.\n\nHomepage:: http://awardwinningfjords.com/2009/06/16/iphone-style-checkboxes.html\njQuery Demo:: http://jsfiddle.net/tdreyno/jye3g/\nPrototype Demo:: http://jsfiddle.net/YNMSa/2/\n\n== Dependencies\n\njQuery or Prototype + Scriptaculous\n\n== Use\n\nPlease see jquery-demo.html or prototype-demo.html for example use.\n\n== YUI\n\nThere is a YUI project which accomplishes the same effect here: http://yuilibrary.com/gallery/show/slidecheckbox\n\nLicense:: Copyright (c) 2009 Thomas Reynolds <me@tdreyno.com>, released under the MIT license"
  },
  {
    "path": "coffee/.gitignore",
    "content": "*.js"
  },
  {
    "path": "coffee/iphone-style-checkboxes.coffee",
    "content": "# iPhone-style Checkboxes Coffee plugin\n\nunless $.browser?\n  userAgent = navigator.userAgent || \"\"\n\n  jQuery.uaMatch = (ua) ->\n    ua = ua.toLowerCase()\n\n    match = /(chrome)[ \\/]([\\w.]+)/.exec( ua ) ||\n      /(webkit)[ \\/]([\\w.]+)/.exec( ua ) ||\n      /(opera)(?:.*version)?[ \\/]([\\w.]+)/.exec( ua ) ||\n      /(msie) ([\\w.]+)/.exec( ua ) ||\n      ua.indexOf(\"compatible\") < 0 && /(mozilla)(?:.*? rv:([\\w.]+))?/.exec( ua ) ||\n      []\n\n    return {\n      browser: match[ 1 ] || \"\",\n      version: match[ 2 ] || \"0\"\n    }\n\n  matched = jQuery.uaMatch( userAgent )\n\n  jQuery.browser = {}\n\n  if matched.browser\n    jQuery.browser[ matched.browser ] = true\n    jQuery.browser.version = matched.version\n\n  if jQuery.browser.webkit\n    jQuery.browser.safari = true\n\nclass iOSCheckbox\n  constructor: (elem, options) ->\n    @elem = $(elem)\n  \n    opts = $.extend({}, iOSCheckbox.defaults, options)\n    \n    # Import options into instance variables\n    for key, value of opts\n      @[key] = value\n\n    @elem.data(@dataName, this)\n\n    # Initialize the control\n    @wrapCheckboxWithDivs()\n    @attachEvents()\n    @disableTextSelection()\n\n    @calculateDimensions()\n\n  calculateDimensions: ->\n    @optionallyResize('handle') if @resizeHandle\n    @optionallyResize('container') if @resizeContainer\n    @initialPosition()\n\n  isDisabled: -> @elem.is(':disabled')\n\n  # Wrap the existing input[type=checkbox] with divs for styling and grab \n  # DOM references to the created nodes\n  wrapCheckboxWithDivs: ->\n    @elem.wrap(\"<div class='#{@containerClass}' />\")\n  \n    @container = @elem.parent()\n  \n    @offLabel  = $(\"\"\"<label class='#{@labelOffClass}'>\n                        <span>#{@uncheckedLabel}</span>\n                      </label>\"\"\").appendTo(@container)\n                     \n    @offSpan   = @offLabel.children('span')\n  \n    @onLabel   = $(\"\"\"<label class='#{@labelOnClass}'>\n                        <span>#{@checkedLabel}</span>\n                      </label>\"\"\").appendTo(@container)\n                     \n    @onSpan    = @onLabel.children('span')\n  \n    @handle    = $(\"\"\"<div class='#{@handleClass}'>\n                        <div class='#{@handleRightClass}'>\n                          <div class='#{@handleCenterClass}' />\n                        </div>\n                      </div>\"\"\").appendTo(this.container)\n  \n  # Disable IE text selection, other browsers are handled in CSS\n  disableTextSelection: ->\n    # Elements containing text should be unselectable\n    if $.browser.msie\n      $([@handle, @offLabel, @onLabel, @container]).attr(\"unselectable\", \"on\")\n\n  _getDimension: (elem, dimension) ->\n    if $.fn.actual?\n      elem.actual(dimension)\n    else\n      elem[dimension]()\n      \n  # Automatically resize the handle or container\n  optionallyResize: (mode) ->\n    onSpan = @onLabel.find('span')\n    onLabelWidth = @_getDimension(onSpan, \"width\")\n    onLabelWidth += parseInt(onSpan.css('padding-left'), 10)\n\n    offSpan = @offLabel.find('span')\n    offLabelWidth = @_getDimension(offSpan, \"width\")\n    offLabelWidth += parseInt(offSpan.css('padding-right'), 10)\n\n    if mode == \"container\"\n      newWidth = if (onLabelWidth > offLabelWidth)\n        onLabelWidth\n      else \n        offLabelWidth\n\n      newWidth += @_getDimension(@handle, \"width\") + @handleMargin\n      @container.css(width: newWidth)\n    else\n      newWidth = if (onLabelWidth > offLabelWidth)\n        onLabelWidth\n      else \n        offLabelWidth\n      @handle.css(width: newWidth)\n\n  onMouseDown: (event) ->\n    event.preventDefault()\n\n    return if @isDisabled()\n\n    x = event.pageX || event.originalEvent.changedTouches[0].pageX\n    iOSCheckbox.currentlyClicking = @handle\n    iOSCheckbox.dragStartPosition = x\n    iOSCheckbox.handleLeftOffset  = parseInt(@handle.css('left'), 10) || 0\n\n  onDragMove: (event, x) ->\n    return unless iOSCheckbox.currentlyClicking == @handle\n    \n    p = (x + iOSCheckbox.handleLeftOffset - iOSCheckbox.dragStartPosition) / @rightSide\n    p = 0 if p < 0\n    p = 1 if p > 1\n\n    newWidth = p * @rightSide\n    @handle.css(left: newWidth)\n    @onLabel.css(width: newWidth + @handleRadius)\n    @offSpan.css(marginRight: -newWidth)\n    @onSpan.css(marginLeft: -(1 - p) * @rightSide)\n\n  onDragEnd: (event, x) ->\n    return unless iOSCheckbox.currentlyClicking == @handle\n    return if @isDisabled()\n  \n    if iOSCheckbox.dragging\n      p = (x - iOSCheckbox.dragStartPosition) / @rightSide\n      @elem.prop('checked', (p >= 0.5))\n    else\n      @elem.prop('checked', !@elem.prop('checked'))\n\n    iOSCheckbox.currentlyClicking = null\n    iOSCheckbox.dragging          = null\n    @didChange()\n\n  refresh: -> @didChange() #TODO: Verify - this might fire event unnecessarily\n    \n  didChange: ->\n    @onChange?(@elem, @elem.prop('checked'))\n    \n    if @isDisabled()\n      @container.addClass(@disabledClass)\n      return false\n    else\n      @container.removeClass(@disabledClass)\n  \n    new_left = if @elem.prop('checked') then @rightSide else 0\n\n    @handle.animate(left: new_left, @duration)\n    @onLabel.animate(width: new_left + @handleRadius, @duration)\n    @offSpan.animate(marginRight: -new_left, @duration)\n    @onSpan.animate(marginLeft: new_left - @rightSide, @duration)\n  \n  attachEvents: ->\n    self = this\n    \n    localMouseMove = (event) ->\n      self.onGlobalMove.apply(self, arguments)\n      \n    localMouseUp = (event) ->\n      self.onGlobalUp.apply(self, arguments)\n      $(document).unbind 'mousemove touchmove', localMouseMove\n      $(document).unbind 'mouseup touchend', localMouseUp\n      \n    # The original checkbox value might be changed by clickig on the associated label or other means\n    # To make sure we are in sync:\n    @elem.change -> self.refresh()\n\n    # A mousedown anywhere in the control will start tracking for dragging\n    @container.bind 'mousedown touchstart', (event) ->\n      self.onMouseDown.apply(self, arguments)\n  \n      # As the mouse moves on the page, animate if we are in a drag state\n      $(document).bind 'mousemove touchmove', localMouseMove\n\n      # When the mouse comes up, leave drag state\n      $(document).bind 'mouseup touchend', localMouseUp\n    \n  # Setup the control's inital position\n  initialPosition: ->\n    containerWidth = @_getDimension(@container, \"width\")\n    @offLabel.css(width: containerWidth - @containerRadius)\n\n    offset     = @containerRadius + 1\n    offset     -= 3 if $.browser.msie and $.browser.version < 7\n    @rightSide = containerWidth - @_getDimension(@handle, \"width\") - offset\n\n    if @elem.is(':checked')\n      @handle.css(left: @rightSide)\n      @onLabel.css(width: @rightSide + @handleRadius)\n      @offSpan.css(marginRight: -@rightSide)\n    else\n      @onLabel.css(width: 0)\n      @onSpan.css(marginLeft: -@rightSide)\n    \n    @container.addClass(@disabledClass) if @isDisabled()\n\n  onGlobalMove: (event) ->\n    return unless !@isDisabled() && iOSCheckbox.currentlyClicking\n    event.preventDefault()\n\n    x = event.pageX || event.originalEvent.changedTouches[0].pageX\n\n    if (!iOSCheckbox.dragging &&\n        (Math.abs(iOSCheckbox.dragStartPosition - x) > @dragThreshold))\n      iOSCheckbox.dragging = true\n    \n    @onDragMove(event, x)\n    \n  onGlobalUp: (event) ->\n    return unless iOSCheckbox.currentlyClicking\n    event.preventDefault()\n\n    x = event.pageX || event.originalEvent.changedTouches[0].pageX\n    \n    @onDragEnd(event, x)\n    false\n\n  @defaults: \n    # Time spent during slide animation\n    duration:          200\n  \n    # Text content of \"on\" state\n    checkedLabel:      'ON'\n  \n    # Text content of \"off\" state\n    uncheckedLabel:    'OFF'\n  \n    # Automatically resize the handle to cover either label\n    resizeHandle:      true\n  \n    # Automatically resize the widget to contain the labels\n    resizeContainer:   true\n  \n    disabledClass:     'iPhoneCheckDisabled'\n    containerClass:    'iPhoneCheckContainer'\n    labelOnClass:      'iPhoneCheckLabelOn'\n    labelOffClass:     'iPhoneCheckLabelOff'\n    handleClass:       'iPhoneCheckHandle'\n    handleCenterClass: 'iPhoneCheckHandleCenter'\n    handleRightClass:  'iPhoneCheckHandleRight'\n  \n    # Pixels that must be dragged for a click to be ignored\n    dragThreshold:     5\n    \n    handleMargin:      15\n    handleRadius:      4\n    containerRadius:   5\n    \n    dataName:          \"iphoneStyle\"\n    \n    onChange: ->\n\n$.iphoneStyle = @iOSCheckbox = iOSCheckbox\n\n$.fn.iphoneStyle = (args...) ->\n  dataName = args[0]?.dataName ? iOSCheckbox.defaults.dataName\n\n  for checkbox in @filter(':checkbox')\n    existingControl = $(checkbox).data(dataName)\n    if existingControl?\n      [method, params...] = args\n      existingControl[method]?.apply(existingControl, params)\n    else\n      new iOSCheckbox(checkbox, args[0])\n\n  this\n  \n$.fn.iOSCheckbox = (options={}) ->\n  # iOS5 style only supports circular handle\n  opts = $.extend({}, options, {\n    resizeHandle:      false\n    disabledClass:     'iOSCheckDisabled'\n    containerClass:    'iOSCheckContainer'\n    labelOnClass:      'iOSCheckLabelOn'\n    labelOffClass:     'iOSCheckLabelOff'\n    handleClass:       'iOSCheckHandle'\n    handleCenterClass: 'iOSCheckHandleCenter'\n    handleRightClass:  'iOSCheckHandleRight'\n    dataName:          'iOSCheckbox'\n  })\n  \n  this.iphoneStyle(opts)\n"
  },
  {
    "path": "coffee-demo.html",
    "content": "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\"\n  \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">\n<html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"en\" lang=\"en\">\n<head>\n  <meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\"/>\n  <title>iPhone Style Radios Demo</title>\n  <script src=\"http://ajax.googleapis.com/ajax/libs/jquery/1.6.4/jquery.min.js\" type=\"text/javascript\"></script>\n  <script src=\"libs/jquery.actual/jquery.actual.js\"></script>\n  <link rel=\"stylesheet\" href=\"style.css\" type=\"text/css\" media=\"screen\" />\n  <style type=\"text/css\">\n    body {\n      padding: 10px; }\n    th {\n      text-align: right;\n      padding: 4px;\n      padding-right: 15px;\n      vertical-align: top; }\n    .css_sized_container .iPhoneCheckContainer {\n      width: 250px; }\n  </style>\n  <script src=\"coffee/iphone-style-checkboxes.js\"></script>\n  <script type=\"text/javascript\">\n    $(window).load(function() {\n      var onchange_checkbox;\n      ($('.on_off :checkbox')).iphoneStyle();\n      ($('.disabled :checkbox')).iphoneStyle();\n      ($('.ios5 :checkbox')).iOSCheckbox();\n      ($('.css_sized_container :checkbox')).iphoneStyle({\n        resizeContainer: false,\n        resizeHandle: false\n      });\n      ($('.long_tiny :checkbox')).iphoneStyle({\n        checkedLabel: 'Very Long Text',\n        uncheckedLabel: 'Tiny'\n      });\n      onchange_checkbox = ($('.onchange :checkbox')).iphoneStyle({\n        onChange: function(elem, value) { \n          $('span#status').html(value.toString());\n        }\n      });\n      \n      setInterval(function() {\n        onchange_checkbox.prop('checked', !onchange_checkbox.is(':checked')).iphoneStyle(\"refresh\");\n        return\n      }, 2500);\n    });\n  </script>\n  \n</head>\n<body>\n  <table>\n    <tr class=\"disabled\">\n      <th><label for=\"disabled\">Disabled</label></th>\n      <td>\n        <input type=\"checkbox\" id=\"disabled\" disabled=\"disabled\" />\n      </td>\n    </tr>\n    <tr class=\"on_off\">\n      <th><label for=\"on_off\">Default</label></th>\n      <td>\n        <input type=\"checkbox\" id=\"on_off\" />\n      </td>\n    </tr>\n    <tr class=\"ios5\">\n      <th><label for=\"ios5\">iOS 5</label></th>\n      <td>\n        <input type=\"checkbox\" id=\"ios5\" />\n      </td>\n    </tr>\n    <tr class=\"on_off\">\n      <th><label for=\"on_off_on\">Default On</label></th>\n      <td>\n        <input type=\"checkbox\" checked=\"checked\" id=\"on_off_on\"/>\n      </td>\n    </tr>\n    <tr class=\"css_sized_container\">\n      <th><label for=\"css_sized_container\">CSS Sized Container and Handle</label></th>\n      <td>\n        <input type=\"checkbox\" id=\"css_sized_container\" />\n      </td>\n    </tr>\n    <tr class=\"long_tiny\">\n      <th><label class=\"left\" for=\"long_tiny\">Long and short labels</label></th>\n      <td>\n        <input type=\"checkbox\" id=\"long_tiny\" />\n      </td>\n    </tr>\n    <tr class=\"onchange\">\n      <th>Event tracking</th>\n      <td>\n        <input type=\"checkbox\" id=\"onchange\" />\n        <p>Checkbox status is <strong><span id=\"status\">...</span></strong>.</p>\n      </td>\n    </tr>\n  </table>\n\n</body>\n</html>\n"
  },
  {
    "path": "compile",
    "content": "coffee -o jquery -c coffee/iphone-style-checkboxes.coffee\npushd rails\nrake update\npopd\n"
  },
  {
    "path": "component.json",
    "content": "{\n    \"name\"         : \"iphone-style-checkboxes\",\n    \"version\"      : \"1.0.0\",\n    \"description\"  : \"Turn your checkboxes into iOS-style binary switches\",\n    \"main\"         : [ \"jquery/iphone-style-checkboxes.js\" ],\n    \"homepage\"     : \"https://github.com/tdreyno/iphone-style-checkboxes\",\n    \"dependencies\" : {\n        \"jquery\" : \">= 1.5.x\"\n    },\n    \"keywords\"     : [\n        \"iphone\",\n        \"ios\",\n        \"input\",\n        \"form\",\n        \"checkbox\"\n    ],\n    \"author\" : {\n        \"name\" : \"Thomas Reynolds\",\n        \"web\"  : \"https://github.com/tdreyno\"\n    },\n    \"license\": \"http://opensource.org/licenses/MIT\"\n}\n"
  },
  {
    "path": "config.rb",
    "content": "# Require any additional compass plugins here.\n# Set this to the root of your project when deployed:\nhttp_path = \"/\"\ncss_dir = \"\"\nsass_dir = \"sass\"\nimages_dir = \"images\"\njavascripts_dir = \"javascripts\"\noutput_style = :nested\n# To enable relative paths to assets via compass helper functions. Uncomment:\nrelative_assets = true\nline_comments = false\n"
  },
  {
    "path": "jquery/iphone-style-checkboxes.js",
    "content": "// Generated by CoffeeScript 1.6.2\n(function() {\n  var iOSCheckbox, matched, userAgent,\n    __slice = [].slice;\n\n  if ($.browser == null) {\n    userAgent = navigator.userAgent || \"\";\n    jQuery.uaMatch = function(ua) {\n      var match;\n\n      ua = ua.toLowerCase();\n      match = /(chrome)[ \\/]([\\w.]+)/.exec(ua) || /(webkit)[ \\/]([\\w.]+)/.exec(ua) || /(opera)(?:.*version)?[ \\/]([\\w.]+)/.exec(ua) || /(msie) ([\\w.]+)/.exec(ua) || ua.indexOf(\"compatible\") < 0 && /(mozilla)(?:.*? rv:([\\w.]+))?/.exec(ua) || [];\n      return {\n        browser: match[1] || \"\",\n        version: match[2] || \"0\"\n      };\n    };\n    matched = jQuery.uaMatch(userAgent);\n    jQuery.browser = {};\n    if (matched.browser) {\n      jQuery.browser[matched.browser] = true;\n      jQuery.browser.version = matched.version;\n    }\n    if (jQuery.browser.webkit) {\n      jQuery.browser.safari = true;\n    }\n  }\n\n  iOSCheckbox = (function() {\n    function iOSCheckbox(elem, options) {\n      var key, opts, value;\n\n      this.elem = $(elem);\n      opts = $.extend({}, iOSCheckbox.defaults, options);\n      for (key in opts) {\n        value = opts[key];\n        this[key] = value;\n      }\n      this.elem.data(this.dataName, this);\n      this.wrapCheckboxWithDivs();\n      this.attachEvents();\n      this.disableTextSelection();\n      this.calculateDimensions();\n    }\n\n    iOSCheckbox.prototype.calculateDimensions = function() {\n      if (this.resizeHandle) {\n        this.optionallyResize('handle');\n      }\n      if (this.resizeContainer) {\n        this.optionallyResize('container');\n      }\n      return this.initialPosition();\n    };\n\n    iOSCheckbox.prototype.isDisabled = function() {\n      return this.elem.is(':disabled');\n    };\n\n    iOSCheckbox.prototype.wrapCheckboxWithDivs = function() {\n      this.elem.wrap(\"<div class='\" + this.containerClass + \"' />\");\n      this.container = this.elem.parent();\n      this.offLabel = $(\"<label class='\" + this.labelOffClass + \"'>\\n  <span>\" + this.uncheckedLabel + \"</span>\\n</label>\").appendTo(this.container);\n      this.offSpan = this.offLabel.children('span');\n      this.onLabel = $(\"<label class='\" + this.labelOnClass + \"'>\\n  <span>\" + this.checkedLabel + \"</span>\\n</label>\").appendTo(this.container);\n      this.onSpan = this.onLabel.children('span');\n      return this.handle = $(\"<div class='\" + this.handleClass + \"'>\\n  <div class='\" + this.handleRightClass + \"'>\\n    <div class='\" + this.handleCenterClass + \"' />\\n  </div>\\n</div>\").appendTo(this.container);\n    };\n\n    iOSCheckbox.prototype.disableTextSelection = function() {\n      if ($.browser.msie) {\n        return $([this.handle, this.offLabel, this.onLabel, this.container]).attr(\"unselectable\", \"on\");\n      }\n    };\n\n    iOSCheckbox.prototype._getDimension = function(elem, dimension) {\n      if ($.fn.actual != null) {\n        return elem.actual(dimension);\n      } else {\n        return elem[dimension]();\n      }\n    };\n\n    iOSCheckbox.prototype.optionallyResize = function(mode) {\n      var newWidth, offLabelWidth, offSpan, onLabelWidth, onSpan;\n\n      onSpan = this.onLabel.find('span');\n      onLabelWidth = this._getDimension(onSpan, \"width\");\n      onLabelWidth += parseInt(onSpan.css('padding-left'), 10);\n      offSpan = this.offLabel.find('span');\n      offLabelWidth = this._getDimension(offSpan, \"width\");\n      offLabelWidth += parseInt(offSpan.css('padding-right'), 10);\n      if (mode === \"container\") {\n        newWidth = onLabelWidth > offLabelWidth ? onLabelWidth : offLabelWidth;\n        newWidth += this._getDimension(this.handle, \"width\") + this.handleMargin;\n        return this.container.css({\n          width: newWidth\n        });\n      } else {\n        newWidth = onLabelWidth > offLabelWidth ? onLabelWidth : offLabelWidth;\n        return this.handle.css({\n          width: newWidth\n        });\n      }\n    };\n\n    iOSCheckbox.prototype.onMouseDown = function(event) {\n      var x;\n\n      event.preventDefault();\n      if (this.isDisabled()) {\n        return;\n      }\n      x = event.pageX || event.originalEvent.changedTouches[0].pageX;\n      iOSCheckbox.currentlyClicking = this.handle;\n      iOSCheckbox.dragStartPosition = x;\n      return iOSCheckbox.handleLeftOffset = parseInt(this.handle.css('left'), 10) || 0;\n    };\n\n    iOSCheckbox.prototype.onDragMove = function(event, x) {\n      var newWidth, p;\n\n      if (iOSCheckbox.currentlyClicking !== this.handle) {\n        return;\n      }\n      p = (x + iOSCheckbox.handleLeftOffset - iOSCheckbox.dragStartPosition) / this.rightSide;\n      if (p < 0) {\n        p = 0;\n      }\n      if (p > 1) {\n        p = 1;\n      }\n      newWidth = p * this.rightSide;\n      this.handle.css({\n        left: newWidth\n      });\n      this.onLabel.css({\n        width: newWidth + this.handleRadius\n      });\n      this.offSpan.css({\n        marginRight: -newWidth\n      });\n      return this.onSpan.css({\n        marginLeft: -(1 - p) * this.rightSide\n      });\n    };\n\n    iOSCheckbox.prototype.onDragEnd = function(event, x) {\n      var p;\n\n      if (iOSCheckbox.currentlyClicking !== this.handle) {\n        return;\n      }\n      if (this.isDisabled()) {\n        return;\n      }\n      if (iOSCheckbox.dragging) {\n        p = (x - iOSCheckbox.dragStartPosition) / this.rightSide;\n        this.elem.prop('checked', p >= 0.5).change();\n      } else {\n        this.elem.prop('checked', !this.elem.prop('checked')).change();\n      }\n      iOSCheckbox.currentlyClicking = null;\n      iOSCheckbox.dragging = null;\n      return this.didChange();\n    };\n\n    iOSCheckbox.prototype.refresh = function() {\n      return this.didChange();\n    };\n\n    iOSCheckbox.prototype.didChange = function() {\n      var new_left;\n\n      if (typeof this.onChange === \"function\") {\n        this.onChange(this.elem, this.elem.prop('checked'));\n      }\n      if (this.isDisabled()) {\n        this.container.addClass(this.disabledClass);\n        return false;\n      } else {\n        this.container.removeClass(this.disabledClass);\n      }\n      new_left = this.elem.prop('checked') ? this.rightSide : 0;\n      this.handle.animate({\n        left: new_left\n      }, this.duration);\n      this.onLabel.animate({\n        width: new_left + this.handleRadius\n      }, this.duration);\n      this.offSpan.animate({\n        marginRight: -new_left\n      }, this.duration);\n      return this.onSpan.animate({\n        marginLeft: new_left - this.rightSide\n      }, this.duration);\n    };\n\n    iOSCheckbox.prototype.attachEvents = function() {\n      var localMouseMove, localMouseUp, self;\n\n      self = this;\n      localMouseMove = function(event) {\n        return self.onGlobalMove.apply(self, arguments);\n      };\n      localMouseUp = function(event) {\n        self.onGlobalUp.apply(self, arguments);\n        $(document).unbind('mousemove touchmove', localMouseMove);\n        return $(document).unbind('mouseup touchend', localMouseUp);\n      };\n      this.elem.change(function() {\n        return self.refresh();\n      });\n      return this.container.bind('mousedown touchstart', function(event) {\n        self.onMouseDown.apply(self, arguments);\n        $(document).bind('mousemove touchmove', localMouseMove);\n        return $(document).bind('mouseup touchend', localMouseUp);\n      });\n    };\n\n    iOSCheckbox.prototype.initialPosition = function() {\n      var containerWidth, offset;\n\n      containerWidth = this._getDimension(this.container, \"width\");\n      this.offLabel.css({\n        width: containerWidth - this.containerRadius\n      });\n      offset = this.containerRadius + 1;\n      if ($.browser.msie && $.browser.version < 7) {\n        offset -= 3;\n      }\n      this.rightSide = containerWidth - this._getDimension(this.handle, \"width\") - offset;\n      if (this.elem.is(':checked')) {\n        this.handle.css({\n          left: this.rightSide\n        });\n        this.onLabel.css({\n          width: this.rightSide + this.handleRadius\n        });\n        this.offSpan.css({\n          marginRight: -this.rightSide\n        });\n      } else {\n        this.onLabel.css({\n          width: 0\n        });\n        this.onSpan.css({\n          marginLeft: -this.rightSide\n        });\n      }\n      if (this.isDisabled()) {\n        return this.container.addClass(this.disabledClass);\n      }\n    };\n\n    iOSCheckbox.prototype.onGlobalMove = function(event) {\n      var x;\n\n      if (!(!this.isDisabled() && iOSCheckbox.currentlyClicking)) {\n        return;\n      }\n      event.preventDefault();\n      x = event.pageX || event.originalEvent.changedTouches[0].pageX;\n      if (!iOSCheckbox.dragging && (Math.abs(iOSCheckbox.dragStartPosition - x) > this.dragThreshold)) {\n        iOSCheckbox.dragging = true;\n      }\n      return this.onDragMove(event, x);\n    };\n\n    iOSCheckbox.prototype.onGlobalUp = function(event) {\n      var x;\n\n      if (!iOSCheckbox.currentlyClicking) {\n        return;\n      }\n      event.preventDefault();\n      x = event.pageX || event.originalEvent.changedTouches[0].pageX;\n      this.onDragEnd(event, x);\n      return false;\n    };\n\n    iOSCheckbox.defaults = {\n      duration: 200,\n      checkedLabel: 'ON',\n      uncheckedLabel: 'OFF',\n      resizeHandle: true,\n      resizeContainer: true,\n      disabledClass: 'iPhoneCheckDisabled',\n      containerClass: 'iPhoneCheckContainer',\n      labelOnClass: 'iPhoneCheckLabelOn',\n      labelOffClass: 'iPhoneCheckLabelOff',\n      handleClass: 'iPhoneCheckHandle',\n      handleCenterClass: 'iPhoneCheckHandleCenter',\n      handleRightClass: 'iPhoneCheckHandleRight',\n      dragThreshold: 5,\n      handleMargin: 15,\n      handleRadius: 4,\n      containerRadius: 5,\n      dataName: \"iphoneStyle\",\n      onChange: function() {}\n    };\n\n    return iOSCheckbox;\n\n  })();\n\n  $.iphoneStyle = this.iOSCheckbox = iOSCheckbox;\n\n  $.fn.iphoneStyle = function() {\n    var args, checkbox, dataName, existingControl, method, params, _i, _len, _ref, _ref1, _ref2, _ref3;\n\n    args = 1 <= arguments.length ? __slice.call(arguments, 0) : [];\n    dataName = (_ref = (_ref1 = args[0]) != null ? _ref1.dataName : void 0) != null ? _ref : iOSCheckbox.defaults.dataName;\n    _ref2 = this.filter(':checkbox');\n    for (_i = 0, _len = _ref2.length; _i < _len; _i++) {\n      checkbox = _ref2[_i];\n      existingControl = $(checkbox).data(dataName);\n      if (existingControl != null) {\n        method = args[0], params = 2 <= args.length ? __slice.call(args, 1) : [];\n        if ((_ref3 = existingControl[method]) != null) {\n          _ref3.apply(existingControl, params);\n        }\n      } else {\n        new iOSCheckbox(checkbox, args[0]);\n      }\n    }\n    return this;\n  };\n\n  $.fn.iOSCheckbox = function(options) {\n    var opts;\n\n    if (options == null) {\n      options = {};\n    }\n    opts = $.extend({}, options, {\n      resizeHandle: false,\n      disabledClass: 'iOSCheckDisabled',\n      containerClass: 'iOSCheckContainer',\n      labelOnClass: 'iOSCheckLabelOn',\n      labelOffClass: 'iOSCheckLabelOff',\n      handleClass: 'iOSCheckHandle',\n      handleCenterClass: 'iOSCheckHandleCenter',\n      handleRightClass: 'iOSCheckHandleRight',\n      dataName: 'iOSCheckbox'\n    });\n    return this.iphoneStyle(opts);\n  };\n\n}).call(this);\n"
  },
  {
    "path": "jquery-demo.html",
    "content": "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\"\n  \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">\n<html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"en\" lang=\"en\">\n<head>\n  <meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\"/>\n  <title>iPhone Style Radios Demo</title>\n  <script src=\"http://ajax.googleapis.com/ajax/libs/jquery/1.9.0/jquery.min.js\" type=\"text/javascript\"></script>\n  <script src=\"jquery/iphone-style-checkboxes.js\" type=\"text/javascript\" charset=\"utf-8\"></script>\n  <link rel=\"stylesheet\" href=\"style.css\" type=\"text/css\" media=\"screen\" charset=\"utf-8\" />\n  <style type=\"text/css\">\n    body {\n      padding: 10px; }\n    th {\n      text-align: right;\n      padding: 4px;\n      padding-right: 15px;\n      vertical-align: top; }\n    .css_sized_container .iPhoneCheckContainer {\n      width: 250px; }\n  </style>\n\n  <script type=\"text/javascript\" charset=\"utf-8\">\n    $(window).load(function() {\n      $('.on_off :checkbox').iphoneStyle();\n      $('.disabled :checkbox').iphoneStyle();\n      $('.css_sized_container :checkbox').iphoneStyle({ resizeContainer: false, resizeHandle: false });\n      $('.long_tiny :checkbox').iphoneStyle({ checkedLabel: 'Very Long Text', uncheckedLabel: 'Tiny' });\n      \n      var onchange_checkbox = ($('.onchange :checkbox')).iphoneStyle({\n        onChange: function(elem, value) { \n          $('span#status').html(value.toString());\n        }\n      });\n      \n      setInterval(function() {\n        onchange_checkbox.prop('checked', !onchange_checkbox.is(':checked')).iphoneStyle(\"refresh\");\n        return\n      }, 2500);\n    });\n  </script>\n  \n</head>\n<body>\n  <table>\n    <tr class=\"disabled\">\n      <th><label for=\"disabled\">Disabled</label></th>\n      <td>\n        <input type=\"checkbox\" id=\"disabled\" disabled=\"disabled\" />\n      </td>\n    </tr>\n    <tr class=\"on_off\">\n      <th><label for=\"on_off\">Default</label></th>\n      <td>\n        <input type=\"checkbox\" id=\"on_off\" />\n      </td>\n    </tr>\n    <tr class=\"on_off\">\n      <th><label for=\"on_off_on\">Default On</label></th>\n      <td>\n        <input type=\"checkbox\" checked=\"checked\" id=\"on_off_on\"/>\n      </td>\n    </tr>\n    <tr class=\"css_sized_container\">\n      <th><label for=\"css_sized_container\">CSS Sized Container and Handle</label></th>\n      <td>\n        <input type=\"checkbox\" id=\"css_sized_container\" />\n      </td>\n    </tr>\n    <tr class=\"long_tiny\">\n      <th><label class=\"left\" for=\"long_tiny\">Long and short labels</label></th>\n      <td>\n        <input type=\"checkbox\" id=\"long_tiny\" />\n      </td>\n    </tr>\n    <tr class=\"onchange\">\n      <th>Event tracking</th>\n      <td>\n        <input type=\"checkbox\" id=\"onchange\" />\n        <p>Checkbox status is <strong><span id=\"status\">...</span></strong>.</p>\n      </td>\n    </tr>\n  </table>\n\n</body>\n</html>\n"
  },
  {
    "path": "package.json",
    "content": "{\n    \"name\": \"iphone-style-checkboxes\",\n    \"version\": \"1.0\",\n    \"title\": \"iPhone-Style Checkboxes\",\n    \"author\": {\n        \"name\": \"Thomas Reynolds\",\n        \"url\": \"https://github.com/tdreyno\"\n    },\n    \"licenses\": [\n        {\n            \"type\": \"MIT\",\n            \"url\": \"LICENSE\"\n        }\n    ],\n    \"dependencies\": {\n        \"jquery\": \"1\"\n    },\n    \"description\": \"Converts normal checkbox inputs into iPhone-style sliders\",\n    \"keywords\": [\n        \"iphone\",\n        \"ios\",\n        \"input\",\n        \"form\",\n        \"checkbox\"\n    ],\n    \"homepage\": \"https://github.com/tdreyno/iphone-style-checkboxes\",\n    \"contributors\": [\n    ],\n    \"files\": [\n        \"jquery/iphone-style-checkboxes.js\"\n    ]\n}"
  },
  {
    "path": "prototype/builder.js",
    "content": "// script.aculo.us builder.js v1.8.3, Thu Oct 08 11:23:33 +0200 2009\n\n// Copyright (c) 2005-2009 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)\n//\n// script.aculo.us is freely distributable under the terms of an MIT-style license.\n// For details, see the script.aculo.us web site: http://script.aculo.us/\n\nvar Builder = {\n  NODEMAP: {\n    AREA: 'map',\n    CAPTION: 'table',\n    COL: 'table',\n    COLGROUP: 'table',\n    LEGEND: 'fieldset',\n    OPTGROUP: 'select',\n    OPTION: 'select',\n    PARAM: 'object',\n    TBODY: 'table',\n    TD: 'table',\n    TFOOT: 'table',\n    TH: 'table',\n    THEAD: 'table',\n    TR: 'table'\n  },\n  // note: For Firefox < 1.5, OPTION and OPTGROUP tags are currently broken,\n  //       due to a Firefox bug\n  node: function(elementName) {\n    elementName = elementName.toUpperCase();\n\n    // try innerHTML approach\n    var parentTag = this.NODEMAP[elementName] || 'div';\n    var parentElement = document.createElement(parentTag);\n    try { // prevent IE \"feature\": http://dev.rubyonrails.org/ticket/2707\n      parentElement.innerHTML = \"<\" + elementName + \"></\" + elementName + \">\";\n    } catch(e) {}\n    var element = parentElement.firstChild || null;\n\n    // see if browser added wrapping tags\n    if(element && (element.tagName.toUpperCase() != elementName))\n      element = element.getElementsByTagName(elementName)[0];\n\n    // fallback to createElement approach\n    if(!element) element = document.createElement(elementName);\n\n    // abort if nothing could be created\n    if(!element) return;\n\n    // attributes (or text)\n    if(arguments[1])\n      if(this._isStringOrNumber(arguments[1]) ||\n        (arguments[1] instanceof Array) ||\n        arguments[1].tagName) {\n          this._children(element, arguments[1]);\n        } else {\n          var attrs = this._attributes(arguments[1]);\n          if(attrs.length) {\n            try { // prevent IE \"feature\": http://dev.rubyonrails.org/ticket/2707\n              parentElement.innerHTML = \"<\" +elementName + \" \" +\n                attrs + \"></\" + elementName + \">\";\n            } catch(e) {}\n            element = parentElement.firstChild || null;\n            // workaround firefox 1.0.X bug\n            if(!element) {\n              element = document.createElement(elementName);\n              for(attr in arguments[1])\n                element[attr == 'class' ? 'className' : attr] = arguments[1][attr];\n            }\n            if(element.tagName.toUpperCase() != elementName)\n              element = parentElement.getElementsByTagName(elementName)[0];\n          }\n        }\n\n    // text, or array of children\n    if(arguments[2])\n      this._children(element, arguments[2]);\n\n     return $(element);\n  },\n  _text: function(text) {\n     return document.createTextNode(text);\n  },\n\n  ATTR_MAP: {\n    'className': 'class',\n    'htmlFor': 'for'\n  },\n\n  _attributes: function(attributes) {\n    var attrs = [];\n    for(attribute in attributes)\n      attrs.push((attribute in this.ATTR_MAP ? this.ATTR_MAP[attribute] : attribute) +\n          '=\"' + attributes[attribute].toString().escapeHTML().gsub(/\"/,'&quot;') + '\"');\n    return attrs.join(\" \");\n  },\n  _children: function(element, children) {\n    if(children.tagName) {\n      element.appendChild(children);\n      return;\n    }\n    if(typeof children=='object') { // array can hold nodes and text\n      children.flatten().each( function(e) {\n        if(typeof e=='object')\n          element.appendChild(e);\n        else\n          if(Builder._isStringOrNumber(e))\n            element.appendChild(Builder._text(e));\n      });\n    } else\n      if(Builder._isStringOrNumber(children))\n        element.appendChild(Builder._text(children));\n  },\n  _isStringOrNumber: function(param) {\n    return(typeof param=='string' || typeof param=='number');\n  },\n  build: function(html) {\n    var element = this.node('div');\n    $(element).update(html.strip());\n    return element.down();\n  },\n  dump: function(scope) {\n    if(typeof scope != 'object' && typeof scope != 'function') scope = window; //global scope\n\n    var tags = (\"A ABBR ACRONYM ADDRESS APPLET AREA B BASE BASEFONT BDO BIG BLOCKQUOTE BODY \" +\n      \"BR BUTTON CAPTION CENTER CITE CODE COL COLGROUP DD DEL DFN DIR DIV DL DT EM FIELDSET \" +\n      \"FONT FORM FRAME FRAMESET H1 H2 H3 H4 H5 H6 HEAD HR HTML I IFRAME IMG INPUT INS ISINDEX \"+\n      \"KBD LABEL LEGEND LI LINK MAP MENU META NOFRAMES NOSCRIPT OBJECT OL OPTGROUP OPTION P \"+\n      \"PARAM PRE Q S SAMP SCRIPT SELECT SMALL SPAN STRIKE STRONG STYLE SUB SUP TABLE TBODY TD \"+\n      \"TEXTAREA TFOOT TH THEAD TITLE TR TT U UL VAR\").split(/\\s+/);\n\n    tags.each( function(tag){\n      scope[tag] = function() {\n        return Builder.node.apply(Builder, [tag].concat($A(arguments)));\n      };\n    });\n  }\n};"
  },
  {
    "path": "prototype/controls.js",
    "content": "// script.aculo.us controls.js v1.8.3, Thu Oct 08 11:23:33 +0200 2009\n\n// Copyright (c) 2005-2009 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)\n//           (c) 2005-2009 Ivan Krstic (http://blogs.law.harvard.edu/ivan)\n//           (c) 2005-2009 Jon Tirsen (http://www.tirsen.com)\n// Contributors:\n//  Richard Livsey\n//  Rahul Bhargava\n//  Rob Wills\n//\n// script.aculo.us is freely distributable under the terms of an MIT-style license.\n// For details, see the script.aculo.us web site: http://script.aculo.us/\n\n// Autocompleter.Base handles all the autocompletion functionality\n// that's independent of the data source for autocompletion. This\n// includes drawing the autocompletion menu, observing keyboard\n// and mouse events, and similar.\n//\n// Specific autocompleters need to provide, at the very least,\n// a getUpdatedChoices function that will be invoked every time\n// the text inside the monitored textbox changes. This method\n// should get the text for which to provide autocompletion by\n// invoking this.getToken(), NOT by directly accessing\n// this.element.value. This is to allow incremental tokenized\n// autocompletion. Specific auto-completion logic (AJAX, etc)\n// belongs in getUpdatedChoices.\n//\n// Tokenized incremental autocompletion is enabled automatically\n// when an autocompleter is instantiated with the 'tokens' option\n// in the options parameter, e.g.:\n// new Ajax.Autocompleter('id','upd', '/url/', { tokens: ',' });\n// will incrementally autocomplete with a comma as the token.\n// Additionally, ',' in the above example can be replaced with\n// a token array, e.g. { tokens: [',', '\\n'] } which\n// enables autocompletion on multiple tokens. This is most\n// useful when one of the tokens is \\n (a newline), as it\n// allows smart autocompletion after linebreaks.\n\nif(typeof Effect == 'undefined')\n  throw(\"controls.js requires including script.aculo.us' effects.js library\");\n\nvar Autocompleter = { };\nAutocompleter.Base = Class.create({\n  baseInitialize: function(element, update, options) {\n    element          = $(element);\n    this.element     = element;\n    this.update      = $(update);\n    this.hasFocus    = false;\n    this.changed     = false;\n    this.active      = false;\n    this.index       = 0;\n    this.entryCount  = 0;\n    this.oldElementValue = this.element.value;\n\n    if(this.setOptions)\n      this.setOptions(options);\n    else\n      this.options = options || { };\n\n    this.options.paramName    = this.options.paramName || this.element.name;\n    this.options.tokens       = this.options.tokens || [];\n    this.options.frequency    = this.options.frequency || 0.4;\n    this.options.minChars     = this.options.minChars || 1;\n    this.options.onShow       = this.options.onShow ||\n      function(element, update){\n        if(!update.style.position || update.style.position=='absolute') {\n          update.style.position = 'absolute';\n          Position.clone(element, update, {\n            setHeight: false,\n            offsetTop: element.offsetHeight\n          });\n        }\n        Effect.Appear(update,{duration:0.15});\n      };\n    this.options.onHide = this.options.onHide ||\n      function(element, update){ new Effect.Fade(update,{duration:0.15}) };\n\n    if(typeof(this.options.tokens) == 'string')\n      this.options.tokens = new Array(this.options.tokens);\n    // Force carriage returns as token delimiters anyway\n    if (!this.options.tokens.include('\\n'))\n      this.options.tokens.push('\\n');\n\n    this.observer = null;\n\n    this.element.setAttribute('autocomplete','off');\n\n    Element.hide(this.update);\n\n    Event.observe(this.element, 'blur', this.onBlur.bindAsEventListener(this));\n    Event.observe(this.element, 'keydown', this.onKeyPress.bindAsEventListener(this));\n  },\n\n  show: function() {\n    if(Element.getStyle(this.update, 'display')=='none') this.options.onShow(this.element, this.update);\n    if(!this.iefix &&\n      (Prototype.Browser.IE) &&\n      (Element.getStyle(this.update, 'position')=='absolute')) {\n      new Insertion.After(this.update,\n       '<iframe id=\"' + this.update.id + '_iefix\" '+\n       'style=\"display:none;position:absolute;filter:progid:DXImageTransform.Microsoft.Alpha(opacity=0);\" ' +\n       'src=\"javascript:false;\" frameborder=\"0\" scrolling=\"no\"></iframe>');\n      this.iefix = $(this.update.id+'_iefix');\n    }\n    if(this.iefix) setTimeout(this.fixIEOverlapping.bind(this), 50);\n  },\n\n  fixIEOverlapping: function() {\n    Position.clone(this.update, this.iefix, {setTop:(!this.update.style.height)});\n    this.iefix.style.zIndex = 1;\n    this.update.style.zIndex = 2;\n    Element.show(this.iefix);\n  },\n\n  hide: function() {\n    this.stopIndicator();\n    if(Element.getStyle(this.update, 'display')!='none') this.options.onHide(this.element, this.update);\n    if(this.iefix) Element.hide(this.iefix);\n  },\n\n  startIndicator: function() {\n    if(this.options.indicator) Element.show(this.options.indicator);\n  },\n\n  stopIndicator: function() {\n    if(this.options.indicator) Element.hide(this.options.indicator);\n  },\n\n  onKeyPress: function(event) {\n    if(this.active)\n      switch(event.keyCode) {\n       case Event.KEY_TAB:\n       case Event.KEY_RETURN:\n         this.selectEntry();\n         Event.stop(event);\n       case Event.KEY_ESC:\n         this.hide();\n         this.active = false;\n         Event.stop(event);\n         return;\n       case Event.KEY_LEFT:\n       case Event.KEY_RIGHT:\n         return;\n       case Event.KEY_UP:\n         this.markPrevious();\n         this.render();\n         Event.stop(event);\n         return;\n       case Event.KEY_DOWN:\n         this.markNext();\n         this.render();\n         Event.stop(event);\n         return;\n      }\n     else\n       if(event.keyCode==Event.KEY_TAB || event.keyCode==Event.KEY_RETURN ||\n         (Prototype.Browser.WebKit > 0 && event.keyCode == 0)) return;\n\n    this.changed = true;\n    this.hasFocus = true;\n\n    if(this.observer) clearTimeout(this.observer);\n      this.observer =\n        setTimeout(this.onObserverEvent.bind(this), this.options.frequency*1000);\n  },\n\n  activate: function() {\n    this.changed = false;\n    this.hasFocus = true;\n    this.getUpdatedChoices();\n  },\n\n  onHover: function(event) {\n    var element = Event.findElement(event, 'LI');\n    if(this.index != element.autocompleteIndex)\n    {\n        this.index = element.autocompleteIndex;\n        this.render();\n    }\n    Event.stop(event);\n  },\n\n  onClick: function(event) {\n    var element = Event.findElement(event, 'LI');\n    this.index = element.autocompleteIndex;\n    this.selectEntry();\n    this.hide();\n  },\n\n  onBlur: function(event) {\n    // needed to make click events working\n    setTimeout(this.hide.bind(this), 250);\n    this.hasFocus = false;\n    this.active = false;\n  },\n\n  render: function() {\n    if(this.entryCount > 0) {\n      for (var i = 0; i < this.entryCount; i++)\n        this.index==i ?\n          Element.addClassName(this.getEntry(i),\"selected\") :\n          Element.removeClassName(this.getEntry(i),\"selected\");\n      if(this.hasFocus) {\n        this.show();\n        this.active = true;\n      }\n    } else {\n      this.active = false;\n      this.hide();\n    }\n  },\n\n  markPrevious: function() {\n    if(this.index > 0) this.index--;\n      else this.index = this.entryCount-1;\n    this.getEntry(this.index).scrollIntoView(true);\n  },\n\n  markNext: function() {\n    if(this.index < this.entryCount-1) this.index++;\n      else this.index = 0;\n    this.getEntry(this.index).scrollIntoView(false);\n  },\n\n  getEntry: function(index) {\n    return this.update.firstChild.childNodes[index];\n  },\n\n  getCurrentEntry: function() {\n    return this.getEntry(this.index);\n  },\n\n  selectEntry: function() {\n    this.active = false;\n    this.updateElement(this.getCurrentEntry());\n  },\n\n  updateElement: function(selectedElement) {\n    if (this.options.updateElement) {\n      this.options.updateElement(selectedElement);\n      return;\n    }\n    var value = '';\n    if (this.options.select) {\n      var nodes = $(selectedElement).select('.' + this.options.select) || [];\n      if(nodes.length>0) value = Element.collectTextNodes(nodes[0], this.options.select);\n    } else\n      value = Element.collectTextNodesIgnoreClass(selectedElement, 'informal');\n\n    var bounds = this.getTokenBounds();\n    if (bounds[0] != -1) {\n      var newValue = this.element.value.substr(0, bounds[0]);\n      var whitespace = this.element.value.substr(bounds[0]).match(/^\\s+/);\n      if (whitespace)\n        newValue += whitespace[0];\n      this.element.value = newValue + value + this.element.value.substr(bounds[1]);\n    } else {\n      this.element.value = value;\n    }\n    this.oldElementValue = this.element.value;\n    this.element.focus();\n\n    if (this.options.afterUpdateElement)\n      this.options.afterUpdateElement(this.element, selectedElement);\n  },\n\n  updateChoices: function(choices) {\n    if(!this.changed && this.hasFocus) {\n      this.update.innerHTML = choices;\n      Element.cleanWhitespace(this.update);\n      Element.cleanWhitespace(this.update.down());\n\n      if(this.update.firstChild && this.update.down().childNodes) {\n        this.entryCount =\n          this.update.down().childNodes.length;\n        for (var i = 0; i < this.entryCount; i++) {\n          var entry = this.getEntry(i);\n          entry.autocompleteIndex = i;\n          this.addObservers(entry);\n        }\n      } else {\n        this.entryCount = 0;\n      }\n\n      this.stopIndicator();\n      this.index = 0;\n\n      if(this.entryCount==1 && this.options.autoSelect) {\n        this.selectEntry();\n        this.hide();\n      } else {\n        this.render();\n      }\n    }\n  },\n\n  addObservers: function(element) {\n    Event.observe(element, \"mouseover\", this.onHover.bindAsEventListener(this));\n    Event.observe(element, \"click\", this.onClick.bindAsEventListener(this));\n  },\n\n  onObserverEvent: function() {\n    this.changed = false;\n    this.tokenBounds = null;\n    if(this.getToken().length>=this.options.minChars) {\n      this.getUpdatedChoices();\n    } else {\n      this.active = false;\n      this.hide();\n    }\n    this.oldElementValue = this.element.value;\n  },\n\n  getToken: function() {\n    var bounds = this.getTokenBounds();\n    return this.element.value.substring(bounds[0], bounds[1]).strip();\n  },\n\n  getTokenBounds: function() {\n    if (null != this.tokenBounds) return this.tokenBounds;\n    var value = this.element.value;\n    if (value.strip().empty()) return [-1, 0];\n    var diff = arguments.callee.getFirstDifferencePos(value, this.oldElementValue);\n    var offset = (diff == this.oldElementValue.length ? 1 : 0);\n    var prevTokenPos = -1, nextTokenPos = value.length;\n    var tp;\n    for (var index = 0, l = this.options.tokens.length; index < l; ++index) {\n      tp = value.lastIndexOf(this.options.tokens[index], diff + offset - 1);\n      if (tp > prevTokenPos) prevTokenPos = tp;\n      tp = value.indexOf(this.options.tokens[index], diff + offset);\n      if (-1 != tp && tp < nextTokenPos) nextTokenPos = tp;\n    }\n    return (this.tokenBounds = [prevTokenPos + 1, nextTokenPos]);\n  }\n});\n\nAutocompleter.Base.prototype.getTokenBounds.getFirstDifferencePos = function(newS, oldS) {\n  var boundary = Math.min(newS.length, oldS.length);\n  for (var index = 0; index < boundary; ++index)\n    if (newS[index] != oldS[index])\n      return index;\n  return boundary;\n};\n\nAjax.Autocompleter = Class.create(Autocompleter.Base, {\n  initialize: function(element, update, url, options) {\n    this.baseInitialize(element, update, options);\n    this.options.asynchronous  = true;\n    this.options.onComplete    = this.onComplete.bind(this);\n    this.options.defaultParams = this.options.parameters || null;\n    this.url                   = url;\n  },\n\n  getUpdatedChoices: function() {\n    this.startIndicator();\n\n    var entry = encodeURIComponent(this.options.paramName) + '=' +\n      encodeURIComponent(this.getToken());\n\n    this.options.parameters = this.options.callback ?\n      this.options.callback(this.element, entry) : entry;\n\n    if(this.options.defaultParams)\n      this.options.parameters += '&' + this.options.defaultParams;\n\n    new Ajax.Request(this.url, this.options);\n  },\n\n  onComplete: function(request) {\n    this.updateChoices(request.responseText);\n  }\n});\n\n// The local array autocompleter. Used when you'd prefer to\n// inject an array of autocompletion options into the page, rather\n// than sending out Ajax queries, which can be quite slow sometimes.\n//\n// The constructor takes four parameters. The first two are, as usual,\n// the id of the monitored textbox, and id of the autocompletion menu.\n// The third is the array you want to autocomplete from, and the fourth\n// is the options block.\n//\n// Extra local autocompletion options:\n// - choices - How many autocompletion choices to offer\n//\n// - partialSearch - If false, the autocompleter will match entered\n//                    text only at the beginning of strings in the\n//                    autocomplete array. Defaults to true, which will\n//                    match text at the beginning of any *word* in the\n//                    strings in the autocomplete array. If you want to\n//                    search anywhere in the string, additionally set\n//                    the option fullSearch to true (default: off).\n//\n// - fullSsearch - Search anywhere in autocomplete array strings.\n//\n// - partialChars - How many characters to enter before triggering\n//                   a partial match (unlike minChars, which defines\n//                   how many characters are required to do any match\n//                   at all). Defaults to 2.\n//\n// - ignoreCase - Whether to ignore case when autocompleting.\n//                 Defaults to true.\n//\n// It's possible to pass in a custom function as the 'selector'\n// option, if you prefer to write your own autocompletion logic.\n// In that case, the other options above will not apply unless\n// you support them.\n\nAutocompleter.Local = Class.create(Autocompleter.Base, {\n  initialize: function(element, update, array, options) {\n    this.baseInitialize(element, update, options);\n    this.options.array = array;\n  },\n\n  getUpdatedChoices: function() {\n    this.updateChoices(this.options.selector(this));\n  },\n\n  setOptions: function(options) {\n    this.options = Object.extend({\n      choices: 10,\n      partialSearch: true,\n      partialChars: 2,\n      ignoreCase: true,\n      fullSearch: false,\n      selector: function(instance) {\n        var ret       = []; // Beginning matches\n        var partial   = []; // Inside matches\n        var entry     = instance.getToken();\n        var count     = 0;\n\n        for (var i = 0; i < instance.options.array.length &&\n          ret.length < instance.options.choices ; i++) {\n\n          var elem = instance.options.array[i];\n          var foundPos = instance.options.ignoreCase ?\n            elem.toLowerCase().indexOf(entry.toLowerCase()) :\n            elem.indexOf(entry);\n\n          while (foundPos != -1) {\n            if (foundPos == 0 && elem.length != entry.length) {\n              ret.push(\"<li><strong>\" + elem.substr(0, entry.length) + \"</strong>\" +\n                elem.substr(entry.length) + \"</li>\");\n              break;\n            } else if (entry.length >= instance.options.partialChars &&\n              instance.options.partialSearch && foundPos != -1) {\n              if (instance.options.fullSearch || /\\s/.test(elem.substr(foundPos-1,1))) {\n                partial.push(\"<li>\" + elem.substr(0, foundPos) + \"<strong>\" +\n                  elem.substr(foundPos, entry.length) + \"</strong>\" + elem.substr(\n                  foundPos + entry.length) + \"</li>\");\n                break;\n              }\n            }\n\n            foundPos = instance.options.ignoreCase ?\n              elem.toLowerCase().indexOf(entry.toLowerCase(), foundPos + 1) :\n              elem.indexOf(entry, foundPos + 1);\n\n          }\n        }\n        if (partial.length)\n          ret = ret.concat(partial.slice(0, instance.options.choices - ret.length));\n        return \"<ul>\" + ret.join('') + \"</ul>\";\n      }\n    }, options || { });\n  }\n});\n\n// AJAX in-place editor and collection editor\n// Full rewrite by Christophe Porteneuve <tdd@tddsworld.com> (April 2007).\n\n// Use this if you notice weird scrolling problems on some browsers,\n// the DOM might be a bit confused when this gets called so do this\n// waits 1 ms (with setTimeout) until it does the activation\nField.scrollFreeActivate = function(field) {\n  setTimeout(function() {\n    Field.activate(field);\n  }, 1);\n};\n\nAjax.InPlaceEditor = Class.create({\n  initialize: function(element, url, options) {\n    this.url = url;\n    this.element = element = $(element);\n    this.prepareOptions();\n    this._controls = { };\n    arguments.callee.dealWithDeprecatedOptions(options); // DEPRECATION LAYER!!!\n    Object.extend(this.options, options || { });\n    if (!this.options.formId && this.element.id) {\n      this.options.formId = this.element.id + '-inplaceeditor';\n      if ($(this.options.formId))\n        this.options.formId = '';\n    }\n    if (this.options.externalControl)\n      this.options.externalControl = $(this.options.externalControl);\n    if (!this.options.externalControl)\n      this.options.externalControlOnly = false;\n    this._originalBackground = this.element.getStyle('background-color') || 'transparent';\n    this.element.title = this.options.clickToEditText;\n    this._boundCancelHandler = this.handleFormCancellation.bind(this);\n    this._boundComplete = (this.options.onComplete || Prototype.emptyFunction).bind(this);\n    this._boundFailureHandler = this.handleAJAXFailure.bind(this);\n    this._boundSubmitHandler = this.handleFormSubmission.bind(this);\n    this._boundWrapperHandler = this.wrapUp.bind(this);\n    this.registerListeners();\n  },\n  checkForEscapeOrReturn: function(e) {\n    if (!this._editing || e.ctrlKey || e.altKey || e.shiftKey) return;\n    if (Event.KEY_ESC == e.keyCode)\n      this.handleFormCancellation(e);\n    else if (Event.KEY_RETURN == e.keyCode)\n      this.handleFormSubmission(e);\n  },\n  createControl: function(mode, handler, extraClasses) {\n    var control = this.options[mode + 'Control'];\n    var text = this.options[mode + 'Text'];\n    if ('button' == control) {\n      var btn = document.createElement('input');\n      btn.type = 'submit';\n      btn.value = text;\n      btn.className = 'editor_' + mode + '_button';\n      if ('cancel' == mode)\n        btn.onclick = this._boundCancelHandler;\n      this._form.appendChild(btn);\n      this._controls[mode] = btn;\n    } else if ('link' == control) {\n      var link = document.createElement('a');\n      link.href = '#';\n      link.appendChild(document.createTextNode(text));\n      link.onclick = 'cancel' == mode ? this._boundCancelHandler : this._boundSubmitHandler;\n      link.className = 'editor_' + mode + '_link';\n      if (extraClasses)\n        link.className += ' ' + extraClasses;\n      this._form.appendChild(link);\n      this._controls[mode] = link;\n    }\n  },\n  createEditField: function() {\n    var text = (this.options.loadTextURL ? this.options.loadingText : this.getText());\n    var fld;\n    if (1 >= this.options.rows && !/\\r|\\n/.test(this.getText())) {\n      fld = document.createElement('input');\n      fld.type = 'text';\n      var size = this.options.size || this.options.cols || 0;\n      if (0 < size) fld.size = size;\n    } else {\n      fld = document.createElement('textarea');\n      fld.rows = (1 >= this.options.rows ? this.options.autoRows : this.options.rows);\n      fld.cols = this.options.cols || 40;\n    }\n    fld.name = this.options.paramName;\n    fld.value = text; // No HTML breaks conversion anymore\n    fld.className = 'editor_field';\n    if (this.options.submitOnBlur)\n      fld.onblur = this._boundSubmitHandler;\n    this._controls.editor = fld;\n    if (this.options.loadTextURL)\n      this.loadExternalText();\n    this._form.appendChild(this._controls.editor);\n  },\n  createForm: function() {\n    var ipe = this;\n    function addText(mode, condition) {\n      var text = ipe.options['text' + mode + 'Controls'];\n      if (!text || condition === false) return;\n      ipe._form.appendChild(document.createTextNode(text));\n    };\n    this._form = $(document.createElement('form'));\n    this._form.id = this.options.formId;\n    this._form.addClassName(this.options.formClassName);\n    this._form.onsubmit = this._boundSubmitHandler;\n    this.createEditField();\n    if ('textarea' == this._controls.editor.tagName.toLowerCase())\n      this._form.appendChild(document.createElement('br'));\n    if (this.options.onFormCustomization)\n      this.options.onFormCustomization(this, this._form);\n    addText('Before', this.options.okControl || this.options.cancelControl);\n    this.createControl('ok', this._boundSubmitHandler);\n    addText('Between', this.options.okControl && this.options.cancelControl);\n    this.createControl('cancel', this._boundCancelHandler, 'editor_cancel');\n    addText('After', this.options.okControl || this.options.cancelControl);\n  },\n  destroy: function() {\n    if (this._oldInnerHTML)\n      this.element.innerHTML = this._oldInnerHTML;\n    this.leaveEditMode();\n    this.unregisterListeners();\n  },\n  enterEditMode: function(e) {\n    if (this._saving || this._editing) return;\n    this._editing = true;\n    this.triggerCallback('onEnterEditMode');\n    if (this.options.externalControl)\n      this.options.externalControl.hide();\n    this.element.hide();\n    this.createForm();\n    this.element.parentNode.insertBefore(this._form, this.element);\n    if (!this.options.loadTextURL)\n      this.postProcessEditField();\n    if (e) Event.stop(e);\n  },\n  enterHover: function(e) {\n    if (this.options.hoverClassName)\n      this.element.addClassName(this.options.hoverClassName);\n    if (this._saving) return;\n    this.triggerCallback('onEnterHover');\n  },\n  getText: function() {\n    return this.element.innerHTML.unescapeHTML();\n  },\n  handleAJAXFailure: function(transport) {\n    this.triggerCallback('onFailure', transport);\n    if (this._oldInnerHTML) {\n      this.element.innerHTML = this._oldInnerHTML;\n      this._oldInnerHTML = null;\n    }\n  },\n  handleFormCancellation: function(e) {\n    this.wrapUp();\n    if (e) Event.stop(e);\n  },\n  handleFormSubmission: function(e) {\n    var form = this._form;\n    var value = $F(this._controls.editor);\n    this.prepareSubmission();\n    var params = this.options.callback(form, value) || '';\n    if (Object.isString(params))\n      params = params.toQueryParams();\n    params.editorId = this.element.id;\n    if (this.options.htmlResponse) {\n      var options = Object.extend({ evalScripts: true }, this.options.ajaxOptions);\n      Object.extend(options, {\n        parameters: params,\n        onComplete: this._boundWrapperHandler,\n        onFailure: this._boundFailureHandler\n      });\n      new Ajax.Updater({ success: this.element }, this.url, options);\n    } else {\n      var options = Object.extend({ method: 'get' }, this.options.ajaxOptions);\n      Object.extend(options, {\n        parameters: params,\n        onComplete: this._boundWrapperHandler,\n        onFailure: this._boundFailureHandler\n      });\n      new Ajax.Request(this.url, options);\n    }\n    if (e) Event.stop(e);\n  },\n  leaveEditMode: function() {\n    this.element.removeClassName(this.options.savingClassName);\n    this.removeForm();\n    this.leaveHover();\n    this.element.style.backgroundColor = this._originalBackground;\n    this.element.show();\n    if (this.options.externalControl)\n      this.options.externalControl.show();\n    this._saving = false;\n    this._editing = false;\n    this._oldInnerHTML = null;\n    this.triggerCallback('onLeaveEditMode');\n  },\n  leaveHover: function(e) {\n    if (this.options.hoverClassName)\n      this.element.removeClassName(this.options.hoverClassName);\n    if (this._saving) return;\n    this.triggerCallback('onLeaveHover');\n  },\n  loadExternalText: function() {\n    this._form.addClassName(this.options.loadingClassName);\n    this._controls.editor.disabled = true;\n    var options = Object.extend({ method: 'get' }, this.options.ajaxOptions);\n    Object.extend(options, {\n      parameters: 'editorId=' + encodeURIComponent(this.element.id),\n      onComplete: Prototype.emptyFunction,\n      onSuccess: function(transport) {\n        this._form.removeClassName(this.options.loadingClassName);\n        var text = transport.responseText;\n        if (this.options.stripLoadedTextTags)\n          text = text.stripTags();\n        this._controls.editor.value = text;\n        this._controls.editor.disabled = false;\n        this.postProcessEditField();\n      }.bind(this),\n      onFailure: this._boundFailureHandler\n    });\n    new Ajax.Request(this.options.loadTextURL, options);\n  },\n  postProcessEditField: function() {\n    var fpc = this.options.fieldPostCreation;\n    if (fpc)\n      $(this._controls.editor)['focus' == fpc ? 'focus' : 'activate']();\n  },\n  prepareOptions: function() {\n    this.options = Object.clone(Ajax.InPlaceEditor.DefaultOptions);\n    Object.extend(this.options, Ajax.InPlaceEditor.DefaultCallbacks);\n    [this._extraDefaultOptions].flatten().compact().each(function(defs) {\n      Object.extend(this.options, defs);\n    }.bind(this));\n  },\n  prepareSubmission: function() {\n    this._saving = true;\n    this.removeForm();\n    this.leaveHover();\n    this.showSaving();\n  },\n  registerListeners: function() {\n    this._listeners = { };\n    var listener;\n    $H(Ajax.InPlaceEditor.Listeners).each(function(pair) {\n      listener = this[pair.value].bind(this);\n      this._listeners[pair.key] = listener;\n      if (!this.options.externalControlOnly)\n        this.element.observe(pair.key, listener);\n      if (this.options.externalControl)\n        this.options.externalControl.observe(pair.key, listener);\n    }.bind(this));\n  },\n  removeForm: function() {\n    if (!this._form) return;\n    this._form.remove();\n    this._form = null;\n    this._controls = { };\n  },\n  showSaving: function() {\n    this._oldInnerHTML = this.element.innerHTML;\n    this.element.innerHTML = this.options.savingText;\n    this.element.addClassName(this.options.savingClassName);\n    this.element.style.backgroundColor = this._originalBackground;\n    this.element.show();\n  },\n  triggerCallback: function(cbName, arg) {\n    if ('function' == typeof this.options[cbName]) {\n      this.options[cbName](this, arg);\n    }\n  },\n  unregisterListeners: function() {\n    $H(this._listeners).each(function(pair) {\n      if (!this.options.externalControlOnly)\n        this.element.stopObserving(pair.key, pair.value);\n      if (this.options.externalControl)\n        this.options.externalControl.stopObserving(pair.key, pair.value);\n    }.bind(this));\n  },\n  wrapUp: function(transport) {\n    this.leaveEditMode();\n    // Can't use triggerCallback due to backward compatibility: requires\n    // binding + direct element\n    this._boundComplete(transport, this.element);\n  }\n});\n\nObject.extend(Ajax.InPlaceEditor.prototype, {\n  dispose: Ajax.InPlaceEditor.prototype.destroy\n});\n\nAjax.InPlaceCollectionEditor = Class.create(Ajax.InPlaceEditor, {\n  initialize: function($super, element, url, options) {\n    this._extraDefaultOptions = Ajax.InPlaceCollectionEditor.DefaultOptions;\n    $super(element, url, options);\n  },\n\n  createEditField: function() {\n    var list = document.createElement('select');\n    list.name = this.options.paramName;\n    list.size = 1;\n    this._controls.editor = list;\n    this._collection = this.options.collection || [];\n    if (this.options.loadCollectionURL)\n      this.loadCollection();\n    else\n      this.checkForExternalText();\n    this._form.appendChild(this._controls.editor);\n  },\n\n  loadCollection: function() {\n    this._form.addClassName(this.options.loadingClassName);\n    this.showLoadingText(this.options.loadingCollectionText);\n    var options = Object.extend({ method: 'get' }, this.options.ajaxOptions);\n    Object.extend(options, {\n      parameters: 'editorId=' + encodeURIComponent(this.element.id),\n      onComplete: Prototype.emptyFunction,\n      onSuccess: function(transport) {\n        var js = transport.responseText.strip();\n        if (!/^\\[.*\\]$/.test(js)) // TODO: improve sanity check\n          throw('Server returned an invalid collection representation.');\n        this._collection = eval(js);\n        this.checkForExternalText();\n      }.bind(this),\n      onFailure: this.onFailure\n    });\n    new Ajax.Request(this.options.loadCollectionURL, options);\n  },\n\n  showLoadingText: function(text) {\n    this._controls.editor.disabled = true;\n    var tempOption = this._controls.editor.firstChild;\n    if (!tempOption) {\n      tempOption = document.createElement('option');\n      tempOption.value = '';\n      this._controls.editor.appendChild(tempOption);\n      tempOption.selected = true;\n    }\n    tempOption.update((text || '').stripScripts().stripTags());\n  },\n\n  checkForExternalText: function() {\n    this._text = this.getText();\n    if (this.options.loadTextURL)\n      this.loadExternalText();\n    else\n      this.buildOptionList();\n  },\n\n  loadExternalText: function() {\n    this.showLoadingText(this.options.loadingText);\n    var options = Object.extend({ method: 'get' }, this.options.ajaxOptions);\n    Object.extend(options, {\n      parameters: 'editorId=' + encodeURIComponent(this.element.id),\n      onComplete: Prototype.emptyFunction,\n      onSuccess: function(transport) {\n        this._text = transport.responseText.strip();\n        this.buildOptionList();\n      }.bind(this),\n      onFailure: this.onFailure\n    });\n    new Ajax.Request(this.options.loadTextURL, options);\n  },\n\n  buildOptionList: function() {\n    this._form.removeClassName(this.options.loadingClassName);\n    this._collection = this._collection.map(function(entry) {\n      return 2 === entry.length ? entry : [entry, entry].flatten();\n    });\n    var marker = ('value' in this.options) ? this.options.value : this._text;\n    var textFound = this._collection.any(function(entry) {\n      return entry[0] == marker;\n    }.bind(this));\n    this._controls.editor.update('');\n    var option;\n    this._collection.each(function(entry, index) {\n      option = document.createElement('option');\n      option.value = entry[0];\n      option.selected = textFound ? entry[0] == marker : 0 == index;\n      option.appendChild(document.createTextNode(entry[1]));\n      this._controls.editor.appendChild(option);\n    }.bind(this));\n    this._controls.editor.disabled = false;\n    Field.scrollFreeActivate(this._controls.editor);\n  }\n});\n\n//**** DEPRECATION LAYER FOR InPlace[Collection]Editor! ****\n//**** This only  exists for a while,  in order to  let ****\n//**** users adapt to  the new API.  Read up on the new ****\n//**** API and convert your code to it ASAP!            ****\n\nAjax.InPlaceEditor.prototype.initialize.dealWithDeprecatedOptions = function(options) {\n  if (!options) return;\n  function fallback(name, expr) {\n    if (name in options || expr === undefined) return;\n    options[name] = expr;\n  };\n  fallback('cancelControl', (options.cancelLink ? 'link' : (options.cancelButton ? 'button' :\n    options.cancelLink == options.cancelButton == false ? false : undefined)));\n  fallback('okControl', (options.okLink ? 'link' : (options.okButton ? 'button' :\n    options.okLink == options.okButton == false ? false : undefined)));\n  fallback('highlightColor', options.highlightcolor);\n  fallback('highlightEndColor', options.highlightendcolor);\n};\n\nObject.extend(Ajax.InPlaceEditor, {\n  DefaultOptions: {\n    ajaxOptions: { },\n    autoRows: 3,                                // Use when multi-line w/ rows == 1\n    cancelControl: 'link',                      // 'link'|'button'|false\n    cancelText: 'cancel',\n    clickToEditText: 'Click to edit',\n    externalControl: null,                      // id|elt\n    externalControlOnly: false,\n    fieldPostCreation: 'activate',              // 'activate'|'focus'|false\n    formClassName: 'inplaceeditor-form',\n    formId: null,                               // id|elt\n    highlightColor: '#ffff99',\n    highlightEndColor: '#ffffff',\n    hoverClassName: '',\n    htmlResponse: true,\n    loadingClassName: 'inplaceeditor-loading',\n    loadingText: 'Loading...',\n    okControl: 'button',                        // 'link'|'button'|false\n    okText: 'ok',\n    paramName: 'value',\n    rows: 1,                                    // If 1 and multi-line, uses autoRows\n    savingClassName: 'inplaceeditor-saving',\n    savingText: 'Saving...',\n    size: 0,\n    stripLoadedTextTags: false,\n    submitOnBlur: false,\n    textAfterControls: '',\n    textBeforeControls: '',\n    textBetweenControls: ''\n  },\n  DefaultCallbacks: {\n    callback: function(form) {\n      return Form.serialize(form);\n    },\n    onComplete: function(transport, element) {\n      // For backward compatibility, this one is bound to the IPE, and passes\n      // the element directly.  It was too often customized, so we don't break it.\n      new Effect.Highlight(element, {\n        startcolor: this.options.highlightColor, keepBackgroundImage: true });\n    },\n    onEnterEditMode: null,\n    onEnterHover: function(ipe) {\n      ipe.element.style.backgroundColor = ipe.options.highlightColor;\n      if (ipe._effect)\n        ipe._effect.cancel();\n    },\n    onFailure: function(transport, ipe) {\n      alert('Error communication with the server: ' + transport.responseText.stripTags());\n    },\n    onFormCustomization: null, // Takes the IPE and its generated form, after editor, before controls.\n    onLeaveEditMode: null,\n    onLeaveHover: function(ipe) {\n      ipe._effect = new Effect.Highlight(ipe.element, {\n        startcolor: ipe.options.highlightColor, endcolor: ipe.options.highlightEndColor,\n        restorecolor: ipe._originalBackground, keepBackgroundImage: true\n      });\n    }\n  },\n  Listeners: {\n    click: 'enterEditMode',\n    keydown: 'checkForEscapeOrReturn',\n    mouseover: 'enterHover',\n    mouseout: 'leaveHover'\n  }\n});\n\nAjax.InPlaceCollectionEditor.DefaultOptions = {\n  loadingCollectionText: 'Loading options...'\n};\n\n// Delayed observer, like Form.Element.Observer,\n// but waits for delay after last key input\n// Ideal for live-search fields\n\nForm.Element.DelayedObserver = Class.create({\n  initialize: function(element, delay, callback) {\n    this.delay     = delay || 0.5;\n    this.element   = $(element);\n    this.callback  = callback;\n    this.timer     = null;\n    this.lastValue = $F(this.element);\n    Event.observe(this.element,'keyup',this.delayedListener.bindAsEventListener(this));\n  },\n  delayedListener: function(event) {\n    if(this.lastValue == $F(this.element)) return;\n    if(this.timer) clearTimeout(this.timer);\n    this.timer = setTimeout(this.onTimerEvent.bind(this), this.delay * 1000);\n    this.lastValue = $F(this.element);\n  },\n  onTimerEvent: function() {\n    this.timer = null;\n    this.callback(this.element, $F(this.element));\n  }\n});"
  },
  {
    "path": "prototype/dragdrop.js",
    "content": "// script.aculo.us dragdrop.js v1.8.3, Thu Oct 08 11:23:33 +0200 2009\n\n// Copyright (c) 2005-2009 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)\n//\n// script.aculo.us is freely distributable under the terms of an MIT-style license.\n// For details, see the script.aculo.us web site: http://script.aculo.us/\n\nif(Object.isUndefined(Effect))\n  throw(\"dragdrop.js requires including script.aculo.us' effects.js library\");\n\nvar Droppables = {\n  drops: [],\n\n  remove: function(element) {\n    this.drops = this.drops.reject(function(d) { return d.element==$(element) });\n  },\n\n  add: function(element) {\n    element = $(element);\n    var options = Object.extend({\n      greedy:     true,\n      hoverclass: null,\n      tree:       false\n    }, arguments[1] || { });\n\n    // cache containers\n    if(options.containment) {\n      options._containers = [];\n      var containment = options.containment;\n      if(Object.isArray(containment)) {\n        containment.each( function(c) { options._containers.push($(c)) });\n      } else {\n        options._containers.push($(containment));\n      }\n    }\n\n    if(options.accept) options.accept = [options.accept].flatten();\n\n    Element.makePositioned(element); // fix IE\n    options.element = element;\n\n    this.drops.push(options);\n  },\n\n  findDeepestChild: function(drops) {\n    deepest = drops[0];\n\n    for (i = 1; i < drops.length; ++i)\n      if (Element.isParent(drops[i].element, deepest.element))\n        deepest = drops[i];\n\n    return deepest;\n  },\n\n  isContained: function(element, drop) {\n    var containmentNode;\n    if(drop.tree) {\n      containmentNode = element.treeNode;\n    } else {\n      containmentNode = element.parentNode;\n    }\n    return drop._containers.detect(function(c) { return containmentNode == c });\n  },\n\n  isAffected: function(point, element, drop) {\n    return (\n      (drop.element!=element) &&\n      ((!drop._containers) ||\n        this.isContained(element, drop)) &&\n      ((!drop.accept) ||\n        (Element.classNames(element).detect(\n          function(v) { return drop.accept.include(v) } ) )) &&\n      Position.within(drop.element, point[0], point[1]) );\n  },\n\n  deactivate: function(drop) {\n    if(drop.hoverclass)\n      Element.removeClassName(drop.element, drop.hoverclass);\n    this.last_active = null;\n  },\n\n  activate: function(drop) {\n    if(drop.hoverclass)\n      Element.addClassName(drop.element, drop.hoverclass);\n    this.last_active = drop;\n  },\n\n  show: function(point, element) {\n    if(!this.drops.length) return;\n    var drop, affected = [];\n\n    this.drops.each( function(drop) {\n      if(Droppables.isAffected(point, element, drop))\n        affected.push(drop);\n    });\n\n    if(affected.length>0)\n      drop = Droppables.findDeepestChild(affected);\n\n    if(this.last_active && this.last_active != drop) this.deactivate(this.last_active);\n    if (drop) {\n      Position.within(drop.element, point[0], point[1]);\n      if(drop.onHover)\n        drop.onHover(element, drop.element, Position.overlap(drop.overlap, drop.element));\n\n      if (drop != this.last_active) Droppables.activate(drop);\n    }\n  },\n\n  fire: function(event, element) {\n    if(!this.last_active) return;\n    Position.prepare();\n\n    if (this.isAffected([Event.pointerX(event), Event.pointerY(event)], element, this.last_active))\n      if (this.last_active.onDrop) {\n        this.last_active.onDrop(element, this.last_active.element, event);\n        return true;\n      }\n  },\n\n  reset: function() {\n    if(this.last_active)\n      this.deactivate(this.last_active);\n  }\n};\n\nvar Draggables = {\n  drags: [],\n  observers: [],\n\n  register: function(draggable) {\n    if(this.drags.length == 0) {\n      this.eventMouseUp   = this.endDrag.bindAsEventListener(this);\n      this.eventMouseMove = this.updateDrag.bindAsEventListener(this);\n      this.eventKeypress  = this.keyPress.bindAsEventListener(this);\n\n      Event.observe(document, \"mouseup\", this.eventMouseUp);\n      Event.observe(document, \"mousemove\", this.eventMouseMove);\n      Event.observe(document, \"keypress\", this.eventKeypress);\n    }\n    this.drags.push(draggable);\n  },\n\n  unregister: function(draggable) {\n    this.drags = this.drags.reject(function(d) { return d==draggable });\n    if(this.drags.length == 0) {\n      Event.stopObserving(document, \"mouseup\", this.eventMouseUp);\n      Event.stopObserving(document, \"mousemove\", this.eventMouseMove);\n      Event.stopObserving(document, \"keypress\", this.eventKeypress);\n    }\n  },\n\n  activate: function(draggable) {\n    if(draggable.options.delay) {\n      this._timeout = setTimeout(function() {\n        Draggables._timeout = null;\n        window.focus();\n        Draggables.activeDraggable = draggable;\n      }.bind(this), draggable.options.delay);\n    } else {\n      window.focus(); // allows keypress events if window isn't currently focused, fails for Safari\n      this.activeDraggable = draggable;\n    }\n  },\n\n  deactivate: function() {\n    this.activeDraggable = null;\n  },\n\n  updateDrag: function(event) {\n    if(!this.activeDraggable) return;\n    var pointer = [Event.pointerX(event), Event.pointerY(event)];\n    // Mozilla-based browsers fire successive mousemove events with\n    // the same coordinates, prevent needless redrawing (moz bug?)\n    if(this._lastPointer && (this._lastPointer.inspect() == pointer.inspect())) return;\n    this._lastPointer = pointer;\n\n    this.activeDraggable.updateDrag(event, pointer);\n  },\n\n  endDrag: function(event) {\n    if(this._timeout) {\n      clearTimeout(this._timeout);\n      this._timeout = null;\n    }\n    if(!this.activeDraggable) return;\n    this._lastPointer = null;\n    this.activeDraggable.endDrag(event);\n    this.activeDraggable = null;\n  },\n\n  keyPress: function(event) {\n    if(this.activeDraggable)\n      this.activeDraggable.keyPress(event);\n  },\n\n  addObserver: function(observer) {\n    this.observers.push(observer);\n    this._cacheObserverCallbacks();\n  },\n\n  removeObserver: function(element) {  // element instead of observer fixes mem leaks\n    this.observers = this.observers.reject( function(o) { return o.element==element });\n    this._cacheObserverCallbacks();\n  },\n\n  notify: function(eventName, draggable, event) {  // 'onStart', 'onEnd', 'onDrag'\n    if(this[eventName+'Count'] > 0)\n      this.observers.each( function(o) {\n        if(o[eventName]) o[eventName](eventName, draggable, event);\n      });\n    if(draggable.options[eventName]) draggable.options[eventName](draggable, event);\n  },\n\n  _cacheObserverCallbacks: function() {\n    ['onStart','onEnd','onDrag'].each( function(eventName) {\n      Draggables[eventName+'Count'] = Draggables.observers.select(\n        function(o) { return o[eventName]; }\n      ).length;\n    });\n  }\n};\n\n/*--------------------------------------------------------------------------*/\n\nvar Draggable = Class.create({\n  initialize: function(element) {\n    var defaults = {\n      handle: false,\n      reverteffect: function(element, top_offset, left_offset) {\n        var dur = Math.sqrt(Math.abs(top_offset^2)+Math.abs(left_offset^2))*0.02;\n        new Effect.Move(element, { x: -left_offset, y: -top_offset, duration: dur,\n          queue: {scope:'_draggable', position:'end'}\n        });\n      },\n      endeffect: function(element) {\n        var toOpacity = Object.isNumber(element._opacity) ? element._opacity : 1.0;\n        new Effect.Opacity(element, {duration:0.2, from:0.7, to:toOpacity,\n          queue: {scope:'_draggable', position:'end'},\n          afterFinish: function(){\n            Draggable._dragging[element] = false\n          }\n        });\n      },\n      zindex: 1000,\n      revert: false,\n      quiet: false,\n      scroll: false,\n      scrollSensitivity: 20,\n      scrollSpeed: 15,\n      snap: false,  // false, or xy or [x,y] or function(x,y){ return [x,y] }\n      delay: 0\n    };\n\n    if(!arguments[1] || Object.isUndefined(arguments[1].endeffect))\n      Object.extend(defaults, {\n        starteffect: function(element) {\n          element._opacity = Element.getOpacity(element);\n          Draggable._dragging[element] = true;\n          new Effect.Opacity(element, {duration:0.2, from:element._opacity, to:0.7});\n        }\n      });\n\n    var options = Object.extend(defaults, arguments[1] || { });\n\n    this.element = $(element);\n\n    if(options.handle && Object.isString(options.handle))\n      this.handle = this.element.down('.'+options.handle, 0);\n\n    if(!this.handle) this.handle = $(options.handle);\n    if(!this.handle) this.handle = this.element;\n\n    if(options.scroll && !options.scroll.scrollTo && !options.scroll.outerHTML) {\n      options.scroll = $(options.scroll);\n      this._isScrollChild = Element.childOf(this.element, options.scroll);\n    }\n\n    Element.makePositioned(this.element); // fix IE\n\n    this.options  = options;\n    this.dragging = false;\n\n    this.eventMouseDown = this.initDrag.bindAsEventListener(this);\n    Event.observe(this.handle, \"mousedown\", this.eventMouseDown);\n\n    Draggables.register(this);\n  },\n\n  destroy: function() {\n    Event.stopObserving(this.handle, \"mousedown\", this.eventMouseDown);\n    Draggables.unregister(this);\n  },\n\n  currentDelta: function() {\n    return([\n      parseInt(Element.getStyle(this.element,'left') || '0'),\n      parseInt(Element.getStyle(this.element,'top') || '0')]);\n  },\n\n  initDrag: function(event) {\n    if(!Object.isUndefined(Draggable._dragging[this.element]) &&\n      Draggable._dragging[this.element]) return;\n    if(Event.isLeftClick(event)) {\n      // abort on form elements, fixes a Firefox issue\n      var src = Event.element(event);\n      if((tag_name = src.tagName.toUpperCase()) && (\n        tag_name=='INPUT' ||\n        tag_name=='SELECT' ||\n        tag_name=='OPTION' ||\n        tag_name=='BUTTON' ||\n        tag_name=='TEXTAREA')) return;\n\n      var pointer = [Event.pointerX(event), Event.pointerY(event)];\n      var pos     = this.element.cumulativeOffset();\n      this.offset = [0,1].map( function(i) { return (pointer[i] - pos[i]) });\n\n      Draggables.activate(this);\n      Event.stop(event);\n    }\n  },\n\n  startDrag: function(event) {\n    this.dragging = true;\n    if(!this.delta)\n      this.delta = this.currentDelta();\n\n    if(this.options.zindex) {\n      this.originalZ = parseInt(Element.getStyle(this.element,'z-index') || 0);\n      this.element.style.zIndex = this.options.zindex;\n    }\n\n    if(this.options.ghosting) {\n      this._clone = this.element.cloneNode(true);\n      this._originallyAbsolute = (this.element.getStyle('position') == 'absolute');\n      if (!this._originallyAbsolute)\n        Position.absolutize(this.element);\n      this.element.parentNode.insertBefore(this._clone, this.element);\n    }\n\n    if(this.options.scroll) {\n      if (this.options.scroll == window) {\n        var where = this._getWindowScroll(this.options.scroll);\n        this.originalScrollLeft = where.left;\n        this.originalScrollTop = where.top;\n      } else {\n        this.originalScrollLeft = this.options.scroll.scrollLeft;\n        this.originalScrollTop = this.options.scroll.scrollTop;\n      }\n    }\n\n    Draggables.notify('onStart', this, event);\n\n    if(this.options.starteffect) this.options.starteffect(this.element);\n  },\n\n  updateDrag: function(event, pointer) {\n    if(!this.dragging) this.startDrag(event);\n\n    if(!this.options.quiet){\n      Position.prepare();\n      Droppables.show(pointer, this.element);\n    }\n\n    Draggables.notify('onDrag', this, event);\n\n    this.draw(pointer);\n    if(this.options.change) this.options.change(this);\n\n    if(this.options.scroll) {\n      this.stopScrolling();\n\n      var p;\n      if (this.options.scroll == window) {\n        with(this._getWindowScroll(this.options.scroll)) { p = [ left, top, left+width, top+height ]; }\n      } else {\n        p = Position.page(this.options.scroll);\n        p[0] += this.options.scroll.scrollLeft + Position.deltaX;\n        p[1] += this.options.scroll.scrollTop + Position.deltaY;\n        p.push(p[0]+this.options.scroll.offsetWidth);\n        p.push(p[1]+this.options.scroll.offsetHeight);\n      }\n      var speed = [0,0];\n      if(pointer[0] < (p[0]+this.options.scrollSensitivity)) speed[0] = pointer[0]-(p[0]+this.options.scrollSensitivity);\n      if(pointer[1] < (p[1]+this.options.scrollSensitivity)) speed[1] = pointer[1]-(p[1]+this.options.scrollSensitivity);\n      if(pointer[0] > (p[2]-this.options.scrollSensitivity)) speed[0] = pointer[0]-(p[2]-this.options.scrollSensitivity);\n      if(pointer[1] > (p[3]-this.options.scrollSensitivity)) speed[1] = pointer[1]-(p[3]-this.options.scrollSensitivity);\n      this.startScrolling(speed);\n    }\n\n    // fix AppleWebKit rendering\n    if(Prototype.Browser.WebKit) window.scrollBy(0,0);\n\n    Event.stop(event);\n  },\n\n  finishDrag: function(event, success) {\n    this.dragging = false;\n\n    if(this.options.quiet){\n      Position.prepare();\n      var pointer = [Event.pointerX(event), Event.pointerY(event)];\n      Droppables.show(pointer, this.element);\n    }\n\n    if(this.options.ghosting) {\n      if (!this._originallyAbsolute)\n        Position.relativize(this.element);\n      delete this._originallyAbsolute;\n      Element.remove(this._clone);\n      this._clone = null;\n    }\n\n    var dropped = false;\n    if(success) {\n      dropped = Droppables.fire(event, this.element);\n      if (!dropped) dropped = false;\n    }\n    if(dropped && this.options.onDropped) this.options.onDropped(this.element);\n    Draggables.notify('onEnd', this, event);\n\n    var revert = this.options.revert;\n    if(revert && Object.isFunction(revert)) revert = revert(this.element);\n\n    var d = this.currentDelta();\n    if(revert && this.options.reverteffect) {\n      if (dropped == 0 || revert != 'failure')\n        this.options.reverteffect(this.element,\n          d[1]-this.delta[1], d[0]-this.delta[0]);\n    } else {\n      this.delta = d;\n    }\n\n    if(this.options.zindex)\n      this.element.style.zIndex = this.originalZ;\n\n    if(this.options.endeffect)\n      this.options.endeffect(this.element);\n\n    Draggables.deactivate(this);\n    Droppables.reset();\n  },\n\n  keyPress: function(event) {\n    if(event.keyCode!=Event.KEY_ESC) return;\n    this.finishDrag(event, false);\n    Event.stop(event);\n  },\n\n  endDrag: function(event) {\n    if(!this.dragging) return;\n    this.stopScrolling();\n    this.finishDrag(event, true);\n    Event.stop(event);\n  },\n\n  draw: function(point) {\n    var pos = this.element.cumulativeOffset();\n    if(this.options.ghosting) {\n      var r   = Position.realOffset(this.element);\n      pos[0] += r[0] - Position.deltaX; pos[1] += r[1] - Position.deltaY;\n    }\n\n    var d = this.currentDelta();\n    pos[0] -= d[0]; pos[1] -= d[1];\n\n    if(this.options.scroll && (this.options.scroll != window && this._isScrollChild)) {\n      pos[0] -= this.options.scroll.scrollLeft-this.originalScrollLeft;\n      pos[1] -= this.options.scroll.scrollTop-this.originalScrollTop;\n    }\n\n    var p = [0,1].map(function(i){\n      return (point[i]-pos[i]-this.offset[i])\n    }.bind(this));\n\n    if(this.options.snap) {\n      if(Object.isFunction(this.options.snap)) {\n        p = this.options.snap(p[0],p[1],this);\n      } else {\n      if(Object.isArray(this.options.snap)) {\n        p = p.map( function(v, i) {\n          return (v/this.options.snap[i]).round()*this.options.snap[i] }.bind(this));\n      } else {\n        p = p.map( function(v) {\n          return (v/this.options.snap).round()*this.options.snap }.bind(this));\n      }\n    }}\n\n    var style = this.element.style;\n    if((!this.options.constraint) || (this.options.constraint=='horizontal'))\n      style.left = p[0] + \"px\";\n    if((!this.options.constraint) || (this.options.constraint=='vertical'))\n      style.top  = p[1] + \"px\";\n\n    if(style.visibility==\"hidden\") style.visibility = \"\"; // fix gecko rendering\n  },\n\n  stopScrolling: function() {\n    if(this.scrollInterval) {\n      clearInterval(this.scrollInterval);\n      this.scrollInterval = null;\n      Draggables._lastScrollPointer = null;\n    }\n  },\n\n  startScrolling: function(speed) {\n    if(!(speed[0] || speed[1])) return;\n    this.scrollSpeed = [speed[0]*this.options.scrollSpeed,speed[1]*this.options.scrollSpeed];\n    this.lastScrolled = new Date();\n    this.scrollInterval = setInterval(this.scroll.bind(this), 10);\n  },\n\n  scroll: function() {\n    var current = new Date();\n    var delta = current - this.lastScrolled;\n    this.lastScrolled = current;\n    if(this.options.scroll == window) {\n      with (this._getWindowScroll(this.options.scroll)) {\n        if (this.scrollSpeed[0] || this.scrollSpeed[1]) {\n          var d = delta / 1000;\n          this.options.scroll.scrollTo( left + d*this.scrollSpeed[0], top + d*this.scrollSpeed[1] );\n        }\n      }\n    } else {\n      this.options.scroll.scrollLeft += this.scrollSpeed[0] * delta / 1000;\n      this.options.scroll.scrollTop  += this.scrollSpeed[1] * delta / 1000;\n    }\n\n    Position.prepare();\n    Droppables.show(Draggables._lastPointer, this.element);\n    Draggables.notify('onDrag', this);\n    if (this._isScrollChild) {\n      Draggables._lastScrollPointer = Draggables._lastScrollPointer || $A(Draggables._lastPointer);\n      Draggables._lastScrollPointer[0] += this.scrollSpeed[0] * delta / 1000;\n      Draggables._lastScrollPointer[1] += this.scrollSpeed[1] * delta / 1000;\n      if (Draggables._lastScrollPointer[0] < 0)\n        Draggables._lastScrollPointer[0] = 0;\n      if (Draggables._lastScrollPointer[1] < 0)\n        Draggables._lastScrollPointer[1] = 0;\n      this.draw(Draggables._lastScrollPointer);\n    }\n\n    if(this.options.change) this.options.change(this);\n  },\n\n  _getWindowScroll: function(w) {\n    var T, L, W, H;\n    with (w.document) {\n      if (w.document.documentElement && documentElement.scrollTop) {\n        T = documentElement.scrollTop;\n        L = documentElement.scrollLeft;\n      } else if (w.document.body) {\n        T = body.scrollTop;\n        L = body.scrollLeft;\n      }\n      if (w.innerWidth) {\n        W = w.innerWidth;\n        H = w.innerHeight;\n      } else if (w.document.documentElement && documentElement.clientWidth) {\n        W = documentElement.clientWidth;\n        H = documentElement.clientHeight;\n      } else {\n        W = body.offsetWidth;\n        H = body.offsetHeight;\n      }\n    }\n    return { top: T, left: L, width: W, height: H };\n  }\n});\n\nDraggable._dragging = { };\n\n/*--------------------------------------------------------------------------*/\n\nvar SortableObserver = Class.create({\n  initialize: function(element, observer) {\n    this.element   = $(element);\n    this.observer  = observer;\n    this.lastValue = Sortable.serialize(this.element);\n  },\n\n  onStart: function() {\n    this.lastValue = Sortable.serialize(this.element);\n  },\n\n  onEnd: function() {\n    Sortable.unmark();\n    if(this.lastValue != Sortable.serialize(this.element))\n      this.observer(this.element)\n  }\n});\n\nvar Sortable = {\n  SERIALIZE_RULE: /^[^_\\-](?:[A-Za-z0-9\\-\\_]*)[_](.*)$/,\n\n  sortables: { },\n\n  _findRootElement: function(element) {\n    while (element.tagName.toUpperCase() != \"BODY\") {\n      if(element.id && Sortable.sortables[element.id]) return element;\n      element = element.parentNode;\n    }\n  },\n\n  options: function(element) {\n    element = Sortable._findRootElement($(element));\n    if(!element) return;\n    return Sortable.sortables[element.id];\n  },\n\n  destroy: function(element){\n    element = $(element);\n    var s = Sortable.sortables[element.id];\n\n    if(s) {\n      Draggables.removeObserver(s.element);\n      s.droppables.each(function(d){ Droppables.remove(d) });\n      s.draggables.invoke('destroy');\n\n      delete Sortable.sortables[s.element.id];\n    }\n  },\n\n  create: function(element) {\n    element = $(element);\n    var options = Object.extend({\n      element:     element,\n      tag:         'li',       // assumes li children, override with tag: 'tagname'\n      dropOnEmpty: false,\n      tree:        false,\n      treeTag:     'ul',\n      overlap:     'vertical', // one of 'vertical', 'horizontal'\n      constraint:  'vertical', // one of 'vertical', 'horizontal', false\n      containment: element,    // also takes array of elements (or id's); or false\n      handle:      false,      // or a CSS class\n      only:        false,\n      delay:       0,\n      hoverclass:  null,\n      ghosting:    false,\n      quiet:       false,\n      scroll:      false,\n      scrollSensitivity: 20,\n      scrollSpeed: 15,\n      format:      this.SERIALIZE_RULE,\n\n      // these take arrays of elements or ids and can be\n      // used for better initialization performance\n      elements:    false,\n      handles:     false,\n\n      onChange:    Prototype.emptyFunction,\n      onUpdate:    Prototype.emptyFunction\n    }, arguments[1] || { });\n\n    // clear any old sortable with same element\n    this.destroy(element);\n\n    // build options for the draggables\n    var options_for_draggable = {\n      revert:      true,\n      quiet:       options.quiet,\n      scroll:      options.scroll,\n      scrollSpeed: options.scrollSpeed,\n      scrollSensitivity: options.scrollSensitivity,\n      delay:       options.delay,\n      ghosting:    options.ghosting,\n      constraint:  options.constraint,\n      handle:      options.handle };\n\n    if(options.starteffect)\n      options_for_draggable.starteffect = options.starteffect;\n\n    if(options.reverteffect)\n      options_for_draggable.reverteffect = options.reverteffect;\n    else\n      if(options.ghosting) options_for_draggable.reverteffect = function(element) {\n        element.style.top  = 0;\n        element.style.left = 0;\n      };\n\n    if(options.endeffect)\n      options_for_draggable.endeffect = options.endeffect;\n\n    if(options.zindex)\n      options_for_draggable.zindex = options.zindex;\n\n    // build options for the droppables\n    var options_for_droppable = {\n      overlap:     options.overlap,\n      containment: options.containment,\n      tree:        options.tree,\n      hoverclass:  options.hoverclass,\n      onHover:     Sortable.onHover\n    };\n\n    var options_for_tree = {\n      onHover:      Sortable.onEmptyHover,\n      overlap:      options.overlap,\n      containment:  options.containment,\n      hoverclass:   options.hoverclass\n    };\n\n    // fix for gecko engine\n    Element.cleanWhitespace(element);\n\n    options.draggables = [];\n    options.droppables = [];\n\n    // drop on empty handling\n    if(options.dropOnEmpty || options.tree) {\n      Droppables.add(element, options_for_tree);\n      options.droppables.push(element);\n    }\n\n    (options.elements || this.findElements(element, options) || []).each( function(e,i) {\n      var handle = options.handles ? $(options.handles[i]) :\n        (options.handle ? $(e).select('.' + options.handle)[0] : e);\n      options.draggables.push(\n        new Draggable(e, Object.extend(options_for_draggable, { handle: handle })));\n      Droppables.add(e, options_for_droppable);\n      if(options.tree) e.treeNode = element;\n      options.droppables.push(e);\n    });\n\n    if(options.tree) {\n      (Sortable.findTreeElements(element, options) || []).each( function(e) {\n        Droppables.add(e, options_for_tree);\n        e.treeNode = element;\n        options.droppables.push(e);\n      });\n    }\n\n    // keep reference\n    this.sortables[element.identify()] = options;\n\n    // for onupdate\n    Draggables.addObserver(new SortableObserver(element, options.onUpdate));\n\n  },\n\n  // return all suitable-for-sortable elements in a guaranteed order\n  findElements: function(element, options) {\n    return Element.findChildren(\n      element, options.only, options.tree ? true : false, options.tag);\n  },\n\n  findTreeElements: function(element, options) {\n    return Element.findChildren(\n      element, options.only, options.tree ? true : false, options.treeTag);\n  },\n\n  onHover: function(element, dropon, overlap) {\n    if(Element.isParent(dropon, element)) return;\n\n    if(overlap > .33 && overlap < .66 && Sortable.options(dropon).tree) {\n      return;\n    } else if(overlap>0.5) {\n      Sortable.mark(dropon, 'before');\n      if(dropon.previousSibling != element) {\n        var oldParentNode = element.parentNode;\n        element.style.visibility = \"hidden\"; // fix gecko rendering\n        dropon.parentNode.insertBefore(element, dropon);\n        if(dropon.parentNode!=oldParentNode)\n          Sortable.options(oldParentNode).onChange(element);\n        Sortable.options(dropon.parentNode).onChange(element);\n      }\n    } else {\n      Sortable.mark(dropon, 'after');\n      var nextElement = dropon.nextSibling || null;\n      if(nextElement != element) {\n        var oldParentNode = element.parentNode;\n        element.style.visibility = \"hidden\"; // fix gecko rendering\n        dropon.parentNode.insertBefore(element, nextElement);\n        if(dropon.parentNode!=oldParentNode)\n          Sortable.options(oldParentNode).onChange(element);\n        Sortable.options(dropon.parentNode).onChange(element);\n      }\n    }\n  },\n\n  onEmptyHover: function(element, dropon, overlap) {\n    var oldParentNode = element.parentNode;\n    var droponOptions = Sortable.options(dropon);\n\n    if(!Element.isParent(dropon, element)) {\n      var index;\n\n      var children = Sortable.findElements(dropon, {tag: droponOptions.tag, only: droponOptions.only});\n      var child = null;\n\n      if(children) {\n        var offset = Element.offsetSize(dropon, droponOptions.overlap) * (1.0 - overlap);\n\n        for (index = 0; index < children.length; index += 1) {\n          if (offset - Element.offsetSize (children[index], droponOptions.overlap) >= 0) {\n            offset -= Element.offsetSize (children[index], droponOptions.overlap);\n          } else if (offset - (Element.offsetSize (children[index], droponOptions.overlap) / 2) >= 0) {\n            child = index + 1 < children.length ? children[index + 1] : null;\n            break;\n          } else {\n            child = children[index];\n            break;\n          }\n        }\n      }\n\n      dropon.insertBefore(element, child);\n\n      Sortable.options(oldParentNode).onChange(element);\n      droponOptions.onChange(element);\n    }\n  },\n\n  unmark: function() {\n    if(Sortable._marker) Sortable._marker.hide();\n  },\n\n  mark: function(dropon, position) {\n    // mark on ghosting only\n    var sortable = Sortable.options(dropon.parentNode);\n    if(sortable && !sortable.ghosting) return;\n\n    if(!Sortable._marker) {\n      Sortable._marker =\n        ($('dropmarker') || Element.extend(document.createElement('DIV'))).\n          hide().addClassName('dropmarker').setStyle({position:'absolute'});\n      document.getElementsByTagName(\"body\").item(0).appendChild(Sortable._marker);\n    }\n    var offsets = dropon.cumulativeOffset();\n    Sortable._marker.setStyle({left: offsets[0]+'px', top: offsets[1] + 'px'});\n\n    if(position=='after')\n      if(sortable.overlap == 'horizontal')\n        Sortable._marker.setStyle({left: (offsets[0]+dropon.clientWidth) + 'px'});\n      else\n        Sortable._marker.setStyle({top: (offsets[1]+dropon.clientHeight) + 'px'});\n\n    Sortable._marker.show();\n  },\n\n  _tree: function(element, options, parent) {\n    var children = Sortable.findElements(element, options) || [];\n\n    for (var i = 0; i < children.length; ++i) {\n      var match = children[i].id.match(options.format);\n\n      if (!match) continue;\n\n      var child = {\n        id: encodeURIComponent(match ? match[1] : null),\n        element: element,\n        parent: parent,\n        children: [],\n        position: parent.children.length,\n        container: $(children[i]).down(options.treeTag)\n      };\n\n      /* Get the element containing the children and recurse over it */\n      if (child.container)\n        this._tree(child.container, options, child);\n\n      parent.children.push (child);\n    }\n\n    return parent;\n  },\n\n  tree: function(element) {\n    element = $(element);\n    var sortableOptions = this.options(element);\n    var options = Object.extend({\n      tag: sortableOptions.tag,\n      treeTag: sortableOptions.treeTag,\n      only: sortableOptions.only,\n      name: element.id,\n      format: sortableOptions.format\n    }, arguments[1] || { });\n\n    var root = {\n      id: null,\n      parent: null,\n      children: [],\n      container: element,\n      position: 0\n    };\n\n    return Sortable._tree(element, options, root);\n  },\n\n  /* Construct a [i] index for a particular node */\n  _constructIndex: function(node) {\n    var index = '';\n    do {\n      if (node.id) index = '[' + node.position + ']' + index;\n    } while ((node = node.parent) != null);\n    return index;\n  },\n\n  sequence: function(element) {\n    element = $(element);\n    var options = Object.extend(this.options(element), arguments[1] || { });\n\n    return $(this.findElements(element, options) || []).map( function(item) {\n      return item.id.match(options.format) ? item.id.match(options.format)[1] : '';\n    });\n  },\n\n  setSequence: function(element, new_sequence) {\n    element = $(element);\n    var options = Object.extend(this.options(element), arguments[2] || { });\n\n    var nodeMap = { };\n    this.findElements(element, options).each( function(n) {\n        if (n.id.match(options.format))\n            nodeMap[n.id.match(options.format)[1]] = [n, n.parentNode];\n        n.parentNode.removeChild(n);\n    });\n\n    new_sequence.each(function(ident) {\n      var n = nodeMap[ident];\n      if (n) {\n        n[1].appendChild(n[0]);\n        delete nodeMap[ident];\n      }\n    });\n  },\n\n  serialize: function(element) {\n    element = $(element);\n    var options = Object.extend(Sortable.options(element), arguments[1] || { });\n    var name = encodeURIComponent(\n      (arguments[1] && arguments[1].name) ? arguments[1].name : element.id);\n\n    if (options.tree) {\n      return Sortable.tree(element, arguments[1]).children.map( function (item) {\n        return [name + Sortable._constructIndex(item) + \"[id]=\" +\n                encodeURIComponent(item.id)].concat(item.children.map(arguments.callee));\n      }).flatten().join('&');\n    } else {\n      return Sortable.sequence(element, arguments[1]).map( function(item) {\n        return name + \"[]=\" + encodeURIComponent(item);\n      }).join('&');\n    }\n  }\n};\n\n// Returns true if child is contained within element\nElement.isParent = function(child, element) {\n  if (!child.parentNode || child == element) return false;\n  if (child.parentNode == element) return true;\n  return Element.isParent(child.parentNode, element);\n};\n\nElement.findChildren = function(element, only, recursive, tagName) {\n  if(!element.hasChildNodes()) return null;\n  tagName = tagName.toUpperCase();\n  if(only) only = [only].flatten();\n  var elements = [];\n  $A(element.childNodes).each( function(e) {\n    if(e.tagName && e.tagName.toUpperCase()==tagName &&\n      (!only || (Element.classNames(e).detect(function(v) { return only.include(v) }))))\n        elements.push(e);\n    if(recursive) {\n      var grandchildren = Element.findChildren(e, only, recursive, tagName);\n      if(grandchildren) elements.push(grandchildren);\n    }\n  });\n\n  return (elements.length>0 ? elements.flatten() : []);\n};\n\nElement.offsetSize = function (element, type) {\n  return element['offset' + ((type=='vertical' || type=='height') ? 'Height' : 'Width')];\n};"
  },
  {
    "path": "prototype/effects.js",
    "content": "// script.aculo.us effects.js v1.8.3, Thu Oct 08 11:23:33 +0200 2009\n\n// Copyright (c) 2005-2009 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)\n// Contributors:\n//  Justin Palmer (http://encytemedia.com/)\n//  Mark Pilgrim (http://diveintomark.org/)\n//  Martin Bialasinki\n//\n// script.aculo.us is freely distributable under the terms of an MIT-style license.\n// For details, see the script.aculo.us web site: http://script.aculo.us/\n\n// converts rgb() and #xxx to #xxxxxx format,\n// returns self (or first argument) if not convertable\nString.prototype.parseColor = function() {\n  var color = '#';\n  if (this.slice(0,4) == 'rgb(') {\n    var cols = this.slice(4,this.length-1).split(',');\n    var i=0; do { color += parseInt(cols[i]).toColorPart() } while (++i<3);\n  } else {\n    if (this.slice(0,1) == '#') {\n      if (this.length==4) for(var i=1;i<4;i++) color += (this.charAt(i) + this.charAt(i)).toLowerCase();\n      if (this.length==7) color = this.toLowerCase();\n    }\n  }\n  return (color.length==7 ? color : (arguments[0] || this));\n};\n\n/*--------------------------------------------------------------------------*/\n\nElement.collectTextNodes = function(element) {\n  return $A($(element).childNodes).collect( function(node) {\n    return (node.nodeType==3 ? node.nodeValue :\n      (node.hasChildNodes() ? Element.collectTextNodes(node) : ''));\n  }).flatten().join('');\n};\n\nElement.collectTextNodesIgnoreClass = function(element, className) {\n  return $A($(element).childNodes).collect( function(node) {\n    return (node.nodeType==3 ? node.nodeValue :\n      ((node.hasChildNodes() && !Element.hasClassName(node,className)) ?\n        Element.collectTextNodesIgnoreClass(node, className) : ''));\n  }).flatten().join('');\n};\n\nElement.setContentZoom = function(element, percent) {\n  element = $(element);\n  element.setStyle({fontSize: (percent/100) + 'em'});\n  if (Prototype.Browser.WebKit) window.scrollBy(0,0);\n  return element;\n};\n\nElement.getInlineOpacity = function(element){\n  return $(element).style.opacity || '';\n};\n\nElement.forceRerendering = function(element) {\n  try {\n    element = $(element);\n    var n = document.createTextNode(' ');\n    element.appendChild(n);\n    element.removeChild(n);\n  } catch(e) { }\n};\n\n/*--------------------------------------------------------------------------*/\n\nvar Effect = {\n  _elementDoesNotExistError: {\n    name: 'ElementDoesNotExistError',\n    message: 'The specified DOM element does not exist, but is required for this effect to operate'\n  },\n  Transitions: {\n    linear: Prototype.K,\n    sinoidal: function(pos) {\n      return (-Math.cos(pos*Math.PI)/2) + .5;\n    },\n    reverse: function(pos) {\n      return 1-pos;\n    },\n    flicker: function(pos) {\n      var pos = ((-Math.cos(pos*Math.PI)/4) + .75) + Math.random()/4;\n      return pos > 1 ? 1 : pos;\n    },\n    wobble: function(pos) {\n      return (-Math.cos(pos*Math.PI*(9*pos))/2) + .5;\n    },\n    pulse: function(pos, pulses) {\n      return (-Math.cos((pos*((pulses||5)-.5)*2)*Math.PI)/2) + .5;\n    },\n    spring: function(pos) {\n      return 1 - (Math.cos(pos * 4.5 * Math.PI) * Math.exp(-pos * 6));\n    },\n    none: function(pos) {\n      return 0;\n    },\n    full: function(pos) {\n      return 1;\n    }\n  },\n  DefaultOptions: {\n    duration:   1.0,   // seconds\n    fps:        100,   // 100= assume 66fps max.\n    sync:       false, // true for combining\n    from:       0.0,\n    to:         1.0,\n    delay:      0.0,\n    queue:      'parallel'\n  },\n  tagifyText: function(element) {\n    var tagifyStyle = 'position:relative';\n    if (Prototype.Browser.IE) tagifyStyle += ';zoom:1';\n\n    element = $(element);\n    $A(element.childNodes).each( function(child) {\n      if (child.nodeType==3) {\n        child.nodeValue.toArray().each( function(character) {\n          element.insertBefore(\n            new Element('span', {style: tagifyStyle}).update(\n              character == ' ' ? String.fromCharCode(160) : character),\n              child);\n        });\n        Element.remove(child);\n      }\n    });\n  },\n  multiple: function(element, effect) {\n    var elements;\n    if (((typeof element == 'object') ||\n        Object.isFunction(element)) &&\n       (element.length))\n      elements = element;\n    else\n      elements = $(element).childNodes;\n\n    var options = Object.extend({\n      speed: 0.1,\n      delay: 0.0\n    }, arguments[2] || { });\n    var masterDelay = options.delay;\n\n    $A(elements).each( function(element, index) {\n      new effect(element, Object.extend(options, { delay: index * options.speed + masterDelay }));\n    });\n  },\n  PAIRS: {\n    'slide':  ['SlideDown','SlideUp'],\n    'blind':  ['BlindDown','BlindUp'],\n    'appear': ['Appear','Fade']\n  },\n  toggle: function(element, effect, options) {\n    element = $(element);\n    effect  = (effect || 'appear').toLowerCase();\n    \n    return Effect[ Effect.PAIRS[ effect ][ element.visible() ? 1 : 0 ] ](element, Object.extend({\n      queue: { position:'end', scope:(element.id || 'global'), limit: 1 }\n    }, options || {}));\n  }\n};\n\nEffect.DefaultOptions.transition = Effect.Transitions.sinoidal;\n\n/* ------------- core effects ------------- */\n\nEffect.ScopedQueue = Class.create(Enumerable, {\n  initialize: function() {\n    this.effects  = [];\n    this.interval = null;\n  },\n  _each: function(iterator) {\n    this.effects._each(iterator);\n  },\n  add: function(effect) {\n    var timestamp = new Date().getTime();\n\n    var position = Object.isString(effect.options.queue) ?\n      effect.options.queue : effect.options.queue.position;\n\n    switch(position) {\n      case 'front':\n        // move unstarted effects after this effect\n        this.effects.findAll(function(e){ return e.state=='idle' }).each( function(e) {\n            e.startOn  += effect.finishOn;\n            e.finishOn += effect.finishOn;\n          });\n        break;\n      case 'with-last':\n        timestamp = this.effects.pluck('startOn').max() || timestamp;\n        break;\n      case 'end':\n        // start effect after last queued effect has finished\n        timestamp = this.effects.pluck('finishOn').max() || timestamp;\n        break;\n    }\n\n    effect.startOn  += timestamp;\n    effect.finishOn += timestamp;\n\n    if (!effect.options.queue.limit || (this.effects.length < effect.options.queue.limit))\n      this.effects.push(effect);\n\n    if (!this.interval)\n      this.interval = setInterval(this.loop.bind(this), 15);\n  },\n  remove: function(effect) {\n    this.effects = this.effects.reject(function(e) { return e==effect });\n    if (this.effects.length == 0) {\n      clearInterval(this.interval);\n      this.interval = null;\n    }\n  },\n  loop: function() {\n    var timePos = new Date().getTime();\n    for(var i=0, len=this.effects.length;i<len;i++)\n      this.effects[i] && this.effects[i].loop(timePos);\n  }\n});\n\nEffect.Queues = {\n  instances: $H(),\n  get: function(queueName) {\n    if (!Object.isString(queueName)) return queueName;\n\n    return this.instances.get(queueName) ||\n      this.instances.set(queueName, new Effect.ScopedQueue());\n  }\n};\nEffect.Queue = Effect.Queues.get('global');\n\nEffect.Base = Class.create({\n  position: null,\n  start: function(options) {\n    if (options && options.transition === false) options.transition = Effect.Transitions.linear;\n    this.options      = Object.extend(Object.extend({ },Effect.DefaultOptions), options || { });\n    this.currentFrame = 0;\n    this.state        = 'idle';\n    this.startOn      = this.options.delay*1000;\n    this.finishOn     = this.startOn+(this.options.duration*1000);\n    this.fromToDelta  = this.options.to-this.options.from;\n    this.totalTime    = this.finishOn-this.startOn;\n    this.totalFrames  = this.options.fps*this.options.duration;\n\n    this.render = (function() {\n      function dispatch(effect, eventName) {\n        if (effect.options[eventName + 'Internal'])\n          effect.options[eventName + 'Internal'](effect);\n        if (effect.options[eventName])\n          effect.options[eventName](effect);\n      }\n\n      return function(pos) {\n        if (this.state === \"idle\") {\n          this.state = \"running\";\n          dispatch(this, 'beforeSetup');\n          if (this.setup) this.setup();\n          dispatch(this, 'afterSetup');\n        }\n        if (this.state === \"running\") {\n          pos = (this.options.transition(pos) * this.fromToDelta) + this.options.from;\n          this.position = pos;\n          dispatch(this, 'beforeUpdate');\n          if (this.update) this.update(pos);\n          dispatch(this, 'afterUpdate');\n        }\n      };\n    })();\n\n    this.event('beforeStart');\n    if (!this.options.sync)\n      Effect.Queues.get(Object.isString(this.options.queue) ?\n        'global' : this.options.queue.scope).add(this);\n  },\n  loop: function(timePos) {\n    if (timePos >= this.startOn) {\n      if (timePos >= this.finishOn) {\n        this.render(1.0);\n        this.cancel();\n        this.event('beforeFinish');\n        if (this.finish) this.finish();\n        this.event('afterFinish');\n        return;\n      }\n      var pos   = (timePos - this.startOn) / this.totalTime,\n          frame = (pos * this.totalFrames).round();\n      if (frame > this.currentFrame) {\n        this.render(pos);\n        this.currentFrame = frame;\n      }\n    }\n  },\n  cancel: function() {\n    if (!this.options.sync)\n      Effect.Queues.get(Object.isString(this.options.queue) ?\n        'global' : this.options.queue.scope).remove(this);\n    this.state = 'finished';\n  },\n  event: function(eventName) {\n    if (this.options[eventName + 'Internal']) this.options[eventName + 'Internal'](this);\n    if (this.options[eventName]) this.options[eventName](this);\n  },\n  inspect: function() {\n    var data = $H();\n    for(property in this)\n      if (!Object.isFunction(this[property])) data.set(property, this[property]);\n    return '#<Effect:' + data.inspect() + ',options:' + $H(this.options).inspect() + '>';\n  }\n});\n\nEffect.Parallel = Class.create(Effect.Base, {\n  initialize: function(effects) {\n    this.effects = effects || [];\n    this.start(arguments[1]);\n  },\n  update: function(position) {\n    this.effects.invoke('render', position);\n  },\n  finish: function(position) {\n    this.effects.each( function(effect) {\n      effect.render(1.0);\n      effect.cancel();\n      effect.event('beforeFinish');\n      if (effect.finish) effect.finish(position);\n      effect.event('afterFinish');\n    });\n  }\n});\n\nEffect.Tween = Class.create(Effect.Base, {\n  initialize: function(object, from, to) {\n    object = Object.isString(object) ? $(object) : object;\n    var args = $A(arguments), method = args.last(),\n      options = args.length == 5 ? args[3] : null;\n    this.method = Object.isFunction(method) ? method.bind(object) :\n      Object.isFunction(object[method]) ? object[method].bind(object) :\n      function(value) { object[method] = value };\n    this.start(Object.extend({ from: from, to: to }, options || { }));\n  },\n  update: function(position) {\n    this.method(position);\n  }\n});\n\nEffect.Event = Class.create(Effect.Base, {\n  initialize: function() {\n    this.start(Object.extend({ duration: 0 }, arguments[0] || { }));\n  },\n  update: Prototype.emptyFunction\n});\n\nEffect.Opacity = Class.create(Effect.Base, {\n  initialize: function(element) {\n    this.element = $(element);\n    if (!this.element) throw(Effect._elementDoesNotExistError);\n    // make this work on IE on elements without 'layout'\n    if (Prototype.Browser.IE && (!this.element.currentStyle.hasLayout))\n      this.element.setStyle({zoom: 1});\n    var options = Object.extend({\n      from: this.element.getOpacity() || 0.0,\n      to:   1.0\n    }, arguments[1] || { });\n    this.start(options);\n  },\n  update: function(position) {\n    this.element.setOpacity(position);\n  }\n});\n\nEffect.Move = Class.create(Effect.Base, {\n  initialize: function(element) {\n    this.element = $(element);\n    if (!this.element) throw(Effect._elementDoesNotExistError);\n    var options = Object.extend({\n      x:    0,\n      y:    0,\n      mode: 'relative'\n    }, arguments[1] || { });\n    this.start(options);\n  },\n  setup: function() {\n    this.element.makePositioned();\n    this.originalLeft = parseFloat(this.element.getStyle('left') || '0');\n    this.originalTop  = parseFloat(this.element.getStyle('top')  || '0');\n    if (this.options.mode == 'absolute') {\n      this.options.x = this.options.x - this.originalLeft;\n      this.options.y = this.options.y - this.originalTop;\n    }\n  },\n  update: function(position) {\n    this.element.setStyle({\n      left: (this.options.x  * position + this.originalLeft).round() + 'px',\n      top:  (this.options.y  * position + this.originalTop).round()  + 'px'\n    });\n  }\n});\n\n// for backwards compatibility\nEffect.MoveBy = function(element, toTop, toLeft) {\n  return new Effect.Move(element,\n    Object.extend({ x: toLeft, y: toTop }, arguments[3] || { }));\n};\n\nEffect.Scale = Class.create(Effect.Base, {\n  initialize: function(element, percent) {\n    this.element = $(element);\n    if (!this.element) throw(Effect._elementDoesNotExistError);\n    var options = Object.extend({\n      scaleX: true,\n      scaleY: true,\n      scaleContent: true,\n      scaleFromCenter: false,\n      scaleMode: 'box',        // 'box' or 'contents' or { } with provided values\n      scaleFrom: 100.0,\n      scaleTo:   percent\n    }, arguments[2] || { });\n    this.start(options);\n  },\n  setup: function() {\n    this.restoreAfterFinish = this.options.restoreAfterFinish || false;\n    this.elementPositioning = this.element.getStyle('position');\n\n    this.originalStyle = { };\n    ['top','left','width','height','fontSize'].each( function(k) {\n      this.originalStyle[k] = this.element.style[k];\n    }.bind(this));\n\n    this.originalTop  = this.element.offsetTop;\n    this.originalLeft = this.element.offsetLeft;\n\n    var fontSize = this.element.getStyle('font-size') || '100%';\n    ['em','px','%','pt'].each( function(fontSizeType) {\n      if (fontSize.indexOf(fontSizeType)>0) {\n        this.fontSize     = parseFloat(fontSize);\n        this.fontSizeType = fontSizeType;\n      }\n    }.bind(this));\n\n    this.factor = (this.options.scaleTo - this.options.scaleFrom)/100;\n\n    this.dims = null;\n    if (this.options.scaleMode=='box')\n      this.dims = [this.element.offsetHeight, this.element.offsetWidth];\n    if (/^content/.test(this.options.scaleMode))\n      this.dims = [this.element.scrollHeight, this.element.scrollWidth];\n    if (!this.dims)\n      this.dims = [this.options.scaleMode.originalHeight,\n                   this.options.scaleMode.originalWidth];\n  },\n  update: function(position) {\n    var currentScale = (this.options.scaleFrom/100.0) + (this.factor * position);\n    if (this.options.scaleContent && this.fontSize)\n      this.element.setStyle({fontSize: this.fontSize * currentScale + this.fontSizeType });\n    this.setDimensions(this.dims[0] * currentScale, this.dims[1] * currentScale);\n  },\n  finish: function(position) {\n    if (this.restoreAfterFinish) this.element.setStyle(this.originalStyle);\n  },\n  setDimensions: function(height, width) {\n    var d = { };\n    if (this.options.scaleX) d.width = width.round() + 'px';\n    if (this.options.scaleY) d.height = height.round() + 'px';\n    if (this.options.scaleFromCenter) {\n      var topd  = (height - this.dims[0])/2;\n      var leftd = (width  - this.dims[1])/2;\n      if (this.elementPositioning == 'absolute') {\n        if (this.options.scaleY) d.top = this.originalTop-topd + 'px';\n        if (this.options.scaleX) d.left = this.originalLeft-leftd + 'px';\n      } else {\n        if (this.options.scaleY) d.top = -topd + 'px';\n        if (this.options.scaleX) d.left = -leftd + 'px';\n      }\n    }\n    this.element.setStyle(d);\n  }\n});\n\nEffect.Highlight = Class.create(Effect.Base, {\n  initialize: function(element) {\n    this.element = $(element);\n    if (!this.element) throw(Effect._elementDoesNotExistError);\n    var options = Object.extend({ startcolor: '#ffff99' }, arguments[1] || { });\n    this.start(options);\n  },\n  setup: function() {\n    // Prevent executing on elements not in the layout flow\n    if (this.element.getStyle('display')=='none') { this.cancel(); return; }\n    // Disable background image during the effect\n    this.oldStyle = { };\n    if (!this.options.keepBackgroundImage) {\n      this.oldStyle.backgroundImage = this.element.getStyle('background-image');\n      this.element.setStyle({backgroundImage: 'none'});\n    }\n    if (!this.options.endcolor)\n      this.options.endcolor = this.element.getStyle('background-color').parseColor('#ffffff');\n    if (!this.options.restorecolor)\n      this.options.restorecolor = this.element.getStyle('background-color');\n    // init color calculations\n    this._base  = $R(0,2).map(function(i){ return parseInt(this.options.startcolor.slice(i*2+1,i*2+3),16) }.bind(this));\n    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));\n  },\n  update: function(position) {\n    this.element.setStyle({backgroundColor: $R(0,2).inject('#',function(m,v,i){\n      return m+((this._base[i]+(this._delta[i]*position)).round().toColorPart()); }.bind(this)) });\n  },\n  finish: function() {\n    this.element.setStyle(Object.extend(this.oldStyle, {\n      backgroundColor: this.options.restorecolor\n    }));\n  }\n});\n\nEffect.ScrollTo = function(element) {\n  var options = arguments[1] || { },\n  scrollOffsets = document.viewport.getScrollOffsets(),\n  elementOffsets = $(element).cumulativeOffset();\n\n  if (options.offset) elementOffsets[1] += options.offset;\n\n  return new Effect.Tween(null,\n    scrollOffsets.top,\n    elementOffsets[1],\n    options,\n    function(p){ scrollTo(scrollOffsets.left, p.round()); }\n  );\n};\n\n/* ------------- combination effects ------------- */\n\nEffect.Fade = function(element) {\n  element = $(element);\n  var oldOpacity = element.getInlineOpacity();\n  var options = Object.extend({\n    from: element.getOpacity() || 1.0,\n    to:   0.0,\n    afterFinishInternal: function(effect) {\n      if (effect.options.to!=0) return;\n      effect.element.hide().setStyle({opacity: oldOpacity});\n    }\n  }, arguments[1] || { });\n  return new Effect.Opacity(element,options);\n};\n\nEffect.Appear = function(element) {\n  element = $(element);\n  var options = Object.extend({\n  from: (element.getStyle('display') == 'none' ? 0.0 : element.getOpacity() || 0.0),\n  to:   1.0,\n  // force Safari to render floated elements properly\n  afterFinishInternal: function(effect) {\n    effect.element.forceRerendering();\n  },\n  beforeSetup: function(effect) {\n    effect.element.setOpacity(effect.options.from).show();\n  }}, arguments[1] || { });\n  return new Effect.Opacity(element,options);\n};\n\nEffect.Puff = function(element) {\n  element = $(element);\n  var oldStyle = {\n    opacity: element.getInlineOpacity(),\n    position: element.getStyle('position'),\n    top:  element.style.top,\n    left: element.style.left,\n    width: element.style.width,\n    height: element.style.height\n  };\n  return new Effect.Parallel(\n   [ new Effect.Scale(element, 200,\n      { sync: true, scaleFromCenter: true, scaleContent: true, restoreAfterFinish: true }),\n     new Effect.Opacity(element, { sync: true, to: 0.0 } ) ],\n     Object.extend({ duration: 1.0,\n      beforeSetupInternal: function(effect) {\n        Position.absolutize(effect.effects[0].element);\n      },\n      afterFinishInternal: function(effect) {\n         effect.effects[0].element.hide().setStyle(oldStyle); }\n     }, arguments[1] || { })\n   );\n};\n\nEffect.BlindUp = function(element) {\n  element = $(element);\n  element.makeClipping();\n  return new Effect.Scale(element, 0,\n    Object.extend({ scaleContent: false,\n      scaleX: false,\n      restoreAfterFinish: true,\n      afterFinishInternal: function(effect) {\n        effect.element.hide().undoClipping();\n      }\n    }, arguments[1] || { })\n  );\n};\n\nEffect.BlindDown = function(element) {\n  element = $(element);\n  var elementDimensions = element.getDimensions();\n  return new Effect.Scale(element, 100, Object.extend({\n    scaleContent: false,\n    scaleX: false,\n    scaleFrom: 0,\n    scaleMode: {originalHeight: elementDimensions.height, originalWidth: elementDimensions.width},\n    restoreAfterFinish: true,\n    afterSetup: function(effect) {\n      effect.element.makeClipping().setStyle({height: '0px'}).show();\n    },\n    afterFinishInternal: function(effect) {\n      effect.element.undoClipping();\n    }\n  }, arguments[1] || { }));\n};\n\nEffect.SwitchOff = function(element) {\n  element = $(element);\n  var oldOpacity = element.getInlineOpacity();\n  return new Effect.Appear(element, Object.extend({\n    duration: 0.4,\n    from: 0,\n    transition: Effect.Transitions.flicker,\n    afterFinishInternal: function(effect) {\n      new Effect.Scale(effect.element, 1, {\n        duration: 0.3, scaleFromCenter: true,\n        scaleX: false, scaleContent: false, restoreAfterFinish: true,\n        beforeSetup: function(effect) {\n          effect.element.makePositioned().makeClipping();\n        },\n        afterFinishInternal: function(effect) {\n          effect.element.hide().undoClipping().undoPositioned().setStyle({opacity: oldOpacity});\n        }\n      });\n    }\n  }, arguments[1] || { }));\n};\n\nEffect.DropOut = function(element) {\n  element = $(element);\n  var oldStyle = {\n    top: element.getStyle('top'),\n    left: element.getStyle('left'),\n    opacity: element.getInlineOpacity() };\n  return new Effect.Parallel(\n    [ new Effect.Move(element, {x: 0, y: 100, sync: true }),\n      new Effect.Opacity(element, { sync: true, to: 0.0 }) ],\n    Object.extend(\n      { duration: 0.5,\n        beforeSetup: function(effect) {\n          effect.effects[0].element.makePositioned();\n        },\n        afterFinishInternal: function(effect) {\n          effect.effects[0].element.hide().undoPositioned().setStyle(oldStyle);\n        }\n      }, arguments[1] || { }));\n};\n\nEffect.Shake = function(element) {\n  element = $(element);\n  var options = Object.extend({\n    distance: 20,\n    duration: 0.5\n  }, arguments[1] || {});\n  var distance = parseFloat(options.distance);\n  var split = parseFloat(options.duration) / 10.0;\n  var oldStyle = {\n    top: element.getStyle('top'),\n    left: element.getStyle('left') };\n    return new Effect.Move(element,\n      { x:  distance, y: 0, duration: split, afterFinishInternal: function(effect) {\n    new Effect.Move(effect.element,\n      { x: -distance*2, y: 0, duration: split*2,  afterFinishInternal: function(effect) {\n    new Effect.Move(effect.element,\n      { x:  distance*2, y: 0, duration: split*2,  afterFinishInternal: function(effect) {\n    new Effect.Move(effect.element,\n      { x: -distance*2, y: 0, duration: split*2,  afterFinishInternal: function(effect) {\n    new Effect.Move(effect.element,\n      { x:  distance*2, y: 0, duration: split*2,  afterFinishInternal: function(effect) {\n    new Effect.Move(effect.element,\n      { x: -distance, y: 0, duration: split, afterFinishInternal: function(effect) {\n        effect.element.undoPositioned().setStyle(oldStyle);\n  }}); }}); }}); }}); }}); }});\n};\n\nEffect.SlideDown = function(element) {\n  element = $(element).cleanWhitespace();\n  // SlideDown need to have the content of the element wrapped in a container element with fixed height!\n  var oldInnerBottom = element.down().getStyle('bottom');\n  var elementDimensions = element.getDimensions();\n  return new Effect.Scale(element, 100, Object.extend({\n    scaleContent: false,\n    scaleX: false,\n    scaleFrom: window.opera ? 0 : 1,\n    scaleMode: {originalHeight: elementDimensions.height, originalWidth: elementDimensions.width},\n    restoreAfterFinish: true,\n    afterSetup: function(effect) {\n      effect.element.makePositioned();\n      effect.element.down().makePositioned();\n      if (window.opera) effect.element.setStyle({top: ''});\n      effect.element.makeClipping().setStyle({height: '0px'}).show();\n    },\n    afterUpdateInternal: function(effect) {\n      effect.element.down().setStyle({bottom:\n        (effect.dims[0] - effect.element.clientHeight) + 'px' });\n    },\n    afterFinishInternal: function(effect) {\n      effect.element.undoClipping().undoPositioned();\n      effect.element.down().undoPositioned().setStyle({bottom: oldInnerBottom}); }\n    }, arguments[1] || { })\n  );\n};\n\nEffect.SlideUp = function(element) {\n  element = $(element).cleanWhitespace();\n  var oldInnerBottom = element.down().getStyle('bottom');\n  var elementDimensions = element.getDimensions();\n  return new Effect.Scale(element, window.opera ? 0 : 1,\n   Object.extend({ scaleContent: false,\n    scaleX: false,\n    scaleMode: 'box',\n    scaleFrom: 100,\n    scaleMode: {originalHeight: elementDimensions.height, originalWidth: elementDimensions.width},\n    restoreAfterFinish: true,\n    afterSetup: function(effect) {\n      effect.element.makePositioned();\n      effect.element.down().makePositioned();\n      if (window.opera) effect.element.setStyle({top: ''});\n      effect.element.makeClipping().show();\n    },\n    afterUpdateInternal: function(effect) {\n      effect.element.down().setStyle({bottom:\n        (effect.dims[0] - effect.element.clientHeight) + 'px' });\n    },\n    afterFinishInternal: function(effect) {\n      effect.element.hide().undoClipping().undoPositioned();\n      effect.element.down().undoPositioned().setStyle({bottom: oldInnerBottom});\n    }\n   }, arguments[1] || { })\n  );\n};\n\n// Bug in opera makes the TD containing this element expand for a instance after finish\nEffect.Squish = function(element) {\n  return new Effect.Scale(element, window.opera ? 1 : 0, {\n    restoreAfterFinish: true,\n    beforeSetup: function(effect) {\n      effect.element.makeClipping();\n    },\n    afterFinishInternal: function(effect) {\n      effect.element.hide().undoClipping();\n    }\n  });\n};\n\nEffect.Grow = function(element) {\n  element = $(element);\n  var options = Object.extend({\n    direction: 'center',\n    moveTransition: Effect.Transitions.sinoidal,\n    scaleTransition: Effect.Transitions.sinoidal,\n    opacityTransition: Effect.Transitions.full\n  }, arguments[1] || { });\n  var oldStyle = {\n    top: element.style.top,\n    left: element.style.left,\n    height: element.style.height,\n    width: element.style.width,\n    opacity: element.getInlineOpacity() };\n\n  var dims = element.getDimensions();\n  var initialMoveX, initialMoveY;\n  var moveX, moveY;\n\n  switch (options.direction) {\n    case 'top-left':\n      initialMoveX = initialMoveY = moveX = moveY = 0;\n      break;\n    case 'top-right':\n      initialMoveX = dims.width;\n      initialMoveY = moveY = 0;\n      moveX = -dims.width;\n      break;\n    case 'bottom-left':\n      initialMoveX = moveX = 0;\n      initialMoveY = dims.height;\n      moveY = -dims.height;\n      break;\n    case 'bottom-right':\n      initialMoveX = dims.width;\n      initialMoveY = dims.height;\n      moveX = -dims.width;\n      moveY = -dims.height;\n      break;\n    case 'center':\n      initialMoveX = dims.width / 2;\n      initialMoveY = dims.height / 2;\n      moveX = -dims.width / 2;\n      moveY = -dims.height / 2;\n      break;\n  }\n\n  return new Effect.Move(element, {\n    x: initialMoveX,\n    y: initialMoveY,\n    duration: 0.01,\n    beforeSetup: function(effect) {\n      effect.element.hide().makeClipping().makePositioned();\n    },\n    afterFinishInternal: function(effect) {\n      new Effect.Parallel(\n        [ new Effect.Opacity(effect.element, { sync: true, to: 1.0, from: 0.0, transition: options.opacityTransition }),\n          new Effect.Move(effect.element, { x: moveX, y: moveY, sync: true, transition: options.moveTransition }),\n          new Effect.Scale(effect.element, 100, {\n            scaleMode: { originalHeight: dims.height, originalWidth: dims.width },\n            sync: true, scaleFrom: window.opera ? 1 : 0, transition: options.scaleTransition, restoreAfterFinish: true})\n        ], Object.extend({\n             beforeSetup: function(effect) {\n               effect.effects[0].element.setStyle({height: '0px'}).show();\n             },\n             afterFinishInternal: function(effect) {\n               effect.effects[0].element.undoClipping().undoPositioned().setStyle(oldStyle);\n             }\n           }, options)\n      );\n    }\n  });\n};\n\nEffect.Shrink = function(element) {\n  element = $(element);\n  var options = Object.extend({\n    direction: 'center',\n    moveTransition: Effect.Transitions.sinoidal,\n    scaleTransition: Effect.Transitions.sinoidal,\n    opacityTransition: Effect.Transitions.none\n  }, arguments[1] || { });\n  var oldStyle = {\n    top: element.style.top,\n    left: element.style.left,\n    height: element.style.height,\n    width: element.style.width,\n    opacity: element.getInlineOpacity() };\n\n  var dims = element.getDimensions();\n  var moveX, moveY;\n\n  switch (options.direction) {\n    case 'top-left':\n      moveX = moveY = 0;\n      break;\n    case 'top-right':\n      moveX = dims.width;\n      moveY = 0;\n      break;\n    case 'bottom-left':\n      moveX = 0;\n      moveY = dims.height;\n      break;\n    case 'bottom-right':\n      moveX = dims.width;\n      moveY = dims.height;\n      break;\n    case 'center':\n      moveX = dims.width / 2;\n      moveY = dims.height / 2;\n      break;\n  }\n\n  return new Effect.Parallel(\n    [ new Effect.Opacity(element, { sync: true, to: 0.0, from: 1.0, transition: options.opacityTransition }),\n      new Effect.Scale(element, window.opera ? 1 : 0, { sync: true, transition: options.scaleTransition, restoreAfterFinish: true}),\n      new Effect.Move(element, { x: moveX, y: moveY, sync: true, transition: options.moveTransition })\n    ], Object.extend({\n         beforeStartInternal: function(effect) {\n           effect.effects[0].element.makePositioned().makeClipping();\n         },\n         afterFinishInternal: function(effect) {\n           effect.effects[0].element.hide().undoClipping().undoPositioned().setStyle(oldStyle); }\n       }, options)\n  );\n};\n\nEffect.Pulsate = function(element) {\n  element = $(element);\n  var options    = arguments[1] || { },\n    oldOpacity = element.getInlineOpacity(),\n    transition = options.transition || Effect.Transitions.linear,\n    reverser   = function(pos){\n      return 1 - transition((-Math.cos((pos*(options.pulses||5)*2)*Math.PI)/2) + .5);\n    };\n\n  return new Effect.Opacity(element,\n    Object.extend(Object.extend({  duration: 2.0, from: 0,\n      afterFinishInternal: function(effect) { effect.element.setStyle({opacity: oldOpacity}); }\n    }, options), {transition: reverser}));\n};\n\nEffect.Fold = function(element) {\n  element = $(element);\n  var oldStyle = {\n    top: element.style.top,\n    left: element.style.left,\n    width: element.style.width,\n    height: element.style.height };\n  element.makeClipping();\n  return new Effect.Scale(element, 5, Object.extend({\n    scaleContent: false,\n    scaleX: false,\n    afterFinishInternal: function(effect) {\n    new Effect.Scale(element, 1, {\n      scaleContent: false,\n      scaleY: false,\n      afterFinishInternal: function(effect) {\n        effect.element.hide().undoClipping().setStyle(oldStyle);\n      } });\n  }}, arguments[1] || { }));\n};\n\nEffect.Morph = Class.create(Effect.Base, {\n  initialize: function(element) {\n    this.element = $(element);\n    if (!this.element) throw(Effect._elementDoesNotExistError);\n    var options = Object.extend({\n      style: { }\n    }, arguments[1] || { });\n\n    if (!Object.isString(options.style)) this.style = $H(options.style);\n    else {\n      if (options.style.include(':'))\n        this.style = options.style.parseStyle();\n      else {\n        this.element.addClassName(options.style);\n        this.style = $H(this.element.getStyles());\n        this.element.removeClassName(options.style);\n        var css = this.element.getStyles();\n        this.style = this.style.reject(function(style) {\n          return style.value == css[style.key];\n        });\n        options.afterFinishInternal = function(effect) {\n          effect.element.addClassName(effect.options.style);\n          effect.transforms.each(function(transform) {\n            effect.element.style[transform.style] = '';\n          });\n        };\n      }\n    }\n    this.start(options);\n  },\n\n  setup: function(){\n    function parseColor(color){\n      if (!color || ['rgba(0, 0, 0, 0)','transparent'].include(color)) color = '#ffffff';\n      color = color.parseColor();\n      return $R(0,2).map(function(i){\n        return parseInt( color.slice(i*2+1,i*2+3), 16 );\n      });\n    }\n    this.transforms = this.style.map(function(pair){\n      var property = pair[0], value = pair[1], unit = null;\n\n      if (value.parseColor('#zzzzzz') != '#zzzzzz') {\n        value = value.parseColor();\n        unit  = 'color';\n      } else if (property == 'opacity') {\n        value = parseFloat(value);\n        if (Prototype.Browser.IE && (!this.element.currentStyle.hasLayout))\n          this.element.setStyle({zoom: 1});\n      } else if (Element.CSS_LENGTH.test(value)) {\n          var components = value.match(/^([\\+\\-]?[0-9\\.]+)(.*)$/);\n          value = parseFloat(components[1]);\n          unit = (components.length == 3) ? components[2] : null;\n      }\n\n      var originalValue = this.element.getStyle(property);\n      return {\n        style: property.camelize(),\n        originalValue: unit=='color' ? parseColor(originalValue) : parseFloat(originalValue || 0),\n        targetValue: unit=='color' ? parseColor(value) : value,\n        unit: unit\n      };\n    }.bind(this)).reject(function(transform){\n      return (\n        (transform.originalValue == transform.targetValue) ||\n        (\n          transform.unit != 'color' &&\n          (isNaN(transform.originalValue) || isNaN(transform.targetValue))\n        )\n      );\n    });\n  },\n  update: function(position) {\n    var style = { }, transform, i = this.transforms.length;\n    while(i--)\n      style[(transform = this.transforms[i]).style] =\n        transform.unit=='color' ? '#'+\n          (Math.round(transform.originalValue[0]+\n            (transform.targetValue[0]-transform.originalValue[0])*position)).toColorPart() +\n          (Math.round(transform.originalValue[1]+\n            (transform.targetValue[1]-transform.originalValue[1])*position)).toColorPart() +\n          (Math.round(transform.originalValue[2]+\n            (transform.targetValue[2]-transform.originalValue[2])*position)).toColorPart() :\n        (transform.originalValue +\n          (transform.targetValue - transform.originalValue) * position).toFixed(3) +\n            (transform.unit === null ? '' : transform.unit);\n    this.element.setStyle(style, true);\n  }\n});\n\nEffect.Transform = Class.create({\n  initialize: function(tracks){\n    this.tracks  = [];\n    this.options = arguments[1] || { };\n    this.addTracks(tracks);\n  },\n  addTracks: function(tracks){\n    tracks.each(function(track){\n      track = $H(track);\n      var data = track.values().first();\n      this.tracks.push($H({\n        ids:     track.keys().first(),\n        effect:  Effect.Morph,\n        options: { style: data }\n      }));\n    }.bind(this));\n    return this;\n  },\n  play: function(){\n    return new Effect.Parallel(\n      this.tracks.map(function(track){\n        var ids = track.get('ids'), effect = track.get('effect'), options = track.get('options');\n        var elements = [$(ids) || $$(ids)].flatten();\n        return elements.map(function(e){ return new effect(e, Object.extend({ sync:true }, options)) });\n      }).flatten(),\n      this.options\n    );\n  }\n});\n\nElement.CSS_PROPERTIES = $w(\n  'backgroundColor backgroundPosition borderBottomColor borderBottomStyle ' +\n  'borderBottomWidth borderLeftColor borderLeftStyle borderLeftWidth ' +\n  'borderRightColor borderRightStyle borderRightWidth borderSpacing ' +\n  'borderTopColor borderTopStyle borderTopWidth bottom clip color ' +\n  'fontSize fontWeight height left letterSpacing lineHeight ' +\n  'marginBottom marginLeft marginRight marginTop markerOffset maxHeight '+\n  'maxWidth minHeight minWidth opacity outlineColor outlineOffset ' +\n  'outlineWidth paddingBottom paddingLeft paddingRight paddingTop ' +\n  'right textIndent top width wordSpacing zIndex');\n\nElement.CSS_LENGTH = /^(([\\+\\-]?[0-9\\.]+)(em|ex|px|in|cm|mm|pt|pc|\\%))|0$/;\n\nString.__parseStyleElement = document.createElement('div');\nString.prototype.parseStyle = function(){\n  var style, styleRules = $H();\n  if (Prototype.Browser.WebKit)\n    style = new Element('div',{style:this}).style;\n  else {\n    String.__parseStyleElement.innerHTML = '<div style=\"' + this + '\"></div>';\n    style = String.__parseStyleElement.childNodes[0].style;\n  }\n\n  Element.CSS_PROPERTIES.each(function(property){\n    if (style[property]) styleRules.set(property, style[property]);\n  });\n\n  if (Prototype.Browser.IE && this.include('opacity'))\n    styleRules.set('opacity', this.match(/opacity:\\s*((?:0|1)?(?:\\.\\d*)?)/)[1]);\n\n  return styleRules;\n};\n\nif (document.defaultView && document.defaultView.getComputedStyle) {\n  Element.getStyles = function(element) {\n    var css = document.defaultView.getComputedStyle($(element), null);\n    return Element.CSS_PROPERTIES.inject({ }, function(styles, property) {\n      styles[property] = css[property];\n      return styles;\n    });\n  };\n} else {\n  Element.getStyles = function(element) {\n    element = $(element);\n    var css = element.currentStyle, styles;\n    styles = Element.CSS_PROPERTIES.inject({ }, function(results, property) {\n      results[property] = css[property];\n      return results;\n    });\n    if (!styles.opacity) styles.opacity = element.getOpacity();\n    return styles;\n  };\n}\n\nEffect.Methods = {\n  morph: function(element, style) {\n    element = $(element);\n    new Effect.Morph(element, Object.extend({ style: style }, arguments[2] || { }));\n    return element;\n  },\n  visualEffect: function(element, effect, options) {\n    element = $(element);\n    var s = effect.dasherize().camelize(), klass = s.charAt(0).toUpperCase() + s.substring(1);\n    new Effect[klass](element, options);\n    return element;\n  },\n  highlight: function(element, options) {\n    element = $(element);\n    new Effect.Highlight(element, options);\n    return element;\n  }\n};\n\n$w('fade appear grow shrink fold blindUp blindDown slideUp slideDown '+\n  'pulsate shake puff squish switchOff dropOut').each(\n  function(effect) {\n    Effect.Methods[effect] = function(element, options){\n      element = $(element);\n      Effect[effect.charAt(0).toUpperCase() + effect.substring(1)](element, options);\n      return element;\n    };\n  }\n);\n\n$w('getInlineOpacity forceRerendering setContentZoom collectTextNodes collectTextNodesIgnoreClass getStyles').each(\n  function(f) { Effect.Methods[f] = Element[f]; }\n);\n\nElement.addMethods(Effect.Methods);"
  },
  {
    "path": "prototype/iphone-style-checkboxes.js",
    "content": "var iPhoneStyle = function(selector_or_elems, options) {\n  options = Object.extend(Object.clone(iPhoneStyle.defaults), options || {});\n  var elems;\n  if (Object.isString(selector_or_elems)) {\n    elems = $$(selector_or_elems);\n  } else {\n    elems = [selector_or_elems].flatten();\n  }\n  return(elems.each(function(elem) {\n    \n    if (!elem.match('input[type=checkbox]')) {\n      return;\n    }\n    \n    if (elem.hasClassName(options.statusClass)) {\n        return;\n    }\n    \n    elem.addClassName(options.statusClass);\n    elem.setOpacity(0);\n    elem.wrap('div', { 'class': options.containerClass});\n    elem.insert({ 'after': '<div class=\"' + options.handleClass + '\"><div class=\"' + options.handleRightClass + '\"><div class=\"' + options.handleCenterClass + '\"></div></div></div>' })\n        .insert({ 'after': '<label class=\"' + options.labelOnClass + '\"><span>' + options.checkedLabel   + '</span></label>' })\n        .insert({ 'after': '<label class=\"' + options.labelOffClass + '\"><span>'+ options.uncheckedLabel + '</span></label>' });\n    \n    var handle  = elem.up().down('.' + options.handleClass),\n      offlabel  = elem.adjacent('.' + options.labelOffClass).first(),\n      offspan   = offlabel.down('span'),\n      onlabel   = elem.adjacent('.' + options.labelOnClass).first(),\n      onspan    = onlabel.down('span'),\n      container = elem.up();\n      \n    if (options.resizeHandle) {\n      var min = (onlabel.getWidth() < offlabel.getWidth()) ? onlabel.getWidth() : offlabel.getWidth();\n      handle.setStyle({width: min + 'px'});\n    }\n    if (options.resizeContainer) {\n      var max = (onlabel.getWidth() > offlabel.getWidth()) ? onlabel.getWidth() : offlabel.getWidth();\n      container.setStyle({width: max + handle.getWidth() + 12 + 'px'});\n    }\n    offlabel.setStyle({width: container.getWidth() - 5  + 'px'});\n\n    var rightside = container.getWidth() - handle.getWidth() - 3;\n\n    if (elem.checked) {\n      handle.setStyle({ left: rightside + 'px' });\n      onlabel.setStyle({ width: rightside + 4 + 'px' });\n      offspan.setStyle({ 'marginRight': rightside + 'px' });\n    } else {\n      handle.setStyle({ left: 0 });\n      onlabel.setStyle({ width: 0 });\n      onspan.setStyle({ 'marginLeft': -rightside + 'px' });\n    }    \n\n    elem.change = function() {\n      var is_onstate = elem.checked;\n      var p = handle.positionedOffset()[0] / rightside;\n      new Effect.Tween(null, p, Number(is_onstate), { duration: options.duration / 1000 }, function(p) {\n        handle.setStyle({ left: p * rightside + 'px' });\n        onlabel.setStyle({ width: p * rightside + 4 + 'px' });\n        offlabel.setStyle({ width: (1 - p) * rightside + 4 + 'px' });\n        offspan.setStyle({ 'marginRight': -p * rightside + 'px' });\n        onspan.setStyle({ 'marginLeft': -(1 - p) * rightside + 'px' });\n      });\n      options.statusChange(elem);\n    };\n    elem.observe('change', elem.change);\n    \n    var down = function(e) {\n      e.stop();\n      iPhoneStyle.clicking = handle;\n      var x = Event.pointerX(e) || e.changedTouches[0].pageX;\n      iPhoneStyle.dragStartPosition = x - (Number(handle.style.left.replace(/px$/, \"\")) || 0);\n      Event.stop(e);\n    };\n\n    container.observe('mousedown', down);\n    container.observe('touchstart', down);\n    \n    \n    var move = function(e) {\n      if (iPhoneStyle.clicking == handle) {\n        e.stop();\n        var x = Event.pointerX(e) || e.changedTouches[0].pageX;\n        if (x != iPhoneStyle.dragStartPosition) {\n          iPhoneStyle.dragging = true;\n        }\n        var p = (x - iPhoneStyle.dragStartPosition) / rightside;\n        if (p < 0) { p = 0; }\n        if (p > 1) { p = 1; }\n        handle.setStyle({ left: p * rightside + 'px' });\n        onlabel.setStyle({ width: p * rightside + 4 + 'px' });\n        offlabel.setStyle({ width: (1 - p) * rightside + 4 + 'px' });\n        offspan.setStyle({ 'marginRight': -p * rightside + 'px' });\n        onspan.setStyle({ 'marginLeft': -(1 - p) * rightside + 'px' });\n      }\n    };\n\n    document.observe('mousemove', move);\n    document.observe('touchmove', move);\n    \n    container.observe('mousedown', function(e) {\n      e.stop();\n      iPhoneStyle.clicking = handle;\n      iPhoneStyle.dragStartPosition = Event.pointerX(e) - (Number(handle.style.left.replace(/px$/, \"\")) || 0);\n      return false;\n    });\n    \n    var up = function(e) {\n      if (iPhoneStyle.clicking == handle) {\n        e.stop();\n        if (!iPhoneStyle.dragging) {\n          var is_onstate = elem.checked;\n          elem.writeAttribute('checked', !is_onstate);\n        } else {\n          var x = Event.pointerX(e) || e.changedTouches[0].pageX;\n          var p = (x - iPhoneStyle.dragStartPosition) / rightside;\n          elem.writeAttribute('checked', (p >= 0.5));\n        }\n        iPhoneStyle.clicking = null;\n        iPhoneStyle.dragging = null;\n        elem.change();\n      }\n    };\n    document.observe('touchend', up);\n    document.observe('mouseup', up);\n\n    // Disable text selection\n    [container, onlabel, offlabel, handle].invoke('observe', 'mousedown', function(e) { e.preventDefault(); return false; });\n    if (Prototype.Browser.IE) {\n      [container, onlabel, offlabel, handle].invoke('observe', 'startselect', function(e) { Event.stop(e); return false; });\n    }\n  }));\n};\n\niPhoneStyle.defaults = {\n  duration:          200,\n  checkedLabel:      'ON', \n  uncheckedLabel:    'OFF', \n  resizeHandle:      true,\n  resizeContainer:   true,\n  background:        '#fff',\n  statusClass:       'checkRendered',\n  containerClass:    'iPhoneCheckContainer',\n  labelOnClass:      'iPhoneCheckLabelOn',\n  labelOffClass:     'iPhoneCheckLabelOff',\n  handleClass:       'iPhoneCheckHandle',\n  handleCenterClass: 'iPhoneCheckHandleCenter',\n  handleRightClass:  'iPhoneCheckHandleRight',\n  statusChange: function(){}\n};\n"
  },
  {
    "path": "prototype/prototype.js",
    "content": "/*  Prototype JavaScript framework, version 1.6.1\n *  (c) 2005-2009 Sam Stephenson\n *\n *  Prototype is freely distributable under the terms of an MIT-style license.\n *  For details, see the Prototype web site: http://www.prototypejs.org/\n *\n *--------------------------------------------------------------------------*/\n\nvar Prototype = {\n  Version: '1.6.1',\n\n  Browser: (function(){\n    var ua = navigator.userAgent;\n    var isOpera = Object.prototype.toString.call(window.opera) == '[object Opera]';\n    return {\n      IE:             !!window.attachEvent && !isOpera,\n      Opera:          isOpera,\n      WebKit:         ua.indexOf('AppleWebKit/') > -1,\n      Gecko:          ua.indexOf('Gecko') > -1 && ua.indexOf('KHTML') === -1,\n      MobileSafari:   /Apple.*Mobile.*Safari/.test(ua)\n    }\n  })(),\n\n  BrowserFeatures: {\n    XPath: !!document.evaluate,\n    SelectorsAPI: !!document.querySelector,\n    ElementExtensions: (function() {\n      var constructor = window.Element || window.HTMLElement;\n      return !!(constructor && constructor.prototype);\n    })(),\n    SpecificElementExtensions: (function() {\n      if (typeof window.HTMLDivElement !== 'undefined')\n        return true;\n\n      var div = document.createElement('div');\n      var form = document.createElement('form');\n      var isSupported = false;\n\n      if (div['__proto__'] && (div['__proto__'] !== form['__proto__'])) {\n        isSupported = true;\n      }\n\n      div = form = null;\n\n      return isSupported;\n    })()\n  },\n\n  ScriptFragment: '<script[^>]*>([\\\\S\\\\s]*?)<\\/script>',\n  JSONFilter: /^\\/\\*-secure-([\\s\\S]*)\\*\\/\\s*$/,\n\n  emptyFunction: function() { },\n  K: function(x) { return x }\n};\n\nif (Prototype.Browser.MobileSafari)\n  Prototype.BrowserFeatures.SpecificElementExtensions = false;\n\n\nvar Abstract = { };\n\n\nvar Try = {\n  these: function() {\n    var returnValue;\n\n    for (var i = 0, length = arguments.length; i < length; i++) {\n      var lambda = arguments[i];\n      try {\n        returnValue = lambda();\n        break;\n      } catch (e) { }\n    }\n\n    return returnValue;\n  }\n};\n\n/* Based on Alex Arnell's inheritance implementation. */\n\nvar Class = (function() {\n  function subclass() {};\n  function create() {\n    var parent = null, properties = $A(arguments);\n    if (Object.isFunction(properties[0]))\n      parent = properties.shift();\n\n    function klass() {\n      this.initialize.apply(this, arguments);\n    }\n\n    Object.extend(klass, Class.Methods);\n    klass.superclass = parent;\n    klass.subclasses = [];\n\n    if (parent) {\n      subclass.prototype = parent.prototype;\n      klass.prototype = new subclass;\n      parent.subclasses.push(klass);\n    }\n\n    for (var i = 0; i < properties.length; i++)\n      klass.addMethods(properties[i]);\n\n    if (!klass.prototype.initialize)\n      klass.prototype.initialize = Prototype.emptyFunction;\n\n    klass.prototype.constructor = klass;\n    return klass;\n  }\n\n  function addMethods(source) {\n    var ancestor   = this.superclass && this.superclass.prototype;\n    var properties = Object.keys(source);\n\n    if (!Object.keys({ toString: true }).length) {\n      if (source.toString != Object.prototype.toString)\n        properties.push(\"toString\");\n      if (source.valueOf != Object.prototype.valueOf)\n        properties.push(\"valueOf\");\n    }\n\n    for (var i = 0, length = properties.length; i < length; i++) {\n      var property = properties[i], value = source[property];\n      if (ancestor && Object.isFunction(value) &&\n          value.argumentNames().first() == \"$super\") {\n        var method = value;\n        value = (function(m) {\n          return function() { return ancestor[m].apply(this, arguments); };\n        })(property).wrap(method);\n\n        value.valueOf = method.valueOf.bind(method);\n        value.toString = method.toString.bind(method);\n      }\n      this.prototype[property] = value;\n    }\n\n    return this;\n  }\n\n  return {\n    create: create,\n    Methods: {\n      addMethods: addMethods\n    }\n  };\n})();\n(function() {\n\n  var _toString = Object.prototype.toString;\n\n  function extend(destination, source) {\n    for (var property in source)\n      destination[property] = source[property];\n    return destination;\n  }\n\n  function inspect(object) {\n    try {\n      if (isUndefined(object)) return 'undefined';\n      if (object === null) return 'null';\n      return object.inspect ? object.inspect() : String(object);\n    } catch (e) {\n      if (e instanceof RangeError) return '...';\n      throw e;\n    }\n  }\n\n  function toJSON(object) {\n    var type = typeof object;\n    switch (type) {\n      case 'undefined':\n      case 'function':\n      case 'unknown': return;\n      case 'boolean': return object.toString();\n    }\n\n    if (object === null) return 'null';\n    if (object.toJSON) return object.toJSON();\n    if (isElement(object)) return;\n\n    var results = [];\n    for (var property in object) {\n      var value = toJSON(object[property]);\n      if (!isUndefined(value))\n        results.push(property.toJSON() + ': ' + value);\n    }\n\n    return '{' + results.join(', ') + '}';\n  }\n\n  function toQueryString(object) {\n    return $H(object).toQueryString();\n  }\n\n  function toHTML(object) {\n    return object && object.toHTML ? object.toHTML() : String.interpret(object);\n  }\n\n  function keys(object) {\n    var results = [];\n    for (var property in object)\n      results.push(property);\n    return results;\n  }\n\n  function values(object) {\n    var results = [];\n    for (var property in object)\n      results.push(object[property]);\n    return results;\n  }\n\n  function clone(object) {\n    return extend({ }, object);\n  }\n\n  function isElement(object) {\n    return !!(object && object.nodeType == 1);\n  }\n\n  function isArray(object) {\n    return _toString.call(object) == \"[object Array]\";\n  }\n\n\n  function isHash(object) {\n    return object instanceof Hash;\n  }\n\n  function isFunction(object) {\n    return typeof object === \"function\";\n  }\n\n  function isString(object) {\n    return _toString.call(object) == \"[object String]\";\n  }\n\n  function isNumber(object) {\n    return _toString.call(object) == \"[object Number]\";\n  }\n\n  function isUndefined(object) {\n    return typeof object === \"undefined\";\n  }\n\n  extend(Object, {\n    extend:        extend,\n    inspect:       inspect,\n    toJSON:        toJSON,\n    toQueryString: toQueryString,\n    toHTML:        toHTML,\n    keys:          keys,\n    values:        values,\n    clone:         clone,\n    isElement:     isElement,\n    isArray:       isArray,\n    isHash:        isHash,\n    isFunction:    isFunction,\n    isString:      isString,\n    isNumber:      isNumber,\n    isUndefined:   isUndefined\n  });\n})();\nObject.extend(Function.prototype, (function() {\n  var slice = Array.prototype.slice;\n\n  function update(array, args) {\n    var arrayLength = array.length, length = args.length;\n    while (length--) array[arrayLength + length] = args[length];\n    return array;\n  }\n\n  function merge(array, args) {\n    array = slice.call(array, 0);\n    return update(array, args);\n  }\n\n  function argumentNames() {\n    var names = this.toString().match(/^[\\s\\(]*function[^(]*\\(([^)]*)\\)/)[1]\n      .replace(/\\/\\/.*?[\\r\\n]|\\/\\*(?:.|[\\r\\n])*?\\*\\//g, '')\n      .replace(/\\s+/g, '').split(',');\n    return names.length == 1 && !names[0] ? [] : names;\n  }\n\n  function bind(context) {\n    if (arguments.length < 2 && Object.isUndefined(arguments[0])) return this;\n    var __method = this, args = slice.call(arguments, 1);\n    return function() {\n      var a = merge(args, arguments);\n      return __method.apply(context, a);\n    }\n  }\n\n  function bindAsEventListener(context) {\n    var __method = this, args = slice.call(arguments, 1);\n    return function(event) {\n      var a = update([event || window.event], args);\n      return __method.apply(context, a);\n    }\n  }\n\n  function curry() {\n    if (!arguments.length) return this;\n    var __method = this, args = slice.call(arguments, 0);\n    return function() {\n      var a = merge(args, arguments);\n      return __method.apply(this, a);\n    }\n  }\n\n  function delay(timeout) {\n    var __method = this, args = slice.call(arguments, 1);\n    timeout = timeout * 1000\n    return window.setTimeout(function() {\n      return __method.apply(__method, args);\n    }, timeout);\n  }\n\n  function defer() {\n    var args = update([0.01], arguments);\n    return this.delay.apply(this, args);\n  }\n\n  function wrap(wrapper) {\n    var __method = this;\n    return function() {\n      var a = update([__method.bind(this)], arguments);\n      return wrapper.apply(this, a);\n    }\n  }\n\n  function methodize() {\n    if (this._methodized) return this._methodized;\n    var __method = this;\n    return this._methodized = function() {\n      var a = update([this], arguments);\n      return __method.apply(null, a);\n    };\n  }\n\n  return {\n    argumentNames:       argumentNames,\n    bind:                bind,\n    bindAsEventListener: bindAsEventListener,\n    curry:               curry,\n    delay:               delay,\n    defer:               defer,\n    wrap:                wrap,\n    methodize:           methodize\n  }\n})());\n\n\nDate.prototype.toJSON = function() {\n  return '\"' + this.getUTCFullYear() + '-' +\n    (this.getUTCMonth() + 1).toPaddedString(2) + '-' +\n    this.getUTCDate().toPaddedString(2) + 'T' +\n    this.getUTCHours().toPaddedString(2) + ':' +\n    this.getUTCMinutes().toPaddedString(2) + ':' +\n    this.getUTCSeconds().toPaddedString(2) + 'Z\"';\n};\n\n\nRegExp.prototype.match = RegExp.prototype.test;\n\nRegExp.escape = function(str) {\n  return String(str).replace(/([.*+?^=!:${}()|[\\]\\/\\\\])/g, '\\\\$1');\n};\nvar PeriodicalExecuter = Class.create({\n  initialize: function(callback, frequency) {\n    this.callback = callback;\n    this.frequency = frequency;\n    this.currentlyExecuting = false;\n\n    this.registerCallback();\n  },\n\n  registerCallback: function() {\n    this.timer = setInterval(this.onTimerEvent.bind(this), this.frequency * 1000);\n  },\n\n  execute: function() {\n    this.callback(this);\n  },\n\n  stop: function() {\n    if (!this.timer) return;\n    clearInterval(this.timer);\n    this.timer = null;\n  },\n\n  onTimerEvent: function() {\n    if (!this.currentlyExecuting) {\n      try {\n        this.currentlyExecuting = true;\n        this.execute();\n        this.currentlyExecuting = false;\n      } catch(e) {\n        this.currentlyExecuting = false;\n        throw e;\n      }\n    }\n  }\n});\nObject.extend(String, {\n  interpret: function(value) {\n    return value == null ? '' : String(value);\n  },\n  specialChar: {\n    '\\b': '\\\\b',\n    '\\t': '\\\\t',\n    '\\n': '\\\\n',\n    '\\f': '\\\\f',\n    '\\r': '\\\\r',\n    '\\\\': '\\\\\\\\'\n  }\n});\n\nObject.extend(String.prototype, (function() {\n\n  function prepareReplacement(replacement) {\n    if (Object.isFunction(replacement)) return replacement;\n    var template = new Template(replacement);\n    return function(match) { return template.evaluate(match) };\n  }\n\n  function gsub(pattern, replacement) {\n    var result = '', source = this, match;\n    replacement = prepareReplacement(replacement);\n\n    if (Object.isString(pattern))\n      pattern = RegExp.escape(pattern);\n\n    if (!(pattern.length || pattern.source)) {\n      replacement = replacement('');\n      return replacement + source.split('').join(replacement) + replacement;\n    }\n\n    while (source.length > 0) {\n      if (match = source.match(pattern)) {\n        result += source.slice(0, match.index);\n        result += String.interpret(replacement(match));\n        source  = source.slice(match.index + match[0].length);\n      } else {\n        result += source, source = '';\n      }\n    }\n    return result;\n  }\n\n  function sub(pattern, replacement, count) {\n    replacement = prepareReplacement(replacement);\n    count = Object.isUndefined(count) ? 1 : count;\n\n    return this.gsub(pattern, function(match) {\n      if (--count < 0) return match[0];\n      return replacement(match);\n    });\n  }\n\n  function scan(pattern, iterator) {\n    this.gsub(pattern, iterator);\n    return String(this);\n  }\n\n  function truncate(length, truncation) {\n    length = length || 30;\n    truncation = Object.isUndefined(truncation) ? '...' : truncation;\n    return this.length > length ?\n      this.slice(0, length - truncation.length) + truncation : String(this);\n  }\n\n  function strip() {\n    return this.replace(/^\\s+/, '').replace(/\\s+$/, '');\n  }\n\n  function stripTags() {\n    return this.replace(/<\\w+(\\s+(\"[^\"]*\"|'[^']*'|[^>])+)?>|<\\/\\w+>/gi, '');\n  }\n\n  function stripScripts() {\n    return this.replace(new RegExp(Prototype.ScriptFragment, 'img'), '');\n  }\n\n  function extractScripts() {\n    var matchAll = new RegExp(Prototype.ScriptFragment, 'img');\n    var matchOne = new RegExp(Prototype.ScriptFragment, 'im');\n    return (this.match(matchAll) || []).map(function(scriptTag) {\n      return (scriptTag.match(matchOne) || ['', ''])[1];\n    });\n  }\n\n  function evalScripts() {\n    return this.extractScripts().map(function(script) { return eval(script) });\n  }\n\n  function escapeHTML() {\n    return this.replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;');\n  }\n\n  function unescapeHTML() {\n    return this.stripTags().replace(/&lt;/g,'<').replace(/&gt;/g,'>').replace(/&amp;/g,'&');\n  }\n\n\n  function toQueryParams(separator) {\n    var match = this.strip().match(/([^?#]*)(#.*)?$/);\n    if (!match) return { };\n\n    return match[1].split(separator || '&').inject({ }, function(hash, pair) {\n      if ((pair = pair.split('='))[0]) {\n        var key = decodeURIComponent(pair.shift());\n        var value = pair.length > 1 ? pair.join('=') : pair[0];\n        if (value != undefined) value = decodeURIComponent(value);\n\n        if (key in hash) {\n          if (!Object.isArray(hash[key])) hash[key] = [hash[key]];\n          hash[key].push(value);\n        }\n        else hash[key] = value;\n      }\n      return hash;\n    });\n  }\n\n  function toArray() {\n    return this.split('');\n  }\n\n  function succ() {\n    return this.slice(0, this.length - 1) +\n      String.fromCharCode(this.charCodeAt(this.length - 1) + 1);\n  }\n\n  function times(count) {\n    return count < 1 ? '' : new Array(count + 1).join(this);\n  }\n\n  function camelize() {\n    var parts = this.split('-'), len = parts.length;\n    if (len == 1) return parts[0];\n\n    var camelized = this.charAt(0) == '-'\n      ? parts[0].charAt(0).toUpperCase() + parts[0].substring(1)\n      : parts[0];\n\n    for (var i = 1; i < len; i++)\n      camelized += parts[i].charAt(0).toUpperCase() + parts[i].substring(1);\n\n    return camelized;\n  }\n\n  function capitalize() {\n    return this.charAt(0).toUpperCase() + this.substring(1).toLowerCase();\n  }\n\n  function underscore() {\n    return this.replace(/::/g, '/')\n               .replace(/([A-Z]+)([A-Z][a-z])/g, '$1_$2')\n               .replace(/([a-z\\d])([A-Z])/g, '$1_$2')\n               .replace(/-/g, '_')\n               .toLowerCase();\n  }\n\n  function dasherize() {\n    return this.replace(/_/g, '-');\n  }\n\n  function inspect(useDoubleQuotes) {\n    var escapedString = this.replace(/[\\x00-\\x1f\\\\]/g, function(character) {\n      if (character in String.specialChar) {\n        return String.specialChar[character];\n      }\n      return '\\\\u00' + character.charCodeAt().toPaddedString(2, 16);\n    });\n    if (useDoubleQuotes) return '\"' + escapedString.replace(/\"/g, '\\\\\"') + '\"';\n    return \"'\" + escapedString.replace(/'/g, '\\\\\\'') + \"'\";\n  }\n\n  function toJSON() {\n    return this.inspect(true);\n  }\n\n  function unfilterJSON(filter) {\n    return this.replace(filter || Prototype.JSONFilter, '$1');\n  }\n\n  function isJSON() {\n    var str = this;\n    if (str.blank()) return false;\n    str = this.replace(/\\\\./g, '@').replace(/\"[^\"\\\\\\n\\r]*\"/g, '');\n    return (/^[,:{}\\[\\]0-9.\\-+Eaeflnr-u \\n\\r\\t]*$/).test(str);\n  }\n\n  function evalJSON(sanitize) {\n    var json = this.unfilterJSON();\n    try {\n      if (!sanitize || json.isJSON()) return eval('(' + json + ')');\n    } catch (e) { }\n    throw new SyntaxError('Badly formed JSON string: ' + this.inspect());\n  }\n\n  function include(pattern) {\n    return this.indexOf(pattern) > -1;\n  }\n\n  function startsWith(pattern) {\n    return this.indexOf(pattern) === 0;\n  }\n\n  function endsWith(pattern) {\n    var d = this.length - pattern.length;\n    return d >= 0 && this.lastIndexOf(pattern) === d;\n  }\n\n  function empty() {\n    return this == '';\n  }\n\n  function blank() {\n    return /^\\s*$/.test(this);\n  }\n\n  function interpolate(object, pattern) {\n    return new Template(this, pattern).evaluate(object);\n  }\n\n  return {\n    gsub:           gsub,\n    sub:            sub,\n    scan:           scan,\n    truncate:       truncate,\n    strip:          String.prototype.trim ? String.prototype.trim : strip,\n    stripTags:      stripTags,\n    stripScripts:   stripScripts,\n    extractScripts: extractScripts,\n    evalScripts:    evalScripts,\n    escapeHTML:     escapeHTML,\n    unescapeHTML:   unescapeHTML,\n    toQueryParams:  toQueryParams,\n    parseQuery:     toQueryParams,\n    toArray:        toArray,\n    succ:           succ,\n    times:          times,\n    camelize:       camelize,\n    capitalize:     capitalize,\n    underscore:     underscore,\n    dasherize:      dasherize,\n    inspect:        inspect,\n    toJSON:         toJSON,\n    unfilterJSON:   unfilterJSON,\n    isJSON:         isJSON,\n    evalJSON:       evalJSON,\n    include:        include,\n    startsWith:     startsWith,\n    endsWith:       endsWith,\n    empty:          empty,\n    blank:          blank,\n    interpolate:    interpolate\n  };\n})());\n\nvar Template = Class.create({\n  initialize: function(template, pattern) {\n    this.template = template.toString();\n    this.pattern = pattern || Template.Pattern;\n  },\n\n  evaluate: function(object) {\n    if (object && Object.isFunction(object.toTemplateReplacements))\n      object = object.toTemplateReplacements();\n\n    return this.template.gsub(this.pattern, function(match) {\n      if (object == null) return (match[1] + '');\n\n      var before = match[1] || '';\n      if (before == '\\\\') return match[2];\n\n      var ctx = object, expr = match[3];\n      var pattern = /^([^.[]+|\\[((?:.*?[^\\\\])?)\\])(\\.|\\[|$)/;\n      match = pattern.exec(expr);\n      if (match == null) return before;\n\n      while (match != null) {\n        var comp = match[1].startsWith('[') ? match[2].replace(/\\\\\\\\]/g, ']') : match[1];\n        ctx = ctx[comp];\n        if (null == ctx || '' == match[3]) break;\n        expr = expr.substring('[' == match[3] ? match[1].length : match[0].length);\n        match = pattern.exec(expr);\n      }\n\n      return before + String.interpret(ctx);\n    });\n  }\n});\nTemplate.Pattern = /(^|.|\\r|\\n)(#\\{(.*?)\\})/;\n\nvar $break = { };\n\nvar Enumerable = (function() {\n  function each(iterator, context) {\n    var index = 0;\n    try {\n      this._each(function(value) {\n        iterator.call(context, value, index++);\n      });\n    } catch (e) {\n      if (e != $break) throw e;\n    }\n    return this;\n  }\n\n  function eachSlice(number, iterator, context) {\n    var index = -number, slices = [], array = this.toArray();\n    if (number < 1) return array;\n    while ((index += number) < array.length)\n      slices.push(array.slice(index, index+number));\n    return slices.collect(iterator, context);\n  }\n\n  function all(iterator, context) {\n    iterator = iterator || Prototype.K;\n    var result = true;\n    this.each(function(value, index) {\n      result = result && !!iterator.call(context, value, index);\n      if (!result) throw $break;\n    });\n    return result;\n  }\n\n  function any(iterator, context) {\n    iterator = iterator || Prototype.K;\n    var result = false;\n    this.each(function(value, index) {\n      if (result = !!iterator.call(context, value, index))\n        throw $break;\n    });\n    return result;\n  }\n\n  function collect(iterator, context) {\n    iterator = iterator || Prototype.K;\n    var results = [];\n    this.each(function(value, index) {\n      results.push(iterator.call(context, value, index));\n    });\n    return results;\n  }\n\n  function detect(iterator, context) {\n    var result;\n    this.each(function(value, index) {\n      if (iterator.call(context, value, index)) {\n        result = value;\n        throw $break;\n      }\n    });\n    return result;\n  }\n\n  function findAll(iterator, context) {\n    var results = [];\n    this.each(function(value, index) {\n      if (iterator.call(context, value, index))\n        results.push(value);\n    });\n    return results;\n  }\n\n  function grep(filter, iterator, context) {\n    iterator = iterator || Prototype.K;\n    var results = [];\n\n    if (Object.isString(filter))\n      filter = new RegExp(RegExp.escape(filter));\n\n    this.each(function(value, index) {\n      if (filter.match(value))\n        results.push(iterator.call(context, value, index));\n    });\n    return results;\n  }\n\n  function include(object) {\n    if (Object.isFunction(this.indexOf))\n      if (this.indexOf(object) != -1) return true;\n\n    var found = false;\n    this.each(function(value) {\n      if (value == object) {\n        found = true;\n        throw $break;\n      }\n    });\n    return found;\n  }\n\n  function inGroupsOf(number, fillWith) {\n    fillWith = Object.isUndefined(fillWith) ? null : fillWith;\n    return this.eachSlice(number, function(slice) {\n      while(slice.length < number) slice.push(fillWith);\n      return slice;\n    });\n  }\n\n  function inject(memo, iterator, context) {\n    this.each(function(value, index) {\n      memo = iterator.call(context, memo, value, index);\n    });\n    return memo;\n  }\n\n  function invoke(method) {\n    var args = $A(arguments).slice(1);\n    return this.map(function(value) {\n      return value[method].apply(value, args);\n    });\n  }\n\n  function max(iterator, context) {\n    iterator = iterator || Prototype.K;\n    var result;\n    this.each(function(value, index) {\n      value = iterator.call(context, value, index);\n      if (result == null || value >= result)\n        result = value;\n    });\n    return result;\n  }\n\n  function min(iterator, context) {\n    iterator = iterator || Prototype.K;\n    var result;\n    this.each(function(value, index) {\n      value = iterator.call(context, value, index);\n      if (result == null || value < result)\n        result = value;\n    });\n    return result;\n  }\n\n  function partition(iterator, context) {\n    iterator = iterator || Prototype.K;\n    var trues = [], falses = [];\n    this.each(function(value, index) {\n      (iterator.call(context, value, index) ?\n        trues : falses).push(value);\n    });\n    return [trues, falses];\n  }\n\n  function pluck(property) {\n    var results = [];\n    this.each(function(value) {\n      results.push(value[property]);\n    });\n    return results;\n  }\n\n  function reject(iterator, context) {\n    var results = [];\n    this.each(function(value, index) {\n      if (!iterator.call(context, value, index))\n        results.push(value);\n    });\n    return results;\n  }\n\n  function sortBy(iterator, context) {\n    return this.map(function(value, index) {\n      return {\n        value: value,\n        criteria: iterator.call(context, value, index)\n      };\n    }).sort(function(left, right) {\n      var a = left.criteria, b = right.criteria;\n      return a < b ? -1 : a > b ? 1 : 0;\n    }).pluck('value');\n  }\n\n  function toArray() {\n    return this.map();\n  }\n\n  function zip() {\n    var iterator = Prototype.K, args = $A(arguments);\n    if (Object.isFunction(args.last()))\n      iterator = args.pop();\n\n    var collections = [this].concat(args).map($A);\n    return this.map(function(value, index) {\n      return iterator(collections.pluck(index));\n    });\n  }\n\n  function size() {\n    return this.toArray().length;\n  }\n\n  function inspect() {\n    return '#<Enumerable:' + this.toArray().inspect() + '>';\n  }\n\n\n\n\n\n\n\n\n\n  return {\n    each:       each,\n    eachSlice:  eachSlice,\n    all:        all,\n    every:      all,\n    any:        any,\n    some:       any,\n    collect:    collect,\n    map:        collect,\n    detect:     detect,\n    findAll:    findAll,\n    select:     findAll,\n    filter:     findAll,\n    grep:       grep,\n    include:    include,\n    member:     include,\n    inGroupsOf: inGroupsOf,\n    inject:     inject,\n    invoke:     invoke,\n    max:        max,\n    min:        min,\n    partition:  partition,\n    pluck:      pluck,\n    reject:     reject,\n    sortBy:     sortBy,\n    toArray:    toArray,\n    entries:    toArray,\n    zip:        zip,\n    size:       size,\n    inspect:    inspect,\n    find:       detect\n  };\n})();\nfunction $A(iterable) {\n  if (!iterable) return [];\n  if ('toArray' in Object(iterable)) return iterable.toArray();\n  var length = iterable.length || 0, results = new Array(length);\n  while (length--) results[length] = iterable[length];\n  return results;\n}\n\nfunction $w(string) {\n  if (!Object.isString(string)) return [];\n  string = string.strip();\n  return string ? string.split(/\\s+/) : [];\n}\n\nArray.from = $A;\n\n\n(function() {\n  var arrayProto = Array.prototype,\n      slice = arrayProto.slice,\n      _each = arrayProto.forEach; // use native browser JS 1.6 implementation if available\n\n  function each(iterator) {\n    for (var i = 0, length = this.length; i < length; i++)\n      iterator(this[i]);\n  }\n  if (!_each) _each = each;\n\n  function clear() {\n    this.length = 0;\n    return this;\n  }\n\n  function first() {\n    return this[0];\n  }\n\n  function last() {\n    return this[this.length - 1];\n  }\n\n  function compact() {\n    return this.select(function(value) {\n      return value != null;\n    });\n  }\n\n  function flatten() {\n    return this.inject([], function(array, value) {\n      if (Object.isArray(value))\n        return array.concat(value.flatten());\n      array.push(value);\n      return array;\n    });\n  }\n\n  function without() {\n    var values = slice.call(arguments, 0);\n    return this.select(function(value) {\n      return !values.include(value);\n    });\n  }\n\n  function reverse(inline) {\n    return (inline !== false ? this : this.toArray())._reverse();\n  }\n\n  function uniq(sorted) {\n    return this.inject([], function(array, value, index) {\n      if (0 == index || (sorted ? array.last() != value : !array.include(value)))\n        array.push(value);\n      return array;\n    });\n  }\n\n  function intersect(array) {\n    return this.uniq().findAll(function(item) {\n      return array.detect(function(value) { return item === value });\n    });\n  }\n\n\n  function clone() {\n    return slice.call(this, 0);\n  }\n\n  function size() {\n    return this.length;\n  }\n\n  function inspect() {\n    return '[' + this.map(Object.inspect).join(', ') + ']';\n  }\n\n  function toJSON() {\n    var results = [];\n    this.each(function(object) {\n      var value = Object.toJSON(object);\n      if (!Object.isUndefined(value)) results.push(value);\n    });\n    return '[' + results.join(', ') + ']';\n  }\n\n  function indexOf(item, i) {\n    i || (i = 0);\n    var length = this.length;\n    if (i < 0) i = length + i;\n    for (; i < length; i++)\n      if (this[i] === item) return i;\n    return -1;\n  }\n\n  function lastIndexOf(item, i) {\n    i = isNaN(i) ? this.length : (i < 0 ? this.length + i : i) + 1;\n    var n = this.slice(0, i).reverse().indexOf(item);\n    return (n < 0) ? n : i - n - 1;\n  }\n\n  function concat() {\n    var array = slice.call(this, 0), item;\n    for (var i = 0, length = arguments.length; i < length; i++) {\n      item = arguments[i];\n      if (Object.isArray(item) && !('callee' in item)) {\n        for (var j = 0, arrayLength = item.length; j < arrayLength; j++)\n          array.push(item[j]);\n      } else {\n        array.push(item);\n      }\n    }\n    return array;\n  }\n\n  Object.extend(arrayProto, Enumerable);\n\n  if (!arrayProto._reverse)\n    arrayProto._reverse = arrayProto.reverse;\n\n  Object.extend(arrayProto, {\n    _each:     _each,\n    clear:     clear,\n    first:     first,\n    last:      last,\n    compact:   compact,\n    flatten:   flatten,\n    without:   without,\n    reverse:   reverse,\n    uniq:      uniq,\n    intersect: intersect,\n    clone:     clone,\n    toArray:   clone,\n    size:      size,\n    inspect:   inspect,\n    toJSON:    toJSON\n  });\n\n  var CONCAT_ARGUMENTS_BUGGY = (function() {\n    return [].concat(arguments)[0][0] !== 1;\n  })(1,2)\n\n  if (CONCAT_ARGUMENTS_BUGGY) arrayProto.concat = concat;\n\n  if (!arrayProto.indexOf) arrayProto.indexOf = indexOf;\n  if (!arrayProto.lastIndexOf) arrayProto.lastIndexOf = lastIndexOf;\n})();\nfunction $H(object) {\n  return new Hash(object);\n};\n\nvar Hash = Class.create(Enumerable, (function() {\n  function initialize(object) {\n    this._object = Object.isHash(object) ? object.toObject() : Object.clone(object);\n  }\n\n  function _each(iterator) {\n    for (var key in this._object) {\n      var value = this._object[key], pair = [key, value];\n      pair.key = key;\n      pair.value = value;\n      iterator(pair);\n    }\n  }\n\n  function set(key, value) {\n    return this._object[key] = value;\n  }\n\n  function get(key) {\n    if (this._object[key] !== Object.prototype[key])\n      return this._object[key];\n  }\n\n  function unset(key) {\n    var value = this._object[key];\n    delete this._object[key];\n    return value;\n  }\n\n  function toObject() {\n    return Object.clone(this._object);\n  }\n\n  function keys() {\n    return this.pluck('key');\n  }\n\n  function values() {\n    return this.pluck('value');\n  }\n\n  function index(value) {\n    var match = this.detect(function(pair) {\n      return pair.value === value;\n    });\n    return match && match.key;\n  }\n\n  function merge(object) {\n    return this.clone().update(object);\n  }\n\n  function update(object) {\n    return new Hash(object).inject(this, function(result, pair) {\n      result.set(pair.key, pair.value);\n      return result;\n    });\n  }\n\n  function toQueryPair(key, value) {\n    if (Object.isUndefined(value)) return key;\n    return key + '=' + encodeURIComponent(String.interpret(value));\n  }\n\n  function toQueryString() {\n    return this.inject([], function(results, pair) {\n      var key = encodeURIComponent(pair.key), values = pair.value;\n\n      if (values && typeof values == 'object') {\n        if (Object.isArray(values))\n          return results.concat(values.map(toQueryPair.curry(key)));\n      } else results.push(toQueryPair(key, values));\n      return results;\n    }).join('&');\n  }\n\n  function inspect() {\n    return '#<Hash:{' + this.map(function(pair) {\n      return pair.map(Object.inspect).join(': ');\n    }).join(', ') + '}>';\n  }\n\n  function toJSON() {\n    return Object.toJSON(this.toObject());\n  }\n\n  function clone() {\n    return new Hash(this);\n  }\n\n  return {\n    initialize:             initialize,\n    _each:                  _each,\n    set:                    set,\n    get:                    get,\n    unset:                  unset,\n    toObject:               toObject,\n    toTemplateReplacements: toObject,\n    keys:                   keys,\n    values:                 values,\n    index:                  index,\n    merge:                  merge,\n    update:                 update,\n    toQueryString:          toQueryString,\n    inspect:                inspect,\n    toJSON:                 toJSON,\n    clone:                  clone\n  };\n})());\n\nHash.from = $H;\nObject.extend(Number.prototype, (function() {\n  function toColorPart() {\n    return this.toPaddedString(2, 16);\n  }\n\n  function succ() {\n    return this + 1;\n  }\n\n  function times(iterator, context) {\n    $R(0, this, true).each(iterator, context);\n    return this;\n  }\n\n  function toPaddedString(length, radix) {\n    var string = this.toString(radix || 10);\n    return '0'.times(length - string.length) + string;\n  }\n\n  function toJSON() {\n    return isFinite(this) ? this.toString() : 'null';\n  }\n\n  function abs() {\n    return Math.abs(this);\n  }\n\n  function round() {\n    return Math.round(this);\n  }\n\n  function ceil() {\n    return Math.ceil(this);\n  }\n\n  function floor() {\n    return Math.floor(this);\n  }\n\n  return {\n    toColorPart:    toColorPart,\n    succ:           succ,\n    times:          times,\n    toPaddedString: toPaddedString,\n    toJSON:         toJSON,\n    abs:            abs,\n    round:          round,\n    ceil:           ceil,\n    floor:          floor\n  };\n})());\n\nfunction $R(start, end, exclusive) {\n  return new ObjectRange(start, end, exclusive);\n}\n\nvar ObjectRange = Class.create(Enumerable, (function() {\n  function initialize(start, end, exclusive) {\n    this.start = start;\n    this.end = end;\n    this.exclusive = exclusive;\n  }\n\n  function _each(iterator) {\n    var value = this.start;\n    while (this.include(value)) {\n      iterator(value);\n      value = value.succ();\n    }\n  }\n\n  function include(value) {\n    if (value < this.start)\n      return false;\n    if (this.exclusive)\n      return value < this.end;\n    return value <= this.end;\n  }\n\n  return {\n    initialize: initialize,\n    _each:      _each,\n    include:    include\n  };\n})());\n\n\n\nvar Ajax = {\n  getTransport: function() {\n    return Try.these(\n      function() {return new XMLHttpRequest()},\n      function() {return new ActiveXObject('Msxml2.XMLHTTP')},\n      function() {return new ActiveXObject('Microsoft.XMLHTTP')}\n    ) || false;\n  },\n\n  activeRequestCount: 0\n};\n\nAjax.Responders = {\n  responders: [],\n\n  _each: function(iterator) {\n    this.responders._each(iterator);\n  },\n\n  register: function(responder) {\n    if (!this.include(responder))\n      this.responders.push(responder);\n  },\n\n  unregister: function(responder) {\n    this.responders = this.responders.without(responder);\n  },\n\n  dispatch: function(callback, request, transport, json) {\n    this.each(function(responder) {\n      if (Object.isFunction(responder[callback])) {\n        try {\n          responder[callback].apply(responder, [request, transport, json]);\n        } catch (e) { }\n      }\n    });\n  }\n};\n\nObject.extend(Ajax.Responders, Enumerable);\n\nAjax.Responders.register({\n  onCreate:   function() { Ajax.activeRequestCount++ },\n  onComplete: function() { Ajax.activeRequestCount-- }\n});\nAjax.Base = Class.create({\n  initialize: function(options) {\n    this.options = {\n      method:       'post',\n      asynchronous: true,\n      contentType:  'application/x-www-form-urlencoded',\n      encoding:     'UTF-8',\n      parameters:   '',\n      evalJSON:     true,\n      evalJS:       true\n    };\n    Object.extend(this.options, options || { });\n\n    this.options.method = this.options.method.toLowerCase();\n\n    if (Object.isString(this.options.parameters))\n      this.options.parameters = this.options.parameters.toQueryParams();\n    else if (Object.isHash(this.options.parameters))\n      this.options.parameters = this.options.parameters.toObject();\n  }\n});\nAjax.Request = Class.create(Ajax.Base, {\n  _complete: false,\n\n  initialize: function($super, url, options) {\n    $super(options);\n    this.transport = Ajax.getTransport();\n    this.request(url);\n  },\n\n  request: function(url) {\n    this.url = url;\n    this.method = this.options.method;\n    var params = Object.clone(this.options.parameters);\n\n    if (!['get', 'post'].include(this.method)) {\n      params['_method'] = this.method;\n      this.method = 'post';\n    }\n\n    this.parameters = params;\n\n    if (params = Object.toQueryString(params)) {\n      if (this.method == 'get')\n        this.url += (this.url.include('?') ? '&' : '?') + params;\n      else if (/Konqueror|Safari|KHTML/.test(navigator.userAgent))\n        params += '&_=';\n    }\n\n    try {\n      var response = new Ajax.Response(this);\n      if (this.options.onCreate) this.options.onCreate(response);\n      Ajax.Responders.dispatch('onCreate', this, response);\n\n      this.transport.open(this.method.toUpperCase(), this.url,\n        this.options.asynchronous);\n\n      if (this.options.asynchronous) this.respondToReadyState.bind(this).defer(1);\n\n      this.transport.onreadystatechange = this.onStateChange.bind(this);\n      this.setRequestHeaders();\n\n      this.body = this.method == 'post' ? (this.options.postBody || params) : null;\n      this.transport.send(this.body);\n\n      /* Force Firefox to handle ready state 4 for synchronous requests */\n      if (!this.options.asynchronous && this.transport.overrideMimeType)\n        this.onStateChange();\n\n    }\n    catch (e) {\n      this.dispatchException(e);\n    }\n  },\n\n  onStateChange: function() {\n    var readyState = this.transport.readyState;\n    if (readyState > 1 && !((readyState == 4) && this._complete))\n      this.respondToReadyState(this.transport.readyState);\n  },\n\n  setRequestHeaders: function() {\n    var headers = {\n      'X-Requested-With': 'XMLHttpRequest',\n      'X-Prototype-Version': Prototype.Version,\n      'Accept': 'text/javascript, text/html, application/xml, text/xml, */*'\n    };\n\n    if (this.method == 'post') {\n      headers['Content-type'] = this.options.contentType +\n        (this.options.encoding ? '; charset=' + this.options.encoding : '');\n\n      /* Force \"Connection: close\" for older Mozilla browsers to work\n       * around a bug where XMLHttpRequest sends an incorrect\n       * Content-length header. See Mozilla Bugzilla #246651.\n       */\n      if (this.transport.overrideMimeType &&\n          (navigator.userAgent.match(/Gecko\\/(\\d{4})/) || [0,2005])[1] < 2005)\n            headers['Connection'] = 'close';\n    }\n\n    if (typeof this.options.requestHeaders == 'object') {\n      var extras = this.options.requestHeaders;\n\n      if (Object.isFunction(extras.push))\n        for (var i = 0, length = extras.length; i < length; i += 2)\n          headers[extras[i]] = extras[i+1];\n      else\n        $H(extras).each(function(pair) { headers[pair.key] = pair.value });\n    }\n\n    for (var name in headers)\n      this.transport.setRequestHeader(name, headers[name]);\n  },\n\n  success: function() {\n    var status = this.getStatus();\n    return !status || (status >= 200 && status < 300);\n  },\n\n  getStatus: function() {\n    try {\n      return this.transport.status || 0;\n    } catch (e) { return 0 }\n  },\n\n  respondToReadyState: function(readyState) {\n    var state = Ajax.Request.Events[readyState], response = new Ajax.Response(this);\n\n    if (state == 'Complete') {\n      try {\n        this._complete = true;\n        (this.options['on' + response.status]\n         || this.options['on' + (this.success() ? 'Success' : 'Failure')]\n         || Prototype.emptyFunction)(response, response.headerJSON);\n      } catch (e) {\n        this.dispatchException(e);\n      }\n\n      var contentType = response.getHeader('Content-type');\n      if (this.options.evalJS == 'force'\n          || (this.options.evalJS && this.isSameOrigin() && contentType\n          && contentType.match(/^\\s*(text|application)\\/(x-)?(java|ecma)script(;.*)?\\s*$/i)))\n        this.evalResponse();\n    }\n\n    try {\n      (this.options['on' + state] || Prototype.emptyFunction)(response, response.headerJSON);\n      Ajax.Responders.dispatch('on' + state, this, response, response.headerJSON);\n    } catch (e) {\n      this.dispatchException(e);\n    }\n\n    if (state == 'Complete') {\n      this.transport.onreadystatechange = Prototype.emptyFunction;\n    }\n  },\n\n  isSameOrigin: function() {\n    var m = this.url.match(/^\\s*https?:\\/\\/[^\\/]*/);\n    return !m || (m[0] == '#{protocol}//#{domain}#{port}'.interpolate({\n      protocol: location.protocol,\n      domain: document.domain,\n      port: location.port ? ':' + location.port : ''\n    }));\n  },\n\n  getHeader: function(name) {\n    try {\n      return this.transport.getResponseHeader(name) || null;\n    } catch (e) { return null; }\n  },\n\n  evalResponse: function() {\n    try {\n      return eval((this.transport.responseText || '').unfilterJSON());\n    } catch (e) {\n      this.dispatchException(e);\n    }\n  },\n\n  dispatchException: function(exception) {\n    (this.options.onException || Prototype.emptyFunction)(this, exception);\n    Ajax.Responders.dispatch('onException', this, exception);\n  }\n});\n\nAjax.Request.Events =\n  ['Uninitialized', 'Loading', 'Loaded', 'Interactive', 'Complete'];\n\n\n\n\n\n\n\n\nAjax.Response = Class.create({\n  initialize: function(request){\n    this.request = request;\n    var transport  = this.transport  = request.transport,\n        readyState = this.readyState = transport.readyState;\n\n    if((readyState > 2 && !Prototype.Browser.IE) || readyState == 4) {\n      this.status       = this.getStatus();\n      this.statusText   = this.getStatusText();\n      this.responseText = String.interpret(transport.responseText);\n      this.headerJSON   = this._getHeaderJSON();\n    }\n\n    if(readyState == 4) {\n      var xml = transport.responseXML;\n      this.responseXML  = Object.isUndefined(xml) ? null : xml;\n      this.responseJSON = this._getResponseJSON();\n    }\n  },\n\n  status:      0,\n\n  statusText: '',\n\n  getStatus: Ajax.Request.prototype.getStatus,\n\n  getStatusText: function() {\n    try {\n      return this.transport.statusText || '';\n    } catch (e) { return '' }\n  },\n\n  getHeader: Ajax.Request.prototype.getHeader,\n\n  getAllHeaders: function() {\n    try {\n      return this.getAllResponseHeaders();\n    } catch (e) { return null }\n  },\n\n  getResponseHeader: function(name) {\n    return this.transport.getResponseHeader(name);\n  },\n\n  getAllResponseHeaders: function() {\n    return this.transport.getAllResponseHeaders();\n  },\n\n  _getHeaderJSON: function() {\n    var json = this.getHeader('X-JSON');\n    if (!json) return null;\n    json = decodeURIComponent(escape(json));\n    try {\n      return json.evalJSON(this.request.options.sanitizeJSON ||\n        !this.request.isSameOrigin());\n    } catch (e) {\n      this.request.dispatchException(e);\n    }\n  },\n\n  _getResponseJSON: function() {\n    var options = this.request.options;\n    if (!options.evalJSON || (options.evalJSON != 'force' &&\n      !(this.getHeader('Content-type') || '').include('application/json')) ||\n        this.responseText.blank())\n          return null;\n    try {\n      return this.responseText.evalJSON(options.sanitizeJSON ||\n        !this.request.isSameOrigin());\n    } catch (e) {\n      this.request.dispatchException(e);\n    }\n  }\n});\n\nAjax.Updater = Class.create(Ajax.Request, {\n  initialize: function($super, container, url, options) {\n    this.container = {\n      success: (container.success || container),\n      failure: (container.failure || (container.success ? null : container))\n    };\n\n    options = Object.clone(options);\n    var onComplete = options.onComplete;\n    options.onComplete = (function(response, json) {\n      this.updateContent(response.responseText);\n      if (Object.isFunction(onComplete)) onComplete(response, json);\n    }).bind(this);\n\n    $super(url, options);\n  },\n\n  updateContent: function(responseText) {\n    var receiver = this.container[this.success() ? 'success' : 'failure'],\n        options = this.options;\n\n    if (!options.evalScripts) responseText = responseText.stripScripts();\n\n    if (receiver = $(receiver)) {\n      if (options.insertion) {\n        if (Object.isString(options.insertion)) {\n          var insertion = { }; insertion[options.insertion] = responseText;\n          receiver.insert(insertion);\n        }\n        else options.insertion(receiver, responseText);\n      }\n      else receiver.update(responseText);\n    }\n  }\n});\n\nAjax.PeriodicalUpdater = Class.create(Ajax.Base, {\n  initialize: function($super, container, url, options) {\n    $super(options);\n    this.onComplete = this.options.onComplete;\n\n    this.frequency = (this.options.frequency || 2);\n    this.decay = (this.options.decay || 1);\n\n    this.updater = { };\n    this.container = container;\n    this.url = url;\n\n    this.start();\n  },\n\n  start: function() {\n    this.options.onComplete = this.updateComplete.bind(this);\n    this.onTimerEvent();\n  },\n\n  stop: function() {\n    this.updater.options.onComplete = undefined;\n    clearTimeout(this.timer);\n    (this.onComplete || Prototype.emptyFunction).apply(this, arguments);\n  },\n\n  updateComplete: function(response) {\n    if (this.options.decay) {\n      this.decay = (response.responseText == this.lastText ?\n        this.decay * this.options.decay : 1);\n\n      this.lastText = response.responseText;\n    }\n    this.timer = this.onTimerEvent.bind(this).delay(this.decay * this.frequency);\n  },\n\n  onTimerEvent: function() {\n    this.updater = new Ajax.Updater(this.container, this.url, this.options);\n  }\n});\n\n\n\nfunction $(element) {\n  if (arguments.length > 1) {\n    for (var i = 0, elements = [], length = arguments.length; i < length; i++)\n      elements.push($(arguments[i]));\n    return elements;\n  }\n  if (Object.isString(element))\n    element = document.getElementById(element);\n  return Element.extend(element);\n}\n\nif (Prototype.BrowserFeatures.XPath) {\n  document._getElementsByXPath = function(expression, parentElement) {\n    var results = [];\n    var query = document.evaluate(expression, $(parentElement) || document,\n      null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);\n    for (var i = 0, length = query.snapshotLength; i < length; i++)\n      results.push(Element.extend(query.snapshotItem(i)));\n    return results;\n  };\n}\n\n/*--------------------------------------------------------------------------*/\n\nif (!window.Node) var Node = { };\n\nif (!Node.ELEMENT_NODE) {\n  Object.extend(Node, {\n    ELEMENT_NODE: 1,\n    ATTRIBUTE_NODE: 2,\n    TEXT_NODE: 3,\n    CDATA_SECTION_NODE: 4,\n    ENTITY_REFERENCE_NODE: 5,\n    ENTITY_NODE: 6,\n    PROCESSING_INSTRUCTION_NODE: 7,\n    COMMENT_NODE: 8,\n    DOCUMENT_NODE: 9,\n    DOCUMENT_TYPE_NODE: 10,\n    DOCUMENT_FRAGMENT_NODE: 11,\n    NOTATION_NODE: 12\n  });\n}\n\n\n(function(global) {\n\n  var SETATTRIBUTE_IGNORES_NAME = (function(){\n    var elForm = document.createElement(\"form\");\n    var elInput = document.createElement(\"input\");\n    var root = document.documentElement;\n    elInput.setAttribute(\"name\", \"test\");\n    elForm.appendChild(elInput);\n    root.appendChild(elForm);\n    var isBuggy = elForm.elements\n      ? (typeof elForm.elements.test == \"undefined\")\n      : null;\n    root.removeChild(elForm);\n    elForm = elInput = null;\n    return isBuggy;\n  })();\n\n  var element = global.Element;\n  global.Element = function(tagName, attributes) {\n    attributes = attributes || { };\n    tagName = tagName.toLowerCase();\n    var cache = Element.cache;\n    if (SETATTRIBUTE_IGNORES_NAME && attributes.name) {\n      tagName = '<' + tagName + ' name=\"' + attributes.name + '\">';\n      delete attributes.name;\n      return Element.writeAttribute(document.createElement(tagName), attributes);\n    }\n    if (!cache[tagName]) cache[tagName] = Element.extend(document.createElement(tagName));\n    return Element.writeAttribute(cache[tagName].cloneNode(false), attributes);\n  };\n  Object.extend(global.Element, element || { });\n  if (element) global.Element.prototype = element.prototype;\n})(this);\n\nElement.cache = { };\nElement.idCounter = 1;\n\nElement.Methods = {\n  visible: function(element) {\n    return $(element).style.display != 'none';\n  },\n\n  toggle: function(element) {\n    element = $(element);\n    Element[Element.visible(element) ? 'hide' : 'show'](element);\n    return element;\n  },\n\n\n  hide: function(element) {\n    element = $(element);\n    element.style.display = 'none';\n    return element;\n  },\n\n  show: function(element) {\n    element = $(element);\n    element.style.display = '';\n    return element;\n  },\n\n  remove: function(element) {\n    element = $(element);\n    element.parentNode.removeChild(element);\n    return element;\n  },\n\n  update: (function(){\n\n    var SELECT_ELEMENT_INNERHTML_BUGGY = (function(){\n      var el = document.createElement(\"select\"),\n          isBuggy = true;\n      el.innerHTML = \"<option value=\\\"test\\\">test</option>\";\n      if (el.options && el.options[0]) {\n        isBuggy = el.options[0].nodeName.toUpperCase() !== \"OPTION\";\n      }\n      el = null;\n      return isBuggy;\n    })();\n\n    var TABLE_ELEMENT_INNERHTML_BUGGY = (function(){\n      try {\n        var el = document.createElement(\"table\");\n        if (el && el.tBodies) {\n          el.innerHTML = \"<tbody><tr><td>test</td></tr></tbody>\";\n          var isBuggy = typeof el.tBodies[0] == \"undefined\";\n          el = null;\n          return isBuggy;\n        }\n      } catch (e) {\n        return true;\n      }\n    })();\n\n    var SCRIPT_ELEMENT_REJECTS_TEXTNODE_APPENDING = (function () {\n      var s = document.createElement(\"script\"),\n          isBuggy = false;\n      try {\n        s.appendChild(document.createTextNode(\"\"));\n        isBuggy = !s.firstChild ||\n          s.firstChild && s.firstChild.nodeType !== 3;\n      } catch (e) {\n        isBuggy = true;\n      }\n      s = null;\n      return isBuggy;\n    })();\n\n    function update(element, content) {\n      element = $(element);\n\n      if (content && content.toElement)\n        content = content.toElement();\n\n      if (Object.isElement(content))\n        return element.update().insert(content);\n\n      content = Object.toHTML(content);\n\n      var tagName = element.tagName.toUpperCase();\n\n      if (tagName === 'SCRIPT' && SCRIPT_ELEMENT_REJECTS_TEXTNODE_APPENDING) {\n        element.text = content;\n        return element;\n      }\n\n      if (SELECT_ELEMENT_INNERHTML_BUGGY || TABLE_ELEMENT_INNERHTML_BUGGY) {\n        if (tagName in Element._insertionTranslations.tags) {\n          while (element.firstChild) {\n            element.removeChild(element.firstChild);\n          }\n          Element._getContentFromAnonymousElement(tagName, content.stripScripts())\n            .each(function(node) {\n              element.appendChild(node)\n            });\n        }\n        else {\n          element.innerHTML = content.stripScripts();\n        }\n      }\n      else {\n        element.innerHTML = content.stripScripts();\n      }\n\n      content.evalScripts.bind(content).defer();\n      return element;\n    }\n\n    return update;\n  })(),\n\n  replace: function(element, content) {\n    element = $(element);\n    if (content && content.toElement) content = content.toElement();\n    else if (!Object.isElement(content)) {\n      content = Object.toHTML(content);\n      var range = element.ownerDocument.createRange();\n      range.selectNode(element);\n      content.evalScripts.bind(content).defer();\n      content = range.createContextualFragment(content.stripScripts());\n    }\n    element.parentNode.replaceChild(content, element);\n    return element;\n  },\n\n  insert: function(element, insertions) {\n    element = $(element);\n\n    if (Object.isString(insertions) || Object.isNumber(insertions) ||\n        Object.isElement(insertions) || (insertions && (insertions.toElement || insertions.toHTML)))\n          insertions = {bottom:insertions};\n\n    var content, insert, tagName, childNodes;\n\n    for (var position in insertions) {\n      content  = insertions[position];\n      position = position.toLowerCase();\n      insert = Element._insertionTranslations[position];\n\n      if (content && content.toElement) content = content.toElement();\n      if (Object.isElement(content)) {\n        insert(element, content);\n        continue;\n      }\n\n      content = Object.toHTML(content);\n\n      tagName = ((position == 'before' || position == 'after')\n        ? element.parentNode : element).tagName.toUpperCase();\n\n      childNodes = Element._getContentFromAnonymousElement(tagName, content.stripScripts());\n\n      if (position == 'top' || position == 'after') childNodes.reverse();\n      childNodes.each(insert.curry(element));\n\n      content.evalScripts.bind(content).defer();\n    }\n\n    return element;\n  },\n\n  wrap: function(element, wrapper, attributes) {\n    element = $(element);\n    if (Object.isElement(wrapper))\n      $(wrapper).writeAttribute(attributes || { });\n    else if (Object.isString(wrapper)) wrapper = new Element(wrapper, attributes);\n    else wrapper = new Element('div', wrapper);\n    if (element.parentNode)\n      element.parentNode.replaceChild(wrapper, element);\n    wrapper.appendChild(element);\n    return wrapper;\n  },\n\n  inspect: function(element) {\n    element = $(element);\n    var result = '<' + element.tagName.toLowerCase();\n    $H({'id': 'id', 'className': 'class'}).each(function(pair) {\n      var property = pair.first(), attribute = pair.last();\n      var value = (element[property] || '').toString();\n      if (value) result += ' ' + attribute + '=' + value.inspect(true);\n    });\n    return result + '>';\n  },\n\n  recursivelyCollect: function(element, property) {\n    element = $(element);\n    var elements = [];\n    while (element = element[property])\n      if (element.nodeType == 1)\n        elements.push(Element.extend(element));\n    return elements;\n  },\n\n  ancestors: function(element) {\n    return Element.recursivelyCollect(element, 'parentNode');\n  },\n\n  descendants: function(element) {\n    return Element.select(element, \"*\");\n  },\n\n  firstDescendant: function(element) {\n    element = $(element).firstChild;\n    while (element && element.nodeType != 1) element = element.nextSibling;\n    return $(element);\n  },\n\n  immediateDescendants: function(element) {\n    if (!(element = $(element).firstChild)) return [];\n    while (element && element.nodeType != 1) element = element.nextSibling;\n    if (element) return [element].concat($(element).nextSiblings());\n    return [];\n  },\n\n  previousSiblings: function(element) {\n    return Element.recursivelyCollect(element, 'previousSibling');\n  },\n\n  nextSiblings: function(element) {\n    return Element.recursivelyCollect(element, 'nextSibling');\n  },\n\n  siblings: function(element) {\n    element = $(element);\n    return Element.previousSiblings(element).reverse()\n      .concat(Element.nextSiblings(element));\n  },\n\n  match: function(element, selector) {\n    if (Object.isString(selector))\n      selector = new Selector(selector);\n    return selector.match($(element));\n  },\n\n  up: function(element, expression, index) {\n    element = $(element);\n    if (arguments.length == 1) return $(element.parentNode);\n    var ancestors = Element.ancestors(element);\n    return Object.isNumber(expression) ? ancestors[expression] :\n      Selector.findElement(ancestors, expression, index);\n  },\n\n  down: function(element, expression, index) {\n    element = $(element);\n    if (arguments.length == 1) return Element.firstDescendant(element);\n    return Object.isNumber(expression) ? Element.descendants(element)[expression] :\n      Element.select(element, expression)[index || 0];\n  },\n\n  previous: function(element, expression, index) {\n    element = $(element);\n    if (arguments.length == 1) return $(Selector.handlers.previousElementSibling(element));\n    var previousSiblings = Element.previousSiblings(element);\n    return Object.isNumber(expression) ? previousSiblings[expression] :\n      Selector.findElement(previousSiblings, expression, index);\n  },\n\n  next: function(element, expression, index) {\n    element = $(element);\n    if (arguments.length == 1) return $(Selector.handlers.nextElementSibling(element));\n    var nextSiblings = Element.nextSiblings(element);\n    return Object.isNumber(expression) ? nextSiblings[expression] :\n      Selector.findElement(nextSiblings, expression, index);\n  },\n\n\n  select: function(element) {\n    var args = Array.prototype.slice.call(arguments, 1);\n    return Selector.findChildElements(element, args);\n  },\n\n  adjacent: function(element) {\n    var args = Array.prototype.slice.call(arguments, 1);\n    return Selector.findChildElements(element.parentNode, args).without(element);\n  },\n\n  identify: function(element) {\n    element = $(element);\n    var id = Element.readAttribute(element, 'id');\n    if (id) return id;\n    do { id = 'anonymous_element_' + Element.idCounter++ } while ($(id));\n    Element.writeAttribute(element, 'id', id);\n    return id;\n  },\n\n  readAttribute: function(element, name) {\n    element = $(element);\n    if (Prototype.Browser.IE) {\n      var t = Element._attributeTranslations.read;\n      if (t.values[name]) return t.values[name](element, name);\n      if (t.names[name]) name = t.names[name];\n      if (name.include(':')) {\n        return (!element.attributes || !element.attributes[name]) ? null :\n         element.attributes[name].value;\n      }\n    }\n    return element.getAttribute(name);\n  },\n\n  writeAttribute: function(element, name, value) {\n    element = $(element);\n    var attributes = { }, t = Element._attributeTranslations.write;\n\n    if (typeof name == 'object') attributes = name;\n    else attributes[name] = Object.isUndefined(value) ? true : value;\n\n    for (var attr in attributes) {\n      name = t.names[attr] || attr;\n      value = attributes[attr];\n      if (t.values[attr]) name = t.values[attr](element, value);\n      if (value === false || value === null)\n        element.removeAttribute(name);\n      else if (value === true)\n        element.setAttribute(name, name);\n      else element.setAttribute(name, value);\n    }\n    return element;\n  },\n\n  getHeight: function(element) {\n    return Element.getDimensions(element).height;\n  },\n\n  getWidth: function(element) {\n    return Element.getDimensions(element).width;\n  },\n\n  classNames: function(element) {\n    return new Element.ClassNames(element);\n  },\n\n  hasClassName: function(element, className) {\n    if (!(element = $(element))) return;\n    var elementClassName = element.className;\n    return (elementClassName.length > 0 && (elementClassName == className ||\n      new RegExp(\"(^|\\\\s)\" + className + \"(\\\\s|$)\").test(elementClassName)));\n  },\n\n  addClassName: function(element, className) {\n    if (!(element = $(element))) return;\n    if (!Element.hasClassName(element, className))\n      element.className += (element.className ? ' ' : '') + className;\n    return element;\n  },\n\n  removeClassName: function(element, className) {\n    if (!(element = $(element))) return;\n    element.className = element.className.replace(\n      new RegExp(\"(^|\\\\s+)\" + className + \"(\\\\s+|$)\"), ' ').strip();\n    return element;\n  },\n\n  toggleClassName: function(element, className) {\n    if (!(element = $(element))) return;\n    return Element[Element.hasClassName(element, className) ?\n      'removeClassName' : 'addClassName'](element, className);\n  },\n\n  cleanWhitespace: function(element) {\n    element = $(element);\n    var node = element.firstChild;\n    while (node) {\n      var nextNode = node.nextSibling;\n      if (node.nodeType == 3 && !/\\S/.test(node.nodeValue))\n        element.removeChild(node);\n      node = nextNode;\n    }\n    return element;\n  },\n\n  empty: function(element) {\n    return $(element).innerHTML.blank();\n  },\n\n  descendantOf: function(element, ancestor) {\n    element = $(element), ancestor = $(ancestor);\n\n    if (element.compareDocumentPosition)\n      return (element.compareDocumentPosition(ancestor) & 8) === 8;\n\n    if (ancestor.contains)\n      return ancestor.contains(element) && ancestor !== element;\n\n    while (element = element.parentNode)\n      if (element == ancestor) return true;\n\n    return false;\n  },\n\n  scrollTo: function(element) {\n    element = $(element);\n    var pos = Element.cumulativeOffset(element);\n    window.scrollTo(pos[0], pos[1]);\n    return element;\n  },\n\n  getStyle: function(element, style) {\n    element = $(element);\n    style = style == 'float' ? 'cssFloat' : style.camelize();\n    var value = element.style[style];\n    if (!value || value == 'auto') {\n      var css = document.defaultView.getComputedStyle(element, null);\n      value = css ? css[style] : null;\n    }\n    if (style == 'opacity') return value ? parseFloat(value) : 1.0;\n    return value == 'auto' ? null : value;\n  },\n\n  getOpacity: function(element) {\n    return $(element).getStyle('opacity');\n  },\n\n  setStyle: function(element, styles) {\n    element = $(element);\n    var elementStyle = element.style, match;\n    if (Object.isString(styles)) {\n      element.style.cssText += ';' + styles;\n      return styles.include('opacity') ?\n        element.setOpacity(styles.match(/opacity:\\s*(\\d?\\.?\\d*)/)[1]) : element;\n    }\n    for (var property in styles)\n      if (property == 'opacity') element.setOpacity(styles[property]);\n      else\n        elementStyle[(property == 'float' || property == 'cssFloat') ?\n          (Object.isUndefined(elementStyle.styleFloat) ? 'cssFloat' : 'styleFloat') :\n            property] = styles[property];\n\n    return element;\n  },\n\n  setOpacity: function(element, value) {\n    element = $(element);\n    element.style.opacity = (value == 1 || value === '') ? '' :\n      (value < 0.00001) ? 0 : value;\n    return element;\n  },\n\n  getDimensions: function(element) {\n    element = $(element);\n    var display = Element.getStyle(element, 'display');\n    if (display != 'none' && display != null) // Safari bug\n      return {width: element.offsetWidth, height: element.offsetHeight};\n\n    var els = element.style;\n    var originalVisibility = els.visibility;\n    var originalPosition = els.position;\n    var originalDisplay = els.display;\n    els.visibility = 'hidden';\n    if (originalPosition != 'fixed') // Switching fixed to absolute causes issues in Safari\n      els.position = 'absolute';\n    els.display = 'block';\n    var originalWidth = element.clientWidth;\n    var originalHeight = element.clientHeight;\n    els.display = originalDisplay;\n    els.position = originalPosition;\n    els.visibility = originalVisibility;\n    return {width: originalWidth, height: originalHeight};\n  },\n\n  makePositioned: function(element) {\n    element = $(element);\n    var pos = Element.getStyle(element, 'position');\n    if (pos == 'static' || !pos) {\n      element._madePositioned = true;\n      element.style.position = 'relative';\n      if (Prototype.Browser.Opera) {\n        element.style.top = 0;\n        element.style.left = 0;\n      }\n    }\n    return element;\n  },\n\n  undoPositioned: function(element) {\n    element = $(element);\n    if (element._madePositioned) {\n      element._madePositioned = undefined;\n      element.style.position =\n        element.style.top =\n        element.style.left =\n        element.style.bottom =\n        element.style.right = '';\n    }\n    return element;\n  },\n\n  makeClipping: function(element) {\n    element = $(element);\n    if (element._overflow) return element;\n    element._overflow = Element.getStyle(element, 'overflow') || 'auto';\n    if (element._overflow !== 'hidden')\n      element.style.overflow = 'hidden';\n    return element;\n  },\n\n  undoClipping: function(element) {\n    element = $(element);\n    if (!element._overflow) return element;\n    element.style.overflow = element._overflow == 'auto' ? '' : element._overflow;\n    element._overflow = null;\n    return element;\n  },\n\n  cumulativeOffset: function(element) {\n    var valueT = 0, valueL = 0;\n    do {\n      valueT += element.offsetTop  || 0;\n      valueL += element.offsetLeft || 0;\n      element = element.offsetParent;\n    } while (element);\n    return Element._returnOffset(valueL, valueT);\n  },\n\n  positionedOffset: function(element) {\n    var valueT = 0, valueL = 0;\n    do {\n      valueT += element.offsetTop  || 0;\n      valueL += element.offsetLeft || 0;\n      element = element.offsetParent;\n      if (element) {\n        if (element.tagName.toUpperCase() == 'BODY') break;\n        var p = Element.getStyle(element, 'position');\n        if (p !== 'static') break;\n      }\n    } while (element);\n    return Element._returnOffset(valueL, valueT);\n  },\n\n  absolutize: function(element) {\n    element = $(element);\n    if (Element.getStyle(element, 'position') == 'absolute') return element;\n\n    var offsets = Element.positionedOffset(element);\n    var top     = offsets[1];\n    var left    = offsets[0];\n    var width   = element.clientWidth;\n    var height  = element.clientHeight;\n\n    element._originalLeft   = left - parseFloat(element.style.left  || 0);\n    element._originalTop    = top  - parseFloat(element.style.top || 0);\n    element._originalWidth  = element.style.width;\n    element._originalHeight = element.style.height;\n\n    element.style.position = 'absolute';\n    element.style.top    = top + 'px';\n    element.style.left   = left + 'px';\n    element.style.width  = width + 'px';\n    element.style.height = height + 'px';\n    return element;\n  },\n\n  relativize: function(element) {\n    element = $(element);\n    if (Element.getStyle(element, 'position') == 'relative') return element;\n\n    element.style.position = 'relative';\n    var top  = parseFloat(element.style.top  || 0) - (element._originalTop || 0);\n    var left = parseFloat(element.style.left || 0) - (element._originalLeft || 0);\n\n    element.style.top    = top + 'px';\n    element.style.left   = left + 'px';\n    element.style.height = element._originalHeight;\n    element.style.width  = element._originalWidth;\n    return element;\n  },\n\n  cumulativeScrollOffset: function(element) {\n    var valueT = 0, valueL = 0;\n    do {\n      valueT += element.scrollTop  || 0;\n      valueL += element.scrollLeft || 0;\n      element = element.parentNode;\n    } while (element);\n    return Element._returnOffset(valueL, valueT);\n  },\n\n  getOffsetParent: function(element) {\n    if (element.offsetParent) return $(element.offsetParent);\n    if (element == document.body) return $(element);\n\n    while ((element = element.parentNode) && element != document.body)\n      if (Element.getStyle(element, 'position') != 'static')\n        return $(element);\n\n    return $(document.body);\n  },\n\n  viewportOffset: function(forElement) {\n    var valueT = 0, valueL = 0;\n\n    var element = forElement;\n    do {\n      valueT += element.offsetTop  || 0;\n      valueL += element.offsetLeft || 0;\n\n      if (element.offsetParent == document.body &&\n        Element.getStyle(element, 'position') == 'absolute') break;\n\n    } while (element = element.offsetParent);\n\n    element = forElement;\n    do {\n      if (!Prototype.Browser.Opera || (element.tagName && (element.tagName.toUpperCase() == 'BODY'))) {\n        valueT -= element.scrollTop  || 0;\n        valueL -= element.scrollLeft || 0;\n      }\n    } while (element = element.parentNode);\n\n    return Element._returnOffset(valueL, valueT);\n  },\n\n  clonePosition: function(element, source) {\n    var options = Object.extend({\n      setLeft:    true,\n      setTop:     true,\n      setWidth:   true,\n      setHeight:  true,\n      offsetTop:  0,\n      offsetLeft: 0\n    }, arguments[2] || { });\n\n    source = $(source);\n    var p = Element.viewportOffset(source);\n\n    element = $(element);\n    var delta = [0, 0];\n    var parent = null;\n    if (Element.getStyle(element, 'position') == 'absolute') {\n      parent = Element.getOffsetParent(element);\n      delta = Element.viewportOffset(parent);\n    }\n\n    if (parent == document.body) {\n      delta[0] -= document.body.offsetLeft;\n      delta[1] -= document.body.offsetTop;\n    }\n\n    if (options.setLeft)   element.style.left  = (p[0] - delta[0] + options.offsetLeft) + 'px';\n    if (options.setTop)    element.style.top   = (p[1] - delta[1] + options.offsetTop) + 'px';\n    if (options.setWidth)  element.style.width = source.offsetWidth + 'px';\n    if (options.setHeight) element.style.height = source.offsetHeight + 'px';\n    return element;\n  }\n};\n\nObject.extend(Element.Methods, {\n  getElementsBySelector: Element.Methods.select,\n\n  childElements: Element.Methods.immediateDescendants\n});\n\nElement._attributeTranslations = {\n  write: {\n    names: {\n      className: 'class',\n      htmlFor:   'for'\n    },\n    values: { }\n  }\n};\n\nif (Prototype.Browser.Opera) {\n  Element.Methods.getStyle = Element.Methods.getStyle.wrap(\n    function(proceed, element, style) {\n      switch (style) {\n        case 'left': case 'top': case 'right': case 'bottom':\n          if (proceed(element, 'position') === 'static') return null;\n        case 'height': case 'width':\n          if (!Element.visible(element)) return null;\n\n          var dim = parseInt(proceed(element, style), 10);\n\n          if (dim !== element['offset' + style.capitalize()])\n            return dim + 'px';\n\n          var properties;\n          if (style === 'height') {\n            properties = ['border-top-width', 'padding-top',\n             'padding-bottom', 'border-bottom-width'];\n          }\n          else {\n            properties = ['border-left-width', 'padding-left',\n             'padding-right', 'border-right-width'];\n          }\n          return properties.inject(dim, function(memo, property) {\n            var val = proceed(element, property);\n            return val === null ? memo : memo - parseInt(val, 10);\n          }) + 'px';\n        default: return proceed(element, style);\n      }\n    }\n  );\n\n  Element.Methods.readAttribute = Element.Methods.readAttribute.wrap(\n    function(proceed, element, attribute) {\n      if (attribute === 'title') return element.title;\n      return proceed(element, attribute);\n    }\n  );\n}\n\nelse if (Prototype.Browser.IE) {\n  Element.Methods.getOffsetParent = Element.Methods.getOffsetParent.wrap(\n    function(proceed, element) {\n      element = $(element);\n      try { element.offsetParent }\n      catch(e) { return $(document.body) }\n      var position = element.getStyle('position');\n      if (position !== 'static') return proceed(element);\n      element.setStyle({ position: 'relative' });\n      var value = proceed(element);\n      element.setStyle({ position: position });\n      return value;\n    }\n  );\n\n  $w('positionedOffset viewportOffset').each(function(method) {\n    Element.Methods[method] = Element.Methods[method].wrap(\n      function(proceed, element) {\n        element = $(element);\n        try { element.offsetParent }\n        catch(e) { return Element._returnOffset(0,0) }\n        var position = element.getStyle('position');\n        if (position !== 'static') return proceed(element);\n        var offsetParent = element.getOffsetParent();\n        if (offsetParent && offsetParent.getStyle('position') === 'fixed')\n          offsetParent.setStyle({ zoom: 1 });\n        element.setStyle({ position: 'relative' });\n        var value = proceed(element);\n        element.setStyle({ position: position });\n        return value;\n      }\n    );\n  });\n\n  Element.Methods.cumulativeOffset = Element.Methods.cumulativeOffset.wrap(\n    function(proceed, element) {\n      try { element.offsetParent }\n      catch(e) { return Element._returnOffset(0,0) }\n      return proceed(element);\n    }\n  );\n\n  Element.Methods.getStyle = function(element, style) {\n    element = $(element);\n    style = (style == 'float' || style == 'cssFloat') ? 'styleFloat' : style.camelize();\n    var value = element.style[style];\n    if (!value && element.currentStyle) value = element.currentStyle[style];\n\n    if (style == 'opacity') {\n      if (value = (element.getStyle('filter') || '').match(/alpha\\(opacity=(.*)\\)/))\n        if (value[1]) return parseFloat(value[1]) / 100;\n      return 1.0;\n    }\n\n    if (value == 'auto') {\n      if ((style == 'width' || style == 'height') && (element.getStyle('display') != 'none'))\n        return element['offset' + style.capitalize()] + 'px';\n      return null;\n    }\n    return value;\n  };\n\n  Element.Methods.setOpacity = function(element, value) {\n    function stripAlpha(filter){\n      return filter.replace(/alpha\\([^\\)]*\\)/gi,'');\n    }\n    element = $(element);\n    var currentStyle = element.currentStyle;\n    if ((currentStyle && !currentStyle.hasLayout) ||\n      (!currentStyle && element.style.zoom == 'normal'))\n        element.style.zoom = 1;\n\n    var filter = element.getStyle('filter'), style = element.style;\n    if (value == 1 || value === '') {\n      (filter = stripAlpha(filter)) ?\n        style.filter = filter : style.removeAttribute('filter');\n      return element;\n    } else if (value < 0.00001) value = 0;\n    style.filter = stripAlpha(filter) +\n      'alpha(opacity=' + (value * 100) + ')';\n    return element;\n  };\n\n  Element._attributeTranslations = (function(){\n\n    var classProp = 'className';\n    var forProp = 'for';\n\n    var el = document.createElement('div');\n\n    el.setAttribute(classProp, 'x');\n\n    if (el.className !== 'x') {\n      el.setAttribute('class', 'x');\n      if (el.className === 'x') {\n        classProp = 'class';\n      }\n    }\n    el = null;\n\n    el = document.createElement('label');\n    el.setAttribute(forProp, 'x');\n    if (el.htmlFor !== 'x') {\n      el.setAttribute('htmlFor', 'x');\n      if (el.htmlFor === 'x') {\n        forProp = 'htmlFor';\n      }\n    }\n    el = null;\n\n    return {\n      read: {\n        names: {\n          'class':      classProp,\n          'className':  classProp,\n          'for':        forProp,\n          'htmlFor':    forProp\n        },\n        values: {\n          _getAttr: function(element, attribute) {\n            return element.getAttribute(attribute);\n          },\n          _getAttr2: function(element, attribute) {\n            return element.getAttribute(attribute, 2);\n          },\n          _getAttrNode: function(element, attribute) {\n            var node = element.getAttributeNode(attribute);\n            return node ? node.value : \"\";\n          },\n          _getEv: (function(){\n\n            var el = document.createElement('div');\n            el.onclick = Prototype.emptyFunction;\n            var value = el.getAttribute('onclick');\n            var f;\n\n            if (String(value).indexOf('{') > -1) {\n              f = function(element, attribute) {\n                attribute = element.getAttribute(attribute);\n                if (!attribute) return null;\n                attribute = attribute.toString();\n                attribute = attribute.split('{')[1];\n                attribute = attribute.split('}')[0];\n                return attribute.strip();\n              };\n            }\n            else if (value === '') {\n              f = function(element, attribute) {\n                attribute = element.getAttribute(attribute);\n                if (!attribute) return null;\n                return attribute.strip();\n              };\n            }\n            el = null;\n            return f;\n          })(),\n          _flag: function(element, attribute) {\n            return $(element).hasAttribute(attribute) ? attribute : null;\n          },\n          style: function(element) {\n            return element.style.cssText.toLowerCase();\n          },\n          title: function(element) {\n            return element.title;\n          }\n        }\n      }\n    }\n  })();\n\n  Element._attributeTranslations.write = {\n    names: Object.extend({\n      cellpadding: 'cellPadding',\n      cellspacing: 'cellSpacing'\n    }, Element._attributeTranslations.read.names),\n    values: {\n      checked: function(element, value) {\n        element.checked = !!value;\n      },\n\n      style: function(element, value) {\n        element.style.cssText = value ? value : '';\n      }\n    }\n  };\n\n  Element._attributeTranslations.has = {};\n\n  $w('colSpan rowSpan vAlign dateTime accessKey tabIndex ' +\n      'encType maxLength readOnly longDesc frameBorder').each(function(attr) {\n    Element._attributeTranslations.write.names[attr.toLowerCase()] = attr;\n    Element._attributeTranslations.has[attr.toLowerCase()] = attr;\n  });\n\n  (function(v) {\n    Object.extend(v, {\n      href:        v._getAttr2,\n      src:         v._getAttr2,\n      type:        v._getAttr,\n      action:      v._getAttrNode,\n      disabled:    v._flag,\n      checked:     v._flag,\n      readonly:    v._flag,\n      multiple:    v._flag,\n      onload:      v._getEv,\n      onunload:    v._getEv,\n      onclick:     v._getEv,\n      ondblclick:  v._getEv,\n      onmousedown: v._getEv,\n      onmouseup:   v._getEv,\n      onmouseover: v._getEv,\n      onmousemove: v._getEv,\n      onmouseout:  v._getEv,\n      onfocus:     v._getEv,\n      onblur:      v._getEv,\n      onkeypress:  v._getEv,\n      onkeydown:   v._getEv,\n      onkeyup:     v._getEv,\n      onsubmit:    v._getEv,\n      onreset:     v._getEv,\n      onselect:    v._getEv,\n      onchange:    v._getEv\n    });\n  })(Element._attributeTranslations.read.values);\n\n  if (Prototype.BrowserFeatures.ElementExtensions) {\n    (function() {\n      function _descendants(element) {\n        var nodes = element.getElementsByTagName('*'), results = [];\n        for (var i = 0, node; node = nodes[i]; i++)\n          if (node.tagName !== \"!\") // Filter out comment nodes.\n            results.push(node);\n        return results;\n      }\n\n      Element.Methods.down = function(element, expression, index) {\n        element = $(element);\n        if (arguments.length == 1) return element.firstDescendant();\n        return Object.isNumber(expression) ? _descendants(element)[expression] :\n          Element.select(element, expression)[index || 0];\n      }\n    })();\n  }\n\n}\n\nelse if (Prototype.Browser.Gecko && /rv:1\\.8\\.0/.test(navigator.userAgent)) {\n  Element.Methods.setOpacity = function(element, value) {\n    element = $(element);\n    element.style.opacity = (value == 1) ? 0.999999 :\n      (value === '') ? '' : (value < 0.00001) ? 0 : value;\n    return element;\n  };\n}\n\nelse if (Prototype.Browser.WebKit) {\n  Element.Methods.setOpacity = function(element, value) {\n    element = $(element);\n    element.style.opacity = (value == 1 || value === '') ? '' :\n      (value < 0.00001) ? 0 : value;\n\n    if (value == 1)\n      if(element.tagName.toUpperCase() == 'IMG' && element.width) {\n        element.width++; element.width--;\n      } else try {\n        var n = document.createTextNode(' ');\n        element.appendChild(n);\n        element.removeChild(n);\n      } catch (e) { }\n\n    return element;\n  };\n\n  Element.Methods.cumulativeOffset = function(element) {\n    var valueT = 0, valueL = 0;\n    do {\n      valueT += element.offsetTop  || 0;\n      valueL += element.offsetLeft || 0;\n      if (element.offsetParent == document.body)\n        if (Element.getStyle(element, 'position') == 'absolute') break;\n\n      element = element.offsetParent;\n    } while (element);\n\n    return Element._returnOffset(valueL, valueT);\n  };\n}\n\nif ('outerHTML' in document.documentElement) {\n  Element.Methods.replace = function(element, content) {\n    element = $(element);\n\n    if (content && content.toElement) content = content.toElement();\n    if (Object.isElement(content)) {\n      element.parentNode.replaceChild(content, element);\n      return element;\n    }\n\n    content = Object.toHTML(content);\n    var parent = element.parentNode, tagName = parent.tagName.toUpperCase();\n\n    if (Element._insertionTranslations.tags[tagName]) {\n      var nextSibling = element.next();\n      var fragments = Element._getContentFromAnonymousElement(tagName, content.stripScripts());\n      parent.removeChild(element);\n      if (nextSibling)\n        fragments.each(function(node) { parent.insertBefore(node, nextSibling) });\n      else\n        fragments.each(function(node) { parent.appendChild(node) });\n    }\n    else element.outerHTML = content.stripScripts();\n\n    content.evalScripts.bind(content).defer();\n    return element;\n  };\n}\n\nElement._returnOffset = function(l, t) {\n  var result = [l, t];\n  result.left = l;\n  result.top = t;\n  return result;\n};\n\nElement._getContentFromAnonymousElement = function(tagName, html) {\n  var div = new Element('div'), t = Element._insertionTranslations.tags[tagName];\n  if (t) {\n    div.innerHTML = t[0] + html + t[1];\n    t[2].times(function() { div = div.firstChild });\n  } else div.innerHTML = html;\n  return $A(div.childNodes);\n};\n\nElement._insertionTranslations = {\n  before: function(element, node) {\n    element.parentNode.insertBefore(node, element);\n  },\n  top: function(element, node) {\n    element.insertBefore(node, element.firstChild);\n  },\n  bottom: function(element, node) {\n    element.appendChild(node);\n  },\n  after: function(element, node) {\n    element.parentNode.insertBefore(node, element.nextSibling);\n  },\n  tags: {\n    TABLE:  ['<table>',                '</table>',                   1],\n    TBODY:  ['<table><tbody>',         '</tbody></table>',           2],\n    TR:     ['<table><tbody><tr>',     '</tr></tbody></table>',      3],\n    TD:     ['<table><tbody><tr><td>', '</td></tr></tbody></table>', 4],\n    SELECT: ['<select>',               '</select>',                  1]\n  }\n};\n\n(function() {\n  var tags = Element._insertionTranslations.tags;\n  Object.extend(tags, {\n    THEAD: tags.TBODY,\n    TFOOT: tags.TBODY,\n    TH:    tags.TD\n  });\n})();\n\nElement.Methods.Simulated = {\n  hasAttribute: function(element, attribute) {\n    attribute = Element._attributeTranslations.has[attribute] || attribute;\n    var node = $(element).getAttributeNode(attribute);\n    return !!(node && node.specified);\n  }\n};\n\nElement.Methods.ByTag = { };\n\nObject.extend(Element, Element.Methods);\n\n(function(div) {\n\n  if (!Prototype.BrowserFeatures.ElementExtensions && div['__proto__']) {\n    window.HTMLElement = { };\n    window.HTMLElement.prototype = div['__proto__'];\n    Prototype.BrowserFeatures.ElementExtensions = true;\n  }\n\n  div = null;\n\n})(document.createElement('div'))\n\nElement.extend = (function() {\n\n  function checkDeficiency(tagName) {\n    if (typeof window.Element != 'undefined') {\n      var proto = window.Element.prototype;\n      if (proto) {\n        var id = '_' + (Math.random()+'').slice(2);\n        var el = document.createElement(tagName);\n        proto[id] = 'x';\n        var isBuggy = (el[id] !== 'x');\n        delete proto[id];\n        el = null;\n        return isBuggy;\n      }\n    }\n    return false;\n  }\n\n  function extendElementWith(element, methods) {\n    for (var property in methods) {\n      var value = methods[property];\n      if (Object.isFunction(value) && !(property in element))\n        element[property] = value.methodize();\n    }\n  }\n\n  var HTMLOBJECTELEMENT_PROTOTYPE_BUGGY = checkDeficiency('object');\n\n  if (Prototype.BrowserFeatures.SpecificElementExtensions) {\n    if (HTMLOBJECTELEMENT_PROTOTYPE_BUGGY) {\n      return function(element) {\n        if (element && typeof element._extendedByPrototype == 'undefined') {\n          var t = element.tagName;\n          if (t && (/^(?:object|applet|embed)$/i.test(t))) {\n            extendElementWith(element, Element.Methods);\n            extendElementWith(element, Element.Methods.Simulated);\n            extendElementWith(element, Element.Methods.ByTag[t.toUpperCase()]);\n          }\n        }\n        return element;\n      }\n    }\n    return Prototype.K;\n  }\n\n  var Methods = { }, ByTag = Element.Methods.ByTag;\n\n  var extend = Object.extend(function(element) {\n    if (!element || typeof element._extendedByPrototype != 'undefined' ||\n        element.nodeType != 1 || element == window) return element;\n\n    var methods = Object.clone(Methods),\n        tagName = element.tagName.toUpperCase();\n\n    if (ByTag[tagName]) Object.extend(methods, ByTag[tagName]);\n\n    extendElementWith(element, methods);\n\n    element._extendedByPrototype = Prototype.emptyFunction;\n    return element;\n\n  }, {\n    refresh: function() {\n      if (!Prototype.BrowserFeatures.ElementExtensions) {\n        Object.extend(Methods, Element.Methods);\n        Object.extend(Methods, Element.Methods.Simulated);\n      }\n    }\n  });\n\n  extend.refresh();\n  return extend;\n})();\n\nElement.hasAttribute = function(element, attribute) {\n  if (element.hasAttribute) return element.hasAttribute(attribute);\n  return Element.Methods.Simulated.hasAttribute(element, attribute);\n};\n\nElement.addMethods = function(methods) {\n  var F = Prototype.BrowserFeatures, T = Element.Methods.ByTag;\n\n  if (!methods) {\n    Object.extend(Form, Form.Methods);\n    Object.extend(Form.Element, Form.Element.Methods);\n    Object.extend(Element.Methods.ByTag, {\n      \"FORM\":     Object.clone(Form.Methods),\n      \"INPUT\":    Object.clone(Form.Element.Methods),\n      \"SELECT\":   Object.clone(Form.Element.Methods),\n      \"TEXTAREA\": Object.clone(Form.Element.Methods)\n    });\n  }\n\n  if (arguments.length == 2) {\n    var tagName = methods;\n    methods = arguments[1];\n  }\n\n  if (!tagName) Object.extend(Element.Methods, methods || { });\n  else {\n    if (Object.isArray(tagName)) tagName.each(extend);\n    else extend(tagName);\n  }\n\n  function extend(tagName) {\n    tagName = tagName.toUpperCase();\n    if (!Element.Methods.ByTag[tagName])\n      Element.Methods.ByTag[tagName] = { };\n    Object.extend(Element.Methods.ByTag[tagName], methods);\n  }\n\n  function copy(methods, destination, onlyIfAbsent) {\n    onlyIfAbsent = onlyIfAbsent || false;\n    for (var property in methods) {\n      var value = methods[property];\n      if (!Object.isFunction(value)) continue;\n      if (!onlyIfAbsent || !(property in destination))\n        destination[property] = value.methodize();\n    }\n  }\n\n  function findDOMClass(tagName) {\n    var klass;\n    var trans = {\n      \"OPTGROUP\": \"OptGroup\", \"TEXTAREA\": \"TextArea\", \"P\": \"Paragraph\",\n      \"FIELDSET\": \"FieldSet\", \"UL\": \"UList\", \"OL\": \"OList\", \"DL\": \"DList\",\n      \"DIR\": \"Directory\", \"H1\": \"Heading\", \"H2\": \"Heading\", \"H3\": \"Heading\",\n      \"H4\": \"Heading\", \"H5\": \"Heading\", \"H6\": \"Heading\", \"Q\": \"Quote\",\n      \"INS\": \"Mod\", \"DEL\": \"Mod\", \"A\": \"Anchor\", \"IMG\": \"Image\", \"CAPTION\":\n      \"TableCaption\", \"COL\": \"TableCol\", \"COLGROUP\": \"TableCol\", \"THEAD\":\n      \"TableSection\", \"TFOOT\": \"TableSection\", \"TBODY\": \"TableSection\", \"TR\":\n      \"TableRow\", \"TH\": \"TableCell\", \"TD\": \"TableCell\", \"FRAMESET\":\n      \"FrameSet\", \"IFRAME\": \"IFrame\"\n    };\n    if (trans[tagName]) klass = 'HTML' + trans[tagName] + 'Element';\n    if (window[klass]) return window[klass];\n    klass = 'HTML' + tagName + 'Element';\n    if (window[klass]) return window[klass];\n    klass = 'HTML' + tagName.capitalize() + 'Element';\n    if (window[klass]) return window[klass];\n\n    var element = document.createElement(tagName);\n    var proto = element['__proto__'] || element.constructor.prototype;\n    element = null;\n    return proto;\n  }\n\n  var elementPrototype = window.HTMLElement ? HTMLElement.prototype :\n   Element.prototype;\n\n  if (F.ElementExtensions) {\n    copy(Element.Methods, elementPrototype);\n    copy(Element.Methods.Simulated, elementPrototype, true);\n  }\n\n  if (F.SpecificElementExtensions) {\n    for (var tag in Element.Methods.ByTag) {\n      var klass = findDOMClass(tag);\n      if (Object.isUndefined(klass)) continue;\n      copy(T[tag], klass.prototype);\n    }\n  }\n\n  Object.extend(Element, Element.Methods);\n  delete Element.ByTag;\n\n  if (Element.extend.refresh) Element.extend.refresh();\n  Element.cache = { };\n};\n\n\ndocument.viewport = {\n\n  getDimensions: function() {\n    return { width: this.getWidth(), height: this.getHeight() };\n  },\n\n  getScrollOffsets: function() {\n    return Element._returnOffset(\n      window.pageXOffset || document.documentElement.scrollLeft || document.body.scrollLeft,\n      window.pageYOffset || document.documentElement.scrollTop  || document.body.scrollTop);\n  }\n};\n\n(function(viewport) {\n  var B = Prototype.Browser, doc = document, element, property = {};\n\n  function getRootElement() {\n    if (B.WebKit && !doc.evaluate)\n      return document;\n\n    if (B.Opera && window.parseFloat(window.opera.version()) < 9.5)\n      return document.body;\n\n    return document.documentElement;\n  }\n\n  function define(D) {\n    if (!element) element = getRootElement();\n\n    property[D] = 'client' + D;\n\n    viewport['get' + D] = function() { return element[property[D]] };\n    return viewport['get' + D]();\n  }\n\n  viewport.getWidth  = define.curry('Width');\n\n  viewport.getHeight = define.curry('Height');\n})(document.viewport);\n\n\nElement.Storage = {\n  UID: 1\n};\n\nElement.addMethods({\n  getStorage: function(element) {\n    if (!(element = $(element))) return;\n\n    var uid;\n    if (element === window) {\n      uid = 0;\n    } else {\n      if (typeof element._prototypeUID === \"undefined\")\n        element._prototypeUID = [Element.Storage.UID++];\n      uid = element._prototypeUID[0];\n    }\n\n    if (!Element.Storage[uid])\n      Element.Storage[uid] = $H();\n\n    return Element.Storage[uid];\n  },\n\n  store: function(element, key, value) {\n    if (!(element = $(element))) return;\n\n    if (arguments.length === 2) {\n      Element.getStorage(element).update(key);\n    } else {\n      Element.getStorage(element).set(key, value);\n    }\n\n    return element;\n  },\n\n  retrieve: function(element, key, defaultValue) {\n    if (!(element = $(element))) return;\n    var hash = Element.getStorage(element), value = hash.get(key);\n\n    if (Object.isUndefined(value)) {\n      hash.set(key, defaultValue);\n      value = defaultValue;\n    }\n\n    return value;\n  },\n\n  clone: function(element, deep) {\n    if (!(element = $(element))) return;\n    var clone = element.cloneNode(deep);\n    clone._prototypeUID = void 0;\n    if (deep) {\n      var descendants = Element.select(clone, '*'),\n          i = descendants.length;\n      while (i--) {\n        descendants[i]._prototypeUID = void 0;\n      }\n    }\n    return Element.extend(clone);\n  }\n});\n/* Portions of the Selector class are derived from Jack Slocum's DomQuery,\n * part of YUI-Ext version 0.40, distributed under the terms of an MIT-style\n * license.  Please see http://www.yui-ext.com/ for more information. */\n\nvar Selector = Class.create({\n  initialize: function(expression) {\n    this.expression = expression.strip();\n\n    if (this.shouldUseSelectorsAPI()) {\n      this.mode = 'selectorsAPI';\n    } else if (this.shouldUseXPath()) {\n      this.mode = 'xpath';\n      this.compileXPathMatcher();\n    } else {\n      this.mode = \"normal\";\n      this.compileMatcher();\n    }\n\n  },\n\n  shouldUseXPath: (function() {\n\n    var IS_DESCENDANT_SELECTOR_BUGGY = (function(){\n      var isBuggy = false;\n      if (document.evaluate && window.XPathResult) {\n        var el = document.createElement('div');\n        el.innerHTML = '<ul><li></li></ul><div><ul><li></li></ul></div>';\n\n        var xpath = \".//*[local-name()='ul' or local-name()='UL']\" +\n          \"//*[local-name()='li' or local-name()='LI']\";\n\n        var result = document.evaluate(xpath, el, null,\n          XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);\n\n        isBuggy = (result.snapshotLength !== 2);\n        el = null;\n      }\n      return isBuggy;\n    })();\n\n    return function() {\n      if (!Prototype.BrowserFeatures.XPath) return false;\n\n      var e = this.expression;\n\n      if (Prototype.Browser.WebKit &&\n       (e.include(\"-of-type\") || e.include(\":empty\")))\n        return false;\n\n      if ((/(\\[[\\w-]*?:|:checked)/).test(e))\n        return false;\n\n      if (IS_DESCENDANT_SELECTOR_BUGGY) return false;\n\n      return true;\n    }\n\n  })(),\n\n  shouldUseSelectorsAPI: function() {\n    if (!Prototype.BrowserFeatures.SelectorsAPI) return false;\n\n    if (Selector.CASE_INSENSITIVE_CLASS_NAMES) return false;\n\n    if (!Selector._div) Selector._div = new Element('div');\n\n    try {\n      Selector._div.querySelector(this.expression);\n    } catch(e) {\n      return false;\n    }\n\n    return true;\n  },\n\n  compileMatcher: function() {\n    var e = this.expression, ps = Selector.patterns, h = Selector.handlers,\n        c = Selector.criteria, le, p, m, len = ps.length, name;\n\n    if (Selector._cache[e]) {\n      this.matcher = Selector._cache[e];\n      return;\n    }\n\n    this.matcher = [\"this.matcher = function(root) {\",\n                    \"var r = root, h = Selector.handlers, c = false, n;\"];\n\n    while (e && le != e && (/\\S/).test(e)) {\n      le = e;\n      for (var i = 0; i<len; i++) {\n        p = ps[i].re;\n        name = ps[i].name;\n        if (m = e.match(p)) {\n          this.matcher.push(Object.isFunction(c[name]) ? c[name](m) :\n            new Template(c[name]).evaluate(m));\n          e = e.replace(m[0], '');\n          break;\n        }\n      }\n    }\n\n    this.matcher.push(\"return h.unique(n);\\n}\");\n    eval(this.matcher.join('\\n'));\n    Selector._cache[this.expression] = this.matcher;\n  },\n\n  compileXPathMatcher: function() {\n    var e = this.expression, ps = Selector.patterns,\n        x = Selector.xpath, le, m, len = ps.length, name;\n\n    if (Selector._cache[e]) {\n      this.xpath = Selector._cache[e]; return;\n    }\n\n    this.matcher = ['.//*'];\n    while (e && le != e && (/\\S/).test(e)) {\n      le = e;\n      for (var i = 0; i<len; i++) {\n        name = ps[i].name;\n        if (m = e.match(ps[i].re)) {\n          this.matcher.push(Object.isFunction(x[name]) ? x[name](m) :\n            new Template(x[name]).evaluate(m));\n          e = e.replace(m[0], '');\n          break;\n        }\n      }\n    }\n\n    this.xpath = this.matcher.join('');\n    Selector._cache[this.expression] = this.xpath;\n  },\n\n  findElements: function(root) {\n    root = root || document;\n    var e = this.expression, results;\n\n    switch (this.mode) {\n      case 'selectorsAPI':\n        if (root !== document) {\n          var oldId = root.id, id = $(root).identify();\n          id = id.replace(/([\\.:])/g, \"\\\\$1\");\n          e = \"#\" + id + \" \" + e;\n        }\n\n        results = $A(root.querySelectorAll(e)).map(Element.extend);\n        root.id = oldId;\n\n        return results;\n      case 'xpath':\n        return document._getElementsByXPath(this.xpath, root);\n      default:\n       return this.matcher(root);\n    }\n  },\n\n  match: function(element) {\n    this.tokens = [];\n\n    var e = this.expression, ps = Selector.patterns, as = Selector.assertions;\n    var le, p, m, len = ps.length, name;\n\n    while (e && le !== e && (/\\S/).test(e)) {\n      le = e;\n      for (var i = 0; i<len; i++) {\n        p = ps[i].re;\n        name = ps[i].name;\n        if (m = e.match(p)) {\n          if (as[name]) {\n            this.tokens.push([name, Object.clone(m)]);\n            e = e.replace(m[0], '');\n          } else {\n            return this.findElements(document).include(element);\n          }\n        }\n      }\n    }\n\n    var match = true, name, matches;\n    for (var i = 0, token; token = this.tokens[i]; i++) {\n      name = token[0], matches = token[1];\n      if (!Selector.assertions[name](element, matches)) {\n        match = false; break;\n      }\n    }\n\n    return match;\n  },\n\n  toString: function() {\n    return this.expression;\n  },\n\n  inspect: function() {\n    return \"#<Selector:\" + this.expression.inspect() + \">\";\n  }\n});\n\nif (Prototype.BrowserFeatures.SelectorsAPI &&\n document.compatMode === 'BackCompat') {\n  Selector.CASE_INSENSITIVE_CLASS_NAMES = (function(){\n    var div = document.createElement('div'),\n     span = document.createElement('span');\n\n    div.id = \"prototype_test_id\";\n    span.className = 'Test';\n    div.appendChild(span);\n    var isIgnored = (div.querySelector('#prototype_test_id .test') !== null);\n    div = span = null;\n    return isIgnored;\n  })();\n}\n\nObject.extend(Selector, {\n  _cache: { },\n\n  xpath: {\n    descendant:   \"//*\",\n    child:        \"/*\",\n    adjacent:     \"/following-sibling::*[1]\",\n    laterSibling: '/following-sibling::*',\n    tagName:      function(m) {\n      if (m[1] == '*') return '';\n      return \"[local-name()='\" + m[1].toLowerCase() +\n             \"' or local-name()='\" + m[1].toUpperCase() + \"']\";\n    },\n    className:    \"[contains(concat(' ', @class, ' '), ' #{1} ')]\",\n    id:           \"[@id='#{1}']\",\n    attrPresence: function(m) {\n      m[1] = m[1].toLowerCase();\n      return new Template(\"[@#{1}]\").evaluate(m);\n    },\n    attr: function(m) {\n      m[1] = m[1].toLowerCase();\n      m[3] = m[5] || m[6];\n      return new Template(Selector.xpath.operators[m[2]]).evaluate(m);\n    },\n    pseudo: function(m) {\n      var h = Selector.xpath.pseudos[m[1]];\n      if (!h) return '';\n      if (Object.isFunction(h)) return h(m);\n      return new Template(Selector.xpath.pseudos[m[1]]).evaluate(m);\n    },\n    operators: {\n      '=':  \"[@#{1}='#{3}']\",\n      '!=': \"[@#{1}!='#{3}']\",\n      '^=': \"[starts-with(@#{1}, '#{3}')]\",\n      '$=': \"[substring(@#{1}, (string-length(@#{1}) - string-length('#{3}') + 1))='#{3}']\",\n      '*=': \"[contains(@#{1}, '#{3}')]\",\n      '~=': \"[contains(concat(' ', @#{1}, ' '), ' #{3} ')]\",\n      '|=': \"[contains(concat('-', @#{1}, '-'), '-#{3}-')]\"\n    },\n    pseudos: {\n      'first-child': '[not(preceding-sibling::*)]',\n      'last-child':  '[not(following-sibling::*)]',\n      'only-child':  '[not(preceding-sibling::* or following-sibling::*)]',\n      'empty':       \"[count(*) = 0 and (count(text()) = 0)]\",\n      'checked':     \"[@checked]\",\n      'disabled':    \"[(@disabled) and (@type!='hidden')]\",\n      'enabled':     \"[not(@disabled) and (@type!='hidden')]\",\n      'not': function(m) {\n        var e = m[6], p = Selector.patterns,\n            x = Selector.xpath, le, v, len = p.length, name;\n\n        var exclusion = [];\n        while (e && le != e && (/\\S/).test(e)) {\n          le = e;\n          for (var i = 0; i<len; i++) {\n            name = p[i].name\n            if (m = e.match(p[i].re)) {\n              v = Object.isFunction(x[name]) ? x[name](m) : new Template(x[name]).evaluate(m);\n              exclusion.push(\"(\" + v.substring(1, v.length - 1) + \")\");\n              e = e.replace(m[0], '');\n              break;\n            }\n          }\n        }\n        return \"[not(\" + exclusion.join(\" and \") + \")]\";\n      },\n      'nth-child':      function(m) {\n        return Selector.xpath.pseudos.nth(\"(count(./preceding-sibling::*) + 1) \", m);\n      },\n      'nth-last-child': function(m) {\n        return Selector.xpath.pseudos.nth(\"(count(./following-sibling::*) + 1) \", m);\n      },\n      'nth-of-type':    function(m) {\n        return Selector.xpath.pseudos.nth(\"position() \", m);\n      },\n      'nth-last-of-type': function(m) {\n        return Selector.xpath.pseudos.nth(\"(last() + 1 - position()) \", m);\n      },\n      'first-of-type':  function(m) {\n        m[6] = \"1\"; return Selector.xpath.pseudos['nth-of-type'](m);\n      },\n      'last-of-type':   function(m) {\n        m[6] = \"1\"; return Selector.xpath.pseudos['nth-last-of-type'](m);\n      },\n      'only-of-type':   function(m) {\n        var p = Selector.xpath.pseudos; return p['first-of-type'](m) + p['last-of-type'](m);\n      },\n      nth: function(fragment, m) {\n        var mm, formula = m[6], predicate;\n        if (formula == 'even') formula = '2n+0';\n        if (formula == 'odd')  formula = '2n+1';\n        if (mm = formula.match(/^(\\d+)$/)) // digit only\n          return '[' + fragment + \"= \" + mm[1] + ']';\n        if (mm = formula.match(/^(-?\\d*)?n(([+-])(\\d+))?/)) { // an+b\n          if (mm[1] == \"-\") mm[1] = -1;\n          var a = mm[1] ? Number(mm[1]) : 1;\n          var b = mm[2] ? Number(mm[2]) : 0;\n          predicate = \"[((#{fragment} - #{b}) mod #{a} = 0) and \" +\n          \"((#{fragment} - #{b}) div #{a} >= 0)]\";\n          return new Template(predicate).evaluate({\n            fragment: fragment, a: a, b: b });\n        }\n      }\n    }\n  },\n\n  criteria: {\n    tagName:      'n = h.tagName(n, r, \"#{1}\", c);      c = false;',\n    className:    'n = h.className(n, r, \"#{1}\", c);    c = false;',\n    id:           'n = h.id(n, r, \"#{1}\", c);           c = false;',\n    attrPresence: 'n = h.attrPresence(n, r, \"#{1}\", c); c = false;',\n    attr: function(m) {\n      m[3] = (m[5] || m[6]);\n      return new Template('n = h.attr(n, r, \"#{1}\", \"#{3}\", \"#{2}\", c); c = false;').evaluate(m);\n    },\n    pseudo: function(m) {\n      if (m[6]) m[6] = m[6].replace(/\"/g, '\\\\\"');\n      return new Template('n = h.pseudo(n, \"#{1}\", \"#{6}\", r, c); c = false;').evaluate(m);\n    },\n    descendant:   'c = \"descendant\";',\n    child:        'c = \"child\";',\n    adjacent:     'c = \"adjacent\";',\n    laterSibling: 'c = \"laterSibling\";'\n  },\n\n  patterns: [\n    { name: 'laterSibling', re: /^\\s*~\\s*/ },\n    { name: 'child',        re: /^\\s*>\\s*/ },\n    { name: 'adjacent',     re: /^\\s*\\+\\s*/ },\n    { name: 'descendant',   re: /^\\s/ },\n\n    { name: 'tagName',      re: /^\\s*(\\*|[\\w\\-]+)(\\b|$)?/ },\n    { name: 'id',           re: /^#([\\w\\-\\*]+)(\\b|$)/ },\n    { name: 'className',    re: /^\\.([\\w\\-\\*]+)(\\b|$)/ },\n    { name: 'pseudo',       re: /^:((first|last|nth|nth-last|only)(-child|-of-type)|empty|checked|(en|dis)abled|not)(\\((.*?)\\))?(\\b|$|(?=\\s|[:+~>]))/ },\n    { name: 'attrPresence', re: /^\\[((?:[\\w-]+:)?[\\w-]+)\\]/ },\n    { name: 'attr',         re: /\\[((?:[\\w-]*:)?[\\w-]+)\\s*(?:([!^$*~|]?=)\\s*((['\"])([^\\4]*?)\\4|([^'\"][^\\]]*?)))?\\]/ }\n  ],\n\n  assertions: {\n    tagName: function(element, matches) {\n      return matches[1].toUpperCase() == element.tagName.toUpperCase();\n    },\n\n    className: function(element, matches) {\n      return Element.hasClassName(element, matches[1]);\n    },\n\n    id: function(element, matches) {\n      return element.id === matches[1];\n    },\n\n    attrPresence: function(element, matches) {\n      return Element.hasAttribute(element, matches[1]);\n    },\n\n    attr: function(element, matches) {\n      var nodeValue = Element.readAttribute(element, matches[1]);\n      return nodeValue && Selector.operators[matches[2]](nodeValue, matches[5] || matches[6]);\n    }\n  },\n\n  handlers: {\n    concat: function(a, b) {\n      for (var i = 0, node; node = b[i]; i++)\n        a.push(node);\n      return a;\n    },\n\n    mark: function(nodes) {\n      var _true = Prototype.emptyFunction;\n      for (var i = 0, node; node = nodes[i]; i++)\n        node._countedByPrototype = _true;\n      return nodes;\n    },\n\n    unmark: (function(){\n\n      var PROPERTIES_ATTRIBUTES_MAP = (function(){\n        var el = document.createElement('div'),\n            isBuggy = false,\n            propName = '_countedByPrototype',\n            value = 'x'\n        el[propName] = value;\n        isBuggy = (el.getAttribute(propName) === value);\n        el = null;\n        return isBuggy;\n      })();\n\n      return PROPERTIES_ATTRIBUTES_MAP ?\n        function(nodes) {\n          for (var i = 0, node; node = nodes[i]; i++)\n            node.removeAttribute('_countedByPrototype');\n          return nodes;\n        } :\n        function(nodes) {\n          for (var i = 0, node; node = nodes[i]; i++)\n            node._countedByPrototype = void 0;\n          return nodes;\n        }\n    })(),\n\n    index: function(parentNode, reverse, ofType) {\n      parentNode._countedByPrototype = Prototype.emptyFunction;\n      if (reverse) {\n        for (var nodes = parentNode.childNodes, i = nodes.length - 1, j = 1; i >= 0; i--) {\n          var node = nodes[i];\n          if (node.nodeType == 1 && (!ofType || node._countedByPrototype)) node.nodeIndex = j++;\n        }\n      } else {\n        for (var i = 0, j = 1, nodes = parentNode.childNodes; node = nodes[i]; i++)\n          if (node.nodeType == 1 && (!ofType || node._countedByPrototype)) node.nodeIndex = j++;\n      }\n    },\n\n    unique: function(nodes) {\n      if (nodes.length == 0) return nodes;\n      var results = [], n;\n      for (var i = 0, l = nodes.length; i < l; i++)\n        if (typeof (n = nodes[i])._countedByPrototype == 'undefined') {\n          n._countedByPrototype = Prototype.emptyFunction;\n          results.push(Element.extend(n));\n        }\n      return Selector.handlers.unmark(results);\n    },\n\n    descendant: function(nodes) {\n      var h = Selector.handlers;\n      for (var i = 0, results = [], node; node = nodes[i]; i++)\n        h.concat(results, node.getElementsByTagName('*'));\n      return results;\n    },\n\n    child: function(nodes) {\n      var h = Selector.handlers;\n      for (var i = 0, results = [], node; node = nodes[i]; i++) {\n        for (var j = 0, child; child = node.childNodes[j]; j++)\n          if (child.nodeType == 1 && child.tagName != '!') results.push(child);\n      }\n      return results;\n    },\n\n    adjacent: function(nodes) {\n      for (var i = 0, results = [], node; node = nodes[i]; i++) {\n        var next = this.nextElementSibling(node);\n        if (next) results.push(next);\n      }\n      return results;\n    },\n\n    laterSibling: function(nodes) {\n      var h = Selector.handlers;\n      for (var i = 0, results = [], node; node = nodes[i]; i++)\n        h.concat(results, Element.nextSiblings(node));\n      return results;\n    },\n\n    nextElementSibling: function(node) {\n      while (node = node.nextSibling)\n        if (node.nodeType == 1) return node;\n      return null;\n    },\n\n    previousElementSibling: function(node) {\n      while (node = node.previousSibling)\n        if (node.nodeType == 1) return node;\n      return null;\n    },\n\n    tagName: function(nodes, root, tagName, combinator) {\n      var uTagName = tagName.toUpperCase();\n      var results = [], h = Selector.handlers;\n      if (nodes) {\n        if (combinator) {\n          if (combinator == \"descendant\") {\n            for (var i = 0, node; node = nodes[i]; i++)\n              h.concat(results, node.getElementsByTagName(tagName));\n            return results;\n          } else nodes = this[combinator](nodes);\n          if (tagName == \"*\") return nodes;\n        }\n        for (var i = 0, node; node = nodes[i]; i++)\n          if (node.tagName.toUpperCase() === uTagName) results.push(node);\n        return results;\n      } else return root.getElementsByTagName(tagName);\n    },\n\n    id: function(nodes, root, id, combinator) {\n      var targetNode = $(id), h = Selector.handlers;\n\n      if (root == document) {\n        if (!targetNode) return [];\n        if (!nodes) return [targetNode];\n      } else {\n        if (!root.sourceIndex || root.sourceIndex < 1) {\n          var nodes = root.getElementsByTagName('*');\n          for (var j = 0, node; node = nodes[j]; j++) {\n            if (node.id === id) return [node];\n          }\n        }\n      }\n\n      if (nodes) {\n        if (combinator) {\n          if (combinator == 'child') {\n            for (var i = 0, node; node = nodes[i]; i++)\n              if (targetNode.parentNode == node) return [targetNode];\n          } else if (combinator == 'descendant') {\n            for (var i = 0, node; node = nodes[i]; i++)\n              if (Element.descendantOf(targetNode, node)) return [targetNode];\n          } else if (combinator == 'adjacent') {\n            for (var i = 0, node; node = nodes[i]; i++)\n              if (Selector.handlers.previousElementSibling(targetNode) == node)\n                return [targetNode];\n          } else nodes = h[combinator](nodes);\n        }\n        for (var i = 0, node; node = nodes[i]; i++)\n          if (node == targetNode) return [targetNode];\n        return [];\n      }\n      return (targetNode && Element.descendantOf(targetNode, root)) ? [targetNode] : [];\n    },\n\n    className: function(nodes, root, className, combinator) {\n      if (nodes && combinator) nodes = this[combinator](nodes);\n      return Selector.handlers.byClassName(nodes, root, className);\n    },\n\n    byClassName: function(nodes, root, className) {\n      if (!nodes) nodes = Selector.handlers.descendant([root]);\n      var needle = ' ' + className + ' ';\n      for (var i = 0, results = [], node, nodeClassName; node = nodes[i]; i++) {\n        nodeClassName = node.className;\n        if (nodeClassName.length == 0) continue;\n        if (nodeClassName == className || (' ' + nodeClassName + ' ').include(needle))\n          results.push(node);\n      }\n      return results;\n    },\n\n    attrPresence: function(nodes, root, attr, combinator) {\n      if (!nodes) nodes = root.getElementsByTagName(\"*\");\n      if (nodes && combinator) nodes = this[combinator](nodes);\n      var results = [];\n      for (var i = 0, node; node = nodes[i]; i++)\n        if (Element.hasAttribute(node, attr)) results.push(node);\n      return results;\n    },\n\n    attr: function(nodes, root, attr, value, operator, combinator) {\n      if (!nodes) nodes = root.getElementsByTagName(\"*\");\n      if (nodes && combinator) nodes = this[combinator](nodes);\n      var handler = Selector.operators[operator], results = [];\n      for (var i = 0, node; node = nodes[i]; i++) {\n        var nodeValue = Element.readAttribute(node, attr);\n        if (nodeValue === null) continue;\n        if (handler(nodeValue, value)) results.push(node);\n      }\n      return results;\n    },\n\n    pseudo: function(nodes, name, value, root, combinator) {\n      if (nodes && combinator) nodes = this[combinator](nodes);\n      if (!nodes) nodes = root.getElementsByTagName(\"*\");\n      return Selector.pseudos[name](nodes, value, root);\n    }\n  },\n\n  pseudos: {\n    'first-child': function(nodes, value, root) {\n      for (var i = 0, results = [], node; node = nodes[i]; i++) {\n        if (Selector.handlers.previousElementSibling(node)) continue;\n          results.push(node);\n      }\n      return results;\n    },\n    'last-child': function(nodes, value, root) {\n      for (var i = 0, results = [], node; node = nodes[i]; i++) {\n        if (Selector.handlers.nextElementSibling(node)) continue;\n          results.push(node);\n      }\n      return results;\n    },\n    'only-child': function(nodes, value, root) {\n      var h = Selector.handlers;\n      for (var i = 0, results = [], node; node = nodes[i]; i++)\n        if (!h.previousElementSibling(node) && !h.nextElementSibling(node))\n          results.push(node);\n      return results;\n    },\n    'nth-child':        function(nodes, formula, root) {\n      return Selector.pseudos.nth(nodes, formula, root);\n    },\n    'nth-last-child':   function(nodes, formula, root) {\n      return Selector.pseudos.nth(nodes, formula, root, true);\n    },\n    'nth-of-type':      function(nodes, formula, root) {\n      return Selector.pseudos.nth(nodes, formula, root, false, true);\n    },\n    'nth-last-of-type': function(nodes, formula, root) {\n      return Selector.pseudos.nth(nodes, formula, root, true, true);\n    },\n    'first-of-type':    function(nodes, formula, root) {\n      return Selector.pseudos.nth(nodes, \"1\", root, false, true);\n    },\n    'last-of-type':     function(nodes, formula, root) {\n      return Selector.pseudos.nth(nodes, \"1\", root, true, true);\n    },\n    'only-of-type':     function(nodes, formula, root) {\n      var p = Selector.pseudos;\n      return p['last-of-type'](p['first-of-type'](nodes, formula, root), formula, root);\n    },\n\n    getIndices: function(a, b, total) {\n      if (a == 0) return b > 0 ? [b] : [];\n      return $R(1, total).inject([], function(memo, i) {\n        if (0 == (i - b) % a && (i - b) / a >= 0) memo.push(i);\n        return memo;\n      });\n    },\n\n    nth: function(nodes, formula, root, reverse, ofType) {\n      if (nodes.length == 0) return [];\n      if (formula == 'even') formula = '2n+0';\n      if (formula == 'odd')  formula = '2n+1';\n      var h = Selector.handlers, results = [], indexed = [], m;\n      h.mark(nodes);\n      for (var i = 0, node; node = nodes[i]; i++) {\n        if (!node.parentNode._countedByPrototype) {\n          h.index(node.parentNode, reverse, ofType);\n          indexed.push(node.parentNode);\n        }\n      }\n      if (formula.match(/^\\d+$/)) { // just a number\n        formula = Number(formula);\n        for (var i = 0, node; node = nodes[i]; i++)\n          if (node.nodeIndex == formula) results.push(node);\n      } else if (m = formula.match(/^(-?\\d*)?n(([+-])(\\d+))?/)) { // an+b\n        if (m[1] == \"-\") m[1] = -1;\n        var a = m[1] ? Number(m[1]) : 1;\n        var b = m[2] ? Number(m[2]) : 0;\n        var indices = Selector.pseudos.getIndices(a, b, nodes.length);\n        for (var i = 0, node, l = indices.length; node = nodes[i]; i++) {\n          for (var j = 0; j < l; j++)\n            if (node.nodeIndex == indices[j]) results.push(node);\n        }\n      }\n      h.unmark(nodes);\n      h.unmark(indexed);\n      return results;\n    },\n\n    'empty': function(nodes, value, root) {\n      for (var i = 0, results = [], node; node = nodes[i]; i++) {\n        if (node.tagName == '!' || node.firstChild) continue;\n        results.push(node);\n      }\n      return results;\n    },\n\n    'not': function(nodes, selector, root) {\n      var h = Selector.handlers, selectorType, m;\n      var exclusions = new Selector(selector).findElements(root);\n      h.mark(exclusions);\n      for (var i = 0, results = [], node; node = nodes[i]; i++)\n        if (!node._countedByPrototype) results.push(node);\n      h.unmark(exclusions);\n      return results;\n    },\n\n    'enabled': function(nodes, value, root) {\n      for (var i = 0, results = [], node; node = nodes[i]; i++)\n        if (!node.disabled && (!node.type || node.type !== 'hidden'))\n          results.push(node);\n      return results;\n    },\n\n    'disabled': function(nodes, value, root) {\n      for (var i = 0, results = [], node; node = nodes[i]; i++)\n        if (node.disabled) results.push(node);\n      return results;\n    },\n\n    'checked': function(nodes, value, root) {\n      for (var i = 0, results = [], node; node = nodes[i]; i++)\n        if (node.checked) results.push(node);\n      return results;\n    }\n  },\n\n  operators: {\n    '=':  function(nv, v) { return nv == v; },\n    '!=': function(nv, v) { return nv != v; },\n    '^=': function(nv, v) { return nv == v || nv && nv.startsWith(v); },\n    '$=': function(nv, v) { return nv == v || nv && nv.endsWith(v); },\n    '*=': function(nv, v) { return nv == v || nv && nv.include(v); },\n    '~=': function(nv, v) { return (' ' + nv + ' ').include(' ' + v + ' '); },\n    '|=': function(nv, v) { return ('-' + (nv || \"\").toUpperCase() +\n     '-').include('-' + (v || \"\").toUpperCase() + '-'); }\n  },\n\n  split: function(expression) {\n    var expressions = [];\n    expression.scan(/(([\\w#:.~>+()\\s-]+|\\*|\\[.*?\\])+)\\s*(,|$)/, function(m) {\n      expressions.push(m[1].strip());\n    });\n    return expressions;\n  },\n\n  matchElements: function(elements, expression) {\n    var matches = $$(expression), h = Selector.handlers;\n    h.mark(matches);\n    for (var i = 0, results = [], element; element = elements[i]; i++)\n      if (element._countedByPrototype) results.push(element);\n    h.unmark(matches);\n    return results;\n  },\n\n  findElement: function(elements, expression, index) {\n    if (Object.isNumber(expression)) {\n      index = expression; expression = false;\n    }\n    return Selector.matchElements(elements, expression || '*')[index || 0];\n  },\n\n  findChildElements: function(element, expressions) {\n    expressions = Selector.split(expressions.join(','));\n    var results = [], h = Selector.handlers;\n    for (var i = 0, l = expressions.length, selector; i < l; i++) {\n      selector = new Selector(expressions[i].strip());\n      h.concat(results, selector.findElements(element));\n    }\n    return (l > 1) ? h.unique(results) : results;\n  }\n});\n\nif (Prototype.Browser.IE) {\n  Object.extend(Selector.handlers, {\n    concat: function(a, b) {\n      for (var i = 0, node; node = b[i]; i++)\n        if (node.tagName !== \"!\") a.push(node);\n      return a;\n    }\n  });\n}\n\nfunction $$() {\n  return Selector.findChildElements(document, $A(arguments));\n}\n\nvar Form = {\n  reset: function(form) {\n    form = $(form);\n    form.reset();\n    return form;\n  },\n\n  serializeElements: function(elements, options) {\n    if (typeof options != 'object') options = { hash: !!options };\n    else if (Object.isUndefined(options.hash)) options.hash = true;\n    var key, value, submitted = false, submit = options.submit;\n\n    var data = elements.inject({ }, function(result, element) {\n      if (!element.disabled && element.name) {\n        key = element.name; value = $(element).getValue();\n        if (value != null && element.type != 'file' && (element.type != 'submit' || (!submitted &&\n            submit !== false && (!submit || key == submit) && (submitted = true)))) {\n          if (key in result) {\n            if (!Object.isArray(result[key])) result[key] = [result[key]];\n            result[key].push(value);\n          }\n          else result[key] = value;\n        }\n      }\n      return result;\n    });\n\n    return options.hash ? data : Object.toQueryString(data);\n  }\n};\n\nForm.Methods = {\n  serialize: function(form, options) {\n    return Form.serializeElements(Form.getElements(form), options);\n  },\n\n  getElements: function(form) {\n    var elements = $(form).getElementsByTagName('*'),\n        element,\n        arr = [ ],\n        serializers = Form.Element.Serializers;\n    for (var i = 0; element = elements[i]; i++) {\n      arr.push(element);\n    }\n    return arr.inject([], function(elements, child) {\n      if (serializers[child.tagName.toLowerCase()])\n        elements.push(Element.extend(child));\n      return elements;\n    })\n  },\n\n  getInputs: function(form, typeName, name) {\n    form = $(form);\n    var inputs = form.getElementsByTagName('input');\n\n    if (!typeName && !name) return $A(inputs).map(Element.extend);\n\n    for (var i = 0, matchingInputs = [], length = inputs.length; i < length; i++) {\n      var input = inputs[i];\n      if ((typeName && input.type != typeName) || (name && input.name != name))\n        continue;\n      matchingInputs.push(Element.extend(input));\n    }\n\n    return matchingInputs;\n  },\n\n  disable: function(form) {\n    form = $(form);\n    Form.getElements(form).invoke('disable');\n    return form;\n  },\n\n  enable: function(form) {\n    form = $(form);\n    Form.getElements(form).invoke('enable');\n    return form;\n  },\n\n  findFirstElement: function(form) {\n    var elements = $(form).getElements().findAll(function(element) {\n      return 'hidden' != element.type && !element.disabled;\n    });\n    var firstByIndex = elements.findAll(function(element) {\n      return element.hasAttribute('tabIndex') && element.tabIndex >= 0;\n    }).sortBy(function(element) { return element.tabIndex }).first();\n\n    return firstByIndex ? firstByIndex : elements.find(function(element) {\n      return /^(?:input|select|textarea)$/i.test(element.tagName);\n    });\n  },\n\n  focusFirstElement: function(form) {\n    form = $(form);\n    form.findFirstElement().activate();\n    return form;\n  },\n\n  request: function(form, options) {\n    form = $(form), options = Object.clone(options || { });\n\n    var params = options.parameters, action = form.readAttribute('action') || '';\n    if (action.blank()) action = window.location.href;\n    options.parameters = form.serialize(true);\n\n    if (params) {\n      if (Object.isString(params)) params = params.toQueryParams();\n      Object.extend(options.parameters, params);\n    }\n\n    if (form.hasAttribute('method') && !options.method)\n      options.method = form.method;\n\n    return new Ajax.Request(action, options);\n  }\n};\n\n/*--------------------------------------------------------------------------*/\n\n\nForm.Element = {\n  focus: function(element) {\n    $(element).focus();\n    return element;\n  },\n\n  select: function(element) {\n    $(element).select();\n    return element;\n  }\n};\n\nForm.Element.Methods = {\n\n  serialize: function(element) {\n    element = $(element);\n    if (!element.disabled && element.name) {\n      var value = element.getValue();\n      if (value != undefined) {\n        var pair = { };\n        pair[element.name] = value;\n        return Object.toQueryString(pair);\n      }\n    }\n    return '';\n  },\n\n  getValue: function(element) {\n    element = $(element);\n    var method = element.tagName.toLowerCase();\n    return Form.Element.Serializers[method](element);\n  },\n\n  setValue: function(element, value) {\n    element = $(element);\n    var method = element.tagName.toLowerCase();\n    Form.Element.Serializers[method](element, value);\n    return element;\n  },\n\n  clear: function(element) {\n    $(element).value = '';\n    return element;\n  },\n\n  present: function(element) {\n    return $(element).value != '';\n  },\n\n  activate: function(element) {\n    element = $(element);\n    try {\n      element.focus();\n      if (element.select && (element.tagName.toLowerCase() != 'input' ||\n          !(/^(?:button|reset|submit)$/i.test(element.type))))\n        element.select();\n    } catch (e) { }\n    return element;\n  },\n\n  disable: function(element) {\n    element = $(element);\n    element.disabled = true;\n    return element;\n  },\n\n  enable: function(element) {\n    element = $(element);\n    element.disabled = false;\n    return element;\n  }\n};\n\n/*--------------------------------------------------------------------------*/\n\nvar Field = Form.Element;\n\nvar $F = Form.Element.Methods.getValue;\n\n/*--------------------------------------------------------------------------*/\n\nForm.Element.Serializers = {\n  input: function(element, value) {\n    switch (element.type.toLowerCase()) {\n      case 'checkbox':\n      case 'radio':\n        return Form.Element.Serializers.inputSelector(element, value);\n      default:\n        return Form.Element.Serializers.textarea(element, value);\n    }\n  },\n\n  inputSelector: function(element, value) {\n    if (Object.isUndefined(value)) return element.checked ? element.value : null;\n    else element.checked = !!value;\n  },\n\n  textarea: function(element, value) {\n    if (Object.isUndefined(value)) return element.value;\n    else element.value = value;\n  },\n\n  select: function(element, value) {\n    if (Object.isUndefined(value))\n      return this[element.type == 'select-one' ?\n        'selectOne' : 'selectMany'](element);\n    else {\n      var opt, currentValue, single = !Object.isArray(value);\n      for (var i = 0, length = element.length; i < length; i++) {\n        opt = element.options[i];\n        currentValue = this.optionValue(opt);\n        if (single) {\n          if (currentValue == value) {\n            opt.selected = true;\n            return;\n          }\n        }\n        else opt.selected = value.include(currentValue);\n      }\n    }\n  },\n\n  selectOne: function(element) {\n    var index = element.selectedIndex;\n    return index >= 0 ? this.optionValue(element.options[index]) : null;\n  },\n\n  selectMany: function(element) {\n    var values, length = element.length;\n    if (!length) return null;\n\n    for (var i = 0, values = []; i < length; i++) {\n      var opt = element.options[i];\n      if (opt.selected) values.push(this.optionValue(opt));\n    }\n    return values;\n  },\n\n  optionValue: function(opt) {\n    return Element.extend(opt).hasAttribute('value') ? opt.value : opt.text;\n  }\n};\n\n/*--------------------------------------------------------------------------*/\n\n\nAbstract.TimedObserver = Class.create(PeriodicalExecuter, {\n  initialize: function($super, element, frequency, callback) {\n    $super(callback, frequency);\n    this.element   = $(element);\n    this.lastValue = this.getValue();\n  },\n\n  execute: function() {\n    var value = this.getValue();\n    if (Object.isString(this.lastValue) && Object.isString(value) ?\n        this.lastValue != value : String(this.lastValue) != String(value)) {\n      this.callback(this.element, value);\n      this.lastValue = value;\n    }\n  }\n});\n\nForm.Element.Observer = Class.create(Abstract.TimedObserver, {\n  getValue: function() {\n    return Form.Element.getValue(this.element);\n  }\n});\n\nForm.Observer = Class.create(Abstract.TimedObserver, {\n  getValue: function() {\n    return Form.serialize(this.element);\n  }\n});\n\n/*--------------------------------------------------------------------------*/\n\nAbstract.EventObserver = Class.create({\n  initialize: function(element, callback) {\n    this.element  = $(element);\n    this.callback = callback;\n\n    this.lastValue = this.getValue();\n    if (this.element.tagName.toLowerCase() == 'form')\n      this.registerFormCallbacks();\n    else\n      this.registerCallback(this.element);\n  },\n\n  onElementEvent: function() {\n    var value = this.getValue();\n    if (this.lastValue != value) {\n      this.callback(this.element, value);\n      this.lastValue = value;\n    }\n  },\n\n  registerFormCallbacks: function() {\n    Form.getElements(this.element).each(this.registerCallback, this);\n  },\n\n  registerCallback: function(element) {\n    if (element.type) {\n      switch (element.type.toLowerCase()) {\n        case 'checkbox':\n        case 'radio':\n          Event.observe(element, 'click', this.onElementEvent.bind(this));\n          break;\n        default:\n          Event.observe(element, 'change', this.onElementEvent.bind(this));\n          break;\n      }\n    }\n  }\n});\n\nForm.Element.EventObserver = Class.create(Abstract.EventObserver, {\n  getValue: function() {\n    return Form.Element.getValue(this.element);\n  }\n});\n\nForm.EventObserver = Class.create(Abstract.EventObserver, {\n  getValue: function() {\n    return Form.serialize(this.element);\n  }\n});\n(function() {\n\n  var Event = {\n    KEY_BACKSPACE: 8,\n    KEY_TAB:       9,\n    KEY_RETURN:   13,\n    KEY_ESC:      27,\n    KEY_LEFT:     37,\n    KEY_UP:       38,\n    KEY_RIGHT:    39,\n    KEY_DOWN:     40,\n    KEY_DELETE:   46,\n    KEY_HOME:     36,\n    KEY_END:      35,\n    KEY_PAGEUP:   33,\n    KEY_PAGEDOWN: 34,\n    KEY_INSERT:   45,\n\n    cache: {}\n  };\n\n  var docEl = document.documentElement;\n  var MOUSEENTER_MOUSELEAVE_EVENTS_SUPPORTED = 'onmouseenter' in docEl\n    && 'onmouseleave' in docEl;\n\n  var _isButton;\n  if (Prototype.Browser.IE) {\n    var buttonMap = { 0: 1, 1: 4, 2: 2 };\n    _isButton = function(event, code) {\n      return event.button === buttonMap[code];\n    };\n  } else if (Prototype.Browser.WebKit) {\n    _isButton = function(event, code) {\n      switch (code) {\n        case 0: return event.which == 1 && !event.metaKey;\n        case 1: return event.which == 1 && event.metaKey;\n        default: return false;\n      }\n    };\n  } else {\n    _isButton = function(event, code) {\n      return event.which ? (event.which === code + 1) : (event.button === code);\n    };\n  }\n\n  function isLeftClick(event)   { return _isButton(event, 0) }\n\n  function isMiddleClick(event) { return _isButton(event, 1) }\n\n  function isRightClick(event)  { return _isButton(event, 2) }\n\n  function element(event) {\n    event = Event.extend(event);\n\n    var node = event.target, type = event.type,\n     currentTarget = event.currentTarget;\n\n    if (currentTarget && currentTarget.tagName) {\n      if (type === 'load' || type === 'error' ||\n        (type === 'click' && currentTarget.tagName.toLowerCase() === 'input'\n          && currentTarget.type === 'radio'))\n            node = currentTarget;\n    }\n\n    if (node.nodeType == Node.TEXT_NODE)\n      node = node.parentNode;\n\n    return Element.extend(node);\n  }\n\n  function findElement(event, expression) {\n    var element = Event.element(event);\n    if (!expression) return element;\n    var elements = [element].concat(element.ancestors());\n    return Selector.findElement(elements, expression, 0);\n  }\n\n  function pointer(event) {\n    return { x: pointerX(event), y: pointerY(event) };\n  }\n\n  function pointerX(event) {\n    var docElement = document.documentElement,\n     body = document.body || { scrollLeft: 0 };\n\n    return event.pageX || (event.clientX +\n      (docElement.scrollLeft || body.scrollLeft) -\n      (docElement.clientLeft || 0));\n  }\n\n  function pointerY(event) {\n    var docElement = document.documentElement,\n     body = document.body || { scrollTop: 0 };\n\n    return  event.pageY || (event.clientY +\n       (docElement.scrollTop || body.scrollTop) -\n       (docElement.clientTop || 0));\n  }\n\n\n  function stop(event) {\n    Event.extend(event);\n    event.preventDefault();\n    event.stopPropagation();\n\n    event.stopped = true;\n  }\n\n  Event.Methods = {\n    isLeftClick: isLeftClick,\n    isMiddleClick: isMiddleClick,\n    isRightClick: isRightClick,\n\n    element: element,\n    findElement: findElement,\n\n    pointer: pointer,\n    pointerX: pointerX,\n    pointerY: pointerY,\n\n    stop: stop\n  };\n\n\n  var methods = Object.keys(Event.Methods).inject({ }, function(m, name) {\n    m[name] = Event.Methods[name].methodize();\n    return m;\n  });\n\n  if (Prototype.Browser.IE) {\n    function _relatedTarget(event) {\n      var element;\n      switch (event.type) {\n        case 'mouseover': element = event.fromElement; break;\n        case 'mouseout':  element = event.toElement;   break;\n        default: return null;\n      }\n      return Element.extend(element);\n    }\n\n    Object.extend(methods, {\n      stopPropagation: function() { this.cancelBubble = true },\n      preventDefault:  function() { this.returnValue = false },\n      inspect: function() { return '[object Event]' }\n    });\n\n    Event.extend = function(event, element) {\n      if (!event) return false;\n      if (event._extendedByPrototype) return event;\n\n      event._extendedByPrototype = Prototype.emptyFunction;\n      var pointer = Event.pointer(event);\n\n      Object.extend(event, {\n        target: event.srcElement || element,\n        relatedTarget: _relatedTarget(event),\n        pageX:  pointer.x,\n        pageY:  pointer.y\n      });\n\n      return Object.extend(event, methods);\n    };\n  } else {\n    Event.prototype = window.Event.prototype || document.createEvent('HTMLEvents').__proto__;\n    Object.extend(Event.prototype, methods);\n    Event.extend = Prototype.K;\n  }\n\n  function _createResponder(element, eventName, handler) {\n    var registry = Element.retrieve(element, 'prototype_event_registry');\n\n    if (Object.isUndefined(registry)) {\n      CACHE.push(element);\n      registry = Element.retrieve(element, 'prototype_event_registry', $H());\n    }\n\n    var respondersForEvent = registry.get(eventName);\n    if (Object.isUndefined(respondersForEvent)) {\n      respondersForEvent = [];\n      registry.set(eventName, respondersForEvent);\n    }\n\n    if (respondersForEvent.pluck('handler').include(handler)) return false;\n\n    var responder;\n    if (eventName.include(\":\")) {\n      responder = function(event) {\n        if (Object.isUndefined(event.eventName))\n          return false;\n\n        if (event.eventName !== eventName)\n          return false;\n\n        Event.extend(event, element);\n        handler.call(element, event);\n      };\n    } else {\n      if (!MOUSEENTER_MOUSELEAVE_EVENTS_SUPPORTED &&\n       (eventName === \"mouseenter\" || eventName === \"mouseleave\")) {\n        if (eventName === \"mouseenter\" || eventName === \"mouseleave\") {\n          responder = function(event) {\n            Event.extend(event, element);\n\n            var parent = event.relatedTarget;\n            while (parent && parent !== element) {\n              try { parent = parent.parentNode; }\n              catch(e) { parent = element; }\n            }\n\n            if (parent === element) return;\n\n            handler.call(element, event);\n          };\n        }\n      } else {\n        responder = function(event) {\n          Event.extend(event, element);\n          handler.call(element, event);\n        };\n      }\n    }\n\n    responder.handler = handler;\n    respondersForEvent.push(responder);\n    return responder;\n  }\n\n  function _destroyCache() {\n    for (var i = 0, length = CACHE.length; i < length; i++) {\n      Event.stopObserving(CACHE[i]);\n      CACHE[i] = null;\n    }\n  }\n\n  var CACHE = [];\n\n  if (Prototype.Browser.IE)\n    window.attachEvent('onunload', _destroyCache);\n\n  if (Prototype.Browser.WebKit)\n    window.addEventListener('unload', Prototype.emptyFunction, false);\n\n\n  var _getDOMEventName = Prototype.K;\n\n  if (!MOUSEENTER_MOUSELEAVE_EVENTS_SUPPORTED) {\n    _getDOMEventName = function(eventName) {\n      var translations = { mouseenter: \"mouseover\", mouseleave: \"mouseout\" };\n      return eventName in translations ? translations[eventName] : eventName;\n    };\n  }\n\n  function observe(element, eventName, handler) {\n    element = $(element);\n\n    var responder = _createResponder(element, eventName, handler);\n\n    if (!responder) return element;\n\n    if (eventName.include(':')) {\n      if (element.addEventListener)\n        element.addEventListener(\"dataavailable\", responder, false);\n      else {\n        element.attachEvent(\"ondataavailable\", responder);\n        element.attachEvent(\"onfilterchange\", responder);\n      }\n    } else {\n      var actualEventName = _getDOMEventName(eventName);\n\n      if (element.addEventListener)\n        element.addEventListener(actualEventName, responder, false);\n      else\n        element.attachEvent(\"on\" + actualEventName, responder);\n    }\n\n    return element;\n  }\n\n  function stopObserving(element, eventName, handler) {\n    element = $(element);\n\n    var registry = Element.retrieve(element, 'prototype_event_registry');\n\n    if (Object.isUndefined(registry)) return element;\n\n    if (eventName && !handler) {\n      var responders = registry.get(eventName);\n\n      if (Object.isUndefined(responders)) return element;\n\n      responders.each( function(r) {\n        Element.stopObserving(element, eventName, r.handler);\n      });\n      return element;\n    } else if (!eventName) {\n      registry.each( function(pair) {\n        var eventName = pair.key, responders = pair.value;\n\n        responders.each( function(r) {\n          Element.stopObserving(element, eventName, r.handler);\n        });\n      });\n      return element;\n    }\n\n    var responders = registry.get(eventName);\n\n    if (!responders) return;\n\n    var responder = responders.find( function(r) { return r.handler === handler; });\n    if (!responder) return element;\n\n    var actualEventName = _getDOMEventName(eventName);\n\n    if (eventName.include(':')) {\n      if (element.removeEventListener)\n        element.removeEventListener(\"dataavailable\", responder, false);\n      else {\n        element.detachEvent(\"ondataavailable\", responder);\n        element.detachEvent(\"onfilterchange\",  responder);\n      }\n    } else {\n      if (element.removeEventListener)\n        element.removeEventListener(actualEventName, responder, false);\n      else\n        element.detachEvent('on' + actualEventName, responder);\n    }\n\n    registry.set(eventName, responders.without(responder));\n\n    return element;\n  }\n\n  function fire(element, eventName, memo, bubble) {\n    element = $(element);\n\n    if (Object.isUndefined(bubble))\n      bubble = true;\n\n    if (element == document && document.createEvent && !element.dispatchEvent)\n      element = document.documentElement;\n\n    var event;\n    if (document.createEvent) {\n      event = document.createEvent('HTMLEvents');\n      event.initEvent('dataavailable', true, true);\n    } else {\n      event = document.createEventObject();\n      event.eventType = bubble ? 'ondataavailable' : 'onfilterchange';\n    }\n\n    event.eventName = eventName;\n    event.memo = memo || { };\n\n    if (document.createEvent)\n      element.dispatchEvent(event);\n    else\n      element.fireEvent(event.eventType, event);\n\n    return Event.extend(event);\n  }\n\n\n  Object.extend(Event, Event.Methods);\n\n  Object.extend(Event, {\n    fire:          fire,\n    observe:       observe,\n    stopObserving: stopObserving\n  });\n\n  Element.addMethods({\n    fire:          fire,\n\n    observe:       observe,\n\n    stopObserving: stopObserving\n  });\n\n  Object.extend(document, {\n    fire:          fire.methodize(),\n\n    observe:       observe.methodize(),\n\n    stopObserving: stopObserving.methodize(),\n\n    loaded:        false\n  });\n\n  if (window.Event) Object.extend(window.Event, Event);\n  else window.Event = Event;\n})();\n\n(function() {\n  /* Support for the DOMContentLoaded event is based on work by Dan Webb,\n     Matthias Miller, Dean Edwards, John Resig, and Diego Perini. */\n\n  var timer;\n\n  function fireContentLoadedEvent() {\n    if (document.loaded) return;\n    if (timer) window.clearTimeout(timer);\n    document.loaded = true;\n    document.fire('dom:loaded');\n  }\n\n  function checkReadyState() {\n    if (document.readyState === 'complete') {\n      document.stopObserving('readystatechange', checkReadyState);\n      fireContentLoadedEvent();\n    }\n  }\n\n  function pollDoScroll() {\n    try { document.documentElement.doScroll('left'); }\n    catch(e) {\n      timer = pollDoScroll.defer();\n      return;\n    }\n    fireContentLoadedEvent();\n  }\n\n  if (document.addEventListener) {\n    document.addEventListener('DOMContentLoaded', fireContentLoadedEvent, false);\n  } else {\n    document.observe('readystatechange', checkReadyState);\n    if (window == top)\n      timer = pollDoScroll.defer();\n  }\n\n  Event.observe(window, 'load', fireContentLoadedEvent);\n})();\n\nElement.addMethods();\n\n/*------------------------------- DEPRECATED -------------------------------*/\n\nHash.toQueryString = Object.toQueryString;\n\nvar Toggle = { display: Element.toggle };\n\nElement.Methods.childOf = Element.Methods.descendantOf;\n\nvar Insertion = {\n  Before: function(element, content) {\n    return Element.insert(element, {before:content});\n  },\n\n  Top: function(element, content) {\n    return Element.insert(element, {top:content});\n  },\n\n  Bottom: function(element, content) {\n    return Element.insert(element, {bottom:content});\n  },\n\n  After: function(element, content) {\n    return Element.insert(element, {after:content});\n  }\n};\n\nvar $continue = new Error('\"throw $continue\" is deprecated, use \"return\" instead');\n\nvar Position = {\n  includeScrollOffsets: false,\n\n  prepare: function() {\n    this.deltaX =  window.pageXOffset\n                || document.documentElement.scrollLeft\n                || document.body.scrollLeft\n                || 0;\n    this.deltaY =  window.pageYOffset\n                || document.documentElement.scrollTop\n                || document.body.scrollTop\n                || 0;\n  },\n\n  within: function(element, x, y) {\n    if (this.includeScrollOffsets)\n      return this.withinIncludingScrolloffsets(element, x, y);\n    this.xcomp = x;\n    this.ycomp = y;\n    this.offset = Element.cumulativeOffset(element);\n\n    return (y >= this.offset[1] &&\n            y <  this.offset[1] + element.offsetHeight &&\n            x >= this.offset[0] &&\n            x <  this.offset[0] + element.offsetWidth);\n  },\n\n  withinIncludingScrolloffsets: function(element, x, y) {\n    var offsetcache = Element.cumulativeScrollOffset(element);\n\n    this.xcomp = x + offsetcache[0] - this.deltaX;\n    this.ycomp = y + offsetcache[1] - this.deltaY;\n    this.offset = Element.cumulativeOffset(element);\n\n    return (this.ycomp >= this.offset[1] &&\n            this.ycomp <  this.offset[1] + element.offsetHeight &&\n            this.xcomp >= this.offset[0] &&\n            this.xcomp <  this.offset[0] + element.offsetWidth);\n  },\n\n  overlap: function(mode, element) {\n    if (!mode) return 0;\n    if (mode == 'vertical')\n      return ((this.offset[1] + element.offsetHeight) - this.ycomp) /\n        element.offsetHeight;\n    if (mode == 'horizontal')\n      return ((this.offset[0] + element.offsetWidth) - this.xcomp) /\n        element.offsetWidth;\n  },\n\n\n  cumulativeOffset: Element.Methods.cumulativeOffset,\n\n  positionedOffset: Element.Methods.positionedOffset,\n\n  absolutize: function(element) {\n    Position.prepare();\n    return Element.absolutize(element);\n  },\n\n  relativize: function(element) {\n    Position.prepare();\n    return Element.relativize(element);\n  },\n\n  realOffset: Element.Methods.cumulativeScrollOffset,\n\n  offsetParent: Element.Methods.getOffsetParent,\n\n  page: Element.Methods.viewportOffset,\n\n  clone: function(source, target, options) {\n    options = options || { };\n    return Element.clonePosition(target, source, options);\n  }\n};\n\n/*--------------------------------------------------------------------------*/\n\nif (!document.getElementsByClassName) document.getElementsByClassName = function(instanceMethods){\n  function iter(name) {\n    return name.blank() ? null : \"[contains(concat(' ', @class, ' '), ' \" + name + \" ')]\";\n  }\n\n  instanceMethods.getElementsByClassName = Prototype.BrowserFeatures.XPath ?\n  function(element, className) {\n    className = className.toString().strip();\n    var cond = /\\s/.test(className) ? $w(className).map(iter).join('') : iter(className);\n    return cond ? document._getElementsByXPath('.//*' + cond, element) : [];\n  } : function(element, className) {\n    className = className.toString().strip();\n    var elements = [], classNames = (/\\s/.test(className) ? $w(className) : null);\n    if (!classNames && !className) return elements;\n\n    var nodes = $(element).getElementsByTagName('*');\n    className = ' ' + className + ' ';\n\n    for (var i = 0, child, cn; child = nodes[i]; i++) {\n      if (child.className && (cn = ' ' + child.className + ' ') && (cn.include(className) ||\n          (classNames && classNames.all(function(name) {\n            return !name.toString().blank() && cn.include(' ' + name + ' ');\n          }))))\n        elements.push(Element.extend(child));\n    }\n    return elements;\n  };\n\n  return function(className, parentElement) {\n    return $(parentElement || document.body).getElementsByClassName(className);\n  };\n}(Element.Methods);\n\n/*--------------------------------------------------------------------------*/\n\nElement.ClassNames = Class.create();\nElement.ClassNames.prototype = {\n  initialize: function(element) {\n    this.element = $(element);\n  },\n\n  _each: function(iterator) {\n    this.element.className.split(/\\s+/).select(function(name) {\n      return name.length > 0;\n    })._each(iterator);\n  },\n\n  set: function(className) {\n    this.element.className = className;\n  },\n\n  add: function(classNameToAdd) {\n    if (this.include(classNameToAdd)) return;\n    this.set($A(this).concat(classNameToAdd).join(' '));\n  },\n\n  remove: function(classNameToRemove) {\n    if (!this.include(classNameToRemove)) return;\n    this.set($A(this).without(classNameToRemove).join(' '));\n  },\n\n  toString: function() {\n    return $A(this).join(' ');\n  }\n};\n\nObject.extend(Element.ClassNames.prototype, Enumerable);\n\n/*--------------------------------------------------------------------------*/"
  },
  {
    "path": "prototype/scriptaculous.js",
    "content": "// script.aculo.us scriptaculous.js v1.8.3, Thu Oct 08 11:23:33 +0200 2009\n\n// Copyright (c) 2005-2009 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)\n//\n// Permission is hereby granted, free of charge, to any person obtaining\n// a copy of this software and associated documentation files (the\n// \"Software\"), to deal in the Software without restriction, including\n// without limitation the rights to use, copy, modify, merge, publish,\n// distribute, sublicense, and/or sell copies of the Software, and to\n// permit persons to whom the Software is furnished to do so, subject to\n// the following conditions:\n//\n// The above copyright notice and this permission notice shall be\n// included in all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\n// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\n// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\n// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\n// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n//\n// For details, see the script.aculo.us web site: http://script.aculo.us/\n\nvar Scriptaculous = {\n  Version: '1.8.3',\n  require: function(libraryName) {\n    try{\n      // inserting via DOM fails in Safari 2.0, so brute force approach\n      document.write('<script type=\"text/javascript\" src=\"'+libraryName+'\"><\\/script>');\n    } catch(e) {\n      // for xhtml+xml served content, fall back to DOM methods\n      var script = document.createElement('script');\n      script.type = 'text/javascript';\n      script.src = libraryName;\n      document.getElementsByTagName('head')[0].appendChild(script);\n    }\n  },\n  REQUIRED_PROTOTYPE: '1.6.0.3',\n  load: function() {\n    function convertVersionString(versionString) {\n      var v = versionString.replace(/_.*|\\./g, '');\n      v = parseInt(v + '0'.times(4-v.length));\n      return versionString.indexOf('_') > -1 ? v-1 : v;\n    }\n\n    if((typeof Prototype=='undefined') ||\n       (typeof Element == 'undefined') ||\n       (typeof Element.Methods=='undefined') ||\n       (convertVersionString(Prototype.Version) <\n        convertVersionString(Scriptaculous.REQUIRED_PROTOTYPE)))\n       throw(\"script.aculo.us requires the Prototype JavaScript framework >= \" +\n        Scriptaculous.REQUIRED_PROTOTYPE);\n\n    var js = /scriptaculous\\.js(\\?.*)?$/;\n    $$('head script[src]').findAll(function(s) {\n      return s.src.match(js);\n    }).each(function(s) {\n      var path = s.src.replace(js, ''),\n      includes = s.src.match(/\\?.*load=([a-z,]*)/);\n      (includes ? includes[1] : 'builder,effects,dragdrop,controls,slider,sound').split(',').each(\n       function(include) { Scriptaculous.require(path+include+'.js') });\n    });\n  }\n};\n\nScriptaculous.load();"
  },
  {
    "path": "prototype/slider.js",
    "content": "// script.aculo.us slider.js v1.8.3, Thu Oct 08 11:23:33 +0200 2009\n\n// Copyright (c) 2005-2009 Marty Haught, Thomas Fuchs\n//\n// script.aculo.us is freely distributable under the terms of an MIT-style license.\n// For details, see the script.aculo.us web site: http://script.aculo.us/\n\nif (!Control) var Control = { };\n\n// options:\n//  axis: 'vertical', or 'horizontal' (default)\n//\n// callbacks:\n//  onChange(value)\n//  onSlide(value)\nControl.Slider = Class.create({\n  initialize: function(handle, track, options) {\n    var slider = this;\n\n    if (Object.isArray(handle)) {\n      this.handles = handle.collect( function(e) { return $(e) });\n    } else {\n      this.handles = [$(handle)];\n    }\n\n    this.track   = $(track);\n    this.options = options || { };\n\n    this.axis      = this.options.axis || 'horizontal';\n    this.increment = this.options.increment || 1;\n    this.step      = parseInt(this.options.step || '1');\n    this.range     = this.options.range || $R(0,1);\n\n    this.value     = 0; // assure backwards compat\n    this.values    = this.handles.map( function() { return 0 });\n    this.spans     = this.options.spans ? this.options.spans.map(function(s){ return $(s) }) : false;\n    this.options.startSpan = $(this.options.startSpan || null);\n    this.options.endSpan   = $(this.options.endSpan || null);\n\n    this.restricted = this.options.restricted || false;\n\n    this.maximum   = this.options.maximum || this.range.end;\n    this.minimum   = this.options.minimum || this.range.start;\n\n    // Will be used to align the handle onto the track, if necessary\n    this.alignX = parseInt(this.options.alignX || '0');\n    this.alignY = parseInt(this.options.alignY || '0');\n\n    this.trackLength = this.maximumOffset() - this.minimumOffset();\n\n    this.handleLength = this.isVertical() ?\n      (this.handles[0].offsetHeight != 0 ?\n        this.handles[0].offsetHeight : this.handles[0].style.height.replace(/px$/,\"\")) :\n      (this.handles[0].offsetWidth != 0 ? this.handles[0].offsetWidth :\n        this.handles[0].style.width.replace(/px$/,\"\"));\n\n    this.active   = false;\n    this.dragging = false;\n    this.disabled = false;\n\n    if (this.options.disabled) this.setDisabled();\n\n    // Allowed values array\n    this.allowedValues = this.options.values ? this.options.values.sortBy(Prototype.K) : false;\n    if (this.allowedValues) {\n      this.minimum = this.allowedValues.min();\n      this.maximum = this.allowedValues.max();\n    }\n\n    this.eventMouseDown = this.startDrag.bindAsEventListener(this);\n    this.eventMouseUp   = this.endDrag.bindAsEventListener(this);\n    this.eventMouseMove = this.update.bindAsEventListener(this);\n\n    // Initialize handles in reverse (make sure first handle is active)\n    this.handles.each( function(h,i) {\n      i = slider.handles.length-1-i;\n      slider.setValue(parseFloat(\n        (Object.isArray(slider.options.sliderValue) ?\n          slider.options.sliderValue[i] : slider.options.sliderValue) ||\n         slider.range.start), i);\n      h.makePositioned().observe(\"mousedown\", slider.eventMouseDown);\n    });\n\n    this.track.observe(\"mousedown\", this.eventMouseDown);\n    document.observe(\"mouseup\", this.eventMouseUp);\n    document.observe(\"mousemove\", this.eventMouseMove);\n\n    this.initialized = true;\n  },\n  dispose: function() {\n    var slider = this;\n    Event.stopObserving(this.track, \"mousedown\", this.eventMouseDown);\n    Event.stopObserving(document, \"mouseup\", this.eventMouseUp);\n    Event.stopObserving(document, \"mousemove\", this.eventMouseMove);\n    this.handles.each( function(h) {\n      Event.stopObserving(h, \"mousedown\", slider.eventMouseDown);\n    });\n  },\n  setDisabled: function(){\n    this.disabled = true;\n  },\n  setEnabled: function(){\n    this.disabled = false;\n  },\n  getNearestValue: function(value){\n    if (this.allowedValues){\n      if (value >= this.allowedValues.max()) return(this.allowedValues.max());\n      if (value <= this.allowedValues.min()) return(this.allowedValues.min());\n\n      var offset = Math.abs(this.allowedValues[0] - value);\n      var newValue = this.allowedValues[0];\n      this.allowedValues.each( function(v) {\n        var currentOffset = Math.abs(v - value);\n        if (currentOffset <= offset){\n          newValue = v;\n          offset = currentOffset;\n        }\n      });\n      return newValue;\n    }\n    if (value > this.range.end) return this.range.end;\n    if (value < this.range.start) return this.range.start;\n    return value;\n  },\n  setValue: function(sliderValue, handleIdx){\n    if (!this.active) {\n      this.activeHandleIdx = handleIdx || 0;\n      this.activeHandle    = this.handles[this.activeHandleIdx];\n      this.updateStyles();\n    }\n    handleIdx = handleIdx || this.activeHandleIdx || 0;\n    if (this.initialized && this.restricted) {\n      if ((handleIdx>0) && (sliderValue<this.values[handleIdx-1]))\n        sliderValue = this.values[handleIdx-1];\n      if ((handleIdx < (this.handles.length-1)) && (sliderValue>this.values[handleIdx+1]))\n        sliderValue = this.values[handleIdx+1];\n    }\n    sliderValue = this.getNearestValue(sliderValue);\n    this.values[handleIdx] = sliderValue;\n    this.value = this.values[0]; // assure backwards compat\n\n    this.handles[handleIdx].style[this.isVertical() ? 'top' : 'left'] =\n      this.translateToPx(sliderValue);\n\n    this.drawSpans();\n    if (!this.dragging || !this.event) this.updateFinished();\n  },\n  setValueBy: function(delta, handleIdx) {\n    this.setValue(this.values[handleIdx || this.activeHandleIdx || 0] + delta,\n      handleIdx || this.activeHandleIdx || 0);\n  },\n  translateToPx: function(value) {\n    return Math.round(\n      ((this.trackLength-this.handleLength)/(this.range.end-this.range.start)) *\n      (value - this.range.start)) + \"px\";\n  },\n  translateToValue: function(offset) {\n    return ((offset/(this.trackLength-this.handleLength) *\n      (this.range.end-this.range.start)) + this.range.start);\n  },\n  getRange: function(range) {\n    var v = this.values.sortBy(Prototype.K);\n    range = range || 0;\n    return $R(v[range],v[range+1]);\n  },\n  minimumOffset: function(){\n    return(this.isVertical() ? this.alignY : this.alignX);\n  },\n  maximumOffset: function(){\n    return(this.isVertical() ?\n      (this.track.offsetHeight != 0 ? this.track.offsetHeight :\n        this.track.style.height.replace(/px$/,\"\")) - this.alignY :\n      (this.track.offsetWidth != 0 ? this.track.offsetWidth :\n        this.track.style.width.replace(/px$/,\"\")) - this.alignX);\n  },\n  isVertical:  function(){\n    return (this.axis == 'vertical');\n  },\n  drawSpans: function() {\n    var slider = this;\n    if (this.spans)\n      $R(0, this.spans.length-1).each(function(r) { slider.setSpan(slider.spans[r], slider.getRange(r)) });\n    if (this.options.startSpan)\n      this.setSpan(this.options.startSpan,\n        $R(0, this.values.length>1 ? this.getRange(0).min() : this.value ));\n    if (this.options.endSpan)\n      this.setSpan(this.options.endSpan,\n        $R(this.values.length>1 ? this.getRange(this.spans.length-1).max() : this.value, this.maximum));\n  },\n  setSpan: function(span, range) {\n    if (this.isVertical()) {\n      span.style.top = this.translateToPx(range.start);\n      span.style.height = this.translateToPx(range.end - range.start + this.range.start);\n    } else {\n      span.style.left = this.translateToPx(range.start);\n      span.style.width = this.translateToPx(range.end - range.start + this.range.start);\n    }\n  },\n  updateStyles: function() {\n    this.handles.each( function(h){ Element.removeClassName(h, 'selected') });\n    Element.addClassName(this.activeHandle, 'selected');\n  },\n  startDrag: function(event) {\n    if (Event.isLeftClick(event)) {\n      if (!this.disabled){\n        this.active = true;\n\n        var handle = Event.element(event);\n        var pointer  = [Event.pointerX(event), Event.pointerY(event)];\n        var track = handle;\n        if (track==this.track) {\n          var offsets  = this.track.cumulativeOffset();\n          this.event = event;\n          this.setValue(this.translateToValue(\n           (this.isVertical() ? pointer[1]-offsets[1] : pointer[0]-offsets[0])-(this.handleLength/2)\n          ));\n          var offsets  = this.activeHandle.cumulativeOffset();\n          this.offsetX = (pointer[0] - offsets[0]);\n          this.offsetY = (pointer[1] - offsets[1]);\n        } else {\n          // find the handle (prevents issues with Safari)\n          while((this.handles.indexOf(handle) == -1) && handle.parentNode)\n            handle = handle.parentNode;\n\n          if (this.handles.indexOf(handle)!=-1) {\n            this.activeHandle    = handle;\n            this.activeHandleIdx = this.handles.indexOf(this.activeHandle);\n            this.updateStyles();\n\n            var offsets  = this.activeHandle.cumulativeOffset();\n            this.offsetX = (pointer[0] - offsets[0]);\n            this.offsetY = (pointer[1] - offsets[1]);\n          }\n        }\n      }\n      Event.stop(event);\n    }\n  },\n  update: function(event) {\n   if (this.active) {\n      if (!this.dragging) this.dragging = true;\n      this.draw(event);\n      if (Prototype.Browser.WebKit) window.scrollBy(0,0);\n      Event.stop(event);\n   }\n  },\n  draw: function(event) {\n    var pointer = [Event.pointerX(event), Event.pointerY(event)];\n    var offsets = this.track.cumulativeOffset();\n    pointer[0] -= this.offsetX + offsets[0];\n    pointer[1] -= this.offsetY + offsets[1];\n    this.event = event;\n    this.setValue(this.translateToValue( this.isVertical() ? pointer[1] : pointer[0] ));\n    if (this.initialized && this.options.onSlide)\n      this.options.onSlide(this.values.length>1 ? this.values : this.value, this);\n  },\n  endDrag: function(event) {\n    if (this.active && this.dragging) {\n      this.finishDrag(event, true);\n      Event.stop(event);\n    }\n    this.active = false;\n    this.dragging = false;\n  },\n  finishDrag: function(event, success) {\n    this.active = false;\n    this.dragging = false;\n    this.updateFinished();\n  },\n  updateFinished: function() {\n    if (this.initialized && this.options.onChange)\n      this.options.onChange(this.values.length>1 ? this.values : this.value, this);\n    this.event = null;\n  }\n});"
  },
  {
    "path": "prototype/sound.js",
    "content": "// script.aculo.us sound.js v1.8.3, Thu Oct 08 11:23:33 +0200 2009\n\n// Copyright (c) 2005-2009 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)\n//\n// Based on code created by Jules Gravinese (http://www.webveteran.com/)\n//\n// script.aculo.us is freely distributable under the terms of an MIT-style license.\n// For details, see the script.aculo.us web site: http://script.aculo.us/\n\nSound = {\n  tracks: {},\n  _enabled: true,\n  template:\n    new Template('<embed style=\"height:0\" id=\"sound_#{track}_#{id}\" src=\"#{url}\" loop=\"false\" autostart=\"true\" hidden=\"true\"/>'),\n  enable: function(){\n    Sound._enabled = true;\n  },\n  disable: function(){\n    Sound._enabled = false;\n  },\n  play: function(url){\n    if(!Sound._enabled) return;\n    var options = Object.extend({\n      track: 'global', url: url, replace: false\n    }, arguments[1] || {});\n\n    if(options.replace && this.tracks[options.track]) {\n      $R(0, this.tracks[options.track].id).each(function(id){\n        var sound = $('sound_'+options.track+'_'+id);\n        sound.Stop && sound.Stop();\n        sound.remove();\n      });\n      this.tracks[options.track] = null;\n    }\n\n    if(!this.tracks[options.track])\n      this.tracks[options.track] = { id: 0 };\n    else\n      this.tracks[options.track].id++;\n\n    options.id = this.tracks[options.track].id;\n    $$('body')[0].insert(\n      Prototype.Browser.IE ? new Element('bgsound',{\n        id: 'sound_'+options.track+'_'+options.id,\n        src: options.url, loop: 1, autostart: true\n      }) : Sound.template.evaluate(options));\n  }\n};\n\nif(Prototype.Browser.Gecko && navigator.userAgent.indexOf(\"Win\") > 0){\n  if(navigator.plugins && $A(navigator.plugins).detect(function(p){ return p.name.indexOf('QuickTime') != -1 }))\n    Sound.template = new Template('<object id=\"sound_#{track}_#{id}\" width=\"0\" height=\"0\" type=\"audio/mpeg\" data=\"#{url}\"/>');\n  else if(navigator.plugins && $A(navigator.plugins).detect(function(p){ return p.name.indexOf('Windows Media') != -1 }))\n    Sound.template = new Template('<object id=\"sound_#{track}_#{id}\" type=\"application/x-mplayer2\" data=\"#{url}\"></object>');\n  else if(navigator.plugins && $A(navigator.plugins).detect(function(p){ return p.name.indexOf('RealPlayer') != -1 }))\n    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\"/>');\n  else\n    Sound.play = function(){};\n} "
  },
  {
    "path": "prototype/unittest.js",
    "content": "// script.aculo.us unittest.js v1.8.3, Thu Oct 08 11:23:33 +0200 2009\n\n// Copyright (c) 2005-2009 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)\n//           (c) 2005-2009 Jon Tirsen (http://www.tirsen.com)\n//           (c) 2005-2009 Michael Schuerig (http://www.schuerig.de/michael/)\n//\n// script.aculo.us is freely distributable under the terms of an MIT-style license.\n// For details, see the script.aculo.us web site: http://script.aculo.us/\n\n// experimental, Firefox-only\nEvent.simulateMouse = function(element, eventName) {\n  var options = Object.extend({\n    pointerX: 0,\n    pointerY: 0,\n    buttons:  0,\n    ctrlKey:  false,\n    altKey:   false,\n    shiftKey: false,\n    metaKey:  false\n  }, arguments[2] || {});\n  var oEvent = document.createEvent(\"MouseEvents\");\n  oEvent.initMouseEvent(eventName, true, true, document.defaultView, \n    options.buttons, options.pointerX, options.pointerY, options.pointerX, options.pointerY, \n    options.ctrlKey, options.altKey, options.shiftKey, options.metaKey, 0, $(element));\n  \n  if(this.mark) Element.remove(this.mark);\n  this.mark = document.createElement('div');\n  this.mark.appendChild(document.createTextNode(\" \"));\n  document.body.appendChild(this.mark);\n  this.mark.style.position = 'absolute';\n  this.mark.style.top = options.pointerY + \"px\";\n  this.mark.style.left = options.pointerX + \"px\";\n  this.mark.style.width = \"5px\";\n  this.mark.style.height = \"5px;\";\n  this.mark.style.borderTop = \"1px solid red;\";\n  this.mark.style.borderLeft = \"1px solid red;\";\n  \n  if(this.step)\n    alert('['+new Date().getTime().toString()+'] '+eventName+'/'+Test.Unit.inspect(options));\n  \n  $(element).dispatchEvent(oEvent);\n};\n\n// Note: Due to a fix in Firefox 1.0.5/6 that probably fixed \"too much\", this doesn't work in 1.0.6 or DP2.\n// You need to downgrade to 1.0.4 for now to get this working\n// See https://bugzilla.mozilla.org/show_bug.cgi?id=289940 for the fix that fixed too much\nEvent.simulateKey = function(element, eventName) {\n  var options = Object.extend({\n    ctrlKey: false,\n    altKey: false,\n    shiftKey: false,\n    metaKey: false,\n    keyCode: 0,\n    charCode: 0\n  }, arguments[2] || {});\n\n  var oEvent = document.createEvent(\"KeyEvents\");\n  oEvent.initKeyEvent(eventName, true, true, window, \n    options.ctrlKey, options.altKey, options.shiftKey, options.metaKey,\n    options.keyCode, options.charCode );\n  $(element).dispatchEvent(oEvent);\n};\n\nEvent.simulateKeys = function(element, command) {\n  for(var i=0; i<command.length; i++) {\n    Event.simulateKey(element,'keypress',{charCode:command.charCodeAt(i)});\n  }\n};\n\nvar Test = {};\nTest.Unit = {};\n\n// security exception workaround\nTest.Unit.inspect = Object.inspect;\n\nTest.Unit.Logger = Class.create();\nTest.Unit.Logger.prototype = {\n  initialize: function(log) {\n    this.log = $(log);\n    if (this.log) {\n      this._createLogTable();\n    }\n  },\n  start: function(testName) {\n    if (!this.log) return;\n    this.testName = testName;\n    this.lastLogLine = document.createElement('tr');\n    this.statusCell = document.createElement('td');\n    this.nameCell = document.createElement('td');\n    this.nameCell.className = \"nameCell\";\n    this.nameCell.appendChild(document.createTextNode(testName));\n    this.messageCell = document.createElement('td');\n    this.lastLogLine.appendChild(this.statusCell);\n    this.lastLogLine.appendChild(this.nameCell);\n    this.lastLogLine.appendChild(this.messageCell);\n    this.loglines.appendChild(this.lastLogLine);\n  },\n  finish: function(status, summary) {\n    if (!this.log) return;\n    this.lastLogLine.className = status;\n    this.statusCell.innerHTML = status;\n    this.messageCell.innerHTML = this._toHTML(summary);\n    this.addLinksToResults();\n  },\n  message: function(message) {\n    if (!this.log) return;\n    this.messageCell.innerHTML = this._toHTML(message);\n  },\n  summary: function(summary) {\n    if (!this.log) return;\n    this.logsummary.innerHTML = this._toHTML(summary);\n  },\n  _createLogTable: function() {\n    this.log.innerHTML =\n    '<div id=\"logsummary\"></div>' +\n    '<table id=\"logtable\">' +\n    '<thead><tr><th>Status</th><th>Test</th><th>Message</th></tr></thead>' +\n    '<tbody id=\"loglines\"></tbody>' +\n    '</table>';\n    this.logsummary = $('logsummary');\n    this.loglines = $('loglines');\n  },\n  _toHTML: function(txt) {\n    return txt.escapeHTML().replace(/\\n/g,\"<br/>\");\n  },\n  addLinksToResults: function(){ \n    $$(\"tr.failed .nameCell\").each( function(td){ // todo: limit to children of this.log\n      td.title = \"Run only this test\";\n      Event.observe(td, 'click', function(){ window.location.search = \"?tests=\" + td.innerHTML;});\n    });\n    $$(\"tr.passed .nameCell\").each( function(td){ // todo: limit to children of this.log\n      td.title = \"Run all tests\";\n      Event.observe(td, 'click', function(){ window.location.search = \"\";});\n    });\n  }\n};\n\nTest.Unit.Runner = Class.create();\nTest.Unit.Runner.prototype = {\n  initialize: function(testcases) {\n    this.options = Object.extend({\n      testLog: 'testlog'\n    }, arguments[1] || {});\n    this.options.resultsURL = this.parseResultsURLQueryParameter();\n    this.options.tests      = this.parseTestsQueryParameter();\n    if (this.options.testLog) {\n      this.options.testLog = $(this.options.testLog) || null;\n    }\n    if(this.options.tests) {\n      this.tests = [];\n      for(var i = 0; i < this.options.tests.length; i++) {\n        if(/^test/.test(this.options.tests[i])) {\n          this.tests.push(new Test.Unit.Testcase(this.options.tests[i], testcases[this.options.tests[i]], testcases[\"setup\"], testcases[\"teardown\"]));\n        }\n      }\n    } else {\n      if (this.options.test) {\n        this.tests = [new Test.Unit.Testcase(this.options.test, testcases[this.options.test], testcases[\"setup\"], testcases[\"teardown\"])];\n      } else {\n        this.tests = [];\n        for(var testcase in testcases) {\n          if(/^test/.test(testcase)) {\n            this.tests.push(\n               new Test.Unit.Testcase(\n                 this.options.context ? ' -> ' + this.options.titles[testcase] : testcase, \n                 testcases[testcase], testcases[\"setup\"], testcases[\"teardown\"]\n               ));\n          }\n        }\n      }\n    }\n    this.currentTest = 0;\n    this.logger = new Test.Unit.Logger(this.options.testLog);\n    setTimeout(this.runTests.bind(this), 1000);\n  },\n  parseResultsURLQueryParameter: function() {\n    return window.location.search.parseQuery()[\"resultsURL\"];\n  },\n  parseTestsQueryParameter: function(){\n    if (window.location.search.parseQuery()[\"tests\"]){\n        return window.location.search.parseQuery()[\"tests\"].split(',');\n    };\n  },\n  // Returns:\n  //  \"ERROR\" if there was an error,\n  //  \"FAILURE\" if there was a failure, or\n  //  \"SUCCESS\" if there was neither\n  getResult: function() {\n    var hasFailure = false;\n    for(var i=0;i<this.tests.length;i++) {\n      if (this.tests[i].errors > 0) {\n        return \"ERROR\";\n      }\n      if (this.tests[i].failures > 0) {\n        hasFailure = true;\n      }\n    }\n    if (hasFailure) {\n      return \"FAILURE\";\n    } else {\n      return \"SUCCESS\";\n    }\n  },\n  postResults: function() {\n    if (this.options.resultsURL) {\n      new Ajax.Request(this.options.resultsURL, \n        { method: 'get', parameters: 'result=' + this.getResult(), asynchronous: false });\n    }\n  },\n  runTests: function() {\n    var test = this.tests[this.currentTest];\n    if (!test) {\n      // finished!\n      this.postResults();\n      this.logger.summary(this.summary());\n      return;\n    }\n    if(!test.isWaiting) {\n      this.logger.start(test.name);\n    }\n    test.run();\n    if(test.isWaiting) {\n      this.logger.message(\"Waiting for \" + test.timeToWait + \"ms\");\n      setTimeout(this.runTests.bind(this), test.timeToWait || 1000);\n    } else {\n      this.logger.finish(test.status(), test.summary());\n      this.currentTest++;\n      // tail recursive, hopefully the browser will skip the stackframe\n      this.runTests();\n    }\n  },\n  summary: function() {\n    var assertions = 0;\n    var failures = 0;\n    var errors = 0;\n    var messages = [];\n    for(var i=0;i<this.tests.length;i++) {\n      assertions +=   this.tests[i].assertions;\n      failures   +=   this.tests[i].failures;\n      errors     +=   this.tests[i].errors;\n    }\n    return (\n      (this.options.context ? this.options.context + ': ': '') + \n      this.tests.length + \" tests, \" + \n      assertions + \" assertions, \" + \n      failures   + \" failures, \" +\n      errors     + \" errors\");\n  }\n};\n\nTest.Unit.Assertions = Class.create();\nTest.Unit.Assertions.prototype = {\n  initialize: function() {\n    this.assertions = 0;\n    this.failures   = 0;\n    this.errors     = 0;\n    this.messages   = [];\n  },\n  summary: function() {\n    return (\n      this.assertions + \" assertions, \" + \n      this.failures   + \" failures, \" +\n      this.errors     + \" errors\" + \"\\n\" +\n      this.messages.join(\"\\n\"));\n  },\n  pass: function() {\n    this.assertions++;\n  },\n  fail: function(message) {\n    this.failures++;\n    this.messages.push(\"Failure: \" + message);\n  },\n  info: function(message) {\n    this.messages.push(\"Info: \" + message);\n  },\n  error: function(error) {\n    this.errors++;\n    this.messages.push(error.name + \": \"+ error.message + \"(\" + Test.Unit.inspect(error) +\")\");\n  },\n  status: function() {\n    if (this.failures > 0) return 'failed';\n    if (this.errors > 0) return 'error';\n    return 'passed';\n  },\n  assert: function(expression) {\n    var message = arguments[1] || 'assert: got \"' + Test.Unit.inspect(expression) + '\"';\n    try { expression ? this.pass() : \n      this.fail(message); }\n    catch(e) { this.error(e); }\n  },\n  assertEqual: function(expected, actual) {\n    var message = arguments[2] || \"assertEqual\";\n    try { (expected == actual) ? this.pass() :\n      this.fail(message + ': expected \"' + Test.Unit.inspect(expected) + \n        '\", actual \"' + Test.Unit.inspect(actual) + '\"'); }\n    catch(e) { this.error(e); }\n  },\n  assertInspect: function(expected, actual) {\n    var message = arguments[2] || \"assertInspect\";\n    try { (expected == actual.inspect()) ? this.pass() :\n      this.fail(message + ': expected \"' + Test.Unit.inspect(expected) + \n        '\", actual \"' + Test.Unit.inspect(actual) + '\"'); }\n    catch(e) { this.error(e); }\n  },\n  assertEnumEqual: function(expected, actual) {\n    var message = arguments[2] || \"assertEnumEqual\";\n    try { $A(expected).length == $A(actual).length && \n      expected.zip(actual).all(function(pair) { return pair[0] == pair[1] }) ?\n        this.pass() : this.fail(message + ': expected ' + Test.Unit.inspect(expected) + \n          ', actual ' + Test.Unit.inspect(actual)); }\n    catch(e) { this.error(e); }\n  },\n  assertNotEqual: function(expected, actual) {\n    var message = arguments[2] || \"assertNotEqual\";\n    try { (expected != actual) ? this.pass() : \n      this.fail(message + ': got \"' + Test.Unit.inspect(actual) + '\"'); }\n    catch(e) { this.error(e); }\n  },\n  assertIdentical: function(expected, actual) { \n    var message = arguments[2] || \"assertIdentical\"; \n    try { (expected === actual) ? this.pass() : \n      this.fail(message + ': expected \"' + Test.Unit.inspect(expected) +  \n        '\", actual \"' + Test.Unit.inspect(actual) + '\"'); } \n    catch(e) { this.error(e); } \n  },\n  assertNotIdentical: function(expected, actual) { \n    var message = arguments[2] || \"assertNotIdentical\"; \n    try { !(expected === actual) ? this.pass() : \n      this.fail(message + ': expected \"' + Test.Unit.inspect(expected) +  \n        '\", actual \"' + Test.Unit.inspect(actual) + '\"'); } \n    catch(e) { this.error(e); } \n  },\n  assertNull: function(obj) {\n    var message = arguments[1] || 'assertNull';\n    try { (obj==null) ? this.pass() : \n      this.fail(message + ': got \"' + Test.Unit.inspect(obj) + '\"'); }\n    catch(e) { this.error(e); }\n  },\n  assertMatch: function(expected, actual) {\n    var message = arguments[2] || 'assertMatch';\n    var regex = new RegExp(expected);\n    try { (regex.exec(actual)) ? this.pass() :\n      this.fail(message + ' : regex: \"' +  Test.Unit.inspect(expected) + ' did not match: ' + Test.Unit.inspect(actual) + '\"'); }\n    catch(e) { this.error(e); }\n  },\n  assertHidden: function(element) {\n    var message = arguments[1] || 'assertHidden';\n    this.assertEqual(\"none\", element.style.display, message);\n  },\n  assertNotNull: function(object) {\n    var message = arguments[1] || 'assertNotNull';\n    this.assert(object != null, message);\n  },\n  assertType: function(expected, actual) {\n    var message = arguments[2] || 'assertType';\n    try { \n      (actual.constructor == expected) ? this.pass() : \n      this.fail(message + ': expected \"' + Test.Unit.inspect(expected) +  \n        '\", actual \"' + (actual.constructor) + '\"'); }\n    catch(e) { this.error(e); }\n  },\n  assertNotOfType: function(expected, actual) {\n    var message = arguments[2] || 'assertNotOfType';\n    try { \n      (actual.constructor != expected) ? this.pass() : \n      this.fail(message + ': expected \"' + Test.Unit.inspect(expected) +  \n        '\", actual \"' + (actual.constructor) + '\"'); }\n    catch(e) { this.error(e); }\n  },\n  assertInstanceOf: function(expected, actual) {\n    var message = arguments[2] || 'assertInstanceOf';\n    try { \n      (actual instanceof expected) ? this.pass() : \n      this.fail(message + \": object was not an instance of the expected type\"); }\n    catch(e) { this.error(e); } \n  },\n  assertNotInstanceOf: function(expected, actual) {\n    var message = arguments[2] || 'assertNotInstanceOf';\n    try { \n      !(actual instanceof expected) ? this.pass() : \n      this.fail(message + \": object was an instance of the not expected type\"); }\n    catch(e) { this.error(e); } \n  },\n  assertRespondsTo: function(method, obj) {\n    var message = arguments[2] || 'assertRespondsTo';\n    try {\n      (obj[method] && typeof obj[method] == 'function') ? this.pass() : \n      this.fail(message + \": object doesn't respond to [\" + method + \"]\"); }\n    catch(e) { this.error(e); }\n  },\n  assertReturnsTrue: function(method, obj) {\n    var message = arguments[2] || 'assertReturnsTrue';\n    try {\n      var m = obj[method];\n      if(!m) m = obj['is'+method.charAt(0).toUpperCase()+method.slice(1)];\n      m() ? this.pass() : \n      this.fail(message + \": method returned false\"); }\n    catch(e) { this.error(e); }\n  },\n  assertReturnsFalse: function(method, obj) {\n    var message = arguments[2] || 'assertReturnsFalse';\n    try {\n      var m = obj[method];\n      if(!m) m = obj['is'+method.charAt(0).toUpperCase()+method.slice(1)];\n      !m() ? this.pass() : \n      this.fail(message + \": method returned true\"); }\n    catch(e) { this.error(e); }\n  },\n  assertRaise: function(exceptionName, method) {\n    var message = arguments[2] || 'assertRaise';\n    try { \n      method();\n      this.fail(message + \": exception expected but none was raised\"); }\n    catch(e) {\n      ((exceptionName == null) || (e.name==exceptionName)) ? this.pass() : this.error(e); \n    }\n  },\n  assertElementsMatch: function() {\n    var expressions = $A(arguments), elements = $A(expressions.shift());\n    if (elements.length != expressions.length) {\n      this.fail('assertElementsMatch: size mismatch: ' + elements.length + ' elements, ' + expressions.length + ' expressions');\n      return false;\n    }\n    elements.zip(expressions).all(function(pair, index) {\n      var element = $(pair.first()), expression = pair.last();\n      if (element.match(expression)) return true;\n      this.fail('assertElementsMatch: (in index ' + index + ') expected ' + expression.inspect() + ' but got ' + element.inspect());\n    }.bind(this)) && this.pass();\n  },\n  assertElementMatches: function(element, expression) {\n    this.assertElementsMatch([element], expression);\n  },\n  benchmark: function(operation, iterations) {\n    var startAt = new Date();\n    (iterations || 1).times(operation);\n    var timeTaken = ((new Date())-startAt);\n    this.info((arguments[2] || 'Operation') + ' finished ' + \n       iterations + ' iterations in ' + (timeTaken/1000)+'s' );\n    return timeTaken;\n  },\n  _isVisible: function(element) {\n    element = $(element);\n    if(!element.parentNode) return true;\n    this.assertNotNull(element);\n    if(element.style && Element.getStyle(element, 'display') == 'none')\n      return false;\n    \n    return this._isVisible(element.parentNode);\n  },\n  assertNotVisible: function(element) {\n    this.assert(!this._isVisible(element), Test.Unit.inspect(element) + \" was not hidden and didn't have a hidden parent either. \" + (\"\" || arguments[1]));\n  },\n  assertVisible: function(element) {\n    this.assert(this._isVisible(element), Test.Unit.inspect(element) + \" was not visible. \" + (\"\" || arguments[1]));\n  },\n  benchmark: function(operation, iterations) {\n    var startAt = new Date();\n    (iterations || 1).times(operation);\n    var timeTaken = ((new Date())-startAt);\n    this.info((arguments[2] || 'Operation') + ' finished ' + \n       iterations + ' iterations in ' + (timeTaken/1000)+'s' );\n    return timeTaken;\n  }\n};\n\nTest.Unit.Testcase = Class.create();\nObject.extend(Object.extend(Test.Unit.Testcase.prototype, Test.Unit.Assertions.prototype), {\n  initialize: function(name, test, setup, teardown) {\n    Test.Unit.Assertions.prototype.initialize.bind(this)();\n    this.name           = name;\n    \n    if(typeof test == 'string') {\n      test = test.gsub(/(\\.should[^\\(]+\\()/,'#{0}this,');\n      test = test.gsub(/(\\.should[^\\(]+)\\(this,\\)/,'#{1}(this)');\n      this.test = function() {\n        eval('with(this){'+test+'}');\n      }\n    } else {\n      this.test = test || function() {};\n    }\n    \n    this.setup          = setup || function() {};\n    this.teardown       = teardown || function() {};\n    this.isWaiting      = false;\n    this.timeToWait     = 1000;\n  },\n  wait: function(time, nextPart) {\n    this.isWaiting = true;\n    this.test = nextPart;\n    this.timeToWait = time;\n  },\n  run: function() {\n    try {\n      try {\n        if (!this.isWaiting) this.setup.bind(this)();\n        this.isWaiting = false;\n        this.test.bind(this)();\n      } finally {\n        if(!this.isWaiting) {\n          this.teardown.bind(this)();\n        }\n      }\n    }\n    catch(e) { this.error(e); }\n  }\n});\n\n// *EXPERIMENTAL* BDD-style testing to please non-technical folk\n// This draws many ideas from RSpec http://rspec.rubyforge.org/\n\nTest.setupBDDExtensionMethods = function(){\n  var METHODMAP = {\n    shouldEqual:     'assertEqual',\n    shouldNotEqual:  'assertNotEqual',\n    shouldEqualEnum: 'assertEnumEqual',\n    shouldBeA:       'assertType',\n    shouldNotBeA:    'assertNotOfType',\n    shouldBeAn:      'assertType',\n    shouldNotBeAn:   'assertNotOfType',\n    shouldBeNull:    'assertNull',\n    shouldNotBeNull: 'assertNotNull',\n    \n    shouldBe:        'assertReturnsTrue',\n    shouldNotBe:     'assertReturnsFalse',\n    shouldRespondTo: 'assertRespondsTo'\n  };\n  var makeAssertion = function(assertion, args, object) { \n   \tthis[assertion].apply(this,(args || []).concat([object]));\n  };\n  \n  Test.BDDMethods = {};   \n  $H(METHODMAP).each(function(pair) { \n    Test.BDDMethods[pair.key] = function() { \n       var args = $A(arguments); \n       var scope = args.shift(); \n       makeAssertion.apply(scope, [pair.value, args, this]); }; \n  });\n  \n  [Array.prototype, String.prototype, Number.prototype, Boolean.prototype].each(\n    function(p){ Object.extend(p, Test.BDDMethods) }\n  );\n};\n\nTest.context = function(name, spec, log){\n  Test.setupBDDExtensionMethods();\n  \n  var compiledSpec = {};\n  var titles = {};\n  for(specName in spec) {\n    switch(specName){\n      case \"setup\":\n      case \"teardown\":\n        compiledSpec[specName] = spec[specName];\n        break;\n      default:\n        var testName = 'test'+specName.gsub(/\\s+/,'-').camelize();\n        var body = spec[specName].toString().split('\\n').slice(1);\n        if(/^\\{/.test(body[0])) body = body.slice(1);\n        body.pop();\n        body = body.map(function(statement){ \n          return statement.strip()\n        });\n        compiledSpec[testName] = body.join('\\n');\n        titles[testName] = specName;\n    }\n  }\n  new Test.Unit.Runner(compiledSpec, { titles: titles, testLog: log || 'testlog', context: name });\n};"
  },
  {
    "path": "prototype-demo.html",
    "content": "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">\n<html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"en\" lang=\"en\">\n<head>\n  <meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\"/>\n  <title>iPhone Style Radios Demo</title>\n  <script src=\"prototype/prototype.js\" type=\"text/javascript\" charset=\"utf-8\"></script>\n  <script src=\"prototype/scriptaculous.js\" type=\"text/javascript\" charset=\"utf-8\"></script>\n  <script src=\"prototype/iphone-style-checkboxes.js\" type=\"text/javascript\" charset=\"utf-8\"></script>\n  <link rel=\"stylesheet\" href=\"style.css\" type=\"text/css\" media=\"screen\" charset=\"utf-8\" />\n  <style type=\"text/css\">\n    body {\n      padding: 10px;\n    }\n    label.left {\n      float: left;\n      padding: 4px;\n      padding-right: 15px;\n    }\n    .css_sized_container .iPhoneCheckContainer {\n      width: 250px;\n    }\n    ol {\n      list-style-type: none;\n      margin: 0;\n      padding: 0;\n    }\n    ol li {\n      padding: 10px;\n      margin: 0;\n    }\n    .onchange input {\n      opacity: 1 !important;\n      left: 100px;\n    }\n    .onchange .iPhoneCheckContainer {\n      overflow: visible;\n    }\n  </style>\n\n  <script type=\"text/javascript\" charset=\"utf-8\">\n    document.observe(\"dom:loaded\", function() {\n      new iPhoneStyle('.on_off input[type=checkbox]');\n      new iPhoneStyle('.css_sized_container input[type=checkbox]', { resizeContainer: false, resizeHandle: false });\n      new iPhoneStyle('.long_tiny input[type=checkbox]', { checkedLabel: 'Very Long Text', uncheckedLabel: 'Tiny' });\n      \n      var onchange_checkbox = $$('.onchange input[type=checkbox]').first();\n      new iPhoneStyle(onchange_checkbox);\n      setInterval(function toggleCheckbox() {\n        onchange_checkbox.writeAttribute('checked', !onchange_checkbox.checked);\n        onchange_checkbox.change();\n        $('status').update(onchange_checkbox.checked);\n      }, 2500);\n    });\n  </script>\n  \n</head>\n<body>\n  <ol class=\"on_off\">\n    <li>\n      <label class=\"left\" for=\"on_off\">Default</label>\n      <input type=\"checkbox\" id=\"on_off\" />\n    </li>\n  \n    <li>\n      <label class=\"left\" for=\"on_off_on\">Default On</label>\n      <input type=\"checkbox\" checked=\"checked\" id=\"on_off_on\"/>\n    </li>\n  </ol>\n\n  <ol class=\"css_sized_container\">\n    <li>\n      <label class=\"left\" for=\"css_sized_container\">CSS Sized Container and Handle</label>\n      <input type=\"checkbox\" id=\"css_sized_container\"/>\n    </li>\n  </ol>\n\n  <ol class=\"long_tiny\">\n    <li>\n      <label class=\"left\" for=\"long_tiny\">Long and short labels</label>\n      <input type=\"checkbox\" id=\"long_tiny\"/>\n    </li>\n  </ol>\n\n  <ol class=\"onchange\">\n    <li>\n      <input type=\"checkbox\" id=\"onchange\"/>\n      <br />\n      <p>Checkbox status is <strong><span id=\"status\">...</span></strong>.</p>\n    </li>\n  </ol>\n</body>\n</html>"
  },
  {
    "path": "rails/.gitignore",
    "content": ".DS_Store\npkg/*\n*.gem\n*.rbc\n*.swp\n.bundle\nGemfile.lock\n"
  },
  {
    "path": "rails/README.md",
    "content": "# iOS Style Checkboxes for Rails 3.1\n\n\nThis gem provides an easy way of using [iOS style checkboxes](https://github.com/tdreyno/iphone-style-checkboxes).\n\n\n## Install\n\nPlease be sure to have [Rails Assets Pipeline](http://guides.rubyonrails.org/asset_pipeline.html) enabled.\n\nAdd the following to your `Gemfile`, preferably inside the assets group:\n\n```ruby\ngem 'ios-checkboxes'\ngem 'jquery' # This is required by ios-checkboxes\n```\n\nThen as usually:\n\n```bash\n$ bundle install\n```\n\n## Usage\n\nAfter installation, all you need to do is to require `ios-checkboxes` inside JavaScript/CoffeeScript and CSS files.\n\nUsually you would add this to your `app/assets/javascripts/application.js` file:\n\n```javascript\n//= require ios-checkboxes\n```\n\nThis will also automatically require `jquery`.\n\nAnd also update your `app/assets/stylesheets/applications.css` to inlude `ios-checkboxes`\n\n```css\n/*\n *= require ios-checkboxes\n *= require_self\n*/\n```\n\nThat's it. Now you can write stuff like `$(\".on-off\").iphoneStyle()`.\n\n## Development\n\n- Source hosted at [GitHub](https://github.com/tdreyno/iphone-style-checkboxes). See `rails` directory.\n- Report issues and feature requests to [GitHub Issues](https://github.com/tdreyno/iphone-style-checkboxes/issues)\n\nTo update the gem with the latest files do:\n\n```bash\ncd rails\nrake update\n# Now go and update the version manually in lib/ios-checkboxes/version.rb\ngit add .\ngit commit -m 'update rails gem'\nrake release\n```\n"
  },
  {
    "path": "rails/Rakefile",
    "content": "require 'rake'\nrequire 'bundler/gem_tasks'\n\ndesc \"Update the gem with the latest files\"\ntask :update do\n  # Copy the js file and require jQuery\n  js_content = File.read \"../jquery/iphone-style-checkboxes.js\"\n  f = File.new(\"vendor/assets/javascripts/ios-checkboxes.js\", \"w\")\n  f.write(\"//= require jquery\\n\\n#{js_content}\")\n  f.close\n\n  # Copy the images\n  cp_r \"../images/\", \"vendor/assets/\"\n\n  # Copy CSS and make sure it references the images via assets pipeline\n  css_content = File.read \"../style.css\"\n  pattern = /url\\('images\\/([^']+)'\\)/\n  css_processed = css_content.gsub(pattern) {|m| \"url('<%= image_path \\\"#{$1.sub(/\\?.*/, '')}\\\" %>')\" }\n  f2 = File.new(\"vendor/assets/stylesheets/ios-checkboxes.css.erb\", \"w\")\n  f2.write(css_processed)\n  f2.close\nend\n"
  },
  {
    "path": "rails/ios-checkboxes.gemspec",
    "content": "# -*- encoding: utf-8 -*-\nrequire File.expand_path('../lib/ios-checkboxes/version', __FILE__)\n\nGem::Specification.new do |s|\n  s.name        = \"ios-checkboxes\"\n  s.version     = IOSCheckboxes::VERSION\n  s.authors     = [\"Thomas Reynolds\"]\n  s.email       = [\"me@tdreyno.com\"]\n  s.homepage    = \"https://github.com/tdreyno/iphone-style-checkboxes\"\n  s.summary     = %q{iOS-style Checkboxes}\n  s.description = %q{iOS-style Checkboxes for Rails asset pipeline.}\n\n  s.rubyforge_project = \"ios-checkboxes\"\n  s.add_dependency \"railties\", \"~> 3.1\"\n\n  s.files = `git ls-files`.split(\"\\n\")\n\n  s.require_paths = [\"lib\"]\nend\n"
  },
  {
    "path": "rails/lib/ios-checkboxes/version.rb",
    "content": "module IOSCheckboxes\n  VERSION = \"0.1.1\"\nend\n"
  },
  {
    "path": "rails/lib/ios-checkboxes.rb",
    "content": "require \"ios-checkboxes/version\"\n\nmodule IOSCheckboxes\n  class Engine < ::Rails::Engine\n  end\nend"
  },
  {
    "path": "rails/vendor/assets/javascripts/ios-checkboxes.js",
    "content": "//= require jquery\n\n(function() {\n  var iOSCheckbox;\n  var __slice = Array.prototype.slice;\n  iOSCheckbox = (function() {\n    function iOSCheckbox(elem, options) {\n      var key, opts, value;\n      this.elem = $(elem);\n      opts = $.extend({}, iOSCheckbox.defaults, options);\n      for (key in opts) {\n        value = opts[key];\n        this[key] = value;\n      }\n      this.elem.data(this.dataName, this);\n      this.wrapCheckboxWithDivs();\n      this.attachEvents();\n      this.disableTextSelection();\n      if (this.resizeHandle) {\n        this.optionallyResize('handle');\n      }\n      if (this.resizeContainer) {\n        this.optionallyResize('container');\n      }\n      this.initialPosition();\n    }\n    iOSCheckbox.prototype.isDisabled = function() {\n      return this.elem.is(':disabled');\n    };\n    iOSCheckbox.prototype.wrapCheckboxWithDivs = function() {\n      this.elem.wrap(\"<div class='\" + this.containerClass + \"' />\");\n      this.container = this.elem.parent();\n      this.offLabel = $(\"<label class='\" + this.labelOffClass + \"'>\\n  <span>\" + this.uncheckedLabel + \"</span>\\n</label>\").appendTo(this.container);\n      this.offSpan = this.offLabel.children('span');\n      this.onLabel = $(\"<label class='\" + this.labelOnClass + \"'>\\n  <span>\" + this.checkedLabel + \"</span>\\n</label>\").appendTo(this.container);\n      this.onSpan = this.onLabel.children('span');\n      return this.handle = $(\"<div class='\" + this.handleClass + \"'>\\n  <div class='\" + this.handleRightClass + \"'>\\n    <div class='\" + this.handleCenterClass + \"' />\\n  </div>\\n</div>\").appendTo(this.container);\n    };\n    iOSCheckbox.prototype.disableTextSelection = function() {\n      if ($.browser.msie) {\n        return $([this.handle, this.offLabel, this.onLabel, this.container]).attr(\"unselectable\", \"on\");\n      }\n    };\n    iOSCheckbox.prototype._getDimension = function(elem, dimension) {\n      if ($.fn.actual != null) {\n        return elem.actual(dimension);\n      } else {\n        return elem[dimension]();\n      }\n    };\n    iOSCheckbox.prototype.optionallyResize = function(mode) {\n      var newWidth, offLabelWidth, onLabelWidth;\n      onLabelWidth = this._getDimension(this.onLabel, \"width\");\n      offLabelWidth = this._getDimension(this.offLabel, \"width\");\n      if (mode === \"container\") {\n        newWidth = onLabelWidth > offLabelWidth ? onLabelWidth : offLabelWidth;\n        newWidth += this._getDimension(this.handle, \"width\") + this.handleMargin;\n        return this.container.css({\n          width: newWidth\n        });\n      } else {\n        newWidth = onLabelWidth > offLabelWidth ? onLabelWidth : offLabelWidth;\n        return this.handle.css({\n          width: newWidth\n        });\n      }\n    };\n    iOSCheckbox.prototype.onMouseDown = function(event) {\n      var x;\n      event.preventDefault();\n      if (this.isDisabled()) {\n        return;\n      }\n      x = event.pageX || event.originalEvent.changedTouches[0].pageX;\n      iOSCheckbox.currentlyClicking = this.handle;\n      iOSCheckbox.dragStartPosition = x;\n      return iOSCheckbox.handleLeftOffset = parseInt(this.handle.css('left'), 10) || 0;\n    };\n    iOSCheckbox.prototype.onDragMove = function(event, x) {\n      var newWidth, p;\n      if (iOSCheckbox.currentlyClicking !== this.handle) {\n        return;\n      }\n      p = (x + iOSCheckbox.handleLeftOffset - iOSCheckbox.dragStartPosition) / this.rightSide;\n      if (p < 0) {\n        p = 0;\n      }\n      if (p > 1) {\n        p = 1;\n      }\n      newWidth = p * this.rightSide;\n      this.handle.css({\n        left: newWidth\n      });\n      this.onLabel.css({\n        width: newWidth + this.handleRadius\n      });\n      this.offSpan.css({\n        marginRight: -newWidth\n      });\n      return this.onSpan.css({\n        marginLeft: -(1 - p) * this.rightSide\n      });\n    };\n    iOSCheckbox.prototype.onDragEnd = function(event, x) {\n      var p;\n      if (iOSCheckbox.currentlyClicking !== this.handle) {\n        return;\n      }\n      if (this.isDisabled()) {\n        return;\n      }\n      if (iOSCheckbox.dragging) {\n        p = (x - iOSCheckbox.dragStartPosition) / this.rightSide;\n        this.elem.prop('checked', p >= 0.5);\n      } else {\n        this.elem.prop('checked', !this.elem.prop('checked'));\n      }\n      iOSCheckbox.currentlyClicking = null;\n      iOSCheckbox.dragging = null;\n      return this.didChange();\n    };\n    iOSCheckbox.prototype.refresh = function() {\n      return this.didChange();\n    };\n    iOSCheckbox.prototype.didChange = function() {\n      var new_left;\n      if (typeof this.onChange === \"function\") {\n        this.onChange(this.elem, this.elem.prop('checked'));\n      }\n      if (this.isDisabled()) {\n        this.container.addClass(this.disabledClass);\n        return false;\n      } else {\n        this.container.removeClass(this.disabledClass);\n      }\n      new_left = this.elem.prop('checked') ? this.rightSide : 0;\n      this.handle.animate({\n        left: new_left\n      }, this.duration);\n      this.onLabel.animate({\n        width: new_left + this.handleRadius\n      }, this.duration);\n      this.offSpan.animate({\n        marginRight: -new_left\n      }, this.duration);\n      return this.onSpan.animate({\n        marginLeft: new_left - this.rightSide\n      }, this.duration);\n    };\n    iOSCheckbox.prototype.attachEvents = function() {\n      var localMouseMove, localMouseUp, self;\n      self = this;\n      localMouseMove = function(event) {\n        return self.onGlobalMove.apply(self, arguments);\n      };\n      localMouseUp = function(event) {\n        self.onGlobalUp.apply(self, arguments);\n        $(document).unbind('mousemove touchmove', localMouseMove);\n        return $(document).unbind('mouseup touchend', localMouseUp);\n      };\n      this.elem.change(function() {\n        return self.refresh();\n      });\n      return this.container.bind('mousedown touchstart', function(event) {\n        self.onMouseDown.apply(self, arguments);\n        $(document).bind('mousemove touchmove', localMouseMove);\n        return $(document).bind('mouseup touchend', localMouseUp);\n      });\n    };\n    iOSCheckbox.prototype.initialPosition = function() {\n      var containerWidth, offset;\n      containerWidth = this._getDimension(this.container, \"width\");\n      this.offLabel.css({\n        width: containerWidth - this.containerRadius\n      });\n      offset = this.containerRadius + 1;\n      if ($.browser.msie && $.browser.version < 7) {\n        offset -= 3;\n      }\n      this.rightSide = containerWidth - this._getDimension(this.handle, \"width\") - offset;\n      if (this.elem.is(':checked')) {\n        this.handle.css({\n          left: this.rightSide\n        });\n        this.onLabel.css({\n          width: this.rightSide + this.handleRadius\n        });\n        this.offSpan.css({\n          marginRight: -this.rightSide\n        });\n      } else {\n        this.onLabel.css({\n          width: 0\n        });\n        this.onSpan.css({\n          marginLeft: -this.rightSide\n        });\n      }\n      if (this.isDisabled()) {\n        return this.container.addClass(this.disabledClass);\n      }\n    };\n    iOSCheckbox.prototype.onGlobalMove = function(event) {\n      var x;\n      if (!(!this.isDisabled() && iOSCheckbox.currentlyClicking)) {\n        return;\n      }\n      event.preventDefault();\n      x = event.pageX || event.originalEvent.changedTouches[0].pageX;\n      if (!iOSCheckbox.dragging && (Math.abs(iOSCheckbox.dragStartPosition - x) > this.dragThreshold)) {\n        iOSCheckbox.dragging = true;\n      }\n      return this.onDragMove(event, x);\n    };\n    iOSCheckbox.prototype.onGlobalUp = function(event) {\n      var x;\n      if (!iOSCheckbox.currentlyClicking) {\n        return;\n      }\n      event.preventDefault();\n      x = event.pageX || event.originalEvent.changedTouches[0].pageX;\n      this.onDragEnd(event, x);\n      return false;\n    };\n    iOSCheckbox.defaults = {\n      duration: 200,\n      checkedLabel: 'ON',\n      uncheckedLabel: 'OFF',\n      resizeHandle: true,\n      resizeContainer: true,\n      disabledClass: 'iPhoneCheckDisabled',\n      containerClass: 'iPhoneCheckContainer',\n      labelOnClass: 'iPhoneCheckLabelOn',\n      labelOffClass: 'iPhoneCheckLabelOff',\n      handleClass: 'iPhoneCheckHandle',\n      handleCenterClass: 'iPhoneCheckHandleCenter',\n      handleRightClass: 'iPhoneCheckHandleRight',\n      dragThreshold: 5,\n      handleMargin: 15,\n      handleRadius: 4,\n      containerRadius: 5,\n      dataName: \"iphoneStyle\",\n      onChange: function() {}\n    };\n    return iOSCheckbox;\n  })();\n  $.iphoneStyle = this.iOSCheckbox = iOSCheckbox;\n  $.fn.iphoneStyle = function() {\n    var args, checkbox, dataName, existingControl, method, params, _i, _len, _ref, _ref2, _ref3, _ref4;\n    args = 1 <= arguments.length ? __slice.call(arguments, 0) : [];\n    dataName = (_ref = (_ref2 = args[0]) != null ? _ref2.dataName : void 0) != null ? _ref : iOSCheckbox.defaults.dataName;\n    _ref3 = this.filter(':checkbox');\n    for (_i = 0, _len = _ref3.length; _i < _len; _i++) {\n      checkbox = _ref3[_i];\n      existingControl = $(checkbox).data(dataName);\n      if (existingControl != null) {\n        method = args[0], params = 2 <= args.length ? __slice.call(args, 1) : [];\n        if ((_ref4 = existingControl[method]) != null) {\n          _ref4.apply(existingControl, params);\n        }\n      } else {\n        new iOSCheckbox(checkbox, args[0]);\n      }\n    }\n    return this;\n  };\n  $.fn.iOSCheckbox = function(options) {\n    var opts;\n    if (options == null) {\n      options = {};\n    }\n    opts = $.extend({}, options, {\n      resizeHandle: false,\n      disabledClass: 'iOSCheckDisabled',\n      containerClass: 'iOSCheckContainer',\n      labelOnClass: 'iOSCheckLabelOn',\n      labelOffClass: 'iOSCheckLabelOff',\n      handleClass: 'iOSCheckHandle',\n      handleCenterClass: 'iOSCheckHandleCenter',\n      handleRightClass: 'iOSCheckHandleRight',\n      dataName: 'iOSCheckbox'\n    });\n    return this.iphoneStyle(opts);\n  };\n}).call(this);\n"
  },
  {
    "path": "rails/vendor/assets/stylesheets/ios-checkboxes.css.erb",
    "content": ".iPhoneCheckContainer {\n  position: relative;\n  height: 27px;\n  cursor: pointer;\n  overflow: hidden; }\n  .iPhoneCheckContainer input {\n    position: absolute;\n    top: 5px;\n    left: 30px;\n    filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=0);\n    opacity: 0; }\n  .iPhoneCheckContainer label {\n    white-space: nowrap;\n    font-size: 17px;\n    line-height: 17px;\n    font-weight: bold;\n    font-family: \"Helvetica Neue\", Arial, Helvetica, sans-serif;\n    cursor: pointer;\n    display: block;\n    height: 27px;\n    position: absolute;\n    width: auto;\n    top: 0;\n    padding-top: 5px;\n    overflow: hidden; }\n  .iPhoneCheckContainer, .iPhoneCheckContainer label {\n    user-select: none;\n    -moz-user-select: none;\n    -khtml-user-select: none; }\n\n.iPhoneCheckDisabled {\n  filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=50);\n  opacity: 0.5; }\n\nlabel.iPhoneCheckLabelOn {\n  color: white;\n  background: url('<%= image_path \"iphone-style-checkboxes/on.png\" %>') no-repeat;\n  text-shadow: 0px 0px 2px rgba(0, 0, 0, 0.6);\n  left: 0;\n  padding-top: 5px; }\n  label.iPhoneCheckLabelOn span {\n    padding-left: 8px; }\nlabel.iPhoneCheckLabelOff {\n  color: #8b8b8b;\n  background: url('<%= image_path \"iphone-style-checkboxes/off.png\" %>') no-repeat right 0;\n  text-shadow: 0px 0px 2px rgba(255, 255, 255, 0.6);\n  text-align: right;\n  right: 0; }\n  label.iPhoneCheckLabelOff span {\n    padding-right: 8px; }\n\n.iPhoneCheckHandle {\n  display: block;\n  height: 27px;\n  cursor: pointer;\n  position: absolute;\n  top: 0;\n  left: 0;\n  width: 0;\n  background: url('<%= image_path \"iphone-style-checkboxes/slider_left.png\" %>') no-repeat;\n  padding-left: 3px; }\n\n.iPhoneCheckHandleRight {\n  height: 100%;\n  width: 100%;\n  padding-right: 3px;\n  background: url('<%= image_path \"iphone-style-checkboxes/slider_right.png\" %>') no-repeat right 0; }\n\n.iPhoneCheckHandleCenter {\n  height: 100%;\n  width: 100%;\n  background: url('<%= image_path \"iphone-style-checkboxes/slider_center.png\" %>'); }\n\n.iOSCheckContainer {\n  position: relative;\n  height: 27px;\n  cursor: pointer;\n  overflow: hidden; }\n  .iOSCheckContainer input {\n    position: absolute;\n    top: 5px;\n    left: 30px;\n    filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=0);\n    opacity: 0; }\n  .iOSCheckContainer label {\n    white-space: nowrap;\n    font-size: 17px;\n    line-height: 17px;\n    font-weight: bold;\n    font-family: \"Helvetica Neue\", Arial, Helvetica, sans-serif;\n    cursor: pointer;\n    display: block;\n    height: 27px;\n    position: absolute;\n    width: auto;\n    top: 0;\n    padding-top: 5px;\n    overflow: hidden; }\n  .iOSCheckContainer, .iOSCheckContainer label {\n    user-select: none;\n    -moz-user-select: none;\n    -khtml-user-select: none; }\n\n.iOSCheckDisabled {\n  filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=50);\n  opacity: 0.5; }\n\nlabel.iOSCheckLabelOn {\n  color: white;\n  background: url('<%= image_path \"ios-style-checkboxes/on.png\" %>') no-repeat;\n  text-shadow: 0px 0px 2px rgba(0, 0, 0, 0.6);\n  left: 0;\n  padding-top: 5px; }\n  label.iOSCheckLabelOn span {\n    padding-left: 8px; }\nlabel.iOSCheckLabelOff {\n  color: #8b8b8b;\n  background: url('<%= image_path \"ios-style-checkboxes/off.png\" %>') no-repeat right 0;\n  text-shadow: 0px 0px 2px rgba(255, 255, 255, 0.6);\n  text-align: right;\n  right: 0; }\n  label.iOSCheckLabelOff span {\n    padding-right: 8px; }\n\n.iOSCheckHandle {\n  display: block;\n  height: 27px;\n  cursor: pointer;\n  position: absolute;\n  top: 0;\n  left: 0;\n  width: 0;\n  background: url('<%= image_path \"ios-style-checkboxes/slider_left.png\" %>') no-repeat;\n  padding-left: 3px; }\n\n.iOSCheckHandleRight {\n  height: 100%;\n  width: 100%;\n  padding-right: 3px;\n  background: url('<%= image_path \"ios-style-checkboxes/slider_right.png\" %>') no-repeat right 0; }\n\n.iOSCheckHandleCenter {\n  height: 100%;\n  width: 100%;\n  background: url('<%= image_path \"ios-style-checkboxes/slider_center.png\" %>'); }\n"
  },
  {
    "path": "sass/style.sass",
    "content": "@import \"iphone-style-checkboxes\"\n+iphone-style-defaults\n\n@import \"ios-style-checkboxes\"\n+ios-style-defaults\n"
  },
  {
    "path": "style.css",
    "content": ".iPhoneCheckContainer {\n  -webkit-transform:translate3d(0,0,0);\n  position: relative;\n  height: 27px;\n  cursor: pointer;\n  overflow: hidden; }\n  .iPhoneCheckContainer input {\n    position: absolute;\n    top: 5px;\n    left: 30px;\n    filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=0);\n    opacity: 0; }\n  .iPhoneCheckContainer label {\n    white-space: nowrap;\n    font-size: 17px;\n    line-height: 17px;\n    font-weight: bold;\n    font-family: \"Helvetica Neue\", Arial, Helvetica, sans-serif;\n    cursor: pointer;\n    display: block;\n    height: 27px;\n    position: absolute;\n    width: auto;\n    top: 0;\n    padding-top: 5px;\n    overflow: hidden; }\n  .iPhoneCheckContainer, .iPhoneCheckContainer label {\n    user-select: none;\n    -moz-user-select: none;\n    -khtml-user-select: none; }\n\n.iPhoneCheckDisabled {\n  filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=50);\n  opacity: 0.5; }\n\nlabel.iPhoneCheckLabelOn {\n  color: white;\n  background: url('images/iphone-style-checkboxes/on.png?1284697268') no-repeat;\n  text-shadow: 0px 0px 2px rgba(0, 0, 0, 0.6);\n  left: 0;\n  padding-top: 5px; }\n  label.iPhoneCheckLabelOn span {\n    padding-left: 8px; }\nlabel.iPhoneCheckLabelOff {\n  color: #8b8b8b;\n  background: url('images/iphone-style-checkboxes/off.png?1284697268') no-repeat right 0;\n  text-shadow: 0px 0px 2px rgba(255, 255, 255, 0.6);\n  text-align: right;\n  right: 0; }\n  label.iPhoneCheckLabelOff span {\n    padding-right: 8px; }\n\n.iPhoneCheckHandle {\n  display: block;\n  height: 27px;\n  cursor: pointer;\n  position: absolute;\n  top: 0;\n  left: 0;\n  width: 0;\n  background: url('images/iphone-style-checkboxes/slider_left.png?1284697268') no-repeat;\n  padding-left: 3px; }\n\n.iPhoneCheckHandleRight {\n  height: 100%;\n  width: 100%;\n  padding-right: 3px;\n  background: url('images/iphone-style-checkboxes/slider_right.png?1284697268') no-repeat right 0; }\n\n.iPhoneCheckHandleCenter {\n  height: 100%;\n  width: 100%;\n  background: url('images/iphone-style-checkboxes/slider_center.png?1284697268'); }\n\n.iOSCheckContainer {\n  position: relative;\n  height: 27px;\n  cursor: pointer;\n  overflow: hidden; }\n  .iOSCheckContainer input {\n    position: absolute;\n    top: 5px;\n    left: 30px;\n    filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=0);\n    opacity: 0; }\n  .iOSCheckContainer label {\n    white-space: nowrap;\n    font-size: 17px;\n    line-height: 17px;\n    font-weight: bold;\n    font-family: \"Helvetica Neue\", Arial, Helvetica, sans-serif;\n    cursor: pointer;\n    display: block;\n    height: 27px;\n    position: absolute;\n    width: auto;\n    top: 0;\n    padding-top: 5px;\n    overflow: hidden; }\n  .iOSCheckContainer, .iOSCheckContainer label {\n    user-select: none;\n    -moz-user-select: none;\n    -khtml-user-select: none; }\n\n.iOSCheckDisabled {\n  filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=50);\n  opacity: 0.5; }\n\nlabel.iOSCheckLabelOn {\n  color: white;\n  background: url('images/ios-style-checkboxes/on.png?1284697268') no-repeat;\n  text-shadow: 0px 0px 2px rgba(0, 0, 0, 0.6);\n  left: 0;\n  padding-top: 5px; }\n  label.iOSCheckLabelOn span {\n    padding-left: 8px; }\nlabel.iOSCheckLabelOff {\n  color: #8b8b8b;\n  background: url('images/ios-style-checkboxes/off.png?1284697268') no-repeat right 0;\n  text-shadow: 0px 0px 2px rgba(255, 255, 255, 0.6);\n  text-align: right;\n  right: 0; }\n  label.iOSCheckLabelOff span {\n    padding-right: 8px; }\n\n.iOSCheckHandle {\n  display: block;\n  height: 27px;\n  cursor: pointer;\n  position: absolute;\n  top: 0;\n  left: 0;\n  width: 0;\n  background: url('images/ios-style-checkboxes/slider_left.png?1284697268') no-repeat;\n  padding-left: 3px; }\n\n.iOSCheckHandleRight {\n  height: 100%;\n  width: 100%;\n  padding-right: 3px;\n  background: url('images/ios-style-checkboxes/slider_right.png?1284697268') no-repeat right 0; }\n\n.iOSCheckHandleCenter {\n  height: 100%;\n  width: 100%;\n  background: url('images/ios-style-checkboxes/slider_center.png?1284697268'); }\n"
  }
]