
// global.js: File List:
//      /js/prototype.js
//      /js/lightboxes.js
//      /js/scriptaculous.js
//      /js/controls.js
//      /js/effects.js
//      /js/prototype_extensions.js
//      /js/tracker.js
//      /js/header.js
//      /js/Cookie.js
//      /js/patterns.js
//      /js/util.js
//      /js/Pulldown.js
//      /js/Share.js
//      /js/search_ui/SearchAlerts.js
//      /js/search_ui/AdvancedSearch.js
//      /js/HelpText.js
//      /js/color_wheel.js
//      /js/boxover.js
//      /js/ContributorDropdown.js
//      /js/PopupAnchor.js
//      /js/ui_widgets/FlyoutLayer.js
//      /js/ui_widgets/ShadowContainer.js
//      /js/ui_widgets/Bubbles.js
//      /js/SlideViewer.js
//      /js/input/TextWithDefault.js
//      /js/input/PassWithDefault.js
//      /js/input/InFieldLabel.js
//      /js/storage/storage.js
//      /js/search/search.js
//      /js/search/history.js
//      /js/search/nextButton.js
//      /js/image/grid/grid.js
//      /js/search/GridPager.js
//      /js/image/Preview.js
//      /js/search_feedback_forms/ContentGapForm.js
//      /js/search_ui/grid/GridNavigation.js
//      /js/AutoSuggest.js
//      /js/feature_announcement/SearchAnnouncement.js
//      /js/Anim.js


// global.js: begin JavaScript file: '/js/prototype.js'
// ================================================================================
/*  Prototype JavaScript framework, version 1.6.0.3
 *  (c) 2005-2008 Sam Stephenson
 *
 *  Prototype is freely distributable under the terms of an MIT-style license.
 *  For details, see the Prototype web site: http://www.prototypejs.org/
 *
 *--------------------------------------------------------------------------*/

var Prototype = {
  Version: '1.6.0.3',

  Browser: {
    IE:     !!(window.attachEvent &&
      navigator.userAgent.indexOf('Opera') === -1),
    Opera:  navigator.userAgent.indexOf('Opera') > -1,
    WebKit: navigator.userAgent.indexOf('AppleWebKit/') > -1,
    Gecko:  navigator.userAgent.indexOf('Gecko') > -1 &&
      navigator.userAgent.indexOf('KHTML') === -1,
    MobileSafari: !!navigator.userAgent.match(/Apple.*Mobile.*Safari/)
  },

  BrowserFeatures: {
    XPath: !!document.evaluate,
    SelectorsAPI: !!document.querySelector,
    ElementExtensions: !!window.HTMLElement,
    SpecificElementExtensions:
      document.createElement('div')['__proto__'] &&
      document.createElement('div')['__proto__'] !==
        document.createElement('form')['__proto__']
  },

  ScriptFragment: '<script[^>]*>([\\S\\s]*?)<\/script>',
  JSONFilter: /^\/\*-secure-([\s\S]*)\*\/\s*$/,

  emptyFunction: function() { },
  K: function(x) { return x }
};

if (Prototype.Browser.MobileSafari)
  Prototype.BrowserFeatures.SpecificElementExtensions = false;


/* Based on Alex Arnell's inheritance implementation. */
var Class = {
  create: function() {
    var parent = null, properties = $A(arguments);
    if (Object.isFunction(properties[0]))
      parent = properties.shift();

    function klass() {
      this.initialize.apply(this, arguments);
    }

    Object.extend(klass, Class.Methods);
    klass.superclass = parent;
    klass.subclasses = [];

    if (parent) {
      var subclass = function() { };
      subclass.prototype = parent.prototype;
      klass.prototype = new subclass;
      parent.subclasses.push(klass);
    }

    for (var i = 0; i < properties.length; i++)
      klass.addMethods(properties[i]);

    if (!klass.prototype.initialize)
      klass.prototype.initialize = Prototype.emptyFunction;

    klass.prototype.constructor = klass;

    return klass;
  }
};

Class.Methods = {
  addMethods: function(source) {
    var ancestor   = this.superclass && this.superclass.prototype;
    var properties = Object.keys(source);

    if (!Object.keys({ toString: true }).length)
      properties.push("toString", "valueOf");

    for (var i = 0, length = properties.length; i < length; i++) {
      var property = properties[i], value = source[property];
      if (ancestor && Object.isFunction(value) &&
          value.argumentNames().first() == "$super") {
        var method = value;
        value = (function(m) {
          return function() { return ancestor[m].apply(this, arguments) };
        })(property).wrap(method);

        value.valueOf = method.valueOf.bind(method);
        value.toString = method.toString.bind(method);
      }
      this.prototype[property] = value;
    }

    return this;
  }
};

var Abstract = { };

Object.extend = function(destination, source) {
  for (var property in source)
    destination[property] = source[property];
  return destination;
};

Object.extend(Object, {
  inspect: function(object) {
    try {
      if (Object.isUndefined(object)) return 'undefined';
      if (object === null) return 'null';
      return object.inspect ? object.inspect() : String(object);
    } catch (e) {
      if (e instanceof RangeError) return '...';
      throw e;
    }
  },

  toJSON: function(object) {
    var type = typeof object;
    switch (type) {
      case 'undefined':
      case 'function':
      case 'unknown': return;
      case 'boolean': return object.toString();
    }

    if (object === null) return 'null';
    if (object.toJSON) return object.toJSON();
    if (Object.isElement(object)) return;

    var results = [];
    for (var property in object) {
      var value = Object.toJSON(object[property]);
      if (!Object.isUndefined(value))
        results.push(property.toJSON() + ': ' + value);
    }

    return '{' + results.join(', ') + '}';
  },

  toQueryString: function(object) {
    return $H(object).toQueryString();
  },

  toHTML: function(object) {
    return object && object.toHTML ? object.toHTML() : String.interpret(object);
  },

  keys: function(object) {
    var keys = [];
    for (var property in object)
      keys.push(property);
    return keys;
  },

  values: function(object) {
    var values = [];
    for (var property in object)
      values.push(object[property]);
    return values;
  },

  clone: function(object) {
    return Object.extend({ }, object);
  },

  isElement: function(object) {
    return !!(object && object.nodeType == 1);
  },

  isArray: function(object) {
    return object != null && typeof object == "object" &&
      'splice' in object && 'join' in object;
  },

  isHash: function(object) {
    return object instanceof Hash;
  },

  isFunction: function(object) {
    return typeof object == "function";
  },

  isString: function(object) {
    return typeof object == "string";
  },

  isNumber: function(object) {
    return typeof object == "number";
  },

  isUndefined: function(object) {
    return typeof object == "undefined";
  }
});

Object.extend(Function.prototype, {
  argumentNames: function() {
    var names = this.toString().match(/^[\s\(]*function[^(]*\(([^\)]*)\)/)[1]
      .replace(/\s+/g, '').split(',');
    return names.length == 1 && !names[0] ? [] : names;
  },

  bind: function() {
    if (arguments.length < 2 && Object.isUndefined(arguments[0])) return this;
    var __method = this, args = $A(arguments), object = args.shift();
    return function() {
      return __method.apply(object, args.concat($A(arguments)));
    }
  },

  bindAsEventListener: function() {
    var __method = this, args = $A(arguments), object = args.shift();
    return function(event) {
      return __method.apply(object, [event || window.event].concat(args));
    }
  },

  curry: function() {
    if (!arguments.length) return this;
    var __method = this, args = $A(arguments);
    return function() {
      return __method.apply(this, args.concat($A(arguments)));
    }
  },

  delay: function() {
    var __method = this, args = $A(arguments), timeout = args.shift() * 1000;
    return window.setTimeout(function() {
      return __method.apply(__method, args);
    }, timeout);
  },

  defer: function() {
    var args = [0.01].concat($A(arguments));
    return this.delay.apply(this, args);
  },

  wrap: function(wrapper) {
    var __method = this;
    return function() {
      return wrapper.apply(this, [__method.bind(this)].concat($A(arguments)));
    }
  },

  methodize: function() {
    if (this._methodized) return this._methodized;
    var __method = this;
    return this._methodized = function() {
      return __method.apply(null, [this].concat($A(arguments)));
    };
  }
});

Date.prototype.toJSON = function() {
  return '"' + this.getUTCFullYear() + '-' +
    (this.getUTCMonth() + 1).toPaddedString(2) + '-' +
    this.getUTCDate().toPaddedString(2) + 'T' +
    this.getUTCHours().toPaddedString(2) + ':' +
    this.getUTCMinutes().toPaddedString(2) + ':' +
    this.getUTCSeconds().toPaddedString(2) + 'Z"';
};

var Try = {
  these: function() {
    var returnValue;

    for (var i = 0, length = arguments.length; i < length; i++) {
      var lambda = arguments[i];
      try {
        returnValue = lambda();
        break;
      } catch (e) { }
    }

    return returnValue;
  }
};

RegExp.prototype.match = RegExp.prototype.test;

RegExp.escape = function(str) {
  return String(str).replace(/([.*+?^=!:${}()|[\]\/\\])/g, '\\$1');
};

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

var PeriodicalExecuter = Class.create({
  initialize: function(callback, frequency) {
    this.callback = callback;
    this.frequency = frequency;
    this.currentlyExecuting = false;

    this.registerCallback();
  },

  registerCallback: function() {
    this.timer = setInterval(this.onTimerEvent.bind(this), this.frequency * 1000);
  },

  execute: function() {
    this.callback(this);
  },

  stop: function() {
    if (!this.timer) return;
    clearInterval(this.timer);
    this.timer = null;
  },

  onTimerEvent: function() {
    if (!this.currentlyExecuting) {
      try {
        this.currentlyExecuting = true;
        this.execute();
      } finally {
        this.currentlyExecuting = false;
      }
    }
  }
});
Object.extend(String, {
  interpret: function(value) {
    return value == null ? '' : String(value);
  },
  specialChar: {
    '\b': '\\b',
    '\t': '\\t',
    '\n': '\\n',
    '\f': '\\f',
    '\r': '\\r',
    '\\': '\\\\'
  }
});

Object.extend(String.prototype, {
  gsub: function(pattern, replacement) {
    var result = '', source = this, match;
    replacement = arguments.callee.prepareReplacement(replacement);

    while (source.length > 0) {
      if (match = source.match(pattern)) {
        result += source.slice(0, match.index);
        result += String.interpret(replacement(match));
        source  = source.slice(match.index + match[0].length);
      } else {
        result += source, source = '';
      }
    }
    return result;
  },

  sub: function(pattern, replacement, count) {
    replacement = this.gsub.prepareReplacement(replacement);
    count = Object.isUndefined(count) ? 1 : count;

    return this.gsub(pattern, function(match) {
      if (--count < 0) return match[0];
      return replacement(match);
    });
  },

  scan: function(pattern, iterator) {
    this.gsub(pattern, iterator);
    return String(this);
  },

  truncate: function(length, truncation) {
    length = length || 30;
    truncation = Object.isUndefined(truncation) ? '...' : truncation;
    return this.length > length ?
      this.slice(0, length - truncation.length) + truncation : String(this);
  },

  strip: function() {
    return this.replace(/^\s+/, '').replace(/\s+$/, '');
  },

  stripTags: function() {
    return this.replace(/<\/?[^>]+>/gi, '');
  },

  stripScripts: function() {
    return this.replace(new RegExp(Prototype.ScriptFragment, 'img'), '');
  },

  extractScripts: function() {
    var matchAll = new RegExp(Prototype.ScriptFragment, 'img');
    var matchOne = new RegExp(Prototype.ScriptFragment, 'im');
    return (this.match(matchAll) || []).map(function(scriptTag) {
      return (scriptTag.match(matchOne) || ['', ''])[1];
    });
  },

  evalScripts: function() {
    return this.extractScripts().map(function(script) { return eval(script) });
  },

  escapeHTML: function() {
    var self = arguments.callee;
    self.text.data = this;
    return self.div.innerHTML;
  },

  unescapeHTML: function() {
    var div = new Element('div');
    div.innerHTML = this.stripTags();
    return div.childNodes[0] ? (div.childNodes.length > 1 ?
      $A(div.childNodes).inject('', function(memo, node) { return memo+node.nodeValue }) :
      div.childNodes[0].nodeValue) : '';
  },

  toQueryParams: function(separator) {
    var match = this.strip().match(/([^?#]*)(#.*)?$/);
    if (!match) return { };

    return match[1].split(separator || '&').inject({ }, function(hash, pair) {
      if ((pair = pair.split('='))[0]) {
        var key = decodeURIComponent(pair.shift());
        var value = pair.length > 1 ? pair.join('=') : pair[0];
        if (value != undefined) value = decodeURIComponent(value);

        if (key in hash) {
          if (!Object.isArray(hash[key])) hash[key] = [hash[key]];
          hash[key].push(value);
        }
        else hash[key] = value;
      }
      return hash;
    });
  },

  toArray: function() {
    return this.split('');
  },

  succ: function() {
    return this.slice(0, this.length - 1) +
      String.fromCharCode(this.charCodeAt(this.length - 1) + 1);
  },

  times: function(count) {
    return count < 1 ? '' : new Array(count + 1).join(this);
  },

  camelize: function() {
    var parts = this.split('-'), len = parts.length;
    if (len == 1) return parts[0];

    var camelized = this.charAt(0) == '-'
      ? parts[0].charAt(0).toUpperCase() + parts[0].substring(1)
      : parts[0];

    for (var i = 1; i < len; i++)
      camelized += parts[i].charAt(0).toUpperCase() + parts[i].substring(1);

    return camelized;
  },

  capitalize: function() {
    return this.charAt(0).toUpperCase() + this.substring(1).toLowerCase();
  },

  underscore: function() {
    return this.gsub(/::/, '/').gsub(/([A-Z]+)([A-Z][a-z])/,'#{1}_#{2}').gsub(/([a-z\d])([A-Z])/,'#{1}_#{2}').gsub(/-/,'_').toLowerCase();
  },

  dasherize: function() {
    return this.gsub(/_/,'-');
  },

  inspect: function(useDoubleQuotes) {
    var escapedString = this.gsub(/[\x00-\x1f\\]/, function(match) {
      var character = String.specialChar[match[0]];
      return character ? character : '\\u00' + match[0].charCodeAt().toPaddedString(2, 16);
    });
    if (useDoubleQuotes) return '"' + escapedString.replace(/"/g, '\\"') + '"';
    return "'" + escapedString.replace(/'/g, '\\\'') + "'";
  },

  toJSON: function() {
    return this.inspect(true);
  },

  unfilterJSON: function(filter) {
    return this.sub(filter || Prototype.JSONFilter, '#{1}');
  },

  isJSON: function() {
    var str = this;
    if (str.blank()) return false;
    str = this.replace(/\\./g, '@').replace(/"[^"\\\n\r]*"/g, '');
    return (/^[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t]*$/).test(str);
  },

  evalJSON: function(sanitize) {
    var json = this.unfilterJSON();
    try {
      if (!sanitize || json.isJSON()) return eval('(' + json + ')');
    } catch (e) { }
    throw new SyntaxError('Badly formed JSON string: ' + this.inspect());
  },

  include: function(pattern) {
    return this.indexOf(pattern) > -1;
  },

  startsWith: function(pattern) {
    return this.indexOf(pattern) === 0;
  },

  endsWith: function(pattern) {
    var d = this.length - pattern.length;
    return d >= 0 && this.lastIndexOf(pattern) === d;
  },

  empty: function() {
    return this == '';
  },

  blank: function() {
    return /^\s*$/.test(this);
  },

  interpolate: function(object, pattern) {
    return new Template(this, pattern).evaluate(object);
  }
});

if (Prototype.Browser.WebKit || Prototype.Browser.IE) Object.extend(String.prototype, {
  escapeHTML: function() {
    return this.replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;');
  },
  unescapeHTML: function() {
    return this.stripTags().replace(/&amp;/g,'&').replace(/&lt;/g,'<').replace(/&gt;/g,'>');
  }
});

String.prototype.gsub.prepareReplacement = function(replacement) {
  if (Object.isFunction(replacement)) return replacement;
  var template = new Template(replacement);
  return function(match) { return template.evaluate(match) };
};

String.prototype.parseQuery = String.prototype.toQueryParams;

Object.extend(String.prototype.escapeHTML, {
  div:  document.createElement('div'),
  text: document.createTextNode('')
});

String.prototype.escapeHTML.div.appendChild(String.prototype.escapeHTML.text);

var Template = Class.create({
  initialize: function(template, pattern) {
    this.template = template.toString();
    this.pattern = pattern || Template.Pattern;
  },

  evaluate: function(object) {
    if (Object.isFunction(object.toTemplateReplacements))
      object = object.toTemplateReplacements();

    return this.template.gsub(this.pattern, function(match) {
      if (object == null) return '';

      var before = match[1] || '';
      if (before == '\\') return match[2];

      var ctx = object, expr = match[3];
      var pattern = /^([^.[]+|\[((?:.*?[^\\])?)\])(\.|\[|$)/;
      match = pattern.exec(expr);
      if (match == null) return before;

      while (match != null) {
        var comp = match[1].startsWith('[') ? match[2].gsub('\\\\]', ']') : match[1];
        ctx = ctx[comp];
        if (null == ctx || '' == match[3]) break;
        expr = expr.substring('[' == match[3] ? match[1].length : match[0].length);
        match = pattern.exec(expr);
      }

      return before + String.interpret(ctx);
    });
  }
});
Template.Pattern = /(^|.|\r|\n)(#\{(.*?)\})/;

var $break = { };

var Enumerable = {
  each: function(iterator, context) {
    var index = 0;
    try {
      this._each(function(value) {
        iterator.call(context, value, index++);
      });
    } catch (e) {
      if (e != $break) throw e;
    }
    return this;
  },

  eachSlice: function(number, iterator, context) {
    var index = -number, slices = [], array = this.toArray();
    if (number < 1) return array;
    while ((index += number) < array.length)
      slices.push(array.slice(index, index+number));
    return slices.collect(iterator, context);
  },

  all: function(iterator, context) {
    iterator = iterator || Prototype.K;
    var result = true;
    this.each(function(value, index) {
      result = result && !!iterator.call(context, value, index);
      if (!result) throw $break;
    });
    return result;
  },

  any: function(iterator, context) {
    iterator = iterator || Prototype.K;
    var result = false;
    this.each(function(value, index) {
      if (result = !!iterator.call(context, value, index))
        throw $break;
    });
    return result;
  },

  collect: function(iterator, context) {
    iterator = iterator || Prototype.K;
    var results = [];
    this.each(function(value, index) {
      results.push(iterator.call(context, value, index));
    });
    return results;
  },

  detect: function(iterator, context) {
    var result;
    this.each(function(value, index) {
      if (iterator.call(context, value, index)) {
        result = value;
        throw $break;
      }
    });
    return result;
  },

  findAll: function(iterator, context) {
    var results = [];
    this.each(function(value, index) {
      if (iterator.call(context, value, index))
        results.push(value);
    });
    return results;
  },

  grep: function(filter, iterator, context) {
    iterator = iterator || Prototype.K;
    var results = [];

    if (Object.isString(filter))
      filter = new RegExp(filter);

    this.each(function(value, index) {
      if (filter.match(value))
        results.push(iterator.call(context, value, index));
    });
    return results;
  },

  include: function(object) {
    if (Object.isFunction(this.indexOf))
      if (this.indexOf(object) != -1) return true;

    var found = false;
    this.each(function(value) {
      if (value == object) {
        found = true;
        throw $break;
      }
    });
    return found;
  },

  inGroupsOf: function(number, fillWith) {
    fillWith = Object.isUndefined(fillWith) ? null : fillWith;
    return this.eachSlice(number, function(slice) {
      while(slice.length < number) slice.push(fillWith);
      return slice;
    });
  },

  inject: function(memo, iterator, context) {
    this.each(function(value, index) {
      memo = iterator.call(context, memo, value, index);
    });
    return memo;
  },

  invoke: function(method) {
    var args = $A(arguments).slice(1);
    return this.map(function(value) {
      return value[method].apply(value, args);
    });
  },

  max: function(iterator, context) {
    iterator = iterator || Prototype.K;
    var result;
    this.each(function(value, index) {
      value = iterator.call(context, value, index);
      if (result == null || value >= result)
        result = value;
    });
    return result;
  },

  min: function(iterator, context) {
    iterator = iterator || Prototype.K;
    var result;
    this.each(function(value, index) {
      value = iterator.call(context, value, index);
      if (result == null || value < result)
        result = value;
    });
    return result;
  },

  partition: function(iterator, context) {
    iterator = iterator || Prototype.K;
    var trues = [], falses = [];
    this.each(function(value, index) {
      (iterator.call(context, value, index) ?
        trues : falses).push(value);
    });
    return [trues, falses];
  },

  pluck: function(property) {
    var results = [];
    this.each(function(value) {
      results.push(value[property]);
    });
    return results;
  },

  reject: function(iterator, context) {
    var results = [];
    this.each(function(value, index) {
      if (!iterator.call(context, value, index))
        results.push(value);
    });
    return results;
  },

  sortBy: function(iterator, context) {
    return this.map(function(value, index) {
      return {
        value: value,
        criteria: iterator.call(context, value, index)
      };
    }).sort(function(left, right) {
      var a = left.criteria, b = right.criteria;
      return a < b ? -1 : a > b ? 1 : 0;
    }).pluck('value');
  },

  toArray: function() {
    return this.map();
  },

  zip: function() {
    var iterator = Prototype.K, args = $A(arguments);
    if (Object.isFunction(args.last()))
      iterator = args.pop();

    var collections = [this].concat(args).map($A);
    return this.map(function(value, index) {
      return iterator(collections.pluck(index));
    });
  },

  size: function() {
    return this.toArray().length;
  },

  inspect: function() {
    return '#<Enumerable:' + this.toArray().inspect() + '>';
  }
};

Object.extend(Enumerable, {
  map:     Enumerable.collect,
  find:    Enumerable.detect,
  select:  Enumerable.findAll,
  filter:  Enumerable.findAll,
  member:  Enumerable.include,
  entries: Enumerable.toArray,
  every:   Enumerable.all,
  some:    Enumerable.any
});
function $A(iterable) {
  if (!iterable) return [];
  if (iterable.toArray) return iterable.toArray();
  var length = iterable.length || 0, results = new Array(length);
  while (length--) results[length] = iterable[length];
  return results;
}

if (Prototype.Browser.WebKit) {
  $A = function(iterable) {
    if (!iterable) return [];



    if (!(typeof iterable === 'function' && typeof iterable.length ===
        'number' && typeof iterable.item === 'function') && iterable.toArray)
      return iterable.toArray();
    var length = iterable.length || 0, results = new Array(length);
    while (length--) results[length] = iterable[length];
    return results;
  };
}

Array.from = $A;

Object.extend(Array.prototype, Enumerable);

if (!Array.prototype._reverse) Array.prototype._reverse = Array.prototype.reverse;

Object.extend(Array.prototype, {
  _each: function(iterator) {
    for (var i = 0, length = this.length; i < length; i++)
      iterator(this[i]);
  },

  clear: function() {
    this.length = 0;
    return this;
  },

  first: function() {
    return this[0];
  },

  last: function() {
    return this[this.length - 1];
  },

  compact: function() {
    return this.select(function(value) {
      return value != null;
    });
  },

  flatten: function() {
    return this.inject([], function(array, value) {
      return array.concat(Object.isArray(value) ?
        value.flatten() : [value]);
    });
  },

  without: function() {
    var values = $A(arguments);
    return this.select(function(value) {
      return !values.include(value);
    });
  },

  reverse: function(inline) {
    return (inline !== false ? this : this.toArray())._reverse();
  },

  reduce: function() {
    return this.length > 1 ? this : this[0];
  },

  uniq: function(sorted) {
    return this.inject([], function(array, value, index) {
      if (0 == index || (sorted ? array.last() != value : !array.include(value)))
        array.push(value);
      return array;
    });
  },

  intersect: function(array) {
    return this.uniq().findAll(function(item) {
      return array.detect(function(value) { return item === value });
    });
  },

  clone: function() {
    return [].concat(this);
  },

  size: function() {
    return this.length;
  },

  inspect: function() {
    return '[' + this.map(Object.inspect).join(', ') + ']';
  },

  toJSON: function() {
    var results = [];
    this.each(function(object) {
      var value = Object.toJSON(object);
      if (!Object.isUndefined(value)) results.push(value);
    });
    return '[' + results.join(', ') + ']';
  }
});


if (Object.isFunction(Array.prototype.forEach))
  Array.prototype._each = Array.prototype.forEach;

if (!Array.prototype.indexOf) Array.prototype.indexOf = function(item, i) {
  i || (i = 0);
  var length = this.length;
  if (i < 0) i = length + i;
  for (; i < length; i++)
    if (this[i] === item) return i;
  return -1;
};

if (!Array.prototype.lastIndexOf) Array.prototype.lastIndexOf = function(item, i) {
  i = isNaN(i) ? this.length : (i < 0 ? this.length + i : i) + 1;
  var n = this.slice(0, i).reverse().indexOf(item);
  return (n < 0) ? n : i - n - 1;
};

Array.prototype.toArray = Array.prototype.clone;

function $w(string) {
  if (!Object.isString(string)) return [];
  string = string.strip();
  return string ? string.split(/\s+/) : [];
}

if (Prototype.Browser.Opera){
  Array.prototype.concat = function() {
    var array = [];
    for (var i = 0, length = this.length; i < length; i++) array.push(this[i]);
    for (var i = 0, length = arguments.length; i < length; i++) {
      if (Object.isArray(arguments[i])) {
        for (var j = 0, arrayLength = arguments[i].length; j < arrayLength; j++)
          array.push(arguments[i][j]);
      } else {
        array.push(arguments[i]);
      }
    }
    return array;
  };
}
Object.extend(Number.prototype, {
  toColorPart: function() {
    return this.toPaddedString(2, 16);
  },

  succ: function() {
    return this + 1;
  },

  times: function(iterator, context) {
    $R(0, this, true).each(iterator, context);
    return this;
  },

  toPaddedString: function(length, radix) {
    var string = this.toString(radix || 10);
    return '0'.times(length - string.length) + string;
  },

  toJSON: function() {
    return isFinite(this) ? this.toString() : 'null';
  }
});

$w('abs round ceil floor').each(function(method){
  Number.prototype[method] = Math[method].methodize();
});
function $H(object) {
  return new Hash(object);
};

var Hash = Class.create(Enumerable, (function() {

  function toQueryPair(key, value) {
    if (Object.isUndefined(value)) return key;
    return key + '=' + encodeURIComponent(String.interpret(value));
  }

  return {
    initialize: function(object) {
      this._object = Object.isHash(object) ? object.toObject() : Object.clone(object);
    },

    _each: function(iterator) {
      for (var key in this._object) {
        var value = this._object[key], pair = [key, value];
        pair.key = key;
        pair.value = value;
        iterator(pair);
      }
    },

    set: function(key, value) {
      return this._object[key] = value;
    },

    get: function(key) {

      if (this._object[key] !== Object.prototype[key])
        return this._object[key];
    },

    unset: function(key) {
      var value = this._object[key];
      delete this._object[key];
      return value;
    },

    toObject: function() {
      return Object.clone(this._object);
    },

    keys: function() {
      return this.pluck('key');
    },

    values: function() {
      return this.pluck('value');
    },

    index: function(value) {
      var match = this.detect(function(pair) {
        return pair.value === value;
      });
      return match && match.key;
    },

    merge: function(object) {
      return this.clone().update(object);
    },

    update: function(object) {
      return new Hash(object).inject(this, function(result, pair) {
        result.set(pair.key, pair.value);
        return result;
      });
    },

    toQueryString: function() {
      return this.inject([], function(results, pair) {
        var key = encodeURIComponent(pair.key), values = pair.value;

        if (values && typeof values == 'object') {
          if (Object.isArray(values))
            return results.concat(values.map(toQueryPair.curry(key)));
        } else results.push(toQueryPair(key, values));
        return results;
      }).join('&');
    },

    inspect: function() {
      return '#<Hash:{' + this.map(function(pair) {
        return pair.map(Object.inspect).join(': ');
      }).join(', ') + '}>';
    },

    toJSON: function() {
      return Object.toJSON(this.toObject());
    },

    clone: function() {
      return new Hash(this);
    }
  }
})());

Hash.prototype.toTemplateReplacements = Hash.prototype.toObject;
Hash.from = $H;
var ObjectRange = Class.create(Enumerable, {
  initialize: function(start, end, exclusive) {
    this.start = start;
    this.end = end;
    this.exclusive = exclusive;
  },

  _each: function(iterator) {
    var value = this.start;
    while (this.include(value)) {
      iterator(value);
      value = value.succ();
    }
  },

  include: function(value) {
    if (value < this.start)
      return false;
    if (this.exclusive)
      return value < this.end;
    return value <= this.end;
  }
});

var $R = function(start, end, exclusive) {
  return new ObjectRange(start, end, exclusive);
};

var Ajax = {
  getTransport: function() {
    return Try.these(
      function() {return new XMLHttpRequest()},
      function() {return new ActiveXObject('Msxml2.XMLHTTP')},
      function() {return new ActiveXObject('Microsoft.XMLHTTP')}
    ) || false;
  },

  activeRequestCount: 0
};

Ajax.Responders = {
  responders: [],

  _each: function(iterator) {
    this.responders._each(iterator);
  },

  register: function(responder) {
    if (!this.include(responder))
      this.responders.push(responder);
  },

  unregister: function(responder) {
    this.responders = this.responders.without(responder);
  },

  dispatch: function(callback, request, transport, json) {
    this.each(function(responder) {
      if (Object.isFunction(responder[callback])) {
        try {
          responder[callback].apply(responder, [request, transport, json]);
        } catch (e) { }
      }
    });
  }
};

Object.extend(Ajax.Responders, Enumerable);

Ajax.Responders.register({
  onCreate:   function() { Ajax.activeRequestCount++ },
  onComplete: function() { Ajax.activeRequestCount-- }
});

Ajax.Base = Class.create({
  initialize: function(options) {
    this.options = {
      method:       'post',
      asynchronous: true,
      contentType:  'application/x-www-form-urlencoded',
      encoding:     'UTF-8',
      parameters:   '',
      evalJSON:     true,
      evalJS:       true
    };
    Object.extend(this.options, options || { });

    this.options.method = this.options.method.toLowerCase();

    if (Object.isString(this.options.parameters))
      this.options.parameters = this.options.parameters.toQueryParams();
    else if (Object.isHash(this.options.parameters))
      this.options.parameters = this.options.parameters.toObject();
  }
});

Ajax.Request = Class.create(Ajax.Base, {
  _complete: false,

  initialize: function($super, url, options) {
    $super(options);
    this.transport = Ajax.getTransport();
    this.request(url);
  },

  request: function(url) {
    this.url = url;
    this.method = this.options.method;
    var params = Object.clone(this.options.parameters);

    if (!['get', 'post'].include(this.method)) {

      params['_method'] = this.method;
      this.method = 'post';
    }

    this.parameters = params;

    if (params = Object.toQueryString(params)) {

      if (this.method == 'get')
        this.url += (this.url.include('?') ? '&' : '?') + params;
      else if (/Konqueror|Safari|KHTML/.test(navigator.userAgent))
        params += '&_=';
    }

    try {
      var response = new Ajax.Response(this);
      if (this.options.onCreate) this.options.onCreate(response);
      Ajax.Responders.dispatch('onCreate', this, response);

      this.transport.open(this.method.toUpperCase(), this.url,
        this.options.asynchronous);

      if (this.options.asynchronous) this.respondToReadyState.bind(this).defer(1);

      this.transport.onreadystatechange = this.onStateChange.bind(this);
      this.setRequestHeaders();

      this.body = this.method == 'post' ? (this.options.postBody || params) : null;
      this.transport.send(this.body);

      /* Force Firefox to handle ready state 4 for synchronous requests */
      if (!this.options.asynchronous && this.transport.overrideMimeType)
        this.onStateChange();

    }
    catch (e) {
      this.dispatchException(e);
    }
  },

  onStateChange: function() {
    var readyState = this.transport.readyState;
    if (readyState > 1 && !((readyState == 4) && this._complete))
      this.respondToReadyState(this.transport.readyState);
  },

  setRequestHeaders: function() {
    var headers = {
      'X-Requested-With': 'XMLHttpRequest',
      'X-Prototype-Version': Prototype.Version,
      'Accept': 'text/javascript, text/html, application/xml, text/xml, */*'
    };

    if (this.method == 'post') {
      headers['Content-type'] = this.options.contentType +
        (this.options.encoding ? '; charset=' + this.options.encoding : '');

      /* Force "Connection: close" for older Mozilla browsers to work
       * around a bug where XMLHttpRequest sends an incorrect
       * Content-length header. See Mozilla Bugzilla #246651.
       */
      if (this.transport.overrideMimeType &&
          (navigator.userAgent.match(/Gecko\/(\d{4})/) || [0,2005])[1] < 2005)
            headers['Connection'] = 'close';
    }


    if (typeof this.options.requestHeaders == 'object') {
      var extras = this.options.requestHeaders;

      if (Object.isFunction(extras.push))
        for (var i = 0, length = extras.length; i < length; i += 2)
          headers[extras[i]] = extras[i+1];
      else
        $H(extras).each(function(pair) { headers[pair.key] = pair.value });
    }

    for (var name in headers)
      this.transport.setRequestHeader(name, headers[name]);
  },

  success: function() {
    var status = this.getStatus();
    return !status || (status >= 200 && status < 300);
  },

  getStatus: function() {
    try {
      return this.transport.status || 0;
    } catch (e) { return 0 }
  },

  respondToReadyState: function(readyState) {
    var state = Ajax.Request.Events[readyState], response = new Ajax.Response(this);

    if (state == 'Complete') {
      try {
        this._complete = true;
        (this.options['on' + response.status]
         || this.options['on' + (this.success() ? 'Success' : 'Failure')]
         || Prototype.emptyFunction)(response, response.headerJSON);
      } catch (e) {
        this.dispatchException(e);
      }

      var contentType = response.getHeader('Content-type');
      if (this.options.evalJS == 'force'
          || (this.options.evalJS && this.isSameOrigin() && contentType
          && contentType.match(/^\s*(text|application)\/(x-)?(java|ecma)script(;.*)?\s*$/i)))
        this.evalResponse();
    }

    try {
      (this.options['on' + state] || Prototype.emptyFunction)(response, response.headerJSON);
      Ajax.Responders.dispatch('on' + state, this, response, response.headerJSON);
    } catch (e) {
      this.dispatchException(e);
    }

    if (state == 'Complete') {

      this.transport.onreadystatechange = Prototype.emptyFunction;
    }
  },

  isSameOrigin: function() {
    var m = this.url.match(/^\s*https?:\/\/[^\/]*/);
    return !m || (m[0] == '#{protocol}//#{domain}#{port}'.interpolate({
      protocol: location.protocol,
      domain: document.domain,
      port: location.port ? ':' + location.port : ''
    }));
  },

  getHeader: function(name) {
    try {
      return this.transport.getResponseHeader(name) || null;
    } catch (e) { return null }
  },

  evalResponse: function() {
    try {
      return eval((this.transport.responseText || '').unfilterJSON());
    } catch (e) {
      this.dispatchException(e);
    }
  },

  dispatchException: function(exception) {
    (this.options.onException || Prototype.emptyFunction)(this, exception);
    Ajax.Responders.dispatch('onException', this, exception);
  }
});

Ajax.Request.Events =
  ['Uninitialized', 'Loading', 'Loaded', 'Interactive', 'Complete'];

Ajax.Response = Class.create({
  initialize: function(request){
    this.request = request;
    var transport  = this.transport  = request.transport,
        readyState = this.readyState = transport.readyState;

    if((readyState > 2 && !Prototype.Browser.IE) || readyState == 4) {
      this.status       = this.getStatus();
      this.statusText   = this.getStatusText();
      this.responseText = String.interpret(transport.responseText);
      this.headerJSON   = this._getHeaderJSON();
    }

    if(readyState == 4) {
      var xml = transport.responseXML;
      this.responseXML  = Object.isUndefined(xml) ? null : xml;
      this.responseJSON = this._getResponseJSON();
    }
  },

  status:      0,
  statusText: '',

  getStatus: Ajax.Request.prototype.getStatus,

  getStatusText: function() {
    try {
      return this.transport.statusText || '';
    } catch (e) { return '' }
  },

  getHeader: Ajax.Request.prototype.getHeader,

  getAllHeaders: function() {
    try {
      return this.getAllResponseHeaders();
    } catch (e) { return null }
  },

  getResponseHeader: function(name) {
    return this.transport.getResponseHeader(name);
  },

  getAllResponseHeaders: function() {
    return this.transport.getAllResponseHeaders();
  },

  _getHeaderJSON: function() {
    var json = this.getHeader('X-JSON');
    if (!json) return null;
    json = decodeURIComponent(escape(json));
    try {
      return json.evalJSON(this.request.options.sanitizeJSON ||
        !this.request.isSameOrigin());
    } catch (e) {
      this.request.dispatchException(e);
    }
  },

  _getResponseJSON: function() {
    var options = this.request.options;
    if (!options.evalJSON || (options.evalJSON != 'force' &&
      !(this.getHeader('Content-type') || '').include('application/json')) ||
        this.responseText.blank())
          return null;
    try {
      return this.responseText.evalJSON(options.sanitizeJSON ||
        !this.request.isSameOrigin());
    } catch (e) {
      this.request.dispatchException(e);
    }
  }
});

Ajax.Updater = Class.create(Ajax.Request, {
  initialize: function($super, container, url, options) {
    this.container = {
      success: (container.success || container),
      failure: (container.failure || (container.success ? null : container))
    };

    options = Object.clone(options);
    var onComplete = options.onComplete;
    options.onComplete = (function(response, json) {
      this.updateContent(response.responseText);
      if (Object.isFunction(onComplete)) onComplete(response, json);
    }).bind(this);

    $super(url, options);
  },

  updateContent: function(responseText) {
    var receiver = this.container[this.success() ? 'success' : 'failure'],
        options = this.options;

    if (!options.evalScripts) responseText = responseText.stripScripts();

    if (receiver = $(receiver)) {
      if (options.insertion) {
        if (Object.isString(options.insertion)) {
          var insertion = { }; insertion[options.insertion] = responseText;
          receiver.insert(insertion);
        }
        else options.insertion(receiver, responseText);
      }
      else receiver.update(responseText);
    }
  }
});

Ajax.PeriodicalUpdater = Class.create(Ajax.Base, {
  initialize: function($super, container, url, options) {
    $super(options);
    this.onComplete = this.options.onComplete;

    this.frequency = (this.options.frequency || 2);
    this.decay = (this.options.decay || 1);

    this.updater = { };
    this.container = container;
    this.url = url;

    this.start();
  },

  start: function() {
    this.options.onComplete = this.updateComplete.bind(this);
    this.onTimerEvent();
  },

  stop: function() {
    this.updater.options.onComplete = undefined;
    clearTimeout(this.timer);
    (this.onComplete || Prototype.emptyFunction).apply(this, arguments);
  },

  updateComplete: function(response) {
    if (this.options.decay) {
      this.decay = (response.responseText == this.lastText ?
        this.decay * this.options.decay : 1);

      this.lastText = response.responseText;
    }
    this.timer = this.onTimerEvent.bind(this).delay(this.decay * this.frequency);
  },

  onTimerEvent: function() {
    this.updater = new Ajax.Updater(this.container, this.url, this.options);
  }
});
function $(element) {
  if (arguments.length > 1) {
    for (var i = 0, elements = [], length = arguments.length; i < length; i++)
      elements.push($(arguments[i]));
    return elements;
  }
  if (Object.isString(element))
    element = document.getElementById(element);
  return Element.extend(element);
}

if (Prototype.BrowserFeatures.XPath) {
  document._getElementsByXPath = function(expression, parentElement) {
    var results = [];
    var query = document.evaluate(expression, $(parentElement) || document,
      null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
    for (var i = 0, length = query.snapshotLength; i < length; i++)
      results.push(Element.extend(query.snapshotItem(i)));
    return results;
  };
}

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

if (!window.Node) var Node = { };

if (!Node.ELEMENT_NODE) {

  Object.extend(Node, {
    ELEMENT_NODE: 1,
    ATTRIBUTE_NODE: 2,
    TEXT_NODE: 3,
    CDATA_SECTION_NODE: 4,
    ENTITY_REFERENCE_NODE: 5,
    ENTITY_NODE: 6,
    PROCESSING_INSTRUCTION_NODE: 7,
    COMMENT_NODE: 8,
    DOCUMENT_NODE: 9,
    DOCUMENT_TYPE_NODE: 10,
    DOCUMENT_FRAGMENT_NODE: 11,
    NOTATION_NODE: 12
  });
}

(function() {
  var element = this.Element;
  this.Element = function(tagName, attributes) {
    attributes = attributes || { };
    tagName = tagName.toLowerCase();
    var cache = Element.cache;
    if (Prototype.Browser.IE && attributes.name) {
      tagName = '<' + tagName + ' name="' + attributes.name + '">';
      delete attributes.name;
      return Element.writeAttribute(document.createElement(tagName), attributes);
    }
    if (!cache[tagName]) cache[tagName] = Element.extend(document.createElement(tagName));
    return Element.writeAttribute(cache[tagName].cloneNode(false), attributes);
  };
  Object.extend(this.Element, element || { });
  if (element) this.Element.prototype = element.prototype;
}).call(window);

Element.cache = { };

Element.Methods = {
  visible: function(element) {
    return $(element).style.display != 'none';
  },

  toggle: function(element) {
    element = $(element);
    Element[Element.visible(element) ? 'hide' : 'show'](element);
    return element;
  },

  hide: function(element) {
    element = $(element);
    element.style.display = 'none';
    return element;
  },

  show: function(element) {
    element = $(element);
    element.style.display = '';
    return element;
  },

  remove: function(element) {
    element = $(element);
    element.parentNode.removeChild(element);
    return element;
  },

  update: function(element, content) {
    element = $(element);
    if (content && content.toElement) content = content.toElement();
    if (Object.isElement(content)) return element.update().insert(content);
    content = Object.toHTML(content);
    element.innerHTML = content.stripScripts();
    content.evalScripts.bind(content).defer();
    return element;
  },

  replace: function(element, content) {
    element = $(element);
    if (content && content.toElement) content = content.toElement();
    else if (!Object.isElement(content)) {
      content = Object.toHTML(content);
      var range = element.ownerDocument.createRange();
      range.selectNode(element);
      content.evalScripts.bind(content).defer();
      content = range.createContextualFragment(content.stripScripts());
    }
    element.parentNode.replaceChild(content, element);
    return element;
  },

  insert: function(element, insertions) {
    element = $(element);

    if (Object.isString(insertions) || Object.isNumber(insertions) ||
        Object.isElement(insertions) || (insertions && (insertions.toElement || insertions.toHTML)))
          insertions = {bottom:insertions};

    var content, insert, tagName, childNodes;

    for (var position in insertions) {
      content  = insertions[position];
      position = position.toLowerCase();
      insert = Element._insertionTranslations[position];

      if (content && content.toElement) content = content.toElement();
      if (Object.isElement(content)) {
        insert(element, content);
        continue;
      }

      content = Object.toHTML(content);

      tagName = ((position == 'before' || position == 'after')
        ? element.parentNode : element).tagName.toUpperCase();

      childNodes = Element._getContentFromAnonymousElement(tagName, content.stripScripts());

      if (position == 'top' || position == 'after') childNodes.reverse();
      childNodes.each(insert.curry(element));

      content.evalScripts.bind(content).defer();
    }

    return element;
  },

  wrap: function(element, wrapper, attributes) {
    element = $(element);
    if (Object.isElement(wrapper))
      $(wrapper).writeAttribute(attributes || { });
    else if (Object.isString(wrapper)) wrapper = new Element(wrapper, attributes);
    else wrapper = new Element('div', wrapper);
    if (element.parentNode)
      element.parentNode.replaceChild(wrapper, element);
    wrapper.appendChild(element);
    return wrapper;
  },

  inspect: function(element) {
    element = $(element);
    var result = '<' + element.tagName.toLowerCase();
    $H({'id': 'id', 'className': 'class'}).each(function(pair) {
      var property = pair.first(), attribute = pair.last();
      var value = (element[property] || '').toString();
      if (value) result += ' ' + attribute + '=' + value.inspect(true);
    });
    return result + '>';
  },

  recursivelyCollect: function(element, property) {
    element = $(element);
    var elements = [];
    while (element = element[property])
      if (element.nodeType == 1)
        elements.push(Element.extend(element));
    return elements;
  },

  ancestors: function(element) {
    return $(element).recursivelyCollect('parentNode');
  },

  descendants: function(element) {
    return $(element).select("*");
  },

  firstDescendant: function(element) {
    element = $(element).firstChild;
    while (element && element.nodeType != 1) element = element.nextSibling;
    return $(element);
  },

  immediateDescendants: function(element) {
    if (!(element = $(element).firstChild)) return [];
    while (element && element.nodeType != 1) element = element.nextSibling;
    if (element) return [element].concat($(element).nextSiblings());
    return [];
  },

  previousSiblings: function(element) {
    return $(element).recursivelyCollect('previousSibling');
  },

  nextSiblings: function(element) {
    return $(element).recursivelyCollect('nextSibling');
  },

  siblings: function(element) {
    element = $(element);
    return element.previousSiblings().reverse().concat(element.nextSiblings());
  },

  match: function(element, selector) {
    if (Object.isString(selector))
      selector = new Selector(selector);
    return selector.match($(element));
  },

  up: function(element, expression, index) {
    element = $(element);
    if (arguments.length == 1) return $(element.parentNode);
    var ancestors = element.ancestors();
    return Object.isNumber(expression) ? ancestors[expression] :
      Selector.findElement(ancestors, expression, index);
  },

  down: function(element, expression, index) {
    element = $(element);
    if (arguments.length == 1) return element.firstDescendant();
    return Object.isNumber(expression) ? element.descendants()[expression] :
      Element.select(element, expression)[index || 0];
  },

  previous: function(element, expression, index) {
    element = $(element);
    if (arguments.length == 1) return $(Selector.handlers.previousElementSibling(element));
    var previousSiblings = element.previousSiblings();
    return Object.isNumber(expression) ? previousSiblings[expression] :
      Selector.findElement(previousSiblings, expression, index);
  },

  next: function(element, expression, index) {
    element = $(element);
    if (arguments.length == 1) return $(Selector.handlers.nextElementSibling(element));
    var nextSiblings = element.nextSiblings();
    return Object.isNumber(expression) ? nextSiblings[expression] :
      Selector.findElement(nextSiblings, expression, index);
  },

  select: function() {
    var args = $A(arguments), element = $(args.shift());
    return Selector.findChildElements(element, args);
  },

  adjacent: function() {
    var args = $A(arguments), element = $(args.shift());
    return Selector.findChildElements(element.parentNode, args).without(element);
  },

  identify: function(element) {
    element = $(element);
    var id = element.readAttribute('id'), self = arguments.callee;
    if (id) return id;
    do { id = 'anonymous_element_' + self.counter++ } while ($(id));
    element.writeAttribute('id', id);
    return id;
  },

  readAttribute: function(element, name) {
    element = $(element);
    if (Prototype.Browser.IE) {
      var t = Element._attributeTranslations.read;
      if (t.values[name]) return t.values[name](element, name);
      if (t.names[name]) name = t.names[name];
      if (name.include(':')) {
        return (!element.attributes || !element.attributes[name]) ? null :
         element.attributes[name].value;
      }
    }
    return element.getAttribute(name);
  },

  writeAttribute: function(element, name, value) {
    element = $(element);
    var attributes = { }, t = Element._attributeTranslations.write;

    if (typeof name == 'object') attributes = name;
    else attributes[name] = Object.isUndefined(value) ? true : value;

    for (var attr in attributes) {
      name = t.names[attr] || attr;
      value = attributes[attr];
      if (t.values[attr]) name = t.values[attr](element, value);
      if (value === false || value === null)
        element.removeAttribute(name);
      else if (value === true)
        element.setAttribute(name, name);
      else element.setAttribute(name, value);
    }
    return element;
  },

  getHeight: function(element) {
    return $(element).getDimensions().height;
  },

  getWidth: function(element) {
    return $(element).getDimensions().width;
  },

  classNames: function(element) {
    return new Element.ClassNames(element);
  },

  hasClassName: function(element, className) {
    if (!(element = $(element))) return;
    var elementClassName = element.className;
    return (elementClassName.length > 0 && (elementClassName == className ||
      new RegExp("(^|\\s)" + className + "(\\s|$)").test(elementClassName)));
  },

  addClassName: function(element, className) {
    if (!(element = $(element))) return;
    if (!element.hasClassName(className))
      element.className += (element.className ? ' ' : '') + className;
    return element;
  },

  removeClassName: function(element, className) {
    if (!(element = $(element))) return;
    element.className = element.className.replace(
      new RegExp("(^|\\s+)" + className + "(\\s+|$)"), ' ').strip();
    return element;
  },

  toggleClassName: function(element, className) {
    if (!(element = $(element))) return;
    return element[element.hasClassName(className) ?
      'removeClassName' : 'addClassName'](className);
  },


  cleanWhitespace: function(element) {
    element = $(element);
    var node = element.firstChild;
    while (node) {
      var nextNode = node.nextSibling;
      if (node.nodeType == 3 && !/\S/.test(node.nodeValue))
        element.removeChild(node);
      node = nextNode;
    }
    return element;
  },

  empty: function(element) {
    return $(element).innerHTML.blank();
  },

  descendantOf: function(element, ancestor) {
    element = $(element), ancestor = $(ancestor);

    if (element.compareDocumentPosition)
      return (element.compareDocumentPosition(ancestor) & 8) === 8;

    if (ancestor.contains)
      return ancestor.contains(element) && ancestor !== element;

    while (element = element.parentNode)
      if (element == ancestor) return true;

    return false;
  },

  scrollTo: function(element) {
    element = $(element);
    var pos = element.cumulativeOffset();
    window.scrollTo(pos[0], pos[1]);
    return element;
  },

  getStyle: function(element, style) {
    element = $(element);
    style = style == 'float' ? 'cssFloat' : style.camelize();
    var value = element.style[style];
    if (!value || value == 'auto') {
      var css = document.defaultView.getComputedStyle(element, null);
      value = css ? css[style] : null;
    }
    if (style == 'opacity') return value ? parseFloat(value) : 1.0;
    return value == 'auto' ? null : value;
  },

  getOpacity: function(element) {
    return $(element).getStyle('opacity');
  },

  setStyle: function(element, styles) {
    element = $(element);
    var elementStyle = element.style, match;
    if (Object.isString(styles)) {
      element.style.cssText += ';' + styles;
      return styles.include('opacity') ?
        element.setOpacity(styles.match(/opacity:\s*(\d?\.?\d*)/)[1]) : element;
    }
    for (var property in styles)
      if (property == 'opacity') element.setOpacity(styles[property]);
      else
        elementStyle[(property == 'float' || property == 'cssFloat') ?
          (Object.isUndefined(elementStyle.styleFloat) ? 'cssFloat' : 'styleFloat') :
            property] = styles[property];

    return element;
  },

  setOpacity: function(element, value) {
    element = $(element);
    element.style.opacity = (value == 1 || value === '') ? '' :
      (value < 0.00001) ? 0 : value;
    return element;
  },

  getDimensions: function(element) {
    element = $(element);
    var display = element.getStyle('display');
    if (display != 'none' && display != null) // Safari bug
      return {width: element.offsetWidth, height: element.offsetHeight};



    var els = element.style;
    var originalVisibility = els.visibility;
    var originalPosition = els.position;
    var originalDisplay = els.display;
    els.visibility = 'hidden';
    els.position = 'absolute';
    els.display = 'block';
    var originalWidth = element.clientWidth;
    var originalHeight = element.clientHeight;
    els.display = originalDisplay;
    els.position = originalPosition;
    els.visibility = originalVisibility;
    return {width: originalWidth, height: originalHeight};
  },

  makePositioned: function(element) {
    element = $(element);
    var pos = Element.getStyle(element, 'position');
    if (pos == 'static' || !pos) {
      element._madePositioned = true;
      element.style.position = 'relative';


      if (Prototype.Browser.Opera) {
        element.style.top = 0;
        element.style.left = 0;
      }
    }
    return element;
  },

  undoPositioned: function(element) {
    element = $(element);
    if (element._madePositioned) {
      element._madePositioned = undefined;
      element.style.position =
        element.style.top =
        element.style.left =
        element.style.bottom =
        element.style.right = '';
    }
    return element;
  },

  makeClipping: function(element) {
    element = $(element);
    if (element._overflow) return element;
    element._overflow = Element.getStyle(element, 'overflow') || 'auto';
    if (element._overflow !== 'hidden')
      element.style.overflow = 'hidden';
    return element;
  },

  undoClipping: function(element) {
    element = $(element);
    if (!element._overflow) return element;
    element.style.overflow = element._overflow == 'auto' ? '' : element._overflow;
    element._overflow = null;
    return element;
  },

  cumulativeOffset: function(element) {
    var valueT = 0, valueL = 0;
    do {
      valueT += element.offsetTop  || 0;
      valueL += element.offsetLeft || 0;
      element = element.offsetParent;
    } while (element);
    return Element._returnOffset(valueL, valueT);
  },

  positionedOffset: function(element) {
    var valueT = 0, valueL = 0;
    do {
      valueT += element.offsetTop  || 0;
      valueL += element.offsetLeft || 0;
      element = element.offsetParent;
      if (element) {
        if (element.tagName.toUpperCase() == 'BODY') break;
        var p = Element.getStyle(element, 'position');
        if (p !== 'static') break;
      }
    } while (element);
    return Element._returnOffset(valueL, valueT);
  },

  absolutize: function(element) {
    element = $(element);
    if (element.getStyle('position') == 'absolute') return element;


    var offsets = element.positionedOffset();
    var top     = offsets[1];
    var left    = offsets[0];
    var width   = element.clientWidth;
    var height  = element.clientHeight;

    element._originalLeft   = left - parseFloat(element.style.left  || 0);
    element._originalTop    = top  - parseFloat(element.style.top || 0);
    element._originalWidth  = element.style.width;
    element._originalHeight = element.style.height;

    element.style.position = 'absolute';
    element.style.top    = top + 'px';
    element.style.left   = left + 'px';
    element.style.width  = width + 'px';
    element.style.height = height + 'px';
    return element;
  },

  relativize: function(element) {
    element = $(element);
    if (element.getStyle('position') == 'relative') return element;


    element.style.position = 'relative';
    var top  = parseFloat(element.style.top  || 0) - (element._originalTop || 0);
    var left = parseFloat(element.style.left || 0) - (element._originalLeft || 0);

    element.style.top    = top + 'px';
    element.style.left   = left + 'px';
    element.style.height = element._originalHeight;
    element.style.width  = element._originalWidth;
    return element;
  },

  cumulativeScrollOffset: function(element) {
    var valueT = 0, valueL = 0;
    do {
      valueT += element.scrollTop  || 0;
      valueL += element.scrollLeft || 0;
      element = element.parentNode;
    } while (element);
    return Element._returnOffset(valueL, valueT);
  },

  getOffsetParent: function(element) {
    if (element.offsetParent) return $(element.offsetParent);
    if (element == document.body) return $(element);

    while ((element = element.parentNode) && element != document.body)
      if (Element.getStyle(element, 'position') != 'static')
        return $(element);

    return $(document.body);
  },

  viewportOffset: function(forElement) {
    var valueT = 0, valueL = 0;

    var element = forElement;
    do {
      valueT += element.offsetTop  || 0;
      valueL += element.offsetLeft || 0;


      if (element.offsetParent == document.body &&
        Element.getStyle(element, 'position') == 'absolute') break;

    } while (element = element.offsetParent);

    element = forElement;
    do {
      if (!Prototype.Browser.Opera || (element.tagName && (element.tagName.toUpperCase() == 'BODY'))) {
        valueT -= element.scrollTop  || 0;
        valueL -= element.scrollLeft || 0;
      }
    } while (element = element.parentNode);

    return Element._returnOffset(valueL, valueT);
  },

  clonePosition: function(element, source) {
    var options = Object.extend({
      setLeft:    true,
      setTop:     true,
      setWidth:   true,
      setHeight:  true,
      offsetTop:  0,
      offsetLeft: 0
    }, arguments[2] || { });


    source = $(source);
    var p = source.viewportOffset();


    element = $(element);
    var delta = [0, 0];
    var parent = null;


    if (Element.getStyle(element, 'position') == 'absolute') {
      parent = element.getOffsetParent();
      delta = parent.viewportOffset();
    }


    if (parent == document.body) {
      delta[0] -= document.body.offsetLeft;
      delta[1] -= document.body.offsetTop;
    }


    if (options.setLeft)   element.style.left  = (p[0] - delta[0] + options.offsetLeft) + 'px';
    if (options.setTop)    element.style.top   = (p[1] - delta[1] + options.offsetTop) + 'px';
    if (options.setWidth)  element.style.width = source.offsetWidth + 'px';
    if (options.setHeight) element.style.height = source.offsetHeight + 'px';
    return element;
  }
};

Element.Methods.identify.counter = 1;

Object.extend(Element.Methods, {
  getElementsBySelector: Element.Methods.select,
  childElements: Element.Methods.immediateDescendants
});

Element._attributeTranslations = {
  write: {
    names: {
      className: 'class',
      htmlFor:   'for'
    },
    values: { }
  }
};

if (Prototype.Browser.Opera) {
  Element.Methods.getStyle = Element.Methods.getStyle.wrap(
    function(proceed, element, style) {
      switch (style) {
        case 'left': case 'top': case 'right': case 'bottom':
          if (proceed(element, 'position') === 'static') return null;
        case 'height': case 'width':

          if (!Element.visible(element)) return null;



          var dim = parseInt(proceed(element, style), 10);

          if (dim !== element['offset' + style.capitalize()])
            return dim + 'px';

          var properties;
          if (style === 'height') {
            properties = ['border-top-width', 'padding-top',
             'padding-bottom', 'border-bottom-width'];
          }
          else {
            properties = ['border-left-width', 'padding-left',
             'padding-right', 'border-right-width'];
          }
          return properties.inject(dim, function(memo, property) {
            var val = proceed(element, property);
            return val === null ? memo : memo - parseInt(val, 10);
          }) + 'px';
        default: return proceed(element, style);
      }
    }
  );

  Element.Methods.readAttribute = Element.Methods.readAttribute.wrap(
    function(proceed, element, attribute) {
      if (attribute === 'title') return element.title;
      return proceed(element, attribute);
    }
  );
}

else if (Prototype.Browser.IE) {


  Element.Methods.getOffsetParent = Element.Methods.getOffsetParent.wrap(
    function(proceed, element) {
      element = $(element);

      try { element.offsetParent }
      catch(e) { return $(document.body) }
      var position = element.getStyle('position');
      if (position !== 'static') return proceed(element);
      element.setStyle({ position: 'relative' });
      var value = proceed(element);
      element.setStyle({ position: position });
      return value;
    }
  );

  $w('positionedOffset viewportOffset').each(function(method) {
    Element.Methods[method] = Element.Methods[method].wrap(
      function(proceed, element) {
        element = $(element);
        try { element.offsetParent }
        catch(e) { return Element._returnOffset(0,0) }
        var position = element.getStyle('position');
        if (position !== 'static') return proceed(element);


        var offsetParent = element.getOffsetParent();
        if (offsetParent && offsetParent.getStyle('position') === 'fixed')
          offsetParent.setStyle({ zoom: 1 });
        element.setStyle({ position: 'relative' });
        var value = proceed(element);
        element.setStyle({ position: position });
        return value;
      }
    );
  });

  Element.Methods.cumulativeOffset = Element.Methods.cumulativeOffset.wrap(
    function(proceed, element) {
      try { element.offsetParent }
      catch(e) { return Element._returnOffset(0,0) }
      return proceed(element);
    }
  );

  Element.Methods.getStyle = function(element, style) {
    element = $(element);
    style = (style == 'float' || style == 'cssFloat') ? 'styleFloat' : style.camelize();
    var value = element.style[style];
    if (!value && element.currentStyle) value = element.currentStyle[style];

    if (style == 'opacity') {
      if (value = (element.getStyle('filter') || '').match(/alpha\(opacity=(.*)\)/))
        if (value[1]) return parseFloat(value[1]) / 100;
      return 1.0;
    }

    if (value == 'auto') {
      if ((style == 'width' || style == 'height') && (element.getStyle('display') != 'none'))
        return element['offset' + style.capitalize()] + 'px';
      return null;
    }
    return value;
  };

  Element.Methods.setOpacity = function(element, value) {
    function stripAlpha(filter){
      return filter.replace(/alpha\([^\)]*\)/gi,'');
    }
    element = $(element);
    var currentStyle = element.currentStyle;
    if ((currentStyle && !currentStyle.hasLayout) ||
      (!currentStyle && element.style.zoom == 'normal'))
        element.style.zoom = 1;

    var filter = element.getStyle('filter'), style = element.style;
    if (value == 1 || value === '') {
      (filter = stripAlpha(filter)) ?
        style.filter = filter : style.removeAttribute('filter');
      return element;
    } else if (value < 0.00001) value = 0;
    style.filter = stripAlpha(filter) +
      'alpha(opacity=' + (value * 100) + ')';
    return element;
  };

  Element._attributeTranslations = {
    read: {
      names: {
        'class': 'className',
        'for':   'htmlFor'
      },
      values: {
        _getAttr: function(element, attribute) {
          return element.getAttribute(attribute, 2);
        },
        _getAttrNode: function(element, attribute) {
          var node = element.getAttributeNode(attribute);
          return node ? node.value : "";
        },
        _getEv: function(element, attribute) {
          attribute = element.getAttribute(attribute);
          return attribute ? attribute.toString().slice(23, -2) : null;
        },
        _flag: function(element, attribute) {
          return $(element).hasAttribute(attribute) ? attribute : null;
        },
        style: function(element) {
          return element.style.cssText.toLowerCase();
        },
        title: function(element) {
          return element.title;
        }
      }
    }
  };

  Element._attributeTranslations.write = {
    names: Object.extend({
      cellpadding: 'cellPadding',
      cellspacing: 'cellSpacing'
    }, Element._attributeTranslations.read.names),
    values: {
      checked: function(element, value) {
        element.checked = !!value;
      },

      style: function(element, value) {
        element.style.cssText = value ? value : '';
      }
    }
  };

  Element._attributeTranslations.has = {};

  $w('colSpan rowSpan vAlign dateTime accessKey tabIndex ' +
      'encType maxLength readOnly longDesc frameBorder').each(function(attr) {
    Element._attributeTranslations.write.names[attr.toLowerCase()] = attr;
    Element._attributeTranslations.has[attr.toLowerCase()] = attr;
  });

  (function(v) {
    Object.extend(v, {
      href:        v._getAttr,
      src:         v._getAttr,
      type:        v._getAttr,
      action:      v._getAttrNode,
      disabled:    v._flag,
      checked:     v._flag,
      readonly:    v._flag,
      multiple:    v._flag,
      onload:      v._getEv,
      onunload:    v._getEv,
      onclick:     v._getEv,
      ondblclick:  v._getEv,
      onmousedown: v._getEv,
      onmouseup:   v._getEv,
      onmouseover: v._getEv,
      onmousemove: v._getEv,
      onmouseout:  v._getEv,
      onfocus:     v._getEv,
      onblur:      v._getEv,
      onkeypress:  v._getEv,
      onkeydown:   v._getEv,
      onkeyup:     v._getEv,
      onsubmit:    v._getEv,
      onreset:     v._getEv,
      onselect:    v._getEv,
      onchange:    v._getEv
    });
  })(Element._attributeTranslations.read.values);
}

else if (Prototype.Browser.Gecko && /rv:1\.8\.0/.test(navigator.userAgent)) {
  Element.Methods.setOpacity = function(element, value) {
    element = $(element);
    element.style.opacity = (value == 1) ? 0.999999 :
      (value === '') ? '' : (value < 0.00001) ? 0 : value;
    return element;
  };
}

else if (Prototype.Browser.WebKit) {
  Element.Methods.setOpacity = function(element, value) {
    element = $(element);
    element.style.opacity = (value == 1 || value === '') ? '' :
      (value < 0.00001) ? 0 : value;

    if (value == 1)
      if(element.tagName.toUpperCase() == 'IMG' && element.width) {
        element.width++; element.width--;
      } else try {
        var n = document.createTextNode(' ');
        element.appendChild(n);
        element.removeChild(n);
      } catch (e) { }

    return element;
  };




  Element.Methods.cumulativeOffset = function(element) {
    var valueT = 0, valueL = 0;
    do {
      valueT += element.offsetTop  || 0;
      valueL += element.offsetLeft || 0;
      if (element.offsetParent == document.body)
        if (Element.getStyle(element, 'position') == 'absolute') break;

      element = element.offsetParent;
    } while (element);

    return Element._returnOffset(valueL, valueT);
  };
}

if (Prototype.Browser.IE || Prototype.Browser.Opera) {

  Element.Methods.update = function(element, content) {
    element = $(element);

    if (content && content.toElement) content = content.toElement();
    if (Object.isElement(content)) return element.update().insert(content);

    content = Object.toHTML(content);
    var tagName = element.tagName.toUpperCase();

    if (tagName in Element._insertionTranslations.tags) {
      $A(element.childNodes).each(function(node) { element.removeChild(node) });
      Element._getContentFromAnonymousElement(tagName, content.stripScripts())
        .each(function(node) { element.appendChild(node) });
    }
    else element.innerHTML = content.stripScripts();

    content.evalScripts.bind(content).defer();
    return element;
  };
}

if ('outerHTML' in document.createElement('div')) {
  Element.Methods.replace = function(element, content) {
    element = $(element);

    if (content && content.toElement) content = content.toElement();
    if (Object.isElement(content)) {
      element.parentNode.replaceChild(content, element);
      return element;
    }

    content = Object.toHTML(content);
    var parent = element.parentNode, tagName = parent.tagName.toUpperCase();

    if (Element._insertionTranslations.tags[tagName]) {
      var nextSibling = element.next();
      var fragments = Element._getContentFromAnonymousElement(tagName, content.stripScripts());
      parent.removeChild(element);
      if (nextSibling)
        fragments.each(function(node) { parent.insertBefore(node, nextSibling) });
      else
        fragments.each(function(node) { parent.appendChild(node) });
    }
    else element.outerHTML = content.stripScripts();

    content.evalScripts.bind(content).defer();
    return element;
  };
}

Element._returnOffset = function(l, t) {
  var result = [l, t];
  result.left = l;
  result.top = t;
  return result;
};

Element._getContentFromAnonymousElement = function(tagName, html) {
  var div = new Element('div'), t = Element._insertionTranslations.tags[tagName];
  if (t) {
    div.innerHTML = t[0] + html + t[1];
    t[2].times(function() { div = div.firstChild });
  } else div.innerHTML = html;
  return $A(div.childNodes);
};

Element._insertionTranslations = {
  before: function(element, node) {
    element.parentNode.insertBefore(node, element);
  },
  top: function(element, node) {
    element.insertBefore(node, element.firstChild);
  },
  bottom: function(element, node) {
    element.appendChild(node);
  },
  after: function(element, node) {
    element.parentNode.insertBefore(node, element.nextSibling);
  },
  tags: {
    TABLE:  ['<table>',                '</table>',                   1],
    TBODY:  ['<table><tbody>',         '</tbody></table>',           2],
    TR:     ['<table><tbody><tr>',     '</tr></tbody></table>',      3],
    TD:     ['<table><tbody><tr><td>', '</td></tr></tbody></table>', 4],
    SELECT: ['<select>',               '</select>',                  1]
  }
};

(function() {
  Object.extend(this.tags, {
    THEAD: this.tags.TBODY,
    TFOOT: this.tags.TBODY,
    TH:    this.tags.TD
  });
}).call(Element._insertionTranslations);

Element.Methods.Simulated = {
  hasAttribute: function(element, attribute) {
    attribute = Element._attributeTranslations.has[attribute] || attribute;
    var node = $(element).getAttributeNode(attribute);
    return !!(node && node.specified);
  }
};

Element.Methods.ByTag = { };

Object.extend(Element, Element.Methods);

if (!Prototype.BrowserFeatures.ElementExtensions &&
    document.createElement('div')['__proto__']) {
  window.HTMLElement = { };
  window.HTMLElement.prototype = document.createElement('div')['__proto__'];
  Prototype.BrowserFeatures.ElementExtensions = true;
}

Element.extend = (function() {
  if (Prototype.BrowserFeatures.SpecificElementExtensions)
    return Prototype.K;

  var Methods = { }, ByTag = Element.Methods.ByTag;

  var extend = Object.extend(function(element) {
    if (!element || element._extendedByPrototype ||
        element.nodeType != 1 || element == window) return element;

    var methods = Object.clone(Methods),
      tagName = element.tagName.toUpperCase(), property, value;


    if (ByTag[tagName]) Object.extend(methods, ByTag[tagName]);

    for (property in methods) {
      value = methods[property];
      if (Object.isFunction(value) && !(property in element))
        element[property] = value.methodize();
    }

    element._extendedByPrototype = Prototype.emptyFunction;
    return element;

  }, {
    refresh: function() {

      if (!Prototype.BrowserFeatures.ElementExtensions) {
        Object.extend(Methods, Element.Methods);
        Object.extend(Methods, Element.Methods.Simulated);
      }
    }
  });

  extend.refresh();
  return extend;
})();

Element.hasAttribute = function(element, attribute) {
  if (element.hasAttribute) return element.hasAttribute(attribute);
  return Element.Methods.Simulated.hasAttribute(element, attribute);
};

Element.addMethods = function(methods) {
  var F = Prototype.BrowserFeatures, T = Element.Methods.ByTag;

  if (!methods) {
    Object.extend(Form, Form.Methods);
    Object.extend(Form.Element, Form.Element.Methods);
    Object.extend(Element.Methods.ByTag, {
      "FORM":     Object.clone(Form.Methods),
      "INPUT":    Object.clone(Form.Element.Methods),
      "SELECT":   Object.clone(Form.Element.Methods),
      "TEXTAREA": Object.clone(Form.Element.Methods)
    });
  }

  if (arguments.length == 2) {
    var tagName = methods;
    methods = arguments[1];
  }

  if (!tagName) Object.extend(Element.Methods, methods || { });
  else {
    if (Object.isArray(tagName)) tagName.each(extend);
    else extend(tagName);
  }

  function extend(tagName) {
    tagName = tagName.toUpperCase();
    if (!Element.Methods.ByTag[tagName])
      Element.Methods.ByTag[tagName] = { };
    Object.extend(Element.Methods.ByTag[tagName], methods);
  }

  function copy(methods, destination, onlyIfAbsent) {
    onlyIfAbsent = onlyIfAbsent || false;
    for (var property in methods) {
      var value = methods[property];
      if (!Object.isFunction(value)) continue;
      if (!onlyIfAbsent || !(property in destination))
        destination[property] = value.methodize();
    }
  }

  function findDOMClass(tagName) {
    var klass;
    var trans = {
      "OPTGROUP": "OptGroup", "TEXTAREA": "TextArea", "P": "Paragraph",
      "FIELDSET": "FieldSet", "UL": "UList", "OL": "OList", "DL": "DList",
      "DIR": "Directory", "H1": "Heading", "H2": "Heading", "H3": "Heading",
      "H4": "Heading", "H5": "Heading", "H6": "Heading", "Q": "Quote",
      "INS": "Mod", "DEL": "Mod", "A": "Anchor", "IMG": "Image", "CAPTION":
      "TableCaption", "COL": "TableCol", "COLGROUP": "TableCol", "THEAD":
      "TableSection", "TFOOT": "TableSection", "TBODY": "TableSection", "TR":
      "TableRow", "TH": "TableCell", "TD": "TableCell", "FRAMESET":
      "FrameSet", "IFRAME": "IFrame"
    };
    if (trans[tagName]) klass = 'HTML' + trans[tagName] + 'Element';
    if (window[klass]) return window[klass];
    klass = 'HTML' + tagName + 'Element';
    if (window[klass]) return window[klass];
    klass = 'HTML' + tagName.capitalize() + 'Element';
    if (window[klass]) return window[klass];

    window[klass] = { };
    window[klass].prototype = document.createElement(tagName)['__proto__'];
    return window[klass];
  }

  if (F.ElementExtensions) {
    copy(Element.Methods, HTMLElement.prototype);
    copy(Element.Methods.Simulated, HTMLElement.prototype, true);
  }

  if (F.SpecificElementExtensions) {
    for (var tag in Element.Methods.ByTag) {
      var klass = findDOMClass(tag);
      if (Object.isUndefined(klass)) continue;
      copy(T[tag], klass.prototype);
    }
  }

  Object.extend(Element, Element.Methods);
  delete Element.ByTag;

  if (Element.extend.refresh) Element.extend.refresh();
  Element.cache = { };
};

document.viewport = {
  getDimensions: function() {
    var dimensions = { }, B = Prototype.Browser;
    $w('width height').each(function(d) {
      var D = d.capitalize();
      if (B.WebKit && !document.evaluate) {

        dimensions[d] = self['inner' + D];
      } else if (B.Opera && parseFloat(window.opera.version()) < 9.5) {

        dimensions[d] = document.body['client' + D]
      } else {
        dimensions[d] = document.documentElement['client' + D];
      }
    });
    return dimensions;
  },

  getWidth: function() {
    return this.getDimensions().width;
  },

  getHeight: function() {
    return this.getDimensions().height;
  },

  getScrollOffsets: function() {
    return Element._returnOffset(
      window.pageXOffset || document.documentElement.scrollLeft || document.body.scrollLeft,
      window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop);
  }
};
/* Portions of the Selector class are derived from Jack Slocum's DomQuery,
 * part of YUI-Ext version 0.40, distributed under the terms of an MIT-style
 * license.  Please see http://www.yui-ext.com/ for more information. */

var Selector = Class.create({
  initialize: function(expression) {
    this.expression = expression.strip();

    if (this.shouldUseSelectorsAPI()) {
      this.mode = 'selectorsAPI';
    } else if (this.shouldUseXPath()) {
      this.mode = 'xpath';
      this.compileXPathMatcher();
    } else {
      this.mode = "normal";
      this.compileMatcher();
    }

  },

  shouldUseXPath: function() {
    if (!Prototype.BrowserFeatures.XPath) return false;

    var e = this.expression;


    if (Prototype.Browser.WebKit &&
     (e.include("-of-type") || e.include(":empty")))
      return false;



    if ((/(\[[\w-]*?:|:checked)/).test(e))
      return false;

    return true;
  },

  shouldUseSelectorsAPI: function() {
    if (!Prototype.BrowserFeatures.SelectorsAPI) return false;

    if (!Selector._div) Selector._div = new Element('div');



    try {
      Selector._div.querySelector(this.expression);
    } catch(e) {
      return false;
    }

    return true;
  },

  compileMatcher: function() {
    var e = this.expression, ps = Selector.patterns, h = Selector.handlers,
        c = Selector.criteria, le, p, m;

    if (Selector._cache[e]) {
      this.matcher = Selector._cache[e];
      return;
    }

    this.matcher = ["this.matcher = function(root) {",
                    "var r = root, h = Selector.handlers, c = false, n;"];

    while (e && le != e && (/\S/).test(e)) {
      le = e;
      for (var i in ps) {
        p = ps[i];
        if (m = e.match(p)) {
          this.matcher.push(Object.isFunction(c[i]) ? c[i](m) :
            new Template(c[i]).evaluate(m));
          e = e.replace(m[0], '');
          break;
        }
      }
    }

    this.matcher.push("return h.unique(n);\n}");
    eval(this.matcher.join('\n'));
    Selector._cache[this.expression] = this.matcher;
  },

  compileXPathMatcher: function() {
    var e = this.expression, ps = Selector.patterns,
        x = Selector.xpath, le, m;

    if (Selector._cache[e]) {
      this.xpath = Selector._cache[e]; return;
    }

    this.matcher = ['.//*'];
    while (e && le != e && (/\S/).test(e)) {
      le = e;
      for (var i in ps) {
        if (m = e.match(ps[i])) {
          this.matcher.push(Object.isFunction(x[i]) ? x[i](m) :
            new Template(x[i]).evaluate(m));
          e = e.replace(m[0], '');
          break;
        }
      }
    }

    this.xpath = this.matcher.join('');
    Selector._cache[this.expression] = this.xpath;
  },

  findElements: function(root) {
    root = root || document;
    var e = this.expression, results;

    switch (this.mode) {
      case 'selectorsAPI':



        if (root !== document) {
          var oldId = root.id, id = $(root).identify();
          e = "#" + id + " " + e;
        }

        results = $A(root.querySelectorAll(e)).map(Element.extend);
        root.id = oldId;

        return results;
      case 'xpath':
        return document._getElementsByXPath(this.xpath, root);
      default:
       return this.matcher(root);
    }
  },

  match: function(element) {
    this.tokens = [];

    var e = this.expression, ps = Selector.patterns, as = Selector.assertions;
    var le, p, m;

    while (e && le !== e && (/\S/).test(e)) {
      le = e;
      for (var i in ps) {
        p = ps[i];
        if (m = e.match(p)) {


          if (as[i]) {
            this.tokens.push([i, Object.clone(m)]);
            e = e.replace(m[0], '');
          } else {


            return this.findElements(document).include(element);
          }
        }
      }
    }

    var match = true, name, matches;
    for (var i = 0, token; token = this.tokens[i]; i++) {
      name = token[0], matches = token[1];
      if (!Selector.assertions[name](element, matches)) {
        match = false; break;
      }
    }

    return match;
  },

  toString: function() {
    return this.expression;
  },

  inspect: function() {
    return "#<Selector:" + this.expression.inspect() + ">";
  }
});

Object.extend(Selector, {
  _cache: { },

  xpath: {
    descendant:   "//*",
    child:        "/*",
    adjacent:     "/following-sibling::*[1]",
    laterSibling: '/following-sibling::*',
    tagName:      function(m) {
      if (m[1] == '*') return '';
      return "[local-name()='" + m[1].toLowerCase() +
             "' or local-name()='" + m[1].toUpperCase() + "']";
    },
    className:    "[contains(concat(' ', @class, ' '), ' #{1} ')]",
    id:           "[@id='#{1}']",
    attrPresence: function(m) {
      m[1] = m[1].toLowerCase();
      return new Template("[@#{1}]").evaluate(m);
    },
    attr: function(m) {
      m[1] = m[1].toLowerCase();
      m[3] = m[5] || m[6];
      return new Template(Selector.xpath.operators[m[2]]).evaluate(m);
    },
    pseudo: function(m) {
      var h = Selector.xpath.pseudos[m[1]];
      if (!h) return '';
      if (Object.isFunction(h)) return h(m);
      return new Template(Selector.xpath.pseudos[m[1]]).evaluate(m);
    },
    operators: {
      '=':  "[@#{1}='#{3}']",
      '!=': "[@#{1}!='#{3}']",
      '^=': "[starts-with(@#{1}, '#{3}')]",
      '$=': "[substring(@#{1}, (string-length(@#{1}) - string-length('#{3}') + 1))='#{3}']",
      '*=': "[contains(@#{1}, '#{3}')]",
      '~=': "[contains(concat(' ', @#{1}, ' '), ' #{3} ')]",
      '|=': "[contains(concat('-', @#{1}, '-'), '-#{3}-')]"
    },
    pseudos: {
      'first-child': '[not(preceding-sibling::*)]',
      'last-child':  '[not(following-sibling::*)]',
      'only-child':  '[not(preceding-sibling::* or following-sibling::*)]',
      'empty':       "[count(*) = 0 and (count(text()) = 0)]",
      'checked':     "[@checked]",
      'disabled':    "[(@disabled) and (@type!='hidden')]",
      'enabled':     "[not(@disabled) and (@type!='hidden')]",
      'not': function(m) {
        var e = m[6], p = Selector.patterns,
            x = Selector.xpath, le, v;

        var exclusion = [];
        while (e && le != e && (/\S/).test(e)) {
          le = e;
          for (var i in p) {
            if (m = e.match(p[i])) {
              v = Object.isFunction(x[i]) ? x[i](m) : new Template(x[i]).evaluate(m);
              exclusion.push("(" + v.substring(1, v.length - 1) + ")");
              e = e.replace(m[0], '');
              break;
            }
          }
        }
        return "[not(" + exclusion.join(" and ") + ")]";
      },
      'nth-child':      function(m) {
        return Selector.xpath.pseudos.nth("(count(./preceding-sibling::*) + 1) ", m);
      },
      'nth-last-child': function(m) {
        return Selector.xpath.pseudos.nth("(count(./following-sibling::*) + 1) ", m);
      },
      'nth-of-type':    function(m) {
        return Selector.xpath.pseudos.nth("position() ", m);
      },
      'nth-last-of-type': function(m) {
        return Selector.xpath.pseudos.nth("(last() + 1 - position()) ", m);
      },
      'first-of-type':  function(m) {
        m[6] = "1"; return Selector.xpath.pseudos['nth-of-type'](m);
      },
      'last-of-type':   function(m) {
        m[6] = "1"; return Selector.xpath.pseudos['nth-last-of-type'](m);
      },
      'only-of-type':   function(m) {
        var p = Selector.xpath.pseudos; return p['first-of-type'](m) + p['last-of-type'](m);
      },
      nth: function(fragment, m) {
        var mm, formula = m[6], predicate;
        if (formula == 'even') formula = '2n+0';
        if (formula == 'odd')  formula = '2n+1';
        if (mm = formula.match(/^(\d+)$/)) // digit only
          return '[' + fragment + "= " + mm[1] + ']';
        if (mm = formula.match(/^(-?\d*)?n(([+-])(\d+))?/)) { // an+b
          if (mm[1] == "-") mm[1] = -1;
          var a = mm[1] ? Number(mm[1]) : 1;
          var b = mm[2] ? Number(mm[2]) : 0;
          predicate = "[((#{fragment} - #{b}) mod #{a} = 0) and " +
          "((#{fragment} - #{b}) div #{a} >= 0)]";
          return new Template(predicate).evaluate({
            fragment: fragment, a: a, b: b });
        }
      }
    }
  },

  criteria: {
    tagName:      'n = h.tagName(n, r, "#{1}", c);      c = false;',
    className:    'n = h.className(n, r, "#{1}", c);    c = false;',
    id:           'n = h.id(n, r, "#{1}", c);           c = false;',
    attrPresence: 'n = h.attrPresence(n, r, "#{1}", c); c = false;',
    attr: function(m) {
      m[3] = (m[5] || m[6]);
      return new Template('n = h.attr(n, r, "#{1}", "#{3}", "#{2}", c); c = false;').evaluate(m);
    },
    pseudo: function(m) {
      if (m[6]) m[6] = m[6].replace(/"/g, '\\"');
      return new Template('n = h.pseudo(n, "#{1}", "#{6}", r, c); c = false;').evaluate(m);
    },
    descendant:   'c = "descendant";',
    child:        'c = "child";',
    adjacent:     'c = "adjacent";',
    laterSibling: 'c = "laterSibling";'
  },

  patterns: {


    laterSibling: /^\s*~\s*/,
    child:        /^\s*>\s*/,
    adjacent:     /^\s*\+\s*/,
    descendant:   /^\s/,


    tagName:      /^\s*(\*|[\w\-]+)(\b|$)?/,
    id:           /^#([\w\-\*]+)(\b|$)/,
    className:    /^\.([\w\-\*]+)(\b|$)/,
    pseudo:
/^:((first|last|nth|nth-last|only)(-child|-of-type)|empty|checked|(en|dis)abled|not)(\((.*?)\))?(\b|$|(?=\s|[:+~>]))/,
    attrPresence: /^\[((?:[\w]+:)?[\w]+)\]/,
    attr:         /\[((?:[\w-]*:)?[\w-]+)\s*(?:([!^$*~|]?=)\s*((['"])([^\4]*?)\4|([^'"][^\]]*?)))?\]/
  },


  assertions: {
    tagName: function(element, matches) {
      return matches[1].toUpperCase() == element.tagName.toUpperCase();
    },

    className: function(element, matches) {
      return Element.hasClassName(element, matches[1]);
    },

    id: function(element, matches) {
      return element.id === matches[1];
    },

    attrPresence: function(element, matches) {
      return Element.hasAttribute(element, matches[1]);
    },

    attr: function(element, matches) {
      var nodeValue = Element.readAttribute(element, matches[1]);
      return nodeValue && Selector.operators[matches[2]](nodeValue, matches[5] || matches[6]);
    }
  },

  handlers: {


    concat: function(a, b) {
      for (var i = 0, node; node = b[i]; i++)
        a.push(node);
      return a;
    },


    mark: function(nodes) {
      var _true = Prototype.emptyFunction;
      for (var i = 0, node; node = nodes[i]; i++)
        node._countedByPrototype = _true;
      return nodes;
    },

    unmark: function(nodes) {
      for (var i = 0, node; node = nodes[i]; i++)
        node._countedByPrototype = undefined;
      return nodes;
    },




    index: function(parentNode, reverse, ofType) {
      parentNode._countedByPrototype = Prototype.emptyFunction;
      if (reverse) {
        for (var nodes = parentNode.childNodes, i = nodes.length - 1, j = 1; i >= 0; i--) {
          var node = nodes[i];
          if (node.nodeType == 1 && (!ofType || node._countedByPrototype)) node.nodeIndex = j++;
        }
      } else {
        for (var i = 0, j = 1, nodes = parentNode.childNodes; node = nodes[i]; i++)
          if (node.nodeType == 1 && (!ofType || node._countedByPrototype)) node.nodeIndex = j++;
      }
    },


    unique: function(nodes) {
      if (nodes.length == 0) return nodes;
      var results = [], n;
      for (var i = 0, l = nodes.length; i < l; i++)
        if (!(n = nodes[i])._countedByPrototype) {
          n._countedByPrototype = Prototype.emptyFunction;
          results.push(Element.extend(n));
        }
      return Selector.handlers.unmark(results);
    },


    descendant: function(nodes) {
      var h = Selector.handlers;
      for (var i = 0, results = [], node; node = nodes[i]; i++)
        h.concat(results, node.getElementsByTagName('*'));
      return results;
    },

    child: function(nodes) {
      var h = Selector.handlers;
      for (var i = 0, results = [], node; node = nodes[i]; i++) {
        for (var j = 0, child; child = node.childNodes[j]; j++)
          if (child.nodeType == 1 && child.tagName != '!') results.push(child);
      }
      return results;
    },

    adjacent: function(nodes) {
      for (var i = 0, results = [], node; node = nodes[i]; i++) {
        var next = this.nextElementSibling(node);
        if (next) results.push(next);
      }
      return results;
    },

    laterSibling: function(nodes) {
      var h = Selector.handlers;
      for (var i = 0, results = [], node; node = nodes[i]; i++)
        h.concat(results, Element.nextSiblings(node));
      return results;
    },

    nextElementSibling: function(node) {
      while (node = node.nextSibling)
        if (node.nodeType == 1) return node;
      return null;
    },

    previousElementSibling: function(node) {
      while (node = node.previousSibling)
        if (node.nodeType == 1) return node;
      return null;
    },


    tagName: function(nodes, root, tagName, combinator) {
      var uTagName = tagName.toUpperCase();
      var results = [], h = Selector.handlers;
      if (nodes) {
        if (combinator) {

          if (combinator == "descendant") {
            for (var i = 0, node; node = nodes[i]; i++)
              h.concat(results, node.getElementsByTagName(tagName));
            return results;
          } else nodes = this[combinator](nodes);
          if (tagName == "*") return nodes;
        }
        for (var i = 0, node; node = nodes[i]; i++)
          if (node.tagName.toUpperCase() === uTagName) results.push(node);
        return results;
      } else return root.getElementsByTagName(tagName);
    },

    id: function(nodes, root, id, combinator) {
      var targetNode = $(id), h = Selector.handlers;
      if (!targetNode) return [];
      if (!nodes && root == document) return [targetNode];
      if (nodes) {
        if (combinator) {
          if (combinator == 'child') {
            for (var i = 0, node; node = nodes[i]; i++)
              if (targetNode.parentNode == node) return [targetNode];
          } else if (combinator == 'descendant') {
            for (var i = 0, node; node = nodes[i]; i++)
              if (Element.descendantOf(targetNode, node)) return [targetNode];
          } else if (combinator == 'adjacent') {
            for (var i = 0, node; node = nodes[i]; i++)
              if (Selector.handlers.previousElementSibling(targetNode) == node)
                return [targetNode];
          } else nodes = h[combinator](nodes);
        }
        for (var i = 0, node; node = nodes[i]; i++)
          if (node == targetNode) return [targetNode];
        return [];
      }
      return (targetNode && Element.descendantOf(targetNode, root)) ? [targetNode] : [];
    },

    className: function(nodes, root, className, combinator) {
      if (nodes && combinator) nodes = this[combinator](nodes);
      return Selector.handlers.byClassName(nodes, root, className);
    },

    byClassName: function(nodes, root, className) {
      if (!nodes) nodes = Selector.handlers.descendant([root]);
      var needle = ' ' + className + ' ';
      for (var i = 0, results = [], node, nodeClassName; node = nodes[i]; i++) {
        nodeClassName = node.className;
        if (nodeClassName.length == 0) continue;
        if (nodeClassName == className || (' ' + nodeClassName + ' ').include(needle))
          results.push(node);
      }
      return results;
    },

    attrPresence: function(nodes, root, attr, combinator) {
      if (!nodes) nodes = root.getElementsByTagName("*");
      if (nodes && combinator) nodes = this[combinator](nodes);
      var results = [];
      for (var i = 0, node; node = nodes[i]; i++)
        if (Element.hasAttribute(node, attr)) results.push(node);
      return results;
    },

    attr: function(nodes, root, attr, value, operator, combinator) {
      if (!nodes) nodes = root.getElementsByTagName("*");
      if (nodes && combinator) nodes = this[combinator](nodes);
      var handler = Selector.operators[operator], results = [];
      for (var i = 0, node; node = nodes[i]; i++) {
        var nodeValue = Element.readAttribute(node, attr);
        if (nodeValue === null) continue;
        if (handler(nodeValue, value)) results.push(node);
      }
      return results;
    },

    pseudo: function(nodes, name, value, root, combinator) {
      if (nodes && combinator) nodes = this[combinator](nodes);
      if (!nodes) nodes = root.getElementsByTagName("*");
      return Selector.pseudos[name](nodes, value, root);
    }
  },

  pseudos: {
    'first-child': function(nodes, value, root) {
      for (var i = 0, results = [], node; node = nodes[i]; i++) {
        if (Selector.handlers.previousElementSibling(node)) continue;
          results.push(node);
      }
      return results;
    },
    'last-child': function(nodes, value, root) {
      for (var i = 0, results = [], node; node = nodes[i]; i++) {
        if (Selector.handlers.nextElementSibling(node)) continue;
          results.push(node);
      }
      return results;
    },
    'only-child': function(nodes, value, root) {
      var h = Selector.handlers;
      for (var i = 0, results = [], node; node = nodes[i]; i++)
        if (!h.previousElementSibling(node) && !h.nextElementSibling(node))
          results.push(node);
      return results;
    },
    'nth-child':        function(nodes, formula, root) {
      return Selector.pseudos.nth(nodes, formula, root);
    },
    'nth-last-child':   function(nodes, formula, root) {
      return Selector.pseudos.nth(nodes, formula, root, true);
    },
    'nth-of-type':      function(nodes, formula, root) {
      return Selector.pseudos.nth(nodes, formula, root, false, true);
    },
    'nth-last-of-type': function(nodes, formula, root) {
      return Selector.pseudos.nth(nodes, formula, root, true, true);
    },
    'first-of-type':    function(nodes, formula, root) {
      return Selector.pseudos.nth(nodes, "1", root, false, true);
    },
    'last-of-type':     function(nodes, formula, root) {
      return Selector.pseudos.nth(nodes, "1", root, true, true);
    },
    'only-of-type':     function(nodes, formula, root) {
      var p = Selector.pseudos;
      return p['last-of-type'](p['first-of-type'](nodes, formula, root), formula, root);
    },


    getIndices: function(a, b, total) {
      if (a == 0) return b > 0 ? [b] : [];
      return $R(1, total).inject([], function(memo, i) {
        if (0 == (i - b) % a && (i - b) / a >= 0) memo.push(i);
        return memo;
      });
    },


    nth: function(nodes, formula, root, reverse, ofType) {
      if (nodes.length == 0) return [];
      if (formula == 'even') formula = '2n+0';
      if (formula == 'odd')  formula = '2n+1';
      var h = Selector.handlers, results = [], indexed = [], m;
      h.mark(nodes);
      for (var i = 0, node; node = nodes[i]; i++) {
        if (!node.parentNode._countedByPrototype) {
          h.index(node.parentNode, reverse, ofType);
          indexed.push(node.parentNode);
        }
      }
      if (formula.match(/^\d+$/)) { // just a number
        formula = Number(formula);
        for (var i = 0, node; node = nodes[i]; i++)
          if (node.nodeIndex == formula) results.push(node);
      } else if (m = formula.match(/^(-?\d*)?n(([+-])(\d+))?/)) { // an+b
        if (m[1] == "-") m[1] = -1;
        var a = m[1] ? Number(m[1]) : 1;
        var b = m[2] ? Number(m[2]) : 0;
        var indices = Selector.pseudos.getIndices(a, b, nodes.length);
        for (var i = 0, node, l = indices.length; node = nodes[i]; i++) {
          for (var j = 0; j < l; j++)
            if (node.nodeIndex == indices[j]) results.push(node);
        }
      }
      h.unmark(nodes);
      h.unmark(indexed);
      return results;
    },

    'empty': function(nodes, value, root) {
      for (var i = 0, results = [], node; node = nodes[i]; i++) {

        if (node.tagName == '!' || node.firstChild) continue;
        results.push(node);
      }
      return results;
    },

    'not': function(nodes, selector, root) {
      var h = Selector.handlers, selectorType, m;
      var exclusions = new Selector(selector).findElements(root);
      h.mark(exclusions);
      for (var i = 0, results = [], node; node = nodes[i]; i++)
        if (!node._countedByPrototype) results.push(node);
      h.unmark(exclusions);
      return results;
    },

    'enabled': function(nodes, value, root) {
      for (var i = 0, results = [], node; node = nodes[i]; i++)
        if (!node.disabled && (!node.type || node.type !== 'hidden'))
          results.push(node);
      return results;
    },

    'disabled': function(nodes, value, root) {
      for (var i = 0, results = [], node; node = nodes[i]; i++)
        if (node.disabled) results.push(node);
      return results;
    },

    'checked': function(nodes, value, root) {
      for (var i = 0, results = [], node; node = nodes[i]; i++)
        if (node.checked) results.push(node);
      return results;
    }
  },

  operators: {
    '=':  function(nv, v) { return nv == v; },
    '!=': function(nv, v) { return nv != v; },
    '^=': function(nv, v) { return nv == v || nv && nv.startsWith(v); },
    '$=': function(nv, v) { return nv == v || nv && nv.endsWith(v); },
    '*=': function(nv, v) { return nv == v || nv && nv.include(v); },
    '$=': function(nv, v) { return nv.endsWith(v); },
    '*=': function(nv, v) { return nv.include(v); },
    '~=': function(nv, v) { return (' ' + nv + ' ').include(' ' + v + ' '); },
    '|=': function(nv, v) { return ('-' + (nv || "").toUpperCase() +
     '-').include('-' + (v || "").toUpperCase() + '-'); }
  },

  split: function(expression) {
    var expressions = [];
    expression.scan(/(([\w#:.~>+()\s-]+|\*|\[.*?\])+)\s*(,|$)/, function(m) {
      expressions.push(m[1].strip());
    });
    return expressions;
  },

  matchElements: function(elements, expression) {
    var matches = $$(expression), h = Selector.handlers;
    h.mark(matches);
    for (var i = 0, results = [], element; element = elements[i]; i++)
      if (element._countedByPrototype) results.push(element);
    h.unmark(matches);
    return results;
  },

  findElement: function(elements, expression, index) {
    if (Object.isNumber(expression)) {
      index = expression; expression = false;
    }
    return Selector.matchElements(elements, expression || '*')[index || 0];
  },

  findChildElements: function(element, expressions) {
    expressions = Selector.split(expressions.join(','));
    var results = [], h = Selector.handlers;
    for (var i = 0, l = expressions.length, selector; i < l; i++) {
      selector = new Selector(expressions[i].strip());
      h.concat(results, selector.findElements(element));
    }
    return (l > 1) ? h.unique(results) : results;
  }
});

if (Prototype.Browser.IE) {
  Object.extend(Selector.handlers, {


    concat: function(a, b) {
      for (var i = 0, node; node = b[i]; i++)
        if (node.tagName !== "!") a.push(node);
      return a;
    },


    unmark: function(nodes) {
      for (var i = 0, node; node = nodes[i]; i++)
        node.removeAttribute('_countedByPrototype');
      return nodes;
    }
  });
}

function $$() {
  return Selector.findChildElements(document, $A(arguments));
}
var Form = {
  reset: function(form) {
    $(form).reset();
    return form;
  },

  serializeElements: function(elements, options) {
    if (typeof options != 'object') options = { hash: !!options };
    else if (Object.isUndefined(options.hash)) options.hash = true;
    var key, value, submitted = false, submit = options.submit;

    var data = elements.inject({ }, function(result, element) {
      if (!element.disabled && element.name) {
        key = element.name; value = $(element).getValue();
        if (value != null && element.type != 'file' && (element.type != 'submit' || (!submitted &&
            submit !== false && (!submit || key == submit) && (submitted = true)))) {
          if (key in result) {

            if (!Object.isArray(result[key])) result[key] = [result[key]];
            result[key].push(value);
          }
          else result[key] = value;
        }
      }
      return result;
    });

    return options.hash ? data : Object.toQueryString(data);
  }
};

Form.Methods = {
  serialize: function(form, options) {
    return Form.serializeElements(Form.getElements(form), options);
  },

  getElements: function(form) {
    return $A($(form).getElementsByTagName('*')).inject([],
      function(elements, child) {
        if (Form.Element.Serializers[child.tagName.toLowerCase()])
          elements.push(Element.extend(child));
        return elements;
      }
    );
  },

  getInputs: function(form, typeName, name) {
    form = $(form);
    var inputs = form.getElementsByTagName('input');

    if (!typeName && !name) return $A(inputs).map(Element.extend);

    for (var i = 0, matchingInputs = [], length = inputs.length; i < length; i++) {
      var input = inputs[i];
      if ((typeName && input.type != typeName) || (name && input.name != name))
        continue;
      matchingInputs.push(Element.extend(input));
    }

    return matchingInputs;
  },

  disable: function(form) {
    form = $(form);
    Form.getElements(form).invoke('disable');
    return form;
  },

  enable: function(form) {
    form = $(form);
    Form.getElements(form).invoke('enable');
    return form;
  },

  findFirstElement: function(form) {
    var elements = $(form).getElements().findAll(function(element) {
      return 'hidden' != element.type && !element.disabled;
    });
    var firstByIndex = elements.findAll(function(element) {
      return element.hasAttribute('tabIndex') && element.tabIndex >= 0;
    }).sortBy(function(element) { return element.tabIndex }).first();

    return firstByIndex ? firstByIndex : elements.find(function(element) {
      return ['input', 'select', 'textarea'].include(element.tagName.toLowerCase());
    });
  },

  focusFirstElement: function(form) {
    form = $(form);
    form.findFirstElement().activate();
    return form;
  },

  request: function(form, options) {
    form = $(form), options = Object.clone(options || { });

    var params = options.parameters, action = form.readAttribute('action') || '';
    if (action.blank()) action = window.location.href;
    options.parameters = form.serialize(true);

    if (params) {
      if (Object.isString(params)) params = params.toQueryParams();
      Object.extend(options.parameters, params);
    }

    if (form.hasAttribute('method') && !options.method)
      options.method = form.method;

    return new Ajax.Request(action, options);
  }
};

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

Form.Element = {
  focus: function(element) {
    $(element).focus();
    return element;
  },

  select: function(element) {
    $(element).select();
    return element;
  }
};

Form.Element.Methods = {
  serialize: function(element) {
    element = $(element);
    if (!element.disabled && element.name) {
      var value = element.getValue();
      if (value != undefined) {
        var pair = { };
        pair[element.name] = value;
        return Object.toQueryString(pair);
      }
    }
    return '';
  },

  getValue: function(element) {
    element = $(element);
    var method = element.tagName.toLowerCase();
    return Form.Element.Serializers[method](element);
  },

  setValue: function(element, value) {
    element = $(element);
    var method = element.tagName.toLowerCase();
    Form.Element.Serializers[method](element, value);
    return element;
  },

  clear: function(element) {
    $(element).value = '';
    return element;
  },

  present: function(element) {
    return $(element).value != '';
  },

  activate: function(element) {
    element = $(element);
    try {
      element.focus();
      if (element.select && (element.tagName.toLowerCase() != 'input' ||
          !['button', 'reset', 'submit'].include(element.type)))
        element.select();
    } catch (e) { }
    return element;
  },

  disable: function(element) {
    element = $(element);
    element.disabled = true;
    return element;
  },

  enable: function(element) {
    element = $(element);
    element.disabled = false;
    return element;
  }
};

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

var Field = Form.Element;
var $F = Form.Element.Methods.getValue;

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

Form.Element.Serializers = {
  input: function(element, value) {
    switch (element.type.toLowerCase()) {
      case 'checkbox':
      case 'radio':
        return Form.Element.Serializers.inputSelector(element, value);
      default:
        return Form.Element.Serializers.textarea(element, value);
    }
  },

  inputSelector: function(element, value) {
    if (Object.isUndefined(value)) return element.checked ? element.value : null;
    else element.checked = !!value;
  },

  textarea: function(element, value) {
    if (Object.isUndefined(value)) return element.value;
    else element.value = value;
  },

  select: function(element, value) {
    if (Object.isUndefined(value))
      return this[element.type == 'select-one' ?
        'selectOne' : 'selectMany'](element);
    else {
      var opt, currentValue, single = !Object.isArray(value);
      for (var i = 0, length = element.length; i < length; i++) {
        opt = element.options[i];
        currentValue = this.optionValue(opt);
        if (single) {
          if (currentValue == value) {
            opt.selected = true;
            return;
          }
        }
        else opt.selected = value.include(currentValue);
      }
    }
  },

  selectOne: function(element) {
    var index = element.selectedIndex;
    return index >= 0 ? this.optionValue(element.options[index]) : null;
  },

  selectMany: function(element) {
    var values, length = element.length;
    if (!length) return null;

    for (var i = 0, values = []; i < length; i++) {
      var opt = element.options[i];
      if (opt.selected) values.push(this.optionValue(opt));
    }
    return values;
  },

  optionValue: function(opt) {

    return Element.extend(opt).hasAttribute('value') ? opt.value : opt.text;
  }
};

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

Abstract.TimedObserver = Class.create(PeriodicalExecuter, {
  initialize: function($super, element, frequency, callback) {
    $super(callback, frequency);
    this.element   = $(element);
    this.lastValue = this.getValue();
  },

  execute: function() {
    var value = this.getValue();
    if (Object.isString(this.lastValue) && Object.isString(value) ?
        this.lastValue != value : String(this.lastValue) != String(value)) {
      this.callback(this.element, value);
      this.lastValue = value;
    }
  }
});

Form.Element.Observer = Class.create(Abstract.TimedObserver, {
  getValue: function() {
    return Form.Element.getValue(this.element);
  }
});

Form.Observer = Class.create(Abstract.TimedObserver, {
  getValue: function() {
    return Form.serialize(this.element);
  }
});

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

Abstract.EventObserver = Class.create({
  initialize: function(element, callback) {
    this.element  = $(element);
    this.callback = callback;

    this.lastValue = this.getValue();
    if (this.element.tagName.toLowerCase() == 'form')
      this.registerFormCallbacks();
    else
      this.registerCallback(this.element);
  },

  onElementEvent: function() {
    var value = this.getValue();
    if (this.lastValue != value) {
      this.callback(this.element, value);
      this.lastValue = value;
    }
  },

  registerFormCallbacks: function() {
    Form.getElements(this.element).each(this.registerCallback, this);
  },

  registerCallback: function(element) {
    if (element.type) {
      switch (element.type.toLowerCase()) {
        case 'checkbox':
        case 'radio':
          Event.observe(element, 'click', this.onElementEvent.bind(this));
          break;
        default:
          Event.observe(element, 'change', this.onElementEvent.bind(this));
          break;
      }
    }
  }
});

Form.Element.EventObserver = Class.create(Abstract.EventObserver, {
  getValue: function() {
    return Form.Element.getValue(this.element);
  }
});

Form.EventObserver = Class.create(Abstract.EventObserver, {
  getValue: function() {
    return Form.serialize(this.element);
  }
});
if (!window.Event) var Event = { };

Object.extend(Event, {
  KEY_BACKSPACE: 8,
  KEY_TAB:       9,
  KEY_RETURN:   13,
  KEY_ESC:      27,
  KEY_LEFT:     37,
  KEY_UP:       38,
  KEY_RIGHT:    39,
  KEY_DOWN:     40,
  KEY_DELETE:   46,
  KEY_HOME:     36,
  KEY_END:      35,
  KEY_PAGEUP:   33,
  KEY_PAGEDOWN: 34,
  KEY_INSERT:   45,

  cache: { },

  relatedTarget: function(event) {
    var element;
    switch(event.type) {
      case 'mouseover': element = event.fromElement; break;
      case 'mouseout':  element = event.toElement;   break;
      default: return null;
    }
    return Element.extend(element);
  }
});

Event.Methods = (function() {
  var isButton;

  if (Prototype.Browser.IE) {
    var buttonMap = { 0: 1, 1: 4, 2: 2 };
    isButton = function(event, code) {
      return event.button == buttonMap[code];
    };

  } else if (Prototype.Browser.WebKit) {
    isButton = function(event, code) {
      switch (code) {
        case 0: return event.which == 1 && !event.metaKey;
        case 1: return event.which == 1 && event.metaKey;
        default: return false;
      }
    };

  } else {
    isButton = function(event, code) {
      return event.which ? (event.which === code + 1) : (event.button === code);
    };
  }

  return {
    isLeftClick:   function(event) { return isButton(event, 0) },
    isMiddleClick: function(event) { return isButton(event, 1) },
    isRightClick:  function(event) { return isButton(event, 2) },

    element: function(event) {
      event = Event.extend(event);

      var node          = event.target,
          type          = event.type,
          currentTarget = event.currentTarget;

      if (currentTarget && currentTarget.tagName) {



        if (type === 'load' || type === 'error' ||
          (type === 'click' && currentTarget.tagName.toLowerCase() === 'input'
            && currentTarget.type === 'radio'))
              node = currentTarget;
      }
      if (node.nodeType == Node.TEXT_NODE) node = node.parentNode;
      return Element.extend(node);
    },

    findElement: function(event, expression) {
      var element = Event.element(event);
      if (!expression) return element;
      var elements = [element].concat(element.ancestors());
      return Selector.findElement(elements, expression, 0);
    },

    pointer: function(event) {
      var docElement = document.documentElement,
      body = document.body || { scrollLeft: 0, scrollTop: 0 };
      return {
        x: event.pageX || (event.clientX +
          (docElement.scrollLeft || body.scrollLeft) -
          (docElement.clientLeft || 0)),
        y: event.pageY || (event.clientY +
          (docElement.scrollTop || body.scrollTop) -
          (docElement.clientTop || 0))
      };
    },

    pointerX: function(event) { return Event.pointer(event).x },
    pointerY: function(event) { return Event.pointer(event).y },

    stop: function(event) {
      Event.extend(event);
      event.preventDefault();
      event.stopPropagation();
      event.stopped = true;
    }
  };
})();

Event.extend = (function() {
  var methods = Object.keys(Event.Methods).inject({ }, function(m, name) {
    m[name] = Event.Methods[name].methodize();
    return m;
  });

  if (Prototype.Browser.IE) {
    Object.extend(methods, {
      stopPropagation: function() { this.cancelBubble = true },
      preventDefault:  function() { this.returnValue = false },
      inspect: function() { return "[object Event]" }
    });

    return function(event) {
      if (!event) return false;
      if (event._extendedByPrototype) return event;

      event._extendedByPrototype = Prototype.emptyFunction;
      var pointer = Event.pointer(event);
      Object.extend(event, {
        target: event.srcElement,
        relatedTarget: Event.relatedTarget(event),
        pageX:  pointer.x,
        pageY:  pointer.y
      });
      return Object.extend(event, methods);
    };

  } else {
    Event.prototype = Event.prototype || document.createEvent("HTMLEvents")['__proto__'];
    Object.extend(Event.prototype, methods);
    return Prototype.K;
  }
})();

Object.extend(Event, (function() {
  var cache = Event.cache;

  function getEventID(element) {
    if (element._prototypeEventID) return element._prototypeEventID[0];
    arguments.callee.id = arguments.callee.id || 1;
    return element._prototypeEventID = [++arguments.callee.id];
  }

  function getDOMEventName(eventName) {
    if (eventName && eventName.include(':')) return "dataavailable";
    return eventName;
  }

  function getCacheForID(id) {
    return cache[id] = cache[id] || { };
  }

  function getWrappersForEventName(id, eventName) {
    var c = getCacheForID(id);
    return c[eventName] = c[eventName] || [];
  }

  function createWrapper(element, eventName, handler) {
    var id = getEventID(element);
    var c = getWrappersForEventName(id, eventName);
    if (c.pluck("handler").include(handler)) return false;

    var wrapper = function(event) {
      if (!Event || !Event.extend ||
        (event.eventName && event.eventName != eventName))
          return false;

      Event.extend(event);
      handler.call(element, event);
    };

    wrapper.handler = handler;
    c.push(wrapper);
    return wrapper;
  }

  function findWrapper(id, eventName, handler) {
    var c = getWrappersForEventName(id, eventName);
    return c.find(function(wrapper) { return wrapper.handler == handler });
  }

  function destroyWrapper(id, eventName, handler) {
    var c = getCacheForID(id);
    if (!c[eventName]) return false;
    c[eventName] = c[eventName].without(findWrapper(id, eventName, handler));
  }

  function destroyCache() {
    for (var id in cache)
      for (var eventName in cache[id])
        cache[id][eventName] = null;
  }




  if (window.attachEvent) {
    window.attachEvent("onunload", destroyCache);
  }




  if (Prototype.Browser.WebKit) {
    window.addEventListener('unload', Prototype.emptyFunction, false);
  }

  return {
    observe: function(element, eventName, handler) {
      element = $(element);
      var name = getDOMEventName(eventName);

      var wrapper = createWrapper(element, eventName, handler);
      if (!wrapper) return element;

      if (element.addEventListener) {
        element.addEventListener(name, wrapper, false);
      } else {
        element.attachEvent("on" + name, wrapper);
      }

      return element;
    },

    stopObserving: function(element, eventName, handler) {
      element = $(element);
      var id = getEventID(element), name = getDOMEventName(eventName);

      if (!handler && eventName) {
        getWrappersForEventName(id, eventName).each(function(wrapper) {
          element.stopObserving(eventName, wrapper.handler);
        });
        return element;

      } else if (!eventName) {
        Object.keys(getCacheForID(id)).each(function(eventName) {
          element.stopObserving(eventName);
        });
        return element;
      }

      var wrapper = findWrapper(id, eventName, handler);
      if (!wrapper) return element;

      if (element.removeEventListener) {
        element.removeEventListener(name, wrapper, false);
      } else {
        element.detachEvent("on" + name, wrapper);
      }

      destroyWrapper(id, eventName, handler);

      return element;
    },

    fire: function(element, eventName, memo) {
      element = $(element);
      if (element == document && document.createEvent && !element.dispatchEvent)
        element = document.documentElement;

      var event;
      if (document.createEvent) {
        event = document.createEvent("HTMLEvents");
        event.initEvent("dataavailable", true, true);
      } else {
        event = document.createEventObject();
        event.eventType = "ondataavailable";
      }

      event.eventName = eventName;
      event.memo = memo || { };

      if (document.createEvent) {
        element.dispatchEvent(event);
      } else {
        element.fireEvent(event.eventType, event);
      }

      return Event.extend(event);
    }
  };
})());

Object.extend(Event, Event.Methods);

Element.addMethods({
  fire:          Event.fire,
  observe:       Event.observe,
  stopObserving: Event.stopObserving
});

Object.extend(document, {
  fire:          Element.Methods.fire.methodize(),
  observe:       Element.Methods.observe.methodize(),
  stopObserving: Element.Methods.stopObserving.methodize(),
  loaded:        false
});

(function() {
  /* Support for the DOMContentLoaded event is based on work by Dan Webb,
     Matthias Miller, Dean Edwards and John Resig. */

  var timer;

  function fireContentLoadedEvent() {
    if (document.loaded) return;
    if (timer) window.clearInterval(timer);
    document.fire("dom:loaded");
    document.loaded = true;
  }

  if (document.addEventListener) {
    if (Prototype.Browser.WebKit) {
      timer = window.setInterval(function() {
        if (/loaded|complete/.test(document.readyState))
          fireContentLoadedEvent();
      }, 0);

      Event.observe(window, "load", fireContentLoadedEvent);

    } else {
      document.addEventListener("DOMContentLoaded",
        fireContentLoadedEvent, false);
    }

  } else {
    document.write("<script id=__onDOMContentLoaded defer src=//:><\/script>");
    $("__onDOMContentLoaded").onreadystatechange = function() {
      if (this.readyState == "complete") {
        this.onreadystatechange = null;
        fireContentLoadedEvent();
      }
    };
  }
})();
/*------------------------------- DEPRECATED -------------------------------*/

Hash.toQueryString = Object.toQueryString;

var Toggle = { display: Element.toggle };

Element.Methods.childOf = Element.Methods.descendantOf;

var Insertion = {
  Before: function(element, content) {
    return Element.insert(element, {before:content});
  },

  Top: function(element, content) {
    return Element.insert(element, {top:content});
  },

  Bottom: function(element, content) {
    return Element.insert(element, {bottom:content});
  },

  After: function(element, content) {
    return Element.insert(element, {after:content});
  }
};

var $continue = new Error('"throw $continue" is deprecated, use "return" instead');



var Position = {



  includeScrollOffsets: false,



  prepare: function() {
    this.deltaX =  window.pageXOffset
                || document.documentElement.scrollLeft
                || document.body.scrollLeft
                || 0;
    this.deltaY =  window.pageYOffset
                || document.documentElement.scrollTop
                || document.body.scrollTop
                || 0;
  },


  within: function(element, x, y) {
    if (this.includeScrollOffsets)
      return this.withinIncludingScrolloffsets(element, x, y);
    this.xcomp = x;
    this.ycomp = y;
    this.offset = Element.cumulativeOffset(element);

    return (y >= this.offset[1] &&
            y <  this.offset[1] + element.offsetHeight &&
            x >= this.offset[0] &&
            x <  this.offset[0] + element.offsetWidth);
  },

  withinIncludingScrolloffsets: function(element, x, y) {
    var offsetcache = Element.cumulativeScrollOffset(element);

    this.xcomp = x + offsetcache[0] - this.deltaX;
    this.ycomp = y + offsetcache[1] - this.deltaY;
    this.offset = Element.cumulativeOffset(element);

    return (this.ycomp >= this.offset[1] &&
            this.ycomp <  this.offset[1] + element.offsetHeight &&
            this.xcomp >= this.offset[0] &&
            this.xcomp <  this.offset[0] + element.offsetWidth);
  },


  overlap: function(mode, element) {
    if (!mode) return 0;
    if (mode == 'vertical')
      return ((this.offset[1] + element.offsetHeight) - this.ycomp) /
        element.offsetHeight;
    if (mode == 'horizontal')
      return ((this.offset[0] + element.offsetWidth) - this.xcomp) /
        element.offsetWidth;
  },



  cumulativeOffset: Element.Methods.cumulativeOffset,

  positionedOffset: Element.Methods.positionedOffset,

  absolutize: function(element) {
    Position.prepare();
    return Element.absolutize(element);
  },

  relativize: function(element) {
    Position.prepare();
    return Element.relativize(element);
  },

  realOffset: Element.Methods.cumulativeScrollOffset,

  offsetParent: Element.Methods.getOffsetParent,

  page: Element.Methods.viewportOffset,

  clone: function(source, target, options) {
    options = options || { };
    return Element.clonePosition(target, source, options);
  }
};

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

if (!document.getElementsByClassName) document.getElementsByClassName = function(instanceMethods){
  function iter(name) {
    return name.blank() ? null : "[contains(concat(' ', @class, ' '), ' " + name + " ')]";
  }

  instanceMethods.getElementsByClassName = Prototype.BrowserFeatures.XPath ?
  function(element, className) {
    className = className.toString().strip();
    var cond = /\s/.test(className) ? $w(className).map(iter).join('') : iter(className);
    return cond ? document._getElementsByXPath('.//*' + cond, element) : [];
  } : function(element, className) {
    className = className.toString().strip();
    var elements = [], classNames = (/\s/.test(className) ? $w(className) : null);
    if (!classNames && !className) return elements;

    var nodes = $(element).getElementsByTagName('*');
    className = ' ' + className + ' ';

    for (var i = 0, child, cn; child = nodes[i]; i++) {
      if (child.className && (cn = ' ' + child.className + ' ') && (cn.include(className) ||
          (classNames && classNames.all(function(name) {
            return !name.toString().blank() && cn.include(' ' + name + ' ');
          }))))
        elements.push(Element.extend(child));
    }
    return elements;
  };

  return function(className, parentElement) {
    return $(parentElement || document.body).getElementsByClassName(className);
  };
}(Element.Methods);

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

Element.ClassNames = Class.create();
Element.ClassNames.prototype = {
  initialize: function(element) {
    this.element = $(element);
  },

  _each: function(iterator) {
    this.element.className.split(/\s+/).select(function(name) {
      return name.length > 0;
    })._each(iterator);
  },

  set: function(className) {
    this.element.className = className;
  },

  add: function(classNameToAdd) {
    if (this.include(classNameToAdd)) return;
    this.set($A(this).concat(classNameToAdd).join(' '));
  },

  remove: function(classNameToRemove) {
    if (!this.include(classNameToRemove)) return;
    this.set($A(this).without(classNameToRemove).join(' '));
  },

  toString: function() {
    return $A(this).join(' ');
  }
};

Object.extend(Element.ClassNames.prototype, Enumerable);

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

Element.addMethods();
// --------------------------------------------------------------------------------
// global.js: end JavaScript file: '/js/prototype.js'

// global.js: begin JavaScript file: '/js/lightboxes.js'
// ================================================================================
Ss = window.Ss || {};
Ss.Lightbox = {};
var activeLightboxPhotoIds = new Hash();
var fallbackLightboxId;
var lightboxContentsPopulated = false;
var lockChangeLightboxDialog = false;

var isSubscribed = false;
var lightboxAlertLimit = 10;

var loadingHTML = 'loading';
var enhanced_lightboxContentsPopulated= false;

function internalVoid() {
}

function setPointer() {
}

function _debug(message) {
	return;
}


/****************************
 * LIGHTBOX COMPONENT CLASSES
 */

/* Lightbox Controls */
Ss.Lightbox.Control = Class.create({
	initialize: function(pulldown, action) {
		this.pulldown = pulldown;
		this.action = action;
	},
	refresh: function(lightboxes) {
		this.pulldown.clearContent();
		this.pulldown.appendJson(this._transform(lightboxes));
	},
	getPulldown: function() { return this.pulldown; },

	_transform: function(lbs) {
		var o = this;
		return lbs.map(function(l){ return {"name":l.title,"onclick":function(){o.action(l.id);/*o.pulldown.collapse();*/}};});
	}
});

/* Adder (Adds the selected photo to a lightbox) */
Ss.Lightbox.Adder = Class.create(Ss.Lightbox.Control, {
	refresh: function($super, lightboxes) {
		$super(lightboxes);
		this.pulldown.appendContent(
			new Element('A')
	            .addClassName('new_lightbox_link')
				.update($t("LB_NEW_LIGHTBOX", "New Lightbox"))
				.observe('click', 
					function() {
						mouseoverEnabled = false;
						createLightbox('untitled', selectedPhotoId); //global var embedded in the page 	
					}
				));
	}
});

/* Switcher (Selects the active lightbox) */
Ss.Lightbox.Switcher = Class.create(Ss.Lightbox.Control, {
	refresh: function($super, activeLightbox, inactiveLightboxes) {
		$super(inactiveLightboxes);
		this.writeActiveLightbox(activeLightbox);
	},
	writeActiveLightbox: function(lightbox) {
	    if(!Object.isHash(lightbox)) {
	        return;
	    }
		this.pulldown.replaceTrigger("<strong>" + lightbox.get('title') + (lightbox.get('count') ? "</strong> (" + lightbox.get('count') + ")" : ''));
		if ($('lightbox_static_title')) {
			$('lightbox_static_title').hide();
		}
	},
	setStyle: function(styles) { this.pulldown.trigger.setStyle(styles); }
});


/************************* 
 * Lightbox Data Retrieval
 **/
function getAllLightboxes() { //returns all lightbox objects sorted by mod. date
	return lightboxes.values().sortBy(function(l){return -l.last_modified;});
}

function getInactiveLightboxes() { //returns lightbox objects (excluding active) sorted by mod. date
	return getAllLightboxes().reject(function(l){return l.id == activeLightboxId;});
}

function getActiveLightbox() { // returns the active lightbox
	return activeLightbox || lightboxes.get(activeLightboxId);
}


/**************************************
 * LIGHTBOX ACTIONS: remove, copy, move 
 * Passed into lightbox components
 **/
function selectLightbox(id) {
	(lightboxPreviewSize == 'full' ? redirectLightboxPage(id) : refreshLightboxContents(id));
}

function removeSelectedPhotos(lightboxId, forMove) {
	
	var selectedPhotoCount = selectedLightboxPhotos.keys().length;
	if (selectedPhotoCount == 0) {
		alert( $t('LB_SELECT_TO_REMOVE', "Please select some photos to remove") );
		return;
	}

	if (!forMove) {
		var confirmationMessage;
		if (selectedPhotoCount == 1) 
			confirmationMessage = $t('LB_REMOVE_CONFIRM_SINGULAR', "Are you sure you want to remove this photo from this lightbox?");
		else 
			confirmationMessage = $t('LB_REMOVE_CONFIRM_PLURAL', "Are you sure you want to remove these __SELECTED_PHOTO_COUNT__ photos from this lightbox?", { __SELECTED_PHOTO_COUNT__: selectedPhotoCount});

		if (!confirm(confirmationMessage)) {
			return;
		}
	}

	selectedLightboxPhotos.keys().each( function(photoId) {
		$('lightbox-photo-' + photoId).addClassName('semi-transparent');
	} );

	var photoIdsList = selectedLightboxPhotos.keys().join(',');
	new Ajax.Request( '/lightbox/remove_photos_from_lightbox.html', {
		parameters: {
			photo_ids: photoIdsList,
			lightbox_id: lightboxId
		},
		onFailure: function(transport) {
			alert( $t('LB_TECHNICAL_ERROR_REMOVING', "Unfortunately, there was a technical error removing these photos") );
		},
		onSuccess: function(transport) {
			var removalCount = 0;
			selectedLightboxPhotos.keys().each( function(photoId) {
				selectedLightboxPhotos.unset(photoId);
				activeLightboxPhotoIds.unset(photoId);
				$('lightbox-photo-' + photoId).hide();
				removalCount++;
			} );
			activeLightbox.set('count', activeLightbox.get('count') - removalCount);
			Ss.Lightbox.switcher.writeActiveLightbox(getActiveLightbox());
			exportLightboxData();
			if(forMove) {
				var confirmationMessage = (selectedPhotoCount == 1 ? 
					$t('LB_MOVE_SUCCESS_SINGULAR', "That photo has been moved to that lightbox"):
					$t('LB_MOVE_SUCCESS_PLURAL', "Those photos have been moved to that lightbox"));
				
				alert(confirmationMessage);
			}
			
            if(Ss.search.enhanced) {
                var lightboxContent = $('main-lightbox-cell').innerHTML;
                Ss.search.lightboxes.store(lightboxContent);
                Ss.search.lightboxes.updateCount();
            }

		}
	} );
}

function copySelectedPhotos(lightboxId, forMove) {
	var selectedPhotoCount = selectedLightboxPhotos.keys().length;
	var photoIdsList = selectedLightboxPhotos.keys().join(',');
	new Ajax.Request( '/lightbox/copy_photos_to_lightbox.html', {
		parameters: {
			photo_ids: photoIdsList,
			lightbox_id: lightboxId
		},
		onFailure: function(transport) {
			alert( $t('LB_TECHNICAL_ERROR_COPYING', "Unfortunately, there was a technical error copying these photos") );
		},
		onSuccess: function(transport) {
			if (forMove) {
				removeSelectedPhotos(activeLightboxId, true);
			} else {
				var confirmationMessage;
				if (selectedPhotoCount == 1) 
					confirmationMessage = $t('LB_COPY_SUCCESS_SINGULAR', "That photo has been copied to that lightbox");
				else 
					confirmationMessage = $t('LB_COPY_SUCCESS_PLURAL', "Those __SELECTED_PHOTO_COUNT__ photos have been copied to that lightbox", { __SELECTED_PHOTO_COUNT__: selectedPhotoCount });

				alert(confirmationMessage);

				selectedLightboxPhotos.keys().each( function(photoId) {
					togglePhotoSelection(photoId);

					selectedPhotoCount++;
				} );
			}
		}
	} );
}

function requestMoveSelectedPhotos(lightboxId) {
	if (lightboxes.keys().length <= 1) {
		alert( $t('LB_NO_OTHER_LIGHTBOXES_MOVE', "You don't have any other lightboxes to move to") );
		return;
	}
	var selectedPhotoCount = selectedLightboxPhotos.keys().length;
	if (selectedPhotoCount < 1) {
		alert( $t('LB_SELECT_PHOTOS_MOVE', "Please select some photos to move") );
		return;
	}
	copySelectedPhotos(lightboxId, true);
}

function requestCopySelectedPhotos(lightboxId) {
	if (lightboxes.keys().length <= 1) {
		alert( $t('LB_NO_OTHER_LIGHTBOXES_COPY', "You don't have any other lightboxes to copy to") );
		return;
	}
	var selectedPhotoCount = selectedLightboxPhotos.keys().length;
	if (selectedPhotoCount < 1) {
		alert( $t('LB_SELECT_PHOTOS_COPY', "Please select some photos to copy") );
		return;
	}
	copySelectedPhotos(lightboxId, false);	
}

function moveSelectedPhotos(lightboxId) {
	copySelectedPhotos(lightboxId, true);

}

function addImageToLightbox(lightboxId, showRenameDialog, photoIdOverride) {

	var lightboxPulldown = Ss.Lightbox.singleAdder || Ss.Lightbox.multipleAdder;
	
	var showLoading = function() {
		if(lightboxPulldown) {
			lightboxPulldown.getPulldown().showLoading();
		}
	};
	
	var hideLoading = function() {
		if(lightboxPulldown) {
			lightboxPulldown.getPulldown().hideLoading();
			lightboxPulldown.getPulldown().collapse();
		}	
	};
	

	mouseoverEnabled = false;
	
	var photoId = photoIdOverride ? photoIdOverride : selectedPhotoId;

	if (!showRenameDialog && lightboxId != activeLightboxId) {
	    var sizeOverride = (lightboxPreviewSize == 'expanded' ? 'expanded' : 'preview');
		refreshLightboxContents(lightboxId, false, photoId, sizeOverride);
		hideLoading();
		return;
	}

	if (lightboxId == activeLightboxId && activeLightboxPhotoIds.get(photoId)) {
		if (lightboxPreviewSize == 'minimized') {
			setLightboxPreviewSize('preview');
		}
		alert( $t('LB_ALREADY_IN_LIGHTBOX', 'That photo is already in this lightbox') );
		hideLoading();
		return;
	}

	if (lightboxId == activeLightboxId && lightboxContentsPopulated) {
		$('lightbox-contents-table').innerHTML = imageTemplateHTML.replace(/<!-- photo_id -->/g, photoId) + $('lightbox-contents-table').innerHTML;
	}
	
	new Ajax.Updater('lightbox-thumb-' + photoId, '/lightbox/add_image_to_lightbox.html', {
		parameters: {
			photo_id: photoId,
			lightbox_id: lightboxId,
			src: getSelectedPhotoSrc(photoId)
		},
		onSuccess: function(transport) {
			activeLightboxPhotoIds.set(photoId, true);
			if (lightboxId != activeLightboxId) {
				lightboxPreviewSize = 'preview';
				$('lightbox-preview-container').show();
				if (showRenameDialog) {
					setLightboxPreviewSize('preview', false, lightboxId, true);
				}
			} else {

				if (lightboxPreviewSize == 'minimized') {
					setLightboxPreviewSize('preview');
				}
			}
			activeLightbox.set('count', parseInt(activeLightbox.get('count')) + 1);
			Ss.Lightbox.switcher.writeActiveLightbox(getActiveLightbox());
			exportLightboxData();

			toggleLightboxLimitAlert();

			hideLoading();
			
		},
		onComplete: function() {
            if(Ss.search.enhanced && lightboxPreviewSize != 'full') {
                var lightboxContent = $('main-lightbox-cell').innerHTML;
                Ss.search.lightboxes.store(lightboxContent);
                Ss.search.lightboxes.updateCount();
            }
		},
		onFailure: function(transport) {
			alert( $t('LB_TECHNICAL_ERROR_ADDING', "Unfortunately, there was a technical error adding the image to your lightbox") );
		}
	} );

	logEventLightboxAdd({ photo_id: photoId, lightbox_id: lightboxId });
}

function logEventLightboxAdd(args) {
	var src = getSelectedPhotoSrc(args.photo_id).split('-');
	var page = src[1];
	var position = src[2];

	window.Ss.tracker.logEvent('lightbox', {
		photo_id:       args.photo_id,
		lightbox_id:    args.lightbox_id,
		page_number:    page,
		image_position: position
	});
}


/****************************
 * LIGHTBOX INTERFACE UPDATES
 **/
function refreshLightboxListings() {
	if(Ss.Lightbox.singleAdder) {
		Ss.Lightbox.singleAdder.refresh(getAllLightboxes());
	}

	if(Ss.Lightbox.switcher) {
		Ss.Lightbox.switcher.refresh(getActiveLightbox(),getInactiveLightboxes());
	}
	
	if(Ss.Lightbox.copier) {
		Ss.Lightbox.copier.refresh(getInactiveLightboxes());
	}
	
	if(Ss.Lightbox.mover) {
		Ss.Lightbox.mover.refresh(getInactiveLightboxes());
	}
}

function refreshLightboxContents(lightboxId, showRenameDialog, addPhotoId, overrideSize) {

	if(!lightboxId) {
		return;
	}
	
	var size = overrideSize ? overrideSize : lightboxPreviewSize;

	Ss.Lightbox.switcher.getPulldown().showLoading();
	
	new Ajax.Request( '/lightbox/refresh_lightbox.html', {
		method: 'GET',
		parameters: {
			lightbox_id: lightboxId,
			size: size,
			rand: new Date().getTime(),
			language: document.language,
			output_format: 'javascript'
		},
		evalScripts: true,
		onComplete: function(transport) {
			if (showRenameDialog) {
				showLightboxDialog('rename');
			}
			selectedLightboxPhotos.keys().each( function(photoId) {
				selectedLightboxPhotos.unset(photoId);
			} );

			activeLightboxId = lightboxId;
			if (lightboxes.get(lightboxId)) {
				lightboxes.get(lightboxId).last_modified = parseInt(new Date().getTime()/1000);
			}
		
			refreshLightboxListings();
			
			if (size != 'minimized') {
				lightboxContentsPopulated = true;
			}

			if (addPhotoId) {
				addImageToLightbox(lightboxId, false, addPhotoId);

			} else if (size != 'minimized') {
				exportLightboxData();
			}
			Ss.Lightbox.switcher.getPulldown().hideLoading();
		},
		onFailure: function(transport) {
			alert("Unfortunately there was a technical problem loading this lightbox");
		}
	} );
}

function generateLightboxListing(lightboxes, onclickAction, dontShowActive) {
	var output = '';
	var t = new Template('<div onmouseover="this.style.backgroundColor = \'#f0f4ff\'" onmouseout="this.style.backgroundColor = \'white\'" onclick="mouseoverEnabled = false; ' + onclickAction + '(#{id})" class="lightbox-dialog-link">#{title}</div>');
	lightboxes.values().sortBy( function(lightbox) {
		return lightbox.last_modified;
	} )
	.reverse()
	.each( function(lightbox) {
		if (!(dontShowActive && lightbox.id == activeLightboxId)) {
			output += t.evaluate(lightbox);
		}
	} );
	return output;
}

/***/

function $t(translationId, english, substitutions) {
	if (document.language == 'en' && !substitutions) {
		return english;
	} else {
		var translation;
		if (document.language == 'en') 
			translation = english;
		else {
			translation = document.translations[translationId];
			if (!translation) {
				translation = english;
			}
		}
		$H(substitutions).keys().each( function(substitutionKey) {
			var subRegex = new RegExp(substitutionKey, 'g');
			translation = translation.replace(subRegex, substitutions[substitutionKey]);
		} );
		return translation;
	}
}

function setLightboxPreviewSize(size, addPhotoId, refreshLightboxId, showRenameDialog) {	
	

	if (size == 'closed') {
		$('lightbox-preview-container').hide();
		lightboxPreviewSize = 'minimized';
		new Ajax.Request('/lightbox/set_preview_size.html', { 
			parameters: { size: 'closed' }
		} );
		if(Ss.search.enhanced) {
		    Ss.search.lightboxes.storeSize(size);
		}
		return;
	}


    if(Ss.search.enhanced && lightboxPreviewSize == 'closed' && size == 'preview') {
		lightboxPreviewSize = size;
		if (!lightboxContentsPopulated) {
			refreshLightboxContents(activeLightboxId, false, addPhotoId);
		}
    }
    

	if (lightboxPreviewSize == 'minimized' && size == 'preview') {
		lightboxPreviewSize = size;
		if (!lightboxContentsPopulated) {
			refreshLightboxContents(activeLightboxId, false, addPhotoId);
		}
	}

	$('lightbox-preview-container').show();

	if (size != 'full') {
		new Ajax.Request('/lightbox/set_preview_size.html', { 
			parameters: { size: size }
		} );
	}

	var conf = $H(lightboxElementsConf[size]);

	$('lightbox-preview-container').style.height = conf.get('preview_container_height');
	$('lightbox-contents-table').style.height = conf.get('contents_table_height') + "px";

	$H(lightboxElementsConf[size]['child_visibility']).each( function(pair) {
		if (pair.value) {
			$(pair.key).show();
		} else {
			$(pair.key).hide();
		}
	} );

	lightboxPreviewSize = size;

	if (refreshLightboxId) {
		refreshLightboxContents(refreshLightboxId, showRenameDialog);
	}
	

	$H(lightboxElementsConf).keys().each(function(sizeKey){$('lightbox-preview-container').removeClassName(sizeKey + "_lightbox");})
	$('lightbox-preview-container').addClassName(size + "_lightbox");

	if(Ss.search.enhanced && size != 'full') {
	    var lightboxContent = $('main-lightbox-cell').innerHTML;
	    Ss.search.lightboxes.store(lightboxContent);
	    Ss.search.lightboxes.storeSize(lightboxPreviewSize);
	}

}

function updateLightboxNameDisplay(newName) {
	if (!newName) {
		newName = $('lightbox-new-name-input').value;
	}

	$('rename-message').innerHTML = '';
	
	newName = newName.replace(/^\s+/, '');
	newName = newName.replace(/\s+/g, ' ');

	$('lightbox-new-name-input').value = newName;

	Ss.Lightbox.switcher.getPulldown().replaceTrigger(newName);
}

function resetLightboxNameDisplay() {
	Ss.Lightbox.switcher.writeActiveLightbox(getActiveLightbox());
	Ss.Lightbox.switcher.setStyle({color: "#383838"});
}

function showLightboxDialog(action) {
	hideLightboxDialogs();

	$(action + '-lightbox-link').className = 'selected-lightbox-link';
	$(action + '-lightbox-dialog').show();
	$(action + '-lightbox-dialog').style.zIndex = parseInt(new Date().getTime()/1000); 	// to appease IE6
	$('lightbox-input-bar').show();

	if (action == 'rename') {
		Ss.Lightbox.switcher.setStyle({color: '#a0a0a0'});
		$('lightbox-new-name-input').focus();
	} else {

		Ss.Lightbox.switcher.setStyle({color: '#383838'});
	}

	if (action == 'share') {
		$('share-lightbox-input').focus();

	} else if (action == 'send') {
		$('send-lightbox-input').focus();
	}
}

function hideLightboxDialogs() {
	$('lightbox-input-bar').hide();
	new Array('send', 'share', 'rename', 'delete', 'alert').each( 
		function(action) {
			$(action + '-lightbox-dialog').hide();
			$(action + '-lightbox-link').className = 'inactive-lightbox-link';
		}
	);
}

function sendLightbox(lightboxId, emailAddress) {

	var error = validateEmailAddress(emailAddress);
	if (error) {
		$('send-message').innerHTML = error;
		return;
	}

	new Ajax.Request( '/lightbox/send_email.html', {
		parameters: {
			lightbox_id: lightboxId,
			email_address: emailAddress
		},
		onFailure: function(transport) {
			alert( $t('LB_TECHNICAL_ERROR_SENDING', "Unfortunately, there was a technical error sending this lightbox") );
		},
		onSuccess: function(transport) {
			alert("This lightbox has been sent to email address: " + emailAddress);
			hideLightboxDialogs();
		}
	} );
}

function renameLightbox(lightboxId, newName) {

	newName = newName.replace(/\s+$/, '');

	var error = validateLightboxName(newName);

	if (!lightboxes.get(lightboxId) || (lightboxes.get(lightboxId) && lightboxes.get(lightboxId).title != newName)) {	
		if (error) {
			$('rename-message').innerHTML = error;
			return;
		}
		new Ajax.Request( '/lightbox/rename_lightbox.html', {
			parameters: {
				lightbox_id: lightboxId,
				new_name: newName
			},
			onFailure: function(transport) {
				alert( $t('LB_TECHNICAL_ERROR_RENAMING', "Unfortunately, there was a technical error changing the lightbox name") );
				Ss.Lightbox.switcher.getPulldown().replaceTrigger(lightboxes.get(lightboxId).title);
			},
			onSuccess: function(transport) {
				lightboxTitle = newName;
				var lightbox = lightboxes.get(lightboxId);
				if (lightbox) {
					lightbox.title = newName;
				}
				activeLightbox.set('title', newName);
				exportLightboxData();
				refreshLightboxListings();
				Ss.Lightbox.switcher.writeActiveLightbox(getActiveLightbox());
			}
		} );
	}
	Ss.Lightbox.switcher.setStyle({color: "#383838"});
	hideLightboxDialogs();
}

function shareLightbox(lightboxId, shareUsername) {

	new Ajax.Request( '/lightbox/share_lightbox.html', {
		parameters: {
			lightbox_id: lightboxId,
			share_username: shareUsername,
			title: lightboxes.get(lightboxId).title
		},
		onFailure: function(transport) {
			alert( $t('LB_TECHNICAL_ERROR_SHARING', "Unfortunately, there was a technical error sharing this lightbox") );
		},
		onSuccess: function(transport) {
			var r = eval(transport.responseText);
			if (r.error) {
				$('share-message').innerHTML = r.error;
			} else {
				alert( $t('LB_SHARE_SUCCESS', "This lightbox has been shared with Shutterstock user: __SHARE_USERNAME__", { __SHARE_USERNAME__: shareUsername }) );
				hideLightboxDialogs();
			}
		}
	} );
}

function createLightbox(lightboxName, photoId) {
	$('lightbox-contents-table').innerHTML = loadingHTML;
	Ss.Lightbox.switcher.getPulldown().replaceTrigger("-untitled- (0)");
	Ss.Lightbox.switcher.setStyle({color: "#a0a0a0"});

	new Ajax.Request( '/lightbox/create_lightbox.html', {
		parameters: {
			lightbox_name: lightboxName
		},
		onSuccess: function(transport) {
			var lightboxId = eval(transport.responseText);
			addImageToLightbox(lightboxId, true);
			lightboxes.set(lightboxId, { title: 'untitled', id: lightboxId, last_modified: parseInt(new Date().getTime()/1000) });
			$('show-lightbox-preview-tab').show();
		},

		onFailure: function(transport) {
			alert( $t('LB_TECHNICAL_ERROR_CREATING', "Unfortunately, there was a technical error creating this lightbox") );
		}
	} );
}

function deleteLightbox(lightboxId) {

	if ($H(selectedLightboxPhotos).keys().length) {
		var proceed = confirm( $t('LB_SELECTED_PHOTOS_CONFIRM_DELETE', "You have selected some photos but you've chosen to delete this entire lightbox.  Are you sure that's what you mean to do?") );
		if (!proceed) {
			hideLightboxDialogs();
			return;
		}
	}
	new Ajax.Request( '/lightbox/delete_lightbox.html', {
		parameters: {
			lightbox_id: lightboxId
		},
		onFailure: function(transport) {
			alert( $t('LB_TECHNICAL_ERROR_DELETING', "Unfortunately, there was a technical error deleting this lightbox") );
		},
		onSuccess: function(transport) {
			lightboxes.unset(lightboxId);
			alert( $t('LB_LIGHTBOX_DELETED', "This lightbox has been deleted") );

			var fallbackLightboxId = (lightboxes.size() > 0 ? lightboxes.values().sortBy( function(lightbox) { return lightbox.last_modified } ).reverse()[0].id : null);

			refreshLightboxListings();
			if (fallbackLightboxId) {
				selectLightbox(fallbackLightboxId);
			} else {
				setLightboxPreviewSize('closed');
				$('show-lightbox-preview-tab').hide();
			}
		}
	} );

}

function togglePhotoSelection(photoId) {

	if (selectedLightboxPhotos.get(photoId)) {
		selectedLightboxPhotos.unset(photoId);
		$('lightbox-photo-' + photoId).removeClassName('selected-lightbox-photo');
		$('lightbox-photo-checkbox-' + photoId).checked = false;
		
	} else {
		selectedLightboxPhotos.set(photoId, true);
		$('lightbox-photo-' + photoId).addClassName('selected-lightbox-photo');
		$('lightbox-photo-checkbox-' + photoId).checked = true;
	}
}


function redirectLightboxPage(lightboxId) {
	location.href = '/lightboxes.mhtml?lightbox_id=' + lightboxId;
}

function toggleElementVisibility(elementId) {
	if ($(elementId).visible()) {
		$(elementId).hide();
	} else {
		$(elementId).show();
	}
}

function toggleLightboxLimitAlert() {

	if ( ! isSubscribed && activeLightbox.get('count') >= lightboxAlertLimit ) {
		if ( activeLightbox.get('count') % lightboxAlertLimit == 0 ) {
			showLightboxDialog('alert');
		}
	}

}


function validateLightboxName(name) {

	var errorCode;
	
	if (name.length > 24) {
		errorCode = $t('LB_NAME_TOO_LONG', 'This lightbox name is too long');
	} else if (!name) {
		errorCode = $t('LB_NAME_TOO_SHORT', 'Please enter a name for this lightbox');

	} else if (name.match(/[<>]/)) {
		errorCode = $t('LB_NAME_BAD_CHARS', 'Lightbox names can only contain numbers and letters');

	} else {
		lightboxes.keys().each( function(lightboxId) {
			if (lightboxes.get(lightboxId) && lightboxes.get(lightboxId).title == name) {
				errorCode = $t('LB_NAME_ALREADY_EXISTS', 'You already have a lightbox with this name');
			}
		} );
	}

	return errorCode;
}

function validateEmailAddress(address) {
	var errorCode;
	if (!address.match(/[\w.-]+\@[\w.-]+\.[\w]{2,5}/)) {
		errorCode = $t('LB_INVALID_EMAIL', "This email address does not appear to be a valid one");
	}

	return errorCode;
}

function acceptLightbox(lightboxAccessId, lightboxName) {

	var errorCode = validateLightboxName(lightboxName);
	
	if (errorCode) {
		$('lightbox-share-prompt-' + lightboxAccessId).hide();
		$('lightbox-share-rename-' + lightboxAccessId).show();
	} else {
		new Ajax.Updater('lightboxes-overview-container', '/lightbox/accept_lightbox.html', {
			parameters: { 
				lightbox_access_id: lightboxAccessId,
				title: lightboxName
			},
			onFailure: function() {
				alert( $t('LB_TECHNICAL_ERROR_ACCEPTING', "Unfortunately, there was a technical error accepting this lightbox") );
			}
		} );
	}
}

function denyLightbox(lightboxAccessId) {
	new Ajax.Updater('lightboxes-overview-container', '/lightbox/deny_lightbox.html', {
		parameters: { lightbox_access_id: lightboxAccessId },
		onFailure: function() {
			alert( $t('LB_TECHNICAL_ERROR_DENYING', "Unfortunately, there was a technical error denying this lightbox") );
		}
	} );
}

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

function exportLightboxData() {
	if (activeLightbox) {
		document.cookie = "active_lightbox=" + escape(activeLightbox.toJSON()) + "; path=/";
	} else {

	}
}

function importLightboxData() {
	new Ajax.Request('/lightbox/import_lightbox_data.html', {
		onSuccess: function(transport) {
			refreshLightboxPreview();
		},
		onComplete: function(transport) {
			refreshLightboxListings();
		}
	} );
}

function setLightboxFreshTime(timestamp) {
	document.cookie = "lightboxes_fresh_time=" + timestamp + "; path=/";
}

function restoreLightboxPreview() {
	var lightboxId;
	if (!activeLightboxId) {
		lightboxId = lightboxes.values().sortBy( function(lightbox) { return lightbox.last_modified } ).reverse()[0].id;
	}
	setLightboxPreviewSize('preview', false, lightboxId);
}
// --------------------------------------------------------------------------------
// global.js: end JavaScript file: '/js/lightboxes.js'

// global.js: begin JavaScript file: '/js/scriptaculous.js'
// ================================================================================



//







//


//







//


var Scriptaculous = {
  Version: '1.9.0',
  require: function(libraryName) {
    try{

      document.write('<script type="text/javascript" src="'+libraryName+'"><\/script>');
    } catch(e) {

      var script = document.createElement('script');
      script.type = 'text/javascript';
      script.src = libraryName;
      document.getElementsByTagName('head')[0].appendChild(script);
    }
  },
  REQUIRED_PROTOTYPE: '1.6.0.3',
  load: function() {
    function convertVersionString(versionString) {
      var v = versionString.replace(/_.*|\./g, '');
      v = parseInt(v + '0'.times(4-v.length));
      return versionString.indexOf('_') > -1 ? v-1 : v;
    }

    if((typeof Prototype=='undefined') ||
       (typeof Element == 'undefined') ||
       (typeof Element.Methods=='undefined') ||
       (convertVersionString(Prototype.Version) <
        convertVersionString(Scriptaculous.REQUIRED_PROTOTYPE)))
       throw("script.aculo.us requires the Prototype JavaScript framework >= " +
        Scriptaculous.REQUIRED_PROTOTYPE);

    var js = /scriptaculous\.js(\?.*)?$/;
    $$('script[src]').findAll(function(s) {
      return s.src.match(js);
    }).each(function(s) {
      var path = s.src.replace(js, ''),
      includes = s.src.match(/\?.*load=([a-z,]*)/);
      (includes ? includes[1] : 'builder,effects,dragdrop,controls,slider,sound').split(',').each(
       function(include) { Scriptaculous.require(path+include+'.js') });
    });
  }
};

Scriptaculous.load();
// --------------------------------------------------------------------------------
// global.js: end JavaScript file: '/js/scriptaculous.js'

// global.js: begin JavaScript file: '/js/controls.js'
// ================================================================================









//







//








//












Object.extend(Event, {
  KEY_SHIFT:    16,
  KEY_CTRL:     17,
  KEY_ALT:      18,
  KEY_CMD:      91
});

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

    this.noChangeKeys = [
      Event.KEY_TAB,
      Event.KEY_RETURN,
      Event.KEY_ESC,
      Event.KEY_LEFT,
      Event.KEY_UP,
      Event.KEY_RIGHT,
      Event.KEY_DOWN,
      Event.KEY_PAGEUP,
      Event.KEY_HOME,
      Event.KEY_END,
      Event.KEY_PAGEDOWN,
      Event.KEY_INSERT,
      Event.KEY_ALT,
      Event.KEY_CTRL,
      Event.KEY_SHIFT,
      Event.KEY_CMD
    ];

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

    this.options.paramName    = this.options.paramName || this.element.name;
    this.options.tokens       = this.options.tokens || [];
    this.options.frequency    = 0;
    this.options.minChars     = this.options.minChars || 1;
    this.options.onShow       = this.options.onShow ||
      function(element, update){
        if(!$(update).style.position || $(update).style.position=='absolute') {
          $(update).style.position = 'absolute';
          Position.clone(element, $(update), {
            setHeight: false,
            offsetTop: element.offsetHeight
          });
        }
        $(update).show();
      };
    this.options.onHide = this.options.onHide ||
      function(element, update){ update.hide() };

    if(typeof(this.options.tokens) == 'string')
      this.options.tokens = new Array(this.options.tokens);

    if (!this.options.tokens.include('\n'))
      this.options.tokens.push('\n');

    this.observer = null;

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

    Element.hide(this.update);

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

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

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

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

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

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

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

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

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

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

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

  onBlur: function(event) {

    setTimeout(this.hide.bind(this), 250);
    this.hasFocus = false;
    this.active = false;
  },

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

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

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

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

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

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

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

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

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

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

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

      this.stopIndicator();
      this.index = -1;

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

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

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

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

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

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

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

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

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

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

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

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

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




//




//


//







//

//




//


//





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

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

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

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

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

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

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

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

// global.js: begin JavaScript file: '/js/effects.js'
// ================================================================================







//



Element.collectTextNodes = function(element) {
  return $A($(element).childNodes).collect( function(node) {
    return (node.nodeType==3 ? node.nodeValue :
      (node.hasChildNodes() ? Element.collectTextNodes(node) : ''));
  }).flatten().join('');
};

Element.collectTextNodesIgnoreClass = function(element, className) {
  return $A($(element).childNodes).collect( function(node) {
    return (node.nodeType==3 ? node.nodeValue :
      ((node.hasChildNodes() && !Element.hasClassName(node,className)) ?
        Element.collectTextNodesIgnoreClass(node, className) : ''));
  }).flatten().join('');
};

var Effect = {
  Methods: {}
};

$w('collectTextNodes collectTextNodesIgnoreClass').each(
  function(f) { Effect.Methods[f] = Element[f]; }
);

Element.addMethods(Effect.Methods);
// --------------------------------------------------------------------------------
// global.js: end JavaScript file: '/js/effects.js'

// global.js: begin JavaScript file: '/js/prototype_extensions.js'
// ================================================================================


Ss.Browser = {
	isIEVersion: function(v) {
		return (Prototype.Browser.IE &&
			parseInt(navigator.userAgent.substring(navigator.userAgent.indexOf("MSIE")+5))==v);
	}
};

Element.addMethods({

	isElementOrDescendantOf: function(element, argElement) {
		return element === argElement || element.descendantOf(argElement);
	},
	
	CSSTransitionsSupported: function(element) {
		return element.style.webkitTransition !== undefined ||element.style.MozTransition !== undefined;
	},
	
	fadeOut: function (element, args) {
		args = args || {};
		args.endValue = 0;
		return element.fadeTo(args);
	},

	fadeIn: function (element, args) {
		args = args || {};
		args.endValue = 1;
		return element.fadeTo(args);
	},
	
	fadeTo: function(element, args) {
		args = args || {};
		args.increment = args.increment || 0.05;
		if(Prototype.Browser.IE) {
			args.increment = args.increment * 5;
		}
		return element.setStylePeriodically({
				property: 	'opacity',
				endValue: 	args.endValue,
				increment: 	args.increment,
				onComplete: args.onComplete || Prototype.emptyFunction()
		});
	},
	
	setStylePeriodically: function(element, args) {
		

		var property = args.property,
			endValue = args.endValue,
			increment = args.increment,
		

			units = args.units || '',
			onComplete = (args.onComplete && Object.isFunction(args.onComplete) ? args.onComplete : Prototype.emptyFunction),
			currentValue = args.startValue || parseFloat(element.getStyle(property));
		

		var getUpdater = function() {
			var updaters = {
				add: function() {
					currentValue = (currentValue + increment <= endValue ? currentValue + increment : endValue);
					return currentValue >= endValue;
				},
				sub: function() {
					currentValue = (currentValue - increment >= endValue ? currentValue - increment : endValue);
					return currentValue <= endValue;
				}
			};
			return (Math.min(currentValue, endValue) == currentValue ? updaters.add : updaters.sub);
		};
		

		var updater = getUpdater();
		

		var _pe = new PeriodicalExecuter(
			function(pe) {
				var done = updater(),
					value = currentValue + units;
				if(property == 'opacity') {



					if(Ss.Browser.isIEVersion(9)
						&& value >= 0.99)
					{
						value = 0.99;
					}
					element.setOpacity(value);
				} else {
					element.style[property] = value;
				}
				if(done) {
					pe.stop();
					onComplete();
				}
			},
		0.010);
		return _pe;
	},
	
		





	viewportOffsetFix: function(forElement) {
		var valueT = 0, valueL = 0;
		
		var element = forElement;
		do {
		  valueT += element.offsetTop  || 0;
		  valueL += element.offsetLeft || 0;

		  if (element.offsetParent == document.body &&
			Element.getStyle(element, 'position') == 'absolute') break;
		
		} while (element = element.offsetParent);
		
		element = forElement;
		do {
		  if (!Prototype.Browser.Opera || element.tagName == 'HTML') {
			valueT -= element.scrollTop  || 0;
			valueL -= element.scrollLeft || 0;
		  }
		} while (element = element.parentNode);
		
		return Element._returnOffset(valueL, valueT);
	}
  
});
// --------------------------------------------------------------------------------
// global.js: end JavaScript file: '/js/prototype_extensions.js'

// global.js: begin JavaScript file: '/js/tracker.js'
// ================================================================================
Ss.tracker = window.Ss.tracker || {};

Object.extend(Ss.tracker, {

	initialize: function() {

		$('lil_brother').observe('click', Ss.tracker.eventHandlers.click);

		this.config.click_event_ids.default_select.each(
			function(id) {
				var select = $(id);
				if(select) {
					select.observe('change', Ss.tracker.eventHandlers.change);
				}
			}
		);
		
		if(Ss.storage.session.supported()) {
			var pendingEvent = this.getPendingEvent();
			pendingEvent && this.request(pendingEvent);
		}

	},

	logEvent: function(eventType, metadata, target) {

		try {

			var _event = this.config.event.clone();


			var _event_type_id = Ss.tracker.config.event_type_ids[eventType];
			if (Object.isUndefined(_event_type_id)) {
				return;
			}

			_event[this.config.name_to_column['event_type']] = _event_type_id;


			var localCallbacks = this.config.event_callbacks[eventType];
			if (localCallbacks) {
				localCallbacks.each(function(callback) {
					try {
						_event[Ss.tracker.config.name_to_column[callback]]
							= Ss.tracker.get[callback](eventType, target);
					} catch (e) {

					}
				});
			}

			if (metadata) {
				Object.keys(metadata).each(function(columnName) {
					_event[Ss.tracker.config.name_to_column[columnName]]
						= metadata[columnName];
				});
			}



			_event[Ss.tracker.config.name_to_column['notes_json']] = 
				"{'client_seed': " + Math.floor(Math.random() * 100000000) + "}";

			_event[Ss.tracker.config.name_to_column['client_timezone']] =
				new Date().getTimezoneOffset();

			this.request(this.config.img_src + '?' + _event.join('\x01'));
			

		} catch(ex) {
		}

	},

	request: function(src, pageLoadEvent) {
		


		var bug = new Image();
		
		if(Ss.storage.session.supported() && !pageLoadEvent) {
			Ss.tracker.setPendingEvent(src);
			bug.onload = function() {
				Ss.tracker.deletePendingEvent();
			};
		}
		
		bug.src = src;
	},
	
	getPendingEvent: function() {
		var pEvent = Ss.storage.session.getItem('pending_event');
		if(pEvent) {
			return decodeURIComponent(pEvent);
		}
		return null;
	},
	
	setPendingEvent: function(src) {
		Ss.storage.session.setItem('pending_event', encodeURIComponent(src));
	},
	
	deletePendingEvent: function() {
		Ss.storage.session.removeItem('pending_event');
	},
	
	eventHandlers: {

		click: function(evt) {

			try {
				var target = Event.element(evt),
					trackedElement = Ss.tracker._getTrackedElement(target),
					excludeTags = ['SELECT', 'OPTION'],
					metadata = {};

				if(!trackedElement || excludeTags.include(target.nodeName)) {
					return;
				}

				metadata.tag_name = trackedElement.nodeName;

				if(trackedElement.nodeName == 'INPUT') {
					
					metadata.input_type = trackedElement.type || '';
					
					if(trackedElement.type != 'text' && trackedElement.type != 'password') {
						metadata.input_name = trackedElement.name || '';
						metadata.input_value = trackedElement.value || '';
						metadata.input_checked = trackedElement.checked || '';
					}
					
				}

				metadata.element_id = Ss.tracker._getTrackedElementId(trackedElement);

				var eType = Ss.tracker._getEventTypeByElementId(trackedElement.id)
				         || 'click';
				Ss.tracker.logEvent(eType, metadata, target);

			} catch (ex) {
			}

		},
		
		change: function(evt) {
			
			try {

				var target = Event.element(evt),
					metadata = {},
					eType = '';
					
				metadata.tag_name = target.nodeName;
				metadata.element_id = Ss.tracker._getTrackedElementId(target);
				metadata.input_name = target.name || '';
				metadata.input_value = target.value || '';
				
				eType = Ss.tracker._getEventTypeByElementId(target.id)
				         || 'click';
				
				Ss.tracker.logEvent(eType, metadata);

			} catch(ex) {
			}

		}

	},

	get: {

		search_term: function(event_type, target) {
			var term = $('keyword_form').down('input[name=searchterm]').getValue();
			return term;
		},

		page_number: function(event_type, target) {
			var page_number;
			if (event_type === 'paginate') {
				page_number =  Ss.search.history.getHashParam('page');
			} else if (event_type === 'download') {
				var src = $('submit_form').src.value.split('-');
				page_number =  src[1];
			} else if (event_type === 'click') {
				page_number =  Ss.search.getCurrentPage();
			}
			return page_number;
		},

		photo_id: function(event_type, target) {
			var photo_id;
			if (event_type === 'download') {
				photo_id = $('submit_form').id.value;
			} else if (event_type === 'click') {
				var split = target.up().href.split('#');
				var p = split[split.length - 1].toQueryParams();
				photo_id =  p.id;
			}
			return photo_id;
		},

		image_position: function(event_type, target) {
			var image_position;
			if (event_type === 'download') {
				var src = $('submit_form').src.value.split('-');
				image_position = src[2];
			} else if (event_type === 'click') {
				var split = target.up().href.split('#');
				var p = split[split.length - 1].toQueryParams();
				var pos = p.src.split('-')[2];
				image_position = pos;
			}
			return image_position;
		}
	},

	_getTrackedElement: function(element) {
		var trackedElement,
			configIds = this._getConfigIds();

		if(configIds.include(element.id)) {
			trackedElement = element;
		} else {
			trackedElement = element.ancestors().find(
				function(elem) {
					return configIds.include(elem.id);
				}
			);
		}




		if(!trackedElement && this.config.track_all_clicks) {
			trackedElement = element;
		}

		return trackedElement;
	},
	
	_getTrackedElementId: function(elem) {
		var id = '';
		if(elem.hasAttribute('id')) {
			id = elem.getAttribute('id');
		} else if(Ss.tracker.config.track_all_clicks) {
			id = elem.ancestors().find(
				function(anc) {
					return anc.hasAttribute('id');
				}
			).getAttribute('id');
		}
		return id;
	},

	_getConfigIds: function() {
		return Object.values(Ss.tracker.config.click_event_ids).flatten();
	},

	_getEventTypeByElementId: function(id) {
		var type = '';
		Object.keys(Ss.tracker.config.click_event_ids).each(function(eventType) {
			if(Ss.tracker.config.click_event_ids[eventType].include(id)) {
				type = eventType;
			}
		});
		if (type == 'default_click' || type == 'default_select') {

			type = 'click';
		}
		return type;
	},

	_log: function(event) {
		var loggedEvent = {};
		for (var i=0; i < event.length; i++) {
			var column = $H(Ss.tracker.config.name_to_column).find(function(entry) {
				return entry.value == i;
			});
			loggedEvent[column.key] = event[i];
		};
		if (console) {
			console.log(loggedEvent);
		}
	}

});
// --------------------------------------------------------------------------------
// global.js: end JavaScript file: '/js/tracker.js'

// global.js: begin JavaScript file: '/js/header.js'
// ================================================================================
Ss.header = {
    
    activeMenu: null,
    
    loginForm: null,
    
    initialize: function(args) {
        this.element = $('navigation');
        
        this.element.observe('click', this.click.bind(this));
        
		Ss.ShadowContainer.observe(
		    function(evt) {
		        if(evt.type == 'hide') {
		            Ss.header._reset();
		        }
		    }
		);

    },

	getMenus: function() {

	    return {
            'language_menu': {
            	trigger:	$('language_selector'),
                element: 	$('language_menu'),
                options: {
                	position: {
						target:	$('language_selector'),
						type: 		'bottom',
						offsetY: 	1,
						offsetX:    -29
					},
					closeButton: false,
					modal:	false,
					className: 'header_menu'
				}
            },
            
            'user_options_menu': {
            	trigger:	$('user_options_selector'),
                element: 	$('user_options_menu'),
                options: {
                	position: {
						target:	$('user_options_selector'),
						type: 		'bottom',
						offsetY: 	12,
						offsetX:    -24
					},
					closeButton: false,
					modal:	false,
					className: 'header_menu'
                }
            },
            
            'inline_login_form': {
            	trigger:	$('inline_login'),
                element: 	$('inline_login_form'),
                options: {
                	position: {
						target:	$('inline_login'),
						type: 		'bottom',
						offsetY: 	28,
						offsetX:    15
					},
					closeButton: false,
					modal:	false,
					notch:		{
						type: 'top',
						styles: {
							left: '80%'
						}
					},
					className: 'header_menu shadow_container_gray'
				}
            }
        };
        
	},

    click: function(evt) {
        
        var trigger = Event.element(evt),
            menus = this.getMenus(),
            activeMenu = this.activeMenu;

        this._reset();
        
        if(activeMenu) {
            
            Ss.header.hideMenu();
            
            if(trigger == activeMenu.trigger || 
                trigger.descendantOf(activeMenu.trigger))
            {
                Event.stop(evt);
                return;
            }
            
        }

        Object.keys(menus).each(
            function(key) {
                var menu = menus[key];
                
                if(menu.trigger && (trigger == menu.trigger || trigger.descendantOf(menu.trigger))) {
                	if(key != 'user_options_menu') {
                		Event.stop(evt);
                	}
                    Ss.header.showMenu(menu, key);
                }
            }
        );
        
    },
    
	showMenu: function(menu, key) {
	
	    var menuElem, header = this;
	    
	    menu.trigger.addClassName('active_menu_trigger');
	    
		if(key == 'inline_login_form') {
		    menuElem = this.loginForm || this.makeLoginForm(menu);
		} else {
		     menuElem = menu.element.cloneNode(true)
		}
		
		Ss.ShadowContainer.write(menuElem, menu.options);
		
		this.activeMenu = menu;
		
		if(key == 'inline_login_form') {
			( function(){ menuElem.down('input[type=text]').focus(); } ).defer(); // deferment is needed to support explorer
		}
	},
	
	hideMenu: function() {
		 Ss.ShadowContainer.hide()
	},
	
	makeLoginForm: function(menu) {
	    var userLabel,userInput, passLabel, passInput, userInFieldLabel, passInFieldLabel;
	    
		this.loginForm = menu.element;
	    
	    userLabel = this.loginForm.down('.inline_login_form_user label span');
	    userField = this.loginForm.down('.inline_login_form_user input');
	    passLabel = this.loginForm.down('.inline_login_form_pass label span');
	    passField = this.loginForm.down('.inline_login_form_pass input');
	    
	    userInFieldLabel = new Ss.input.InFieldLabel({
	    		label: userLabel,
	    		field: userField
		});
		
	    passInFieldLabel = new Ss.input.InFieldLabel({
	    		label: passLabel,
	    		field: passField
		});
		
		userInFieldLabel.subscribe(passInFieldLabel);
		
        return this.loginForm;
	},
	
	_reset: function() {

	    $$('#language_selector, #user_options_selector, #inline_login').compact().invoke('removeClassName', 'active_menu_trigger');
	    

	    this.activeMenu = null;
	}
	
};
// --------------------------------------------------------------------------------
// global.js: end JavaScript file: '/js/header.js'

// global.js: begin JavaScript file: '/js/Cookie.js'
// ================================================================================
function createCookie(args) {
	if (args.days) {
		var date = new Date();
		date.setTime(date.getTime() + (args.days*24*60*60*1000));
		var expires = "; expires=" + date.toGMTString();
	} else {
		var expires = "";
	}
	document.cookie = args.name + "=" + args.value + expires + "; path=/";
}

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

function eraseCookie(name) {
	createCookie(name, "", -1);
}
// --------------------------------------------------------------------------------
// global.js: end JavaScript file: '/js/Cookie.js'

// global.js: begin JavaScript file: '/js/patterns.js'
// ================================================================================
/************************
 * Patterns (patterns.js)
 */
Ss = window.Ss || {};
Ss.patterns = {};


/* SUBJECT */
Ss.patterns.Subject = Class.create({
	initialize: function() {
		this.observers = [];
	},
	subscribe: function(f) {
		this.observers.push(f);	
	},
	unsubscribe: function(f) {
		this.observers = this.observers.without(f);
	},
	notify: function(data) {
		this.observers.each(function(fn) {
			fn(data);
		});
	}
});


/* ABSTRACT MODEL */
Ss.patterns.Model = Class.create(Ss.patterns.Subject, {
	initialize: function($super, data) {
		$super();
		this.data = data;
	},
	set: function(data) {
		this.data = data;
		this.notify(data);
	}
});


/* ABSTRACT VIEWS */
Ss.patterns.View = Class.create({
	initialize: function(element, model, controller) {
		this.element = element;
		this.model = model;
		this.controller = controller;
		
		this.model.subscribe(this.update.bind(this));
		this._events();
	},
	observe: function(eType) {
		this.element.observe(eType, this.controller[eType]);
	},
	update: function(data) { 

		this.element.update(data);
	},
	_events: function() { /*abstract*/ }
});

Ss.patterns.CompositeView = Class.create(Ss.patterns.View, {
	initialize: function($super, element, model, controller) {
		$super(element, model, controller);
		this._views = [];
	},
	update: function() {
		this._views.invoke('update');
	},
	add: function(view) {
		this._views.push(view);
	},
	remove: function(view) {
		this._views = this.views.reject(function(v){return view == v});
	},
	get: function(index) {
		return this.children[index];
	}
});


/* ABSTRACT CONTROLLERS */
Ss.patterns.Controller = Class.create({
	initialize: function(model) {
		this.model = model;
	},
	click: function(event) { /*abstract*/ }
});
// --------------------------------------------------------------------------------
// global.js: end JavaScript file: '/js/patterns.js'

// global.js: begin JavaScript file: '/js/util.js'
// ================================================================================
/*********************
 * Utilities (util.js)
 */
Ss = window.Ss || {};
Ss.util = {};
Ss.util.disableTextSelection = function(element) {
	element.onselectstart = function() { return false; };
	element.unselectable = "on";
	element.style.MozUserSelect = "none";
};
Ss.util.getElementText = function(element) {
	var ret = $A(element.childNodes).collect(function(c){
		if(c.nodeType != 8){
			return (c.nodeType != 1 ? c.nodeValue: Ss.util.getElementText(c))
		}
	}).join('');
	return ret.strip();
};
Ss.util.isIE6 = function() {
	return (Prototype.Browser.IE &&
		parseInt(navigator.userAgent.substring(navigator.userAgent.indexOf("MSIE")+5))==6);
};

/*
 * creates hidden inputs in "form" for all form elements that
 * are a decendent of "node"
 * and creates onchange handlers to syncronize the values between
 * the decendent elements of "node" and the hiddne elements
 * created in "form"
 */
Ss.util.mirrorInForm = function(node, form, opts){
    var accept = {SELECT:true, INPUT:true, TEXTAREA:true, RADIO:true};
    var felems = node.descendants().findAll(function(x){
        return accept[x.nodeName];
    });

	var events = opts && opts.events || ['change'];

    var radios = {};
    var trans = {
        radio: {
            duplicate: function(elem, form){
                if(!radios[elem.name]){
                    var e = new Element('input', {type:'hidden'});
                    e.setAttribute('name', elem.name); // using set attribute for name since IE9 doesn't like object properties named 'name'
                    form.appendChild(e);
                    radios[elem.name] = e
                }
                if(elem.checked){
                    radios[elem.name].value = elem.value;
                }
                return radios[elem.name];
            },
            set: function(elem, clone){
                if( elem.checked ){
                    clone.value = elem.value;
                }
            }
        },
		def: {
            duplicate: function(elem, form){
                var e = new Element('input', {type:'hidden', value:elem.value});
                e.setAttribute('name', elem.name); // using set attribute for name since IE9 doesn't like object properties named 'name'
                form.appendChild(e);
                return e;
            },
            set: function(elem, clone){
                clone.value = elem.value;
            }
        }
    }

    var ident = function(elem){
        return (elem.type.toLowerCase() == 'radio' ? trans.radio : trans.def);
    }
	felems.each(function(x){
        var p = ident(x);
        var e = p.duplicate(x, form);
		var set = opts.set || p.set;
		var o = opts.observe_elem && opts.observe_elem(x) || x;
		for(var i = 0, l = events.length; i < l; i++){
			o.observe(events[i],function(evt){
				set(x, e);
			});
		}
    });
} 
// --------------------------------------------------------------------------------
// global.js: end JavaScript file: '/js/util.js'

// global.js: begin JavaScript file: '/js/Pulldown.js'
// ================================================================================
/***********
 * Pulldown
 */
Ss = window.Ss || {};
Ss.Pulldown = Class.create(Ss.patterns.Subject, {
	initialize: function($super, element, trigger, content, contentContainer, titleBar) {
		$super();
		this.element = element
		this.trigger = trigger;
		this.content = content;
		this.contentContainer = contentContainer;
		this.titleBar = titleBar;
		this.state = "";
		this.collapse();
		this._events();
		this._tId;
		Ss.Pulldown.INSTANCES.set(this.element.identify(), this);
	},
	/* change state/collaspe/expand */
	collapse: function() {
		this._setState(Ss.Pulldown.STATES.collapsed);
	},
	expand: function() {
		this._setState(Ss.Pulldown.STATES.expanded);
		

		this.titleBar.update(this.trigger.cloneNode(true));
		this.titleBar.insert('<a class="close_btn_small"></a>');
		

		var width = this.contentContainer.getDimensions().width,
		    left =  this.contentContainer.cumulativeOffset().left,
		    vpWidth = document.viewport.getDimensions().width;
		
		if(width + left > vpWidth) {
			var edgeDistance = width + left - vpWidth + 20;
			this.contentContainer.setStyle({
				marginLeft: -edgeDistance + 'px'
			});
		}
	},
	/* change content */
	appendJson: function(jsonArr) {
		jsonArr = (Object.isArray(jsonArr) ? jsonArr : [jsonArr]);
		Ss.Pulldown.jsonToItems(jsonArr).each(
			function(item) {
				this._appendItem(item);		
			}, this);
	},
	appendContent: function(content) {
		$(this.content).insert(content);
	},
	replaceContent: function(content) {
		$(this.content).update(content);
	},
	clearContent: function(){
		this.replaceContent('');
	},
	/*change trigger*/
	appendToTrigger: function(content) {
		$(this.trigger).insert(content);
	},
	replaceTrigger: function(trigger) {
		$(this.trigger).update(trigger);
		$(this.trigger).insert('<span class="pulldown_open_icon">&#9660;</span>');
	},
	/* disable/enable automatic collapse on mouseout*/	
	disableAutoClose: function() {
		$(this.element).stopObserving('mouseout');
	},
	enableAutoClose: function() {
		$(this.element).observe('mouseout', this._mouseout.bind(this));
	},
	/* show/hide loading state */
	showLoading: function() {
		$(this.element).addClassName(Ss.Pulldown.COMPONENTS.loading);
	},
	hideLoading: function() {
		$(this.element).removeClassName(Ss.Pulldown.COMPONENTS.loading);
	},
	getElement: function() {
		return this.element;
	},
	destroy: function() {
		$(this.element, this.trigger, this.content, this.contentContainer).invoke('stopObserving');
		try { $(this.element).remove(); }
		catch(e) { }
	},
	
	/* private */
	_appendItem: function(item) {
		if(!$(this.content).select("UL").size() > 0) {
			$(this.content).insert(new Element("UL"));
		}
		$(this.content).select("UL").pop().insert(item);
	},
	_toggleState: function() {
		this.state == Ss.Pulldown.STATES.expanded ? this.collapse() : this.expand();
	},
	_events: function() {
		this.enableAutoClose();
		$(this.trigger).observe('click', this._click.bind(this));
		$(this.element).observe('mouseover', this._mouseover.bind(this));
		$(this.titleBar).observe('click', this.collapse.bind(this));
		Ss.util.disableTextSelection(this.trigger);
	},
	_click: function(event) {
		this._toggleState();
	},
	_mouseout: function(event) {
		if( !Position.within($(this.element), Event.pointerX(event), Event.pointerY(event)) &&
			!Position.within($(this.contentContainer), Event.pointerX(event), Event.pointerY(event)) &&
			this.state != Ss.Pulldown.STATES.collapsed)
		{
			this._tId = (function() {
				this.collapse();
			}).bind(this).delay(Ss.Pulldown.COLLAPSE_DELAY_SECONDS);
		}
	},
	_mouseover: function(event) {
		if(this._tId)
			window.clearTimeout(this._tId);
	},
	_reset: function() {
		$H(Ss.Pulldown.STATES).values().each(function(state) {
			$(this.element).removeClassName(state);
		}, this);
		this.contentContainer.setStyle({
				marginLeft: ''
		});
	},

	_setState: function(state) {
		this._reset();
		this.state = state;
		$(this.element).addClassName(state);
		this.notify({state: state});
	}
	
});


if(Prototype.Browser.IE) {
	Ss.Pulldown.addMethods({
		_showIframe: function() {
			if(!this._iframe) {
				var iframe = new Element('IFRAME', {
					frameborder: 0
				});
				var dim = $(this.contentContainer).getDimensions();
				iframe.setStyle({
					position: 'absolute',
					top: '0',
					left: '0',
					height: dim.height + 'px',
					width: dim.width + 'px',
					zIndex: '-1'
				});
				this.contentContainer.insert({top: iframe});
				this._iframe = iframe;
			}
			$(this._iframe).show();
		},
		_hideIframe: function() {
			if(this._iframe) {
				$(this._iframe).hide();
			}
		}
	});
}

/****************
 * Ajax Pulldown
 */
Ss.AjaxPulldown = Class.create(Ss.Pulldown, {
	initialize: function($super, element, trigger, content, contentContainer, titleBar, url, parameters, callback) {
		$super(element, trigger, content, contentContainer, titleBar);
		this.url = url;
		this.parameters = parameters;
		this.callback = callback;
		this.isLoaded = false;
	},
	load: function() {
		this.showLoading();
		this.isLoaded = true;
		var ap = this;
		var xhr = new Ajax.Request(ap.url, {
			parameters: ap.parameters,
			onSuccess: function(response) {
				ap.appendContent(response.responseText);
				response.responseText.evalScripts();
				ap._doCallback(response);
				ap.expand();
				ap.hideLoading();
			}
		});			
	},
	setCallback: function(f) {
		this.callback = f;
	},
	_doCallback: function(response) {
		if(Object.isFunction(this.callback)) {
			this.callback.call(this, response);
		}		
	},
	_click: function(event) {
		this[ this.isLoaded ? '_toggleState' : 'load']();
	}
});


/******************
 * Select Pulldown
 */
Ss.SelectPulldown = Class.create(Ss.Pulldown, {
	initialize: function($super, element, trigger, content, contentContainer, titleBar, charLimit) {
		$super(element, trigger, content, contentContainer, titleBar);
		this.charLimit = charLimit;
	},
	items: function() {
		return $(this.element).select('LI');
	},
	select: function(key) {
		var item = this.find(key);
		if(item) {
			return this._select(item);
		}
		throw("Item '" + key + "' not found");
	},
	find: function(key) {
		if(Object.isNumber(key) 
			&& this.items().length > key) 
		{
			return this.items()[key];
		}
		if(Object.isString(key)) {
			return this.items().find(function(item) {
				return (Ss.util.getElementText(item).toLowerCase() == key.toLowerCase())
			});
		}
		return false;
	},
	_appendItem: function($super, item) {
		this._registerItem(item);
		$super(item);
	},
	_select: function(item) {
		this.items().invoke("show");
		$(item).hide();
		this.replaceTrigger(this._toTrigger(item));
		this.collapse();
		this.notify({state: this.state, selected: item});
	},
	_toTrigger : function(item) {
		var triggerText = Ss.util.getElementText(item).truncate(this.charLimit);
		var icon = $(item).select("IMG").shift();
		var trigger = new Element('SPAN').update(triggerText);
		if(icon) {
			trigger.insert({top: icon.cloneNode(true)});
		}
		return trigger;
	},
	_events: function($super) {
		$super();
		this._registerItems();
	},
	_registerItems: function() {
		this.items().each(function(item) {
			this._registerItem(item);
		}, this);
	},
	_registerItem: function(item) {
		$(item).observe('click', (function(event) {
			this._select(item);
		}).bind(this));
	}
});


/***********************
 * Ajax Select Pulldown
 */
Ss.AjaxSelectPulldown = Class.create(Ss.SelectPulldown);
Ss.AjaxSelectPulldown.addMethods({
	initialize: function($super, element, trigger, content, contentContainer, titleBar, charLimit, url, parameters, callback) {
		$super(element, trigger, content, contentContainer, titleBar, charLimit);
		this.url = url;
		this.parameters = parameters;
		this.callback = callback;
		this.isLoaded = false;
	},
	load: Ss.AjaxPulldown.prototype.load,
	setCallback: Ss.AjaxPulldown.prototype.setCallback,
	_click: Ss.AjaxPulldown.prototype._click,
	_doCallback: function(response) {
		this._registerItems();
		Ss.AjaxPulldown.prototype._doCallback.apply(this);
	}
});


/**************************
 * Class Properties/Methods
 */
Ss.Pulldown.INSTANCES = new Hash();
Ss.Pulldown.COLLAPSE_DELAY_SECONDS = .5;
Ss.Pulldown.STATES = { //CSS class names applied/removed when current state changes
	collapsed: 'collapsed',
	expanded:  'expanded'
};
Ss.Pulldown.COMPONENTS = { //CSS class names used for various elements/subcomponents
	element:            'pulldown',
	trigger:            'pulldown_trigger',
	contentContainer:   'pulldown_content_container',
	content:            'pulldown_content',
	loading:            'pulldown_loading',
	openIcon:           'pulldown_open_icon',
	titleBar:           'pulldown_title_bar'
};

Ss.Pulldown.get = function(id) {
	return Ss.Pulldown.INSTANCES.get(id);
};

Ss.Pulldown.create = function(id) {
	return Ss.Pulldown.construct(Ss.Pulldown.createElement(id));
};

Ss.Pulldown.construct = function(element) {
	return new Ss.Pulldown(element, 
		Ss.Pulldown.getComponent(element, Ss.Pulldown.COMPONENTS.trigger), 
		Ss.Pulldown.getComponent(element, Ss.Pulldown.COMPONENTS.content),
		Ss.Pulldown.getComponent(element, Ss.Pulldown.COMPONENTS.contentContainer),
		Ss.Pulldown.getComponent(element, Ss.Pulldown.COMPONENTS.titleBar));
};
Ss.AjaxPulldown.construct = function(element, url, parameters, callback) {
	return new Ss.AjaxPulldown(element,
		Ss.Pulldown.getComponent(element, Ss.Pulldown.COMPONENTS.trigger), 
		Ss.Pulldown.getComponent(element, Ss.Pulldown.COMPONENTS.content),
		Ss.Pulldown.getComponent(element, Ss.Pulldown.COMPONENTS.contentContainer),
		Ss.Pulldown.getComponent(element, Ss.Pulldown.COMPONENTS.titleBar),
		url, parameters, callback);
};
Ss.SelectPulldown.construct = function(element, charLimit) {
	return new Ss.SelectPulldown(element, 
		Ss.Pulldown.getComponent(element, Ss.Pulldown.COMPONENTS.trigger), 
		Ss.Pulldown.getComponent(element, Ss.Pulldown.COMPONENTS.content),
		Ss.Pulldown.getComponent(element, Ss.Pulldown.COMPONENTS.contentContainer),
		Ss.Pulldown.getComponent(element, Ss.Pulldown.COMPONENTS.titleBar),
		charLimit);
};
Ss.AjaxSelectPulldown.construct = function(element, charLimit, url, parameters, callback) {
	return new Ss.AjaxSelectPulldown(element,
		Ss.Pulldown.getComponent(element, Ss.Pulldown.COMPONENTS.trigger), 
		Ss.Pulldown.getComponent(element, Ss.Pulldown.COMPONENTS.content),
		Ss.Pulldown.getComponent(element, Ss.Pulldown.COMPONENTS.contentContainer),	
		Ss.Pulldown.getComponent(element, Ss.Pulldown.COMPONENTS.titleBar),
		charLimit, url, parameters, callback);
};

Ss.Pulldown.createElement = function(id) {
	var t = new Template('<div class="#{trigger}"><a class="#{openIcon}">&#9660;</a></div><div class="#{contentContainer}"><div class="#{content}"></div><div class="#{titleBar}"><a>&#215;</a></div></div>');
	var element = new Element("DIV", {"class": Ss.Pulldown.COMPONENTS.element}).insert(t.evaluate(Ss.Pulldown.COMPONENTS));
	return ( id ? element.writeAttribute({"id":id}) : element );
};

Ss.Pulldown.getComponent = function(element, type) {
	return $(element).select("." + type).shift();
};

Ss.Pulldown.jsonToItem = function(obj) {
	var anchor = new Element('A').update(obj.name);
	var item = new Element('LI').update(anchor);	
	if(obj.icon_src) {
		anchor.insert({top: new Element('IMG', {src: obj.icon_src})});
	}
	if(Object.isString(obj.onclick)) {
		item.observe('click', function() {eval(obj.onclick);})
	}
	else if(Object.isFunction(obj.onclick)) {
		item.observe('click', obj.onclick);
	}
	return item;
};
Ss.Pulldown.jsonToItems = function(jsonArr) {
	var items = [];
	jsonArr.each(function(obj) {
		items.push(Ss.Pulldown.jsonToItem(obj));
	});
	return items;
};


Ss.Pulldown.convert = function(select, id, charLimit) {
	var sOptions = select.select('option');
	var sPulldown = Ss.SelectPulldown.construct(Ss.Pulldown.createElement(id), charLimit);
	var optionsJson = sOptions.collect(function(option, i){return {name: option.text, index: i};});
	sPulldown.appendJson(optionsJson);
	sPulldown.select(select.options[select.selectedIndex].text);
	sPulldown.subscribe(function(state){
		var selectedItem = state.selected;
		if(selectedItem){
			var selectedText = Ss.util.getElementText(selectedItem);
			var itemObj = optionsJson.find(function(obj){return obj.name == selectedText});
			select.selectedIndex = itemObj.index;
		}
	});
	select.insert({after: sPulldown.element});
	select.hide();
	return sPulldown;
};
// --------------------------------------------------------------------------------
// global.js: end JavaScript file: '/js/Pulldown.js'

// global.js: begin JavaScript file: '/js/Share.js'
// ================================================================================
Ss = window.Ss || {};
Ss.share = {
	pulldowns: [],
	translatedToError: "",
	translatedFromError: "",
	translatedAddressError: "",
	
	closeEmailForm: function(e) {
		$(e).down('.network-form').reset();
		$(e).down('.message-area').hide();
		$(e).down('.email-form-container').hide();
		$(e).down('.network-errors').update();
		$(e).down('.social_network_list').show();
	},
	
	openEmailForm: function(e) {
		$(e).up('.social_network_list').hide();
		$(e).up('.share_interface_content').down('.email-form-container').show();
	},
	

	addPulldown: function(pulldown) {
		this.pulldowns.push(pulldown);
	},
	
	getPulldown: function(e){
		return this.pulldowns.find(function(p){ return e.descendantOf(p.getElement()); });
	},
	
	toggleNetworkEmail: function(e) {		
		this.openEmailForm(e);
		

		var pulldown = this.getPulldown(e);
		

		pulldown.reconfigure();
		

		pulldown.disableAutoClose();
		

		pulldown.subscribe( function(data) {
			if(data.state == Ss.Pulldown.STATES.collapsed) {
				pulldown.enableAutoClose();
				Ss.share.closeEmailForm(pulldown.getElement());
				pulldown.unsubscribe(arguments.callee);
			}
		});
	},
	
	sendNetworkEmail: function(t) {
		var container = $(t).up('.share_interface_content');
		if (this.validateAddresses($(t).up('form'))) {
			$(t).up('form').request({
				onFailure: function(){ 
					alert('Sorry, your message was not sent.');
				}
			});
			container.down('.email-form-container').hide();
			container.down('.message-area').show();
		}
	},
	
	validateAddresses: function(form) {
		var addressList = new Array();
		var errors      = new Array();
		var fromAddress = form.getInputs('text', 'from')[0].getValue();
	
		if (!fromAddress) {
			errors.push(this.translatedFromError);
		}
	
		var toAddresses = form.getInputs('text', 'to')[0].getValue().split(/\s*\,\s*/);
	
		if (toAddresses && !toAddresses[0]) {
			errors.push(this.translatedToError);
		}
	
		var allAddressesValid = true;
		var translatedAddressError = this.translatedAddressError;
		$A([[fromAddress], toAddresses]).flatten().each(function(address) {
			if (!address.match(/[\w.-]+\@[\w.-]+\.[\w]{2,5}/)) {
				allAddressesValid = false;
				errors.push( translatedAddressError + " '" + address + "'");
			}
		});
	
		if (!allAddressesValid) { 
			var errorMessage = errors.shift();
			form.previous().update(errorMessage);
		}
		return allAddressesValid;
	},
	
	open: function(path) {
		window.open(path);
	},
	
	close: function(e) {
		this.getPulldown(e).collapse();
	}
};
// --------------------------------------------------------------------------------
// global.js: end JavaScript file: '/js/Share.js'

// global.js: begin JavaScript file: '/js/search_ui/SearchAlerts.js'
// ================================================================================
Ss.SearchAlerts = {
    overlayInit: false,
    css: {
        ALERTS_LOADING: 'search_alerts_loading',
        ALERTS_COMPLETE: 'search_alerts_complete'
    },
    init: function() {


        if(Ss.user.loggedIn) {

            Ss.AdvancedSearch.criteria.isValid() ? this.enableAdd() : this.disableAdd();
        }
    },
    initializeOverlay: function() { // TODO: move most of this to the click handler

        this.addButton = $('add_search_alert');
        this.helpText = $('search_alerts_help');
        this.overlay = $('search_alert_overlay');
        this.titleInput = $('search_alert_title');
        this.filtersElem = $('search_alert_filters');
        this.keywordsElem = $('search_alert_keywords');
        this.notificationFrequencyElem = $('notification_frequency');
        this.cancelButton= $('cancel_search_alert');
        this.form = $('search_alert_form');
        


        Ss.HelpText.add(this.helpText, 'search_alerts_help');
        

        this.addButton.observe('click', this.showOverlay.bind(this));
        this.cancelButton.observe('click', this.hideOverlay.bind(this));
        this.form.observe('submit', function(e) {
                Event.stop(e);
                this.save();
        }.bind(this));
        this.overlayInit = true;
    },
	updateOverlay: function() {

	    this.hideLoading();
	    this.hideComplete();
	    

	    this.titleInput.clear();
	    this.notificationFrequencyElem.value = '604800';
	    

	    this.titleInput.value = Ss.AdvancedSearch.criteria.toString(); 
	    this.keywordsElem.update(Ss.AdvancedSearch.criteria.getKeywords());
	    this.filtersElem.update(Ss.AdvancedSearch.criteria.getActiveFilterText());
	},
    save: function() {
        var overlay = this;
        var criteria_json = Ss.AdvancedSearch.criteria.serialize();
        var searchAlertTitle = $F($('search_alert_title')).strip();
		var notify_freq = $F($('notification_frequency'));
		if(searchAlertTitle.empty()) {
		    return alert('Please enter a title for your search alert');
		}
		this.showLoading();
		var req = new Ajax.Request('/show_component.mhtml', {
			method: 'post',
			parameters: {
				component_path:        '/search_ui/saved_searches.mh',
				action:                'save',
				name:                  searchAlertTitle,
				notify_freq:           notify_freq,
				search_criteria_json:  criteria_json,
				encoding:              'UTF-8'
			},
			onSuccess: function(response) {
			    Ss.SearchAlerts.hideLoading();
				var json = response.responseJSON;
				var content = json.content;
				var numSearchAlerts = json.content.searches;
				if(numSearchAlerts) {
				    Ss.SearchAlerts.updateCount(numSearchAlerts);
				    Ss.SearchAlerts.showComplete();
				    Ss.SearchAlerts.hideOverlay.delay(3);
				} else {

				   alert(content);
				}
			}
		});
    },
    disableAdd: function() {
        if(this.addButton && this.helpText) {
            this.addButton.hide();
            this.helpText.hide();
        }
    },
    enableAdd: function() {
        if(!this.overlayInit) {
            this.initializeOverlay();
        }
        if(this.addButton && this.helpText) {
            this.addButton.show();
            this.helpText.show();
        }
    },
	updateCount: function(count) {
	    var countElem =  $('search_alert_count');
	     countElem && countElem.update(count);
	},
    showOverlay: function() {
        this.updateOverlay();
        Ss.ShadowContainer.write(this.overlay, {
        	className: 'search_alert_overlay'
        });

	    this.form.down('input[type=text]').focus();
    },
    hideOverlay: function() {
        Ss.ShadowContainer.hide();
    },
    showLoading: function() {
        this.overlay.addClassName(this.css.ALERTS_LOADING);    
    },
    hideLoading: function() {
        this.overlay.removeClassName(this.css.ALERTS_LOADING);
    },
    showComplete: function() {
        this.overlay.addClassName(this.css.ALERTS_COMPLETE);
    },
    hideComplete: function() {
        this.overlay.removeClassName(this.css.ALERTS_COMPLETE);
    }
};
// --------------------------------------------------------------------------------
// global.js: end JavaScript file: '/js/search_ui/SearchAlerts.js'

// global.js: begin JavaScript file: '/js/search_ui/AdvancedSearch.js'
// ================================================================================
/******************************************************************************
 * Advanced Search JavaScript: AdvancedSearchVersion: 6 : Thurs Nov. 11, 10:40
 */

Ss = window.Ss || {};
Ss.AdvancedSearch = {
    menus:  {}, // Hash of Ss.AdvancedSearch.Menu instances
    text:   {}, // defined with translated text in advanced_search_box.mh
    css: {
        ACTIVE: 'active-menu',
        OPEN: 'search-menu-open',
        TEXTBOX_DEFAULT: 'default_searchbox_text'
    },
    getKeywordInput: function() {
        return this.keywordInput;
    },
    getMenus: function() {
        return this.menus.values();
    },
    getMenu: function(key) {
        return this.menus.get(key);
    },
    clearDefaultTextInput: function() {
        Ss.AdvancedSearch.TextInput._INSTANCES.invoke('clearDefaultValue');
    },
    sync:  function() {
        this.getMenus().invoke('sync');
        this.getKeywordInput().sync();
        this.update();
        Ss.SearchAlerts.init();
        

        this.getMenus().each(function(menu) {
                if(menu.isActive() && !menu.isOpen()) {
                    menu.open();
                }
        });
    },
    setContributorGalleryName: function(galleryName) {

        var populateContributorLink = $('populate-photographer');
        if(populateContributorLink && !galleryName.empty()) {
            populateContributorLink.observe('click', function() {
                var contributorMenu = this.getMenu('contributor');
                contributorMenu.setInputValue('photographer_name', galleryName);
                this.getKeywordInput().getElement().focus();
            }.bind(this));
        }
    },
    update: function() {
        this.getMenus().invoke('update');
    },

    resetMenusAndKeywordInput: function() {
        this.reset();
        this.getKeywordInput().showDefaultValue();

    },

    reset: function() {
        this.getMenus().invoke('reset');
    },
    clearKeywordInput: function() {
        this.getKeywordInput().clear();
    },
    init: function() {
    
        var keywordForm = $('keyword_form');
        
        var excludeInput =  new Ss.AdvancedSearch.TextInput({
                                element: $('exclude-keywords'),
                                defaultText: Ss.AdvancedSearch.text.EXCLUDE_DEFAULT,
                                defaultCss: Ss.AdvancedSearch.css.TEXTBOX_DEFAULT,
                                form: keywordForm,
                                filterName: 'searchtermx'
                            });
        
        this.keywordInput =  new Ss.AdvancedSearch.KeywordTextInput({
                                element: $('keyword_input'),
                                defaultText: Ss.AdvancedSearch.text.KEYWORD_DEFAULT,
                                defaultCss: Ss.AdvancedSearch.css.TEXTBOX_DEFAULT,
                                form: keywordForm
                            });
        
        this.menus = new Hash({
            
            mediaType: new Ss.AdvancedSearch.Menu({ 
                element: $('media-type-menu'),
                inputMap: {
                    photos: 	$('media-type-photos'),
                    vectors: 	$('media-type-vectors'),
                    illustrations: 	$('media-type-illustrations')
                },
                evtTypes: ['click']
            }),
            
            orientation: new Ss.AdvancedSearch.Menu({ 
                element: $('orientation-menu'),
                inputMap: {
                    vertical: 	$('orientation-vertical'),
                    horizontal: 	$('orientation-horizontal')
                },
                evtTypes: ['click']
            }),
            
            category: new Ss.AdvancedSearch.Menu({ 
                element: $('category-menu'),
                inputMap: {
                    category: $('search-category-select')
                },
                evtTypes: ['change']
            }),
            
            excludeKeywords: new Ss.AdvancedSearch.ExcludeKeywordsMenu({ 
                element: $('exclude-keywords-menu'),
                textInput: excludeInput,
                inputMap: {
                    searchtermx: $('exclude-keywords')
                },
                evtTypes: ['keyup', 'blur']
            }),
            
            contributor: new Ss.AdvancedSearch.Menu({ 
                element: $('contributor-menu'),
                inputMap: {
                    photographer_name: $('contributor-input')
                },
                evtTypes: ['keyup', 'change']
            }),
            
            people: new Ss.AdvancedSearch.PeopleMenu({ 
                element: $('people-menu'),
                inputMap: {
                    model_released: 	$('model-released-checkbox'),
                    people_age:            	$('people-age'),
                    people_ethnicity:      	$('people-ethnicity'),
                    people_gender:         	$('people-gender'),
                    people_number:         	$('people-number')
                },
                evtTypes: ['click', 'change'],
                expander: $('people-menu').down('.search-section-header'),
                editorialCheckbox: $('image-rights-editorial')
            }),
            color: new Ss.AdvancedSearch.ColorMenu({ 
                element: $('color-menu'),
                inputMap: {
                    color: $('color-hex-input')
                },
                evtTypes: ['blur', 'change'],
                clearButton: $('clear_color_menu'),
                expander: $('color-menu').down('.search-section-header')
            })
        });
        
		if ($('image-rights-menu')) {
			this.menus.set('imageRights',
				new Ss.AdvancedSearch.Menu({
					element: $('image-rights-menu'),
					inputMap: {
						commercial:  $('image-rights-creative'),
						editorial: 	$('image-rights-editorial')
					},
					evtTypes: ['click'],
					expander: $('image-rights-menu').down('.search-section-header')
				})
			);
			Ss.HelpText.add($('usage_rights_help'), 'usage_rights_help');
		}


        this.sync();
        this.update();
        

        $('clear_all').observe('click', Ss.AdvancedSearch.reset.bind(this));
        

        keywordForm.observe('submit', function(evt) {
            Ss.AdvancedSearch.clearDefaultTextInput();
            Ss.tracker.logEvent('new_search'); 
        });
       

        Ss.SearchAlerts.init();
        

        Ss.HelpText.add($('contributor_help'), 'contributor_help');
        Ss.HelpText.add($('people_search_help'), 'people_search_help');
        Ss.HelpText.add($('color_help'), 'color_help');
        
    }
};

Ss.AdvancedSearch.Menu = Class.create({
	initialize: function(args) {
		this.element = args.element; 
		this.inputMap = args.inputMap;
		this.evtTypes = args.evtTypes;
		this.expander = args.expander || false;
		

		this.events();
	},
	events: function() {
	    this.evtTypes.each(function(evtType) {
	        this.getInputs().invoke('observe', evtType, this.update.bind(this));
	    }, this);
	    
        if(this.expander) {
            this.expander.observe('click', this.toggle.bind(this)); 
        }
	},
	update: function(evt) {
	    this[( this.isModified() ? 'activate' : 'deactivate' )]();
	},
	sync: function() {
	    $H(this.inputMap).each( function(entry) {
	            
	            var key = entry.key;
	            var input = entry.value;
	            var appliedValue = Ss.AdvancedSearch.criteria.getFilterValue(key);
	            

                if(input.type && input.type == 'checkbox') {
                    input.checked = (appliedValue == 'on'); // TODO: remove 'on'/'off' usages
                } else {
                    input.value = appliedValue; // works on selects and text inputs
                }
	            
	        }, this);
	},
	isModified: function() {
	    var isModified = this.getInputs().any(function(input) {
	        if(input.type && input.type == 'checkbox') {
	            return input.checked;    
	        }
	        else if(input.type && input.type == 'text') {
	            return !$F(input).strip().empty();
	        }
	        else if(input.selectedIndex) {
	            return input.selectedIndex > 0;
	        }
	    });
	    return isModified;
	},
	clear: function() {
	    this.getInputs().each(function(input) {
	           if(input.type && input.type == 'checkbox') {
	                input.checked = false;   
	           } else {
	                input.value = ''; // works on selects and text inputs
	           }
	    }, this);
	},
	reset: function() {
	    this.clear();
	    this.update();
	},
	getInputs: function() {
	    return Object.values(this.inputMap);
	},
	getInput: function(name) {
	    return this.inputMap[name];
	},
	setInputValue: function(inputName, value) {
	    this.getInput(inputName).value = value;
	    this.update();
	},
	activate: function() {
	    this.element.addClassName(Ss.AdvancedSearch.css.ACTIVE);
	},
	deactivate: function() {
	    this.element.removeClassName(Ss.AdvancedSearch.css.ACTIVE);
	},
	isActive: function() {
	    return this.element.hasClassName(Ss.AdvancedSearch.css.ACTIVE);
	},
	open: function(){
	    this.element.addClassName(Ss.AdvancedSearch.css.OPEN);
	},
	close: function() {
	    this.element.removeClassName(Ss.AdvancedSearch.css.OPEN);
	},
	isOpen: function() {
	    return this.element.hasClassName(Ss.AdvancedSearch.css.OPEN);
	},
	toggle: function() {
	    this[ this.isOpen() ? 'close' : 'open' ]();
	}
});


Ss.AdvancedSearch.PeopleMenu = Class.create(Ss.AdvancedSearch.Menu, {
    initialize: function($super, args) {
        this.editorialCheckbox = args.editorialCheckbox;
        $super(args);
    },
	update: function($super, evt){
	    
	    var releasedCheckbox = this.inputMap.model_released;
	    var editorialCheckbox = this.editorialCheckbox;
	    var input;
	    

	    if(evt && evt.findElement()) {
	        
	        input = evt.findElement();
	        

            if(input == releasedCheckbox && !input.checked) {
                this.reset();
            }
            

            else if(input.selectedIndex > 0) {
                releasedCheckbox.checked = true;
            }
        }
        

		if (editorialCheckbox) {
			if(releasedCheckbox.checked) {
				editorialCheckbox.checked = false;
				editorialCheckbox.disabled = true;
				Ss.AdvancedSearch.getMenu('imageRights').update(); // TODO: clean this up later
			} else {
				editorialCheckbox.disabled = false;
			}
		}
        
        $super(evt);

	}
});

Ss.AdvancedSearch.ColorMenu = Class.create(Ss.AdvancedSearch.Menu, {
    initialize: function($super, args) {
        $super(args);
        

        args.clearButton.observe('click', this.reset.bind(this));
        

        deMoronize();
        colorWheelEventOverride = "<a href='javascript://' onclick=\"javascript:clickWheel(); colorMenu.update(); return false;\"><img src='/color/hsvwheel-half.png' alt='color wheel' width='135' height='129' border='0'></a>";
        $('wheel').innerHTML = colorWheelEventOverride;
    },
    sync: function() {
        var appliedColor = Ss.AdvancedSearch.criteria.getFilterValue('color');
        if(appliedColor) {
            this.setValue(appliedColor);
        } else {
            this.reset();
        }
    },
	isModified: function() {
	    return !(this.inputMap.color.getValue().strip().empty());
	},
	setValue: function(color) {

	    this.inputMap.color.value = swatchc = color;
	    setSwatchColor();
	},
	clear: function() {
		swatchc = '#ffffff';
		setSwatchColor(); 
		this.inputMap.color.clear();
	}
});

Ss.AdvancedSearch.ExcludeKeywordsMenu = Class.create(Ss.AdvancedSearch.Menu, {
    initialize: function($super, args) {
        $super(args);
        this.textInput = args.textInput;
    },
    sync: function() {
        this.textInput.sync();
    },
    isModified: function() {
        return this.textInput.isModified();
    },
    clear: function() {
        this.textInput.showDefaultValue();
    },
    getTextInput: function() {
        return this.textInput;
    }
});

Ss.AdvancedSearch.TextInput = Class.create({
    initialize: function(args) { // element, defaultText, defaultCss, filterName
        this.element = args.element;
        this.defaultText = args.defaultText;
        this.defaultCss = args.defaultCss;
        this.filterName = args.filterName;
        this.defaultShowing = false;
        

        Ss.AdvancedSearch.TextInput._INSTANCES.push(this);
        

        this.element.observe('blur', function() {
                if(this.isEmpty()) {
                    this.showDefaultValue();
                }
        }.bind(this));
    },
    sync: function() {
        var appliedValue = this.getAppliedValue();
        (appliedValue ? this.setValue(appliedValue) : this.showDefaultValue());
    },
    clear: function() {
        this.element.clear();
    },
    getElement: function() {
        return this.element;
    },
    getAppliedValue: function() {
        return Ss.AdvancedSearch.criteria.getFilterValue(this.filterName).strip();
    },
    getValue: function() {
        return this.element.getValue();
    },
    setValue: function(value) {
        this.clearDefaultValue();
        this.element.value = value;
        this.defaultShowing = false;
    },
    showDefaultValue: function(){
        if(this.defaultShowing) {
            return;
        }
        this.element.value = this.defaultText;
        this.element.addClassName(this.defaultCss);
        this.element.observe('focus', this.clearDefaultValue.bind(this)); // when user focuses on the text field
        this.element.observe('keypress', this.clearDefaultValue.bind(this)); // (or presses a key.. user might already be focused on the text field)
        this.defaultShowing = true;
    },
    clearDefaultValue: function() {
        if(!this.defaultShowing) {
            return;
        }
        this.element.clear();
        this.element.removeClassName(this.defaultCss);
        this.element.stopObserving('focus');
        this.element.stopObserving('keypress');
        this.defaultShowing = false;
    },
    isEmpty: function() {
        return this.getValue().strip().empty();
    },
    hasDefaultValue: function() {
        return (this.getValue() == this.defaultText);
    },
    isModified: function() {
        return (!this.isEmpty() && !this.defaultShowing && !this.hasDefaultValue());
    }
});
Ss.AdvancedSearch.TextInput._INSTANCES = [];


Ss.AdvancedSearch.KeywordTextInput = Class.create(Ss.AdvancedSearch.TextInput, {
    getAppliedValue: function() {
        return Ss.AdvancedSearch.criteria.getKeywords().strip();
    }
});
// --------------------------------------------------------------------------------
// global.js: end JavaScript file: '/js/search_ui/AdvancedSearch.js'

// global.js: begin JavaScript file: '/js/HelpText.js'
// ================================================================================
Ss = window.Ss || {};
Ss.HelpText = {
    css: {
        ACTIVE: 'help_text_trigger_active',
        LOADING: 'help_text_trigger_loading'
    },
    elems: [],
    keyToText: new Hash(), // updated via ajax
    textLoaded: false,
    add: function(elem, key){
        if(this.elems.include(elem)) {
            return;
        }
        elem.href = ""; // TODO: temp ie6 hack to make mouseovers work
        elem.observe('click', function(e) {
            Event.stop(e); // prevent the default for the event
            this.isActive(elem) ? this.hideText(elem) : this.showText(elem, key);
        }.bind(Ss.HelpText));   
        this.elems.push(elem);
    },
    loadText: function(elem, key) {
        this.textLoaded = true;
        this.showLoading(elem);
        new Ajax.Request('/show_component.mhtml', {
                method: 'get',
                evalJSON: true,
                parameters: {
                    component_path: '/search_ui/get_help_text.mh'
                },
                onSuccess: function(response) {
                    Ss.HelpText.hideLoading(elem);
                    Ss.HelpText.keyToText.update(response.responseJSON);
                    Ss.HelpText.showText(elem, key);
                }
        });
    },
    showText: function(elem, key) {
        if(!this.textLoaded) {
            this.loadText(elem, key);
            return;
        }
        this.activateElem(elem);
        Ss.ShadowContainer.write(this.getTextByKey(key), {
        	position: {
        		target: elem
        	},
			modal: false
        });
        var observer= function(e) {
            if(e.type == 'hide') {
                Ss.HelpText.deactivateElem(elem); // deactivate the icon when the text is hidden
                Ss.ShadowContainer.stopObserving(observer);
            }
        };
        Ss.ShadowContainer.observe(observer);
    },
    hideText: function(elem) {
        this.deactivateElem(elem);
        Ss.ShadowContainer.hide();
    },
    hideLoading: function(elem) {
        elem.removeClassName(this.css.LOADING);
    },
    showLoading: function(elem) {
        elem.addClassName(this.css.LOADING);
    },
    isActive: function(elem) {
        return elem.hasClassName(this.css.ACTIVE);
    },
    activateElem: function(elem) {
        this.elems.invoke('removeClassName', this.css.ACTIVE);
        elem.addClassName(this.css.ACTIVE);
    },
    deactivateElem: function(elem) {
        elem.removeClassName(this.css.ACTIVE);
    },
    getTextByKey: function(key) {
        return this.keyToText.get(key);
    }
};
// --------------------------------------------------------------------------------
// global.js: end JavaScript file: '/js/HelpText.js'

// global.js: begin JavaScript file: '/js/color_wheel.js'
// ================================================================================



hue = 60;
adeg = 60;
sat = 1;
val = 1;







function BrowserDetectXLite() {
	var ua = navigator.userAgent.toLowerCase(); 
	this.ua = ua;


	this.isIE        = ( (ua.indexOf("msie") != -1) && (ua.indexOf("opera") == -1) && (ua.indexOf("webtv") == -1) ); 
        this.isSafari      = (ua.indexOf('safari') != - 1);


	this.versionMinor = parseFloat(navigator.appVersion); 
	

	if (this.isIE && this.versionMinor >= 4) {
		this.versionMinor = parseFloat( ua.substring( ua.indexOf('msie ') + 5 ) );
	}
	
	this.versionMajor = parseInt(this.versionMinor); 
	

	this.isWin   = (ua.indexOf('win') != -1);
	this.isWin32 = (this.isWin && ( ua.indexOf('95') != -1 || ua.indexOf('98') != -1 || ua.indexOf('nt') != -1 || ua.indexOf('win32') != -1 || ua.indexOf('32bit') != -1) );
	this.isMac   = (ua.indexOf('mac') != -1);

	this.isIE4x = (this.isIE && this.versionMajor == 4);
	this.isIE4up = (this.isIE && this.versionMajor >= 4);
	this.isIE5x = (this.isIE && this.versionMajor == 5);
	this.isIE55 = (this.isIE && this.versionMinor == 5.5);
	this.isIE5up = (this.isIE && this.versionMajor >= 5);
	this.isIE6x = (this.isIE && this.versionMajor == 6);
	this.isIE6up = (this.isIE && this.versionMajor >= 6);
	
	this.isIE4xMac = (this.isIE4x && this.isMac);
}
var browser = new BrowserDetectXLite();


function handleShowColorClick(checked) {
	if (checked) {
		document.getElementById('colortable').style.display = '';
		document.getElementById('small-color-wheel').style.visibility = 'hidden';
	} else {
		document.getElementById('colortable').style.display = 'none';
		document.getElementById('small-color-wheel').style.visibility = 'visible';
	}
}

function getRealLeft() {
  xPos = this.offsetLeft;
  tempEl = this.offsetParent;
  while (tempEl != null) {
	  xPos += tempEl.offsetLeft;
	  tempEl = tempEl.offsetParent;
  }
  return xPos;
}

function getRealTop() {
  yPos = this.offsetTop;
  tempEl = this.offsetParent;
  while (tempEl != null) {
	  yPos += tempEl.offsetTop;
	  tempEl = tempEl.offsetParent;
  }
  return yPos;
}


function deMoronize() {//now the onload function

  if (!((browser.isIE55 || browser.isIE6up) && browser.isWin32)) {

     thesmartversion = "<a href='javascript://' onclick='javascript:clickWheel(); return false;'><img src='/color/hsvwheel-half.png' alt='color wheel' width='135' height='129' border='0'></a>";
  document.getElementById("wheel").innerHTML = thesmartversion;
  }
  if (document.getElementById) {
	  var wheel = document.getElementById("wheel");
	  wheel.getRealTop = getRealTop;
	  wheel.getRealLeft = getRealLeft;
  }

  capture();
  updateColor();
}


function hsv2rgb(Hdeg,S,V) {
  H = Hdeg/360;     // convert from degrees to 0 to 1
  if (S==0) {       // HSV values = From 0 to 1
    R = V*255;     // RGB results = From 0 to 255
    G = V*255;
    B = V*255;}
  else {
    var_h = H*6;
    var_i = Math.floor( var_h );     //Or ... var_i = floor( var_h )
    var_1 = V*(1-S);
    var_2 = V*(1-S*(var_h-var_i));
    var_3 = V*(1-S*(1-(var_h-var_i)));
    if (var_i==0)      {var_r=V ;    var_g=var_3; var_b=var_1}
    else if (var_i==1) {var_r=var_2; var_g=V;     var_b=var_1}
    else if (var_i==2) {var_r=var_1; var_g=V;     var_b=var_3}
    else if (var_i==3) {var_r=var_1; var_g=var_2; var_b=V}
    else if (var_i==4) {var_r=var_3; var_g=var_1; var_b=V}
    else               {var_r=V;     var_g=var_1; var_b=var_2}
    R = Math.round(var_r*255);   //RGB results = From 0 to 255
    G = Math.round(var_g*255);
    B = Math.round(var_b*255);
  }
  return new Array(R,G,B);
}

function rgb2hex(rgbary) {
  cary = new Array; 
  cary[3] = "#";
  for (i=0; i < 3; i++) {
    cary[i] = parseInt(rgbary[i]).toString(16);
    if (cary[i].length < 2) cary[i] = "0"+ cary[i];
    cary[3] = cary[3] + cary[i];
    cary[i+4] = rgbary[i]; //save dec values for later
  }


  return cary;
}

function webRounder(c,d) {//d is the divisor

  thec = "#";
  for (i=0; i<3; i++) {
      num = Math.round(c[i+4]/d) * d; //use saved rgb value
      numc = num.toString(16);
      if (String(numc).length < 2) numc = "0" + numc;
      thec += numc;
  }
  return thec;
}

function hexColorArray(c) { //now takes string hex value with #
	swatchc = c[3];
    return false;
}

function capture() {
 if(document.layers) {
  layobj = document.layers['wheel'];
  layobj.document.captureEvents(Event.MOUSEMOVE);
  layobj.document.onmousemove = mouseMoved;
 }
 else if (document.all) {
  layobj = document.all["wheel"];
  layobj.onmousemove = mouseMoved;
   }
 else if (document.getElementById) {
  window.document.getElementById("wheel").onmousemove = mouseMoved;
 }
}

function mouseMoved(e) {
 if (document.layers) {
  x = e.layerX;
  y = e.layerY;
 }
 else if (document.all) {
  x = event.offsetX;
  y = event.offsetY + document.body.scrollTop;;
 }
 else if (document.getElementById) {
  x = (e.pageX - document.getElementById("wheel").getRealLeft());
  y = (e.pageY - document.getElementById("wheel").getRealTop());
 }
}

function clickWheel() {
 if (y > 256) {return false;}

    cartx = x - 64;
    carty = 64 - y;
    cartx2 = cartx * cartx;
    carty2 = carty * carty;
    cartxs = (cartx < 0)?-1:1;
    cartys = (carty < 0)?-1:1;
    cartxn = cartx/64;                      //normalize x
    rraw = Math.sqrt(cartx2 + carty2);       //raw radius
    rnorm = rraw/64;                        //normalized radius
    if (rraw == 0) {
      sat = 0;
      val = 0;
      rgb = new Array(0,0,0);
      }
    else {
      arad = Math.acos(cartx/rraw);            //angle in radians 
      aradc = (carty>=0)?arad:2*Math.PI - arad;  //correct below axis
      adeg = 360 * aradc/(2*Math.PI);  //convert to degrees
      if (rnorm > 1) {    // outside circle
            rgb = new Array(255,255,255);
            sat = 1;
            val = 1;            
            }

            else if (rnorm >= .5) {
	      sat = 1 - ((rnorm - .5) *2);
              val = 1;
	      rgb = hsv2rgb(adeg,sat,val);
	      }
              else {
                   sat = 1;
	      	   val = rnorm * 2;
	      	   rgb = hsv2rgb(adeg,sat,val);}
   }
   c = rgb2hex(rgb);
   hexColorArray(c);
   setSwatchColor();
   return false;
}

function setSwatchColor() {
 if (document.layers) {
  document.layers["swatch"].bgColor = swatchc;
  document.layers["swatch"].document.keyword_form.color.value = swatchc;
 } else if (document.all) {
  document.all["swatch"].style.backgroundColor = swatchc;
  document.all["swatch"].document.keyword_form.color.value = swatchc;
 } else if (document.getElementById) {
  document.getElementById("swatch").style.backgroundColor = swatchc;
  document.keyword_form.color.value = swatchc;
 }
 return false;
}

function updateColor() {
 if (document.layers) {
  color = document.layers["swatch"].document.keyword_form.color.value;
 } else if (document.all) {
  color = document.all["swatch"].document.keyword_form.color.value;
 } else if (document.getElementById) {
  color = document.keyword_form.color.value;
 }

 if ((color != swatchc) && (color.length == 7)) {
  swatchc = color;
  setSwatchColor();
 }

}
// --------------------------------------------------------------------------------
// global.js: end JavaScript file: '/js/color_wheel.js'

// global.js: begin JavaScript file: '/js/boxover.js'
// ================================================================================
/* --- BoxOver ---
/* --- v 2.1 17th June 2006
By Oliver Bryant with help of Matthew Tagg
http://boxover.swazz.org */

if (typeof document.attachEvent!='undefined') {
   window.attachEvent('onload',init);
   document.attachEvent('onmousemove',moveMouse);
   document.attachEvent('onclick',checkMove); }
else {
   window.addEventListener('load',init,false);
   document.addEventListener('mousemove',moveMouse,false);
   document.addEventListener('click',checkMove,false);
}

var oDv=document.createElement("div");
var dvHdr=document.createElement("div");
var dvBdy=document.createElement("div");
var windowlock,boxMove,fixposx,fixposy,lockX,lockY,fixx,fixy,ox,oy,boxLeft,boxRight,boxTop,boxBottom,evt,mouseX,mouseY,boxOpen,totalScrollTop,totalScrollLeft;
boxOpen=false;
ox=10;
oy=10;
lockX=0;
lockY=0;

function init() {
	oDv.appendChild(dvHdr);
	oDv.appendChild(dvBdy);
	oDv.style.position="absolute";
	oDv.style.visibility='hidden';
	document.body.appendChild(oDv);	
}

function defHdrStyle() {

	dvHdr.style.fontWeight='bold';
	dvHdr.style.width='250px';
	dvHdr.style.fontFamily='arial';
	dvHdr.style.border='1px solid #A5CFE9';
	dvHdr.style.padding='3';
	dvHdr.style.fontSize='11';
	dvHdr.style.color='#4B7A98';
	dvHdr.style.background='#D5EBF9';
	dvHdr.style.filter='alpha(opacity=85)'; // IE
	dvHdr.style.opacity='0.85'; // FF
}

function defBdyStyle() {
	dvBdy.style.borderBottom='1px solid #A5CFE9';
	dvBdy.style.borderLeft='1px solid #A5CFE9';
	dvBdy.style.borderRight='1px solid #A5CFE9';
	dvBdy.style.width='250px';
	dvBdy.style.fontFamily='arial';
	dvBdy.style.fontSize='11';
	dvBdy.style.padding='3';
	dvBdy.style.color='#1B4966';
	dvBdy.style.background='#FFFFFF';
	dvBdy.style.filter='alpha(opacity=85)'; // IE
	dvBdy.style.opacity='0.85'; // FF
}

function checkElemBO(txt) {
if (!txt || typeof(txt) != 'string') return false;
if ((txt.indexOf('header')>-1)&&(txt.indexOf('body')>-1)&&(txt.indexOf('[')>-1)&&(txt.indexOf('[')>-1)) 
   return true;
else
   return false;
}

function scanBO(curNode) {
	  if (checkElemBO(curNode.title)) {
         curNode.boHDR=getParam('header',curNode.title);
         curNode.boBDY=getParam('body',curNode.title);
			curNode.boCSSBDY=getParam('cssbody',curNode.title);			
			curNode.boCSSHDR=getParam('cssheader',curNode.title);
			curNode.IEbugfix=(getParam('hideselects',curNode.title)=='on')?true:false;
			curNode.fixX=parseInt(getParam('fixedrelx',curNode.title));
			curNode.fixY=parseInt(getParam('fixedrely',curNode.title));
			curNode.absX=parseInt(getParam('fixedabsx',curNode.title));
			curNode.absY=parseInt(getParam('fixedabsy',curNode.title));
			curNode.offY=(getParam('offsety',curNode.title)!='')?parseInt(getParam('offsety',curNode.title)):10;
			curNode.offX=(getParam('offsetx',curNode.title)!='')?parseInt(getParam('offsetx',curNode.title)):10;
			curNode.fade=(getParam('fade',curNode.title)=='on')?true:false;
			curNode.fadespeed=(getParam('fadespeed',curNode.title)!='')?getParam('fadespeed',curNode.title):0.04;
			curNode.delay=(getParam('delay',curNode.title)!='')?parseInt(getParam('delay',curNode.title)):0;
			if (getParam('requireclick',curNode.title)=='on') {
				curNode.requireclick=true;
				document.all?curNode.attachEvent('onclick',showHideBox):curNode.addEventListener('click',showHideBox,false);
				document.all?curNode.attachEvent('onmouseover',hideBox):curNode.addEventListener('mouseover',hideBox,false);
			}
			else {// Note : if requireclick is on the stop clicks are ignored   			
   			if (getParam('doubleclickstop',curNode.title)!='off') {
   				document.all?curNode.attachEvent('ondblclick',pauseBox):curNode.addEventListener('dblclick',pauseBox,false);
   			}	
   			if (getParam('singleclickstop',curNode.title)=='on') {
   				document.all?curNode.attachEvent('onclick',pauseBox):curNode.addEventListener('click',pauseBox,false);
   			}
   		}
			curNode.windowLock=getParam('windowlock',curNode.title).toLowerCase()=='off'?false:true;
			curNode.title='';
			curNode.hasbox=1;
	   }
	   else
	      curNode.hasbox=2;   
}


function getParam(param,list) {
	var reg = new RegExp('([^a-zA-Z]' + param + '|^' + param + ')\\s*=\\s*\\[\\s*(((\\[\\[)|(\\]\\])|([^\\]\\[]))*)\\s*\\]');
	var res = reg.exec(list);
	var returnvar;
	if(res)
		return res[2].replace('[[','[').replace(']]',']');
	else
		return '';
}

function Left(elem){	
	var x=0;
	if (elem.calcLeft)
		return elem.calcLeft;
	var oElem=elem;
	while(elem){
		 if ((elem.currentStyle)&& (!isNaN(parseInt(elem.currentStyle.borderLeftWidth)))&&(x!=0))
		 	x+=parseInt(elem.currentStyle.borderLeftWidth);
		 x+=elem.offsetLeft;
		 elem=elem.offsetParent;
	  } 
	oElem.calcLeft=x;
	return x;
	}

function Top(elem){
	 var x=0;
	 if (elem.calcTop)
	 	return elem.calcTop;
	 var oElem=elem;
	 while(elem){		
	 	 if ((elem.currentStyle)&& (!isNaN(parseInt(elem.currentStyle.borderTopWidth)))&&(x!=0))
		 	x+=parseInt(elem.currentStyle.borderTopWidth); 
		 x+=elem.offsetTop;
	         elem=elem.offsetParent;
 	 } 
 	 oElem.calcTop=x;
 	 return x;
 	 
}

var ah,ab;
function applyStyles() {
	if(ab)
		oDv.removeChild(dvBdy);
	if (ah)
		oDv.removeChild(dvHdr);
	dvHdr=document.createElement("div");
	dvBdy=document.createElement("div");
	CBE.boCSSBDY?dvBdy.className=CBE.boCSSBDY:defBdyStyle();
	CBE.boCSSHDR?dvHdr.className=CBE.boCSSHDR:defHdrStyle();
	dvHdr.innerHTML=CBE.boHDR;
	dvBdy.innerHTML=CBE.boBDY;
	ah=false;
	ab=false;
	if (CBE.boHDR!='') {		
		oDv.appendChild(dvHdr);
		ah=true;
	}	
	if (CBE.boBDY!=''){
		oDv.appendChild(dvBdy);
		ab=true;
	}	
}

var CSE,iterElem,LSE,CBE,LBE, totalScrollLeft, totalScrollTop, width, height ;
var ini=false;


function SHW() {
   if (document.body && (document.body.clientWidth !=0)) {
      width=document.body.clientWidth;
      height=document.body.clientHeight;
   }
   if (document.documentElement && (document.documentElement.clientWidth!=0) && (document.body.clientWidth + 20 >= document.documentElement.clientWidth)) {
      width=document.documentElement.clientWidth;   
      height=document.documentElement.clientHeight;   
   }   
   return [width,height];
}


var ID=null;
function moveMouse(e) {

	e?evt=e:evt=event;
	
	CSE=evt.target?evt.target:evt.srcElement;
	
	if (!CSE.hasbox) {

	   iElem=CSE;
	   while ((iElem.parentNode) && (!iElem.hasbox)) {
	      scanBO(iElem);
	      iElem=iElem.parentNode;
	   }	   
	}
	
	if ((CSE!=LSE)&&(!isChild(CSE,dvHdr))&&(!isChild(CSE,dvBdy))){		
	   if (!CSE.boxItem) {
			iterElem=CSE;
			while ((iterElem.hasbox==2)&&(iterElem.parentNode))
					iterElem=iterElem.parentNode; 
			CSE.boxItem=iterElem;
			}
		iterElem=CSE.boxItem;
		if (CSE.boxItem&&(CSE.boxItem.hasbox==1))  {
			LBE=CBE;
			CBE=iterElem;
			if (CBE!=LBE) {
				applyStyles();
				if (!CBE.requireclick)
					if (CBE.fade) {
						if (ID!=null)
							clearTimeout(ID);
						ID=setTimeout("fadeIn("+CBE.fadespeed+")",CBE.delay);
					}
					else {
						if (ID!=null)
							clearTimeout(ID);
						COL=1;
						ID=setTimeout("oDv.style.visibility='visible';ID=null;",CBE.delay);						
					}
				if (CBE.IEbugfix) {hideSelects();} 
				fixposx=!isNaN(CBE.fixX)?Left(CBE)+CBE.fixX:CBE.absX;
				fixposy=!isNaN(CBE.fixY)?Top(CBE)+CBE.fixY:CBE.absY;			
				lockX=0;
				lockY=0;
				boxMove=true;
				ox=CBE.offX?CBE.offX:10;
				oy=CBE.offY?CBE.offY:10;
			}
		}
		else if (!isChild(CSE,dvHdr) && !isChild(CSE,dvBdy) && (boxMove))	{

			if ((!isChild(CBE,CSE)) || (CSE.tagName!='TABLE')) {   			
   			CBE=null;
   			if (ID!=null)
  					clearTimeout(ID);
   			fadeOut();
   			showSelects();
			}
		}
		LSE=CSE;
	}
	else if (((isChild(CSE,dvHdr) || isChild(CSE,dvBdy))&&(boxMove))) {
		totalScrollLeft=0;
		totalScrollTop=0;
		
		iterElem=CSE;
		while(iterElem) {
			if(!isNaN(parseInt(iterElem.scrollTop)))
				totalScrollTop+=parseInt(iterElem.scrollTop);
			if(!isNaN(parseInt(iterElem.scrollLeft)))
				totalScrollLeft+=parseInt(iterElem.scrollLeft);
			iterElem=iterElem.parentNode;			
		}
		if (CBE!=null) {
			boxLeft=Left(CBE)-totalScrollLeft;
			boxRight=parseInt(Left(CBE)+CBE.offsetWidth)-totalScrollLeft;
			boxTop=Top(CBE)-totalScrollTop;
			boxBottom=parseInt(Top(CBE)+CBE.offsetHeight)-totalScrollTop;
			doCheck();
		}
	}
	
	if (boxMove&&CBE) {

		bodyScrollTop=document.documentElement&&document.documentElement.scrollTop?document.documentElement.scrollTop:document.body.scrollTop;
		bodyScrollLet=document.documentElement&&document.documentElement.scrollLeft?document.documentElement.scrollLeft:document.body.scrollLeft;
		mouseX=evt.pageX?evt.pageX-bodyScrollLet:evt.clientX-document.body.clientLeft;
		mouseY=evt.pageY?evt.pageY-bodyScrollTop:evt.clientY-document.body.clientTop;
		if ((CBE)&&(CBE.windowLock)) {
			mouseY < -oy?lockY=-mouseY-oy:lockY=0;
			mouseX < -ox?lockX=-mouseX-ox:lockX=0;
			mouseY > (SHW()[1]-oDv.offsetHeight-oy)?lockY=-mouseY+SHW()[1]-oDv.offsetHeight-oy:lockY=lockY;
			mouseX > (SHW()[0]-dvBdy.offsetWidth-ox)?lockX=-mouseX-ox+SHW()[0]-dvBdy.offsetWidth:lockX=lockX;			
		}
		oDv.style.left=((fixposx)||(fixposx==0))?fixposx:bodyScrollLet+mouseX+ox+lockX+"px";
		oDv.style.top=((fixposy)||(fixposy==0))?fixposy:bodyScrollTop+mouseY+oy+lockY+"px";		
		
	}
}

function doCheck() {	
	if (   (mouseX < boxLeft)    ||     (mouseX >boxRight)     || (mouseY < boxTop) || (mouseY > boxBottom)) {
		if (!CBE.requireclick)
			fadeOut();
		if (CBE.IEbugfix) {showSelects();}
		CBE=null;
	}
}

function pauseBox(e) {
   e?evt=e:evt=event;
	boxMove=false;
	evt.cancelBubble=true;
}

function showHideBox(e) {
	oDv.style.visibility=(oDv.style.visibility!='visible')?'visible':'hidden';
}

function hideBox(e) {
	oDv.style.visibility='hidden';
}

var COL=0;
var stopfade=false;
function fadeIn(fs) {
		ID=null;
		COL=0;
		oDv.style.visibility='visible';
		fadeIn2(fs);
}

function fadeIn2(fs) {
		COL=COL+fs;
		COL=(COL>1)?1:COL;
		oDv.style.filter='alpha(opacity='+parseInt(100*COL)+')';
		oDv.style.opacity=COL;
		if (COL<1)
		 setTimeout("fadeIn2("+fs+")",20);		
}


function fadeOut() {
	oDv.style.visibility='hidden';
	
}

function isChild(s,d) {
	while(s) {
		if (s==d) 
			return true;
		s=s.parentNode;
	}
	return false;
}

var cSrc;
function checkMove(e) {
	e?evt=e:evt=event;
	cSrc=evt.target?evt.target:evt.srcElement;
	if ((!boxMove)&&(!isChild(cSrc,oDv))) {
		fadeOut();
		if (CBE&&CBE.IEbugfix) {showSelects();}
		boxMove=true;
		CBE=null;
	}
}

function showSelects(){
   var elements = document.getElementsByTagName("select");
   for (i=0;i< elements.length;i++){
      elements[i].style.visibility='visible';
   }
}

function hideSelects(){
   var elements = document.getElementsByTagName("select");
   for (i=0;i< elements.length;i++){
   elements[i].style.visibility='hidden';
   }
}
// --------------------------------------------------------------------------------
// global.js: end JavaScript file: '/js/boxover.js'

// global.js: begin JavaScript file: '/js/ContributorDropdown.js'
// ================================================================================
Ss = window.Ss || {};

Ss.ContributorDropdown = {
    
	photographers: [],
	lastSequenceNumber: 0,
	sequenceNumber: 0,
	maxDisplayCount: 10,
	pendingRequests: [],
	activePrefix: undefined,
	dropdownDialogShowing: false,
	
	
	setPhotographer: function(item) {
		$('contributor-input').value = item;
		this.hideList();
	},
	
	hideList: function() {
		$('list').style.display = 'none';
		return;
	},
	
	populateList: function() {
		

		if (this.sequenceNumber != this.lastSequenceNumber) 
			return;
		
		var prefix = this.activePrefix;
		var indexPhotographers = this.photographers[ prefix.substr(0, 2) ];

		var prefixRegex = new RegExp('\\b(' + prefix + ')', 'i');
		var matchingPhotographers = indexPhotographers.pluck('n').grep(prefixRegex);
		var matchesCount = matchingPhotographers.length;
		matchingPhotographers = matchingPhotographers.splice(0, 10);


		if ( matchesCount == 1 ) {
			var testRegex = new RegExp('^' + matchingPhotographers + '$', 'i');
			if (prefix.match(testRegex)) {
				this.hideList();
				return;
			}
		}

		var htmlBuffer = '';
		matchingPhotographers.each(function(item) {
			var highlightedItem = item.replace(prefixRegex, "<b>$1</b>"); 
			var escapedItem = item.replace(/\'/g, '\\\'');
			htmlBuffer += '<div style="cursor: default;" onmouseover="this.style.backgroundColor = \'#f0f4ff\'" onmouseout="this.style.backgroundColor = \'white\'" class="photog" onClick="Ss.ContributorDropdown.setPhotographer(\'' + escapedItem + '\')">' + highlightedItem + '</div>';
		});

		if (matchesCount > this.maxDisplayCount) 
			htmlBuffer += '<div class="photog elipsis">...</div>';

		var listDiv = $('list');
		listDiv.innerHTML = htmlBuffer;
		listDiv.style.display = 'block';

		this.dropdownDialogShowing = true;

		if (this.sequenceNumber == this.lastSequenceNumber) {
			$('loading-anim').hide();
		}
	},
	
	getMatchingNames: function(prefix) {
		
		if (!prefix) {
			this.hideList();
			return;
		}

		prefix = prefix.toLowerCase();
		this.activePrefix = prefix;

		if (this.pendingRequests[prefix.substr(0,2)] == undefined) {

			if (!prefix.substr(0,2).match(/^\w/))
				return;

			this.pendingRequests[prefix.substr(0,2)] = true;
			
			_debug(prefix + ' : request: ' + prefix.substr(0,2));
			
			$('loading-anim').show();
			new Ajax.Request('/display_names.js', {
				method: 'get',
				parameters: { prefix: prefix.substr(0,2), sequence_number: ++this.lastSequenceNumber },
				onComplete: function(transport) {
					Ss.ContributorDropdown.populateList();
				}
			});
		} else if (this.pendingRequests[prefix.substr(0,2)] && !this.photographers[prefix.substr(0,2)]) {
			/* do nothing */
		} else {
			Ss.ContributorDropdown.populateList();
		}
	}
};
// --------------------------------------------------------------------------------
// global.js: end JavaScript file: '/js/ContributorDropdown.js'

// global.js: begin JavaScript file: '/js/PopupAnchor.js'
// ================================================================================
var Ss = window.Ss || Ss;

Ss.PopupAnchor = Class.create({
	initialize: function(element, winName, winWidth, winHeight) {
		this.element = element;
		this.winName = winName;
		this.winWidth = winWidth || 800;
		this.winHeight = winHeight || 600;
		

		this.element.observe('click', this.click.bind(this));
	},
	click: function(evt) {
		

			Event.stop(evt);
			

			var winSpecs = this._winSpecs();
			

			var url = this.element.href;
			var name = this.name;
			var features = $H({
					
				height: 	winSpecs.height,
				width: 		winSpecs.width,
				top: 		winSpecs.getTop(),
				left: 		winSpecs.getLeft(),
				menubar: 	'no',
				resizable: 	'yes',
				scrollbars: 'yes'
				
			}).collect(function(feat){ return feat.key + '=' + feat.value; }).join(', ');
			

			var newWin = window.open(url, name, features);
			

			newWin.focus();
			
	},
	_winSpecs: function() {
		

			var cWin = {
				width: 		window.outerWidth,
				height: 	window.outerHeight,
				left: 		(window.screenLeft || window.screenX),
				top: 		(window.screenTop || window.screenY)
			};
			

			return {
				width: 		this.winWidth,
				height: 	this.winHeight,
				getLeft: 	function(){return (cWin.width > this.width ? (cWin.width - this.width)/2 + cWin.left : cWin.left);},
				getTop: 	function(){return (cWin.height > this.height ? (cWin.height - this.height)/2 + cWin.top : cWin.top);}
			};
			
	}
});
// --------------------------------------------------------------------------------
// global.js: end JavaScript file: '/js/PopupAnchor.js'

// global.js: begin JavaScript file: '/js/ui_widgets/FlyoutLayer.js'
// ================================================================================

Ss.FlyoutLayer = {
	

	TRANSITION_DURATION:	.75, // matches transition-duration value defined in stylesheet
	VIEWPORT_Y_POSITION: 	.25, // show up 25% up from the bottom of the viewport
	CSS: {
		open: 					'flyout_layer_open',
		transition_ready: 		'flyout_transition_ready'
	},
	

	_isOpen: 		false,
	_scrollHandler:	false,
	_observers: 	[],
	

    initialize: function() {


        this.elements = {
            layer:		$('flyout_layer'),
            content:	$('flyout_layer_content')
        };
        
        this._events();
    },
	
	write: function(content) {
	    
		var layer = this.elements.layer,
			contentContainer = this.elements.content;
			

	    if(this.hidden()) {
	        this.show();
	    }
	    

	    contentContainer.childElements().invoke('remove');
	    contentContainer.update();
	    contentContainer.insert(content);
	    
		if(this.isClosed()) {
			layer.removeClassName(this.CSS.transition_ready);
		    this.updateXPosition(); // recalculate the closed x position to fit the new content
		    layer.addClassName.bind(layer).defer(this.CSS.transition_ready);
		}
		
		this.updateYPosition();
		
		Ss.FlyoutLayer.notifyObservers({type: 'write'});
	},
	
	open: function() {
		if(this.isOpen()) {
			return;
		}
		this.elements.layer.addClassName(this.CSS.open);
		this._slideTo(0, function() {
            this.notifyObservers({type: 'open'});
            this._isOpen = true;
		}.bind(this));
	},
	
	close: function() {
		if(this.isClosed()) {
			return;
		}
        this._slideTo(this._calculateXPosition(), function() {
            this.notifyObservers({type: 'close'});
            this.elements.layer.removeClassName(this.CSS.open);
            this._isOpen = false;
        }.bind(this));
	},
	
	isOpen: function() {
		return this._isOpen;
	},

	isClosed: function() {
	    return !this._isOpen;
	},
	
	show: function() {
	    this.elements.layer.show();
	},
	
	hide: function() {
	    this.elements.layer.hide();
	},
	
	visible: function() {
	    return this.elements.layer.visible();
	},

	hidden: function() {
	    return !this.elements.layer.visible();
	},
	
	enableAutoOpen: function(scrollTarget, extraOffsetY) { 


	    
        if(this._scrollHandler) {
            return;
        }
        
		function cleanup(evt) {
            if(evt.type == 'open') {
                Ss.FlyoutLayer.disableAutoOpen();
                Ss.FlyoutLayer.unsubscribeObserver(cleanup);
            }
        }
        
	    var vpH = document.viewport.getHeight();
	    var offsetY = this.getYPosition();
	    
	    if(extraOffsetY) {
	        offsetY += extraOffsetY;
	    }
	    
	    this._scrollHandler = function(e) {
            if(scrollTarget.viewportOffsetFix().top + scrollTarget.getHeight() + this.getHeight() + offsetY <= vpH) {
                if(this.isClosed()) {
                    this.open();
                    this.disableAutoOpen();
                }
            }
        }.bind(this);
        Event.observe(window, 'scroll', this._scrollHandler);
		this.subscribeObserver(cleanup);
	},
	
	disableAutoOpen: function() {
	    if(this._scrollHandler) {
	        Event.stopObserving(window, 'scroll', this._scrollHandler);
	        this._scrollHandler = false;
	    }
	},
	
	subscribeObserver: function(f) {
		this._observers.push(f);	
	},
	
	unsubscribeObserver: function(f) {
		this._observers = this._observers.without(f);
	},
	
	notifyObservers: function(event) {
		this._observers.each(function(fn) {
			fn(event);
		});
	},

	getHeight: function() {
	    return this.elements.layer.getHeight();
	},

	getYPosition: function() {
	    return  parseInt(this.elements.layer.getStyle('bottom'));
	},

	updateXPosition: function() {
	    this._isOpen = false;
	    this.elements.layer.setStyle({ right: this._calculateXPosition() + 'px' });
	},
	
    updateYPosition: function() {
        var yPos = this._calculateYPosition();
        this.elements.layer.setStyle({ bottom: yPos  + 'px'});
    },
	

	_calculateXPosition: function() {
	    return -this.elements.layer.getWidth();
	},
	
	_calculateYPosition: function() {
	    return ((document.viewport.getHeight() * this.VIEWPORT_Y_POSITION ) - (this.elements.layer.getHeight() * this.VIEWPORT_Y_POSITION)).round();
	},
	
	_slideTo: function(xPosEnd, callback) {
	    
		if(this.elements.layer.CSSTransitionsSupported()) {
			this.elements.layer.setStyle({ 'right': xPosEnd + 'px' });
			Object.isFunction(callback) && callback.delay(this.TRANSITION_DURATION);
		} else {
			this.elements.layer.setStylePeriodically({
				property:	'right',
				endValue:	xPosEnd,
				increment:	30,
				units:		'px',
				onComplete:	callback
			});
		}

	},
	
    _events: function() {


        this.elements.layer.observe('mousedown', function(evt) {
                var target = Event.element(evt);
                
                if(this.isClosed()) {
                    this.open();
                }

                else if(target != this.elements.content && !target.descendantOf(this.elements.content)) {
                    this.close();
                }
        }.bind(this));
    }
	
};



if(Prototype.Browser.IE &&
        parseInt(navigator.userAgent.substring(navigator.userAgent.indexOf("MSIE")+5))==6) 
{
    

    Ss.FlyoutLayer.updateYPosition = function() {
        var yPos = -(this._calculateYPosition());
        this.elements.layer.setStyle({ marginTop: yPos + 'px'});
        this.adjustShim();
        this._shim.setStyle({marginTop: yPos + 'px'});
    };
    


    Ss.FlyoutLayer.open = function() {
        this._isOpen = true;
        this.elements.layer.addClassName(this.CSS.open);
        this.elements.layer.setStyle({height: ''});
        this.elements.content.show();
        this.notifyObservers({type: 'open'});
        this.adjustShim();
        this._shim.show();
    };
    
    Ss.FlyoutLayer.close = function() {
        this._isOpen = false;
        this.elements.layer.removeClassName(this.CSS.open);
        this.updateXPosition();
        this.notifyObservers({type: 'close'});
        this._shim.hide();
    };
    
    Ss.FlyoutLayer.updateXPosition = function() {
        this.elements.layer.setStyle({height: this.elements.content.getHeight() + 'px'});
        this.elements.content.hide();
    };
    
    Ss.FlyoutLayer.getYPosition = function() {
	    return  parseInt(this.elements.layer.getStyle('bottom'));
	};
	
	Ss.FlyoutLayer.getHeight = function() {
	    return this.elements.layer.getHeight();
	};
    


	Ss.FlyoutLayer.adjustShim = function() {
        if(!this._shim) {
            this._shim = new Element('IFRAME', {
                style: 'filter:progid:DXImageTransform.Microsoft.Alpha(opacity=0);display:none',
                frameborder: 0
            });
            this.elements.layer.insert({before: this._shim});
        }
        
        var dimensions = this.elements.layer.getDimensions();
        
        this._shim.setStyle({
            width: dimensions.width + 'px',
            height: dimensions.height + 'px'
        });
	};
}



if(Prototype.Browser.Opera) {
	Object.extend(Ss.FlyoutLayer, {
		open: function() {
			this.openInstantly();
			window.scrollBy(0,1); // force reflow of fixed element in opera
		},
		
		close: function() {
			this.closeInstantly();
			window.scrollBy(0,1); // force reflow of fixed element in opera
		},
		
		openInstantly: function() {
			this.elements.layer.setStyle({ right: 0 });
			this._isOpen = true;
			this.notifyObservers({type: 'open'});
			this.elements.layer.addClassName(this.CSS.open);
		},
		
		closeInstantly: function() {
			this.updateXPosition();
			this._isOpen = false;
			this.elements.layer.removeClassName(this.CSS.open);
		}
	});
}
// --------------------------------------------------------------------------------
// global.js: end JavaScript file: '/js/ui_widgets/FlyoutLayer.js'

// global.js: begin JavaScript file: '/js/ui_widgets/ShadowContainer.js'
// ================================================================================
var Ss = window.Ss || {};

Ss.ShadowContainer = {

/*
OPTIONS: 
{

	template: string (shadow/legacy),
	
	modal: {
		color: 		string (color code)
	},
	
	position: { 
		target: 	element,
		type: 		string (bottom/right/bottom-center), (TODO: more position types)
		offsetX: 	number,
		offsetY: 	number
	},
	
	notch: {,
		type:		string (top/right/bottom/left)
		styles: 	{}
	}
	
	closeButton: 	{
		type:		string (css className.. pass false/empty string for no close button)
	},
	
	className: 		string,

}
*/

	DEFAULT_OPTIONS: {

		template: 'shadow',
		
		modal: {
			color: '#FFF'
		},
		
		position: null,
		
		notch: null,
		
		closeButton: {
			type: 'close_btn'
		},
		
		className: ''
	
	},
	

	templates: {
	    legacy: 		'<table class="shadow-container" border="0" cellpadding="0" cellspacing="0" align="left"><tr><td class="shadow-corner-cell shadow-1 shadow-cell"><div style="_width: 30px;"></div></td><td class="shadow-top-cell shadow-2 shadow-cell"><div style="_width: 30px;"></div></td><td class="shadow-corner-cell shadow-3 shadow-cell"><div style="_width: 30px;"></div>&nbsp;</td></tr><tr><td class="shadow-side-cell shadow-4">&nbsp;</td><td id="ss_shadow_container_content" valign="middle" class="shadow-cc"></td><td class="shadow-side-cell shadow-6">&nbsp;</td></tr><tr><td class="shadow-corner-cell shadow-7 shadow-cell"><div style="_width: 30px;"></div>&nbsp;</td><td class="shadow-bottom-cell shadow-8 shadow-cell"><div style="_width: 30px;"></div>&nbsp;</td><td class="shadow-corner-cell shadow-9 shadow-cell"><div style="_width: 30px;"></div>&nbsp;</td></tr></table><div id="sc_notch"></div><span id="ss_shadow_container_close" class="legacy_close_btn">x</span>',
	    shadow:     	'<div class="shadow"><div id="ss_shadow_container_content"></div></div><div id="sc_notch"></div><div id="ss_shadow_container_close" class="close_btn"></div>',
        notch: {
        	top:		'<div class="shadow_arrow_top"><span class="sa_border"></span><span class="sa_arrow"></span></div>',
        	right:		'<div class="shadow_arrow_right"><span class="sa_border"></span><span class="sa_arrow"></span></div>',
        	bottom: 	'<div class="shadow_arrow_bottom"><span class="sa_border"></span><span class="sa_arrow"></span></div>',
        	left:		'<div class="shadow_arrow_left"><span class="sa_border"></span><span class="sa_arrow"></span></div>'
        }
	},
	
    template:   '',

    css: {
        modal: "ss_shadow_container_modal"
    },
    
    initialized: false,
    
    activeOptions: null,
    
    observers: [],
    
    initialize: function(template) {
        

        this.setTemplate(template);
        

        this.initialized = true;

		this.current_content = null;
    },
    
    setTemplate: function(template) {

        this.template = template ? this.templates[template] : this.templates.legacy;
        

        this.element = $('ss_shadow_container');
        this.body = $$('body').first();
        this.clearContent();
        this.element.update(this.template);
        this.content = $('ss_shadow_container_content');
        this.closeButton = $('ss_shadow_container_close');
        this.pageCover = $('ss_shadow_container_page_cover');
        this.notchContainer = $('sc_notch');
        

        this.closeButton.observe('click', this.hide.bind(this));
    },
   
    show: function(_options) { // show the shadow container
    	
    	var options = Object.clone(this.DEFAULT_OPTIONS);
    	_options && Object.extend(options, _options);

        if(this.element.visible()) {
            this._reset();
        }
        
        this.element.show();
        
        this.element.className = options.className;

		if(options.modal) {
			this.doModal(options);
		}
		
		if(options.notch) {
			this.doNotch(options);
		}
		
		if(options.position) {
			this.positionNextTo(options);
		} else {
			this.positionAtCenter();
		}
		
		if(options.closeButton && options.closeButton.type) {
			this.closeButton.className = options.closeButton.type;
			this.closeButton.show();
		} else {
			this.closeButton.hide();
		}

        this._setEvents();
        this._notifyObservers({type: 'show'});
        
        this.activeOptions = options;
    },

    hide: function() { // hide the shadow container
        this.element.hide();
        this._reset();
        this._notifyObservers({type: 'hide'});
    },
    
    visible: function() {
    	return this.element && this.element.visible();
    },
    
    write: function(content, options) { // write content, show, and position the shadow container (@content:String/Element, @options: {className:String, target:Element, modal:Boolean, template:String})
        var template = this.DEFAULT_OPTIONS.template;
        if(options && options.template) {
            template = options.template;
        }
        if(!this.initialized) {
            this.initialize(template);
        } else {
            this._reset();
        }
        if(template != this.template) {
            this.setTemplate(template);
        }
	    this.clearContent();
		this.current_content = content;
        this.content.update(content);
        this.show(options);
        return this.element;
    },
    
    clearContent: function() {

        this.content && this.content.childElements().invoke('remove');
    },
    
    getContent: function() {
    	return this.content;
    },
    
    doModal: function(options) {
        this.body.addClassName(this.css.modal);
        this.pageCover.setStyle({
        		backgroundColor: options.modal.color
        });
    },
    
    undoModal: function() {
        this.body.removeClassName(this.css.modal);
    },
    
    doNotch: function(options) {
    	var type = options.notch.type || 'top',
    		styles = options.notch.styles;

    	this.notchContainer.update(this.templates.notch[type]);

    	if(styles) {
    		this.notchContainer.down().setStyle(styles);
    	}
    },
    
    undoNotch: function() {
    	this.notchContainer.update('');
    },
    
    getNotch: function() {
    	return this.notchContainer.down();
    },
    
    positionNotch: function(target) { // NOTE: currently only supports notches on the of container
		var maxLeft 		= this.element.getDimensions().width,
			notch 			= this.getNotch(),
			notchWidth 		= 37,
			notchPos 		= parseInt(notch.getStyle('left')),
			targetCenterPos = target.cumulativeOffset().left + (target.getWidth()/2).round(),
			notchCenterPos 	= notch.cumulativeOffset().left + (notchWidth/2).round(),
			pixelsOff 		= targetCenterPos - notchCenterPos,
			left 			= (notchPos + pixelsOff > maxLeft ? maxLeft : notchPos + pixelsOff);
		
		if(pixelsOff > notchWidth) {
			notch.setStyle({
					left: left + 'px'
			});
		}
	
    },
    
    positionNextTo: function(options) { // position the container (centers it or places it next to an optional @target:Element)
        var left, 
            top, 
            vpDim = 	document.viewport.getDimensions(), 
            eDim = 		this.element.getDimensions(), 
            tDim = 		options.position.target.getDimensions(), 
            tPos = 		options.position.target.cumulativeOffset(),
            dPos = 		options.position.type || 'right',
            offsetX = 	options.position.offsetX || 0,
            offsetY = 	options.position.offsetY || 0,
            SOME_PADDING = 20;

        
        switch(dPos) {
            
            case 'bottom':
                left =  ((vpDim.width > tPos.left + eDim.width) ?
                            tPos.left :
                            vpDim.width - eDim.width - SOME_PADDING);
                top =   tPos.top + tDim.height;
                break;
                
            case 'right':
                left =  ((vpDim.width > tPos.left + eDim.width + tDim.width) ?
                            tPos.left + tDim.width :
                            tPos.left - eDim.width - tDim.width);
                top =   ((vpDim.height > tPos.top + eDim.height) ?
                            tPos.top :
                            tPos.top - eDim.height);
                break;
                
            case 'bottom-center':
                left =  ((vpDim.width > tPos.left + (eDim.width/2).round() + (tDim.width/2).round()) ?
                            tPos.left - (eDim.width/2).round() + (tDim.width/2).round():
                            vpDim.width - eDim.width - SOME_PADDING);
                top =   tPos.top + tDim.height;
                break;
            
        }
        
        left += offsetX;
        top += offsetY;
        
        this.element.setStyle({
                top: top + 'px',
                left: left + 'px'
        });

    },
    
    positionAtCenter: function() {
        var vpDim = document.viewport.getDimensions(), eDim = this.element.getDimensions();
        this.element.setStyle({
                top: (vpDim.height/2 - eDim.height/2) + 'px',
                left: (vpDim.width/2 - eDim.width/2) + 'px'
        });
    },
    
	observe: function(f) { // register a function @f as an observer of events
		this.observers.push(f);	
	},
	
	stopObserving: function(f) { // register a function @f as an observer of events
		this.observers = this.observers.without(f);
	},
	

	_notifyObservers: function(e) {
		this.observers.each(function(f){f(e);});
	},
	
    _clickAwayHandler: function(e) {
        var elem = e.findElement() 
        if( !elem.descendantOf(Ss.ShadowContainer.element) ||
            elem == Ss.ShadowContainer.closeButton) 
        {
            Ss.ShadowContainer.hide();
        }
    },
    
    _keypressHandler: function(e) {
        if(e.keyCode == Event.KEY_ESC) {
            Ss.ShadowContainer.hide();
        }
    },
    
    _resizeHandler: function(e) {
    	Ss.ShadowContainer.positionNextTo(Ss.ShadowContainer.activeOptions || Ss.ShadowContainer.DEFAULT_OPTIONS);
    },
    
    _setEvents: function() {
        (function(){
            $(document).observe('click', this._clickAwayHandler);
        }.bind(this)).defer(); // using defer here to avoid running this document handler prematurely when called by the handler of a click event that bubbles up.
        $(document).observe('keypress', this._keypressHandler);
        Event.observe(window, 'resize', this._resizeHandler);
    },
    
    _unsetEvents: function() {
        $(document).stopObserving('click', this._clickAwayHandler);
        $(document).stopObserving('keypress', this._keypressHandler);
        Event.stopObserving(window, 'resize', this._resizeHandler);
    },
    
    _reset: function() {
        this.element.className = "";
		this.current_content = null;
		this.activeOptions = null;
        this._unsetEvents();
        this.undoModal();
        this.undoNotch();
    }
};

/*
 * register(obj) format = {
 *		key:key to use
 *		content: string or node
 *		sc_opts: options object for ShadowContainer
 *		callbacks: {show:func, hide:func}
 *		toggle: true | false 
 *	}
 */
Ss.ShadowContainer.Stateful = {
	instances: {},
	_current_key: null,
	_initialized: false,
	initialize: function(){
		var self = this;
		Ss.ShadowContainer.observe(function(obj){
			if(obj.type == 'show'){
				if(self._current_key &&
						Ss.ShadowContainer.current_content != self.instances[self._current_key].content){
					self._current_key = null;
				}
			}else{
				self._current_key = null;
			}
		});
	},
	register: function(obj){
		if(!this._initialized){
			this.initialize();
			this._initialized = true;
		}
		if(!obj.callbacks) obj.callbacks = {};
		this.instances[obj.key] = obj;
	},
	toggle: function(key){
		var inst = this.instances[key];
		var isShow = (this._current_key != key);
		var sc_funcname = (isShow ? 'write' : 'hide');
		var callback = (isShow ? inst.callbacks.show : inst.callbacks.hide);
		if(callback) callback(inst);
		Ss.ShadowContainer[sc_funcname](inst.content, inst.sc_opts);
		this._current_key = (isShow) ? key : null;
	}
}

        
/*  Internet Explorer 6: Shadow Container Support
 *  This is especially unfortunate, but some necessary features are not supported out of the box for Internet Explorer 6.
 *  We will only implement these features manually by modifying the Ss.ShadowContainer object for IE6 users.
 *  If IE6 support isn't needed, lines below can be safely removed.
 */
if(Prototype.Browser.IE &&
        parseInt(navigator.userAgent.substring(navigator.userAgent.indexOf("MSIE")+5))==6) 
{
    Ss.ShadowContainer.overridedMethods = { // the following methods will be overrided for IE6
        doModal: Ss.ShadowContainer.doModal,
        show: Ss.ShadowContainer.show,
        _reset: Ss.ShadowContainer._reset,
        positionAtCenter: Ss.ShadowContainer.positionAtCenter
    };
    Ss.ShadowContainer.doModal = function(options) {
        Ss.ShadowContainer.overridedMethods.doModal.call(this, options); // call the normal method and provide IE6 support (iframe shim, and sizing of page cover)
        if(!this.pageCover) {
            this.pageCover = $('ss_shadow_container_page_cover');
        }
        this.pageCover.setStyle({
            width: document.body.clientWidth + 'px',
            height: document.body.clientHeight + 'px'
        });
        if(!this.modal_shim) {
            this.modal_shim = Ss.ShadowContainer.insertShim();
        }
        this.positionShim(this.modal_shim, this.pageCover);
    };
    Ss.ShadowContainer.show = function(_options) {
    	var options = Object.clone(this.DEFAULT_OPTIONS);
    	_options && Object.extend(options, _options);
        Ss.ShadowContainer.overridedMethods.show.call(this, options); // call the normal method, then create and position a shim
        if(!this.element_shim) {
            this.element_shim = Ss.ShadowContainer.insertShim();
        }
        this.positionShim(this.element_shim, this.element);
    };
    Ss.ShadowContainer._reset = function() {
        Ss.ShadowContainer.overridedMethods._reset.call(this); // call the normal method, then clean up after ie6 specific stuff
        this.shims.invoke('hide');
        this.body.removeClassName('ss_ie_centered');
    };
    Ss.ShadowContainer.positionAtCenter = function() { // alternate centering instructions for ie6
        var elem = this.element;
        this.body.addClassName('ss_ie_centered');
        elem.setStyle({
                top: ((document.body.scrollTop) ?
                                document.body.scrollTop + (document.body.clientHeight/2 - elem.clientHeight/2) :
                                document.documentElement.scrollTop + (document.documentElement.clientHeight/2 - elem.clientHeight/2)) + 'px', 
                left: ((document.body.clientWidth) ?
                        document.body.clientWidth/2 - elem.clientWidth/2 :
                        document.documentElement.clientWidth/2 - elem.clientWidth/2) + 'px'
        });
    };

    Ss.ShadowContainer.shims = [];
    Ss.ShadowContainer.insertShim = function() {
        var shim = new Element('iframe', {
          style: 'position:absolute;filter:progid:DXImageTransform.Microsoft.Alpha(opacity=0);display:none',
          frameborder: 0
        });
        this.body.insert(shim);
        this.shims.push(shim);
        return shim;
    };
    Ss.ShadowContainer.positionShim = function(shim, element) {
        var element = $(element),
            offset = element.cumulativeOffset(),
            dimensions = element.getDimensions(),
            style = {
              left: offset[0] + 'px',
              top: offset[1] + 'px',
              width: dimensions.width + 'px',
              height: dimensions.height + 'px',
              zIndex: element.getStyle('zIndex') - 1
            };
        shim.setStyle(style).show();
    };
}
/* end IE6 support */
// --------------------------------------------------------------------------------
// global.js: end JavaScript file: '/js/ui_widgets/ShadowContainer.js'

// global.js: begin JavaScript file: '/js/ui_widgets/Bubbles.js'
// ================================================================================
var Ss = window.Ss || {};

Ss.Bubbles = {

	STATES: {
		HIDDEN: 1,
		SHOWING: 2,
		VISIBLE: 3,
		HIDING: 4
	},
	
	keyToBubble: {},
	
	keyToState: {},
	
	keyToTransition: {},
	
	keyToPosition: {},
	
	addBubble: function(key, elem, position) {
		this.keyToBubble[key] = elem;
		this.setState(key, this.STATES.HIDDEN);
		this.keyToPosition[key] = position;
	},

	getBubble: function(key) {
		return this.keyToBubble[key];
	},
	
	getPosition: function(key) {
		return this.keyToPosition[key];
	},
	
	setState: function(key, state) {
		this.keyToState[key] = state;
	},
	
	getState: function(key) {
		return this.keyToState[key];
	},

	stopTransition: function(key) {
		if(this.keyToTransition[key]) { 
			this.keyToTransition[key].stop();
		}
	},
	
	showBubble: function(key) {
		var state = this.getState(key),
			bubble = this.getBubble(key);
		
		window.scrollTo(0,0); // scroll to top of page regardless of state       
		
		if( state === this.STATES.SHOWING ||
				state === this.STATES.VISIBLE )
		{
			return;
		}
		
		if( state === this.STATES.HIDING ) {
			this.stopTransition(key); // if the bubble is currently transitioning in the opposite direction, stop the transition
		}
		
		this.doModal(); // show the page cover
		
		bubble.show(); // show the bubble if it's invisible (only initially invisible)
		

		this.setState(key, this.STATES.SHOWING);
		this.keyToTransition[key] = bubble.fadeIn({
			onComplete: function() {
				Ss.Bubbles.setState(key, Ss.Bubbles.STATES.VISIBLE);
			}
		});
	},

	showSingleBubble: function(key) {
		this.getKeys().each(
			function(_key) {
				if(key !== _key) {
					this.hideBubble(_key);
				} else {
					this.showBubble(_key);
				}
			}.bind(this)
		);
	},
	
	hideBubble: function(key) {
		var state = this.getState(key),
			bubble = this.getBubble(key);
		
		if( state === this.STATES.HIDING || 
				state === this.STATES.HIDDEN )
		{
			return;
		}

		if( state === this.STATES.SHOWING ) {
			this.stopTransition(key);
		}
		
		this.setState(key, this.STATES.HIDING);
		this.keyToTransition[key] = bubble.fadeOut({
			onComplete: function() {
				Ss.Bubbles.setState(key, Ss.Bubbles.STATES.HIDDEN);
			}
		});
	},
	
	showAll: function () {
		this.getKeys().each(
			function(key) {
				Ss.Bubbles.showBubble(key);
			}
		);
	},

	hideAll: function() {
		this.undoModal();
		this.getBubbles().invoke('setOpacity', 0);
		this.getBubbles().invoke('hide');
		this.getKeys().each(
			function(key) {
				this.setState(key, this.STATES.HIDDEN);
			}.bind(this)
		);

		if (Ss.SearchAnnouncement) {
			Ss.SearchAnnouncement.getFeatureElements().invoke('removeClassName', 'active_feature');
		}
	},
	
	getKeys: function() {
		return Object.keys(this.keyToBubble);
	},
	
	getBubbles: function() {
		return Object.values(this.keyToBubble);
	},

	doModal: function() {
		$('bubbles_mask_page_cover').show();
		$$('body')[0].addClassName('bubbles_visible');
		this.setClickAwayToClose();
	},
	
	undoModal: function() {
		$$('body')[0].removeClassName('bubbles_visible');
		$('bubbles_mask_page_cover').hide();
	},
	

	setClickAwayToClose: function () {
		$(document).stopObserving('click', Ss.Bubbles.clickAwayHandler);
		$(document).observe('click', Ss.Bubbles.clickAwayHandler);
	},
	

	clickAwayHandler: function (e) {
		var elem = e.findElement(),
			bubbles = Ss.Bubbles.getBubbles(),
			isDescendant = false;
		

		bubbles.each(
			function (bubble) {
				isDescendant = isDescendant || elem.descendantOf(bubble) || elem.hasClassName('bubble');
			}
		);

		if ( !isDescendant ) {
			Ss.Bubbles.hideAll();
			$(document).stopObserving('click', Ss.Bubbles.clickAwayHandler);
		}
	}

};
// --------------------------------------------------------------------------------
// global.js: end JavaScript file: '/js/ui_widgets/Bubbles.js'

// global.js: begin JavaScript file: '/js/SlideViewer.js'
// ================================================================================
Ss.SlideViewer = Class.create({
    
    initialize: function(args) {
        

        this.mover = args.mover.addClassName(this.CSS.mover);
        this.clipper = args.clipper.addClassName(this.CSS.clipper);
        this.sizeOfSlide = args.sizeOfSlide;
        this.sizeOfClipper = this._getClipperSize();
        

        this.speed = args.speed || 30;
        

        this.slides = [];
        this._locked = false;
        this._sizeType = 'width';
        this._posType = 'left';



		this.callbacks = ((args.callbacks) ? args.callbacks : []);
    },
    

    push: function(content) {
        this._add();
        var slide = this._newSlide(content);
        this.mover.insert(slide);
        this.slides.push(slide);
        return slide;
    },
    
    unshift: function(content) {

        var slide = this._newSlide(content);
        this.mover.insert({top: slide});
        this._add(true);
        this.slides.unshift(slide);
        return slide;
    },
    

    pop: function() {
        if(!this.slides.size()) {
            return;
        }
        var slide = this.slides.pop().remove();
        this._remove();
        return slide;
    },
    
    shift: function() {
        if(!this.slides.size()) {
            return;
        }
        var slide = this.slides.shift().remove();
        this._remove(true);
        return slide;
    },

    indexOf: function(slide) {
        if(!Object.isElement(slide)) {
            return -1;
        }
        slide = (slide.hasClassName('slide') ? slide : slide.up('.slide'));
        return (slide ? this.slides.indexOf(slide) : -1);
    },

    removeSlide: function(slide) {
        slide.remove();
        this.slides = this.slides.without(slide);
    },
    
    clear: function() {
        this._clear();
        this.mover.update(''); // TODO: remove differently
        this.slides.clear();
    },
    

    next: function(options) {
        this._navigate(1, options);
    },
    
    prev: function(options) {
        this._navigate(-1, options);
    },
    
    navigate: function(by, options) {
       this._navigate(by, options);
    },
    
    navigateToIndex: function(index, options) {
        var range = this.getVisibleSlideRange(),
            by = index - range.first();
        this._navigate(by, options);
    },
    

    setSizeOfSlide: function(sizeOfSlide) {
        this.sizeOfSlide = sizeOfSlide;
    },

    setSpeed: function(speed) {
        this.speed = speed;
    },
    

    getSlideFromElement: function(elem) {
        var index = this.indexOf(elem);
        return (index != -1 ? this.slides[index] : false);
    },
    
    getSlideByIndex: function(index) {
        return this.slides[index];
    },
    
    getSlides: function() {
        return this.slides;
    },
    
    getMover: function() {
        return this.mover;
    },
    
    getClipper: function() {
        return this.clipper;
    },
    
    getVisibleSlideRange: function() {
        var firstIndex = this._getFirstVisibleSlideIndex(),
            visibleSlideCapacity = this.getVisibleSlideCapacity(),
            lastIndex = (this.slides.size() > firstIndex + visibleSlideCapacity ? firstIndex + visibleSlideCapacity : this.slides.size())
        return $A($R(firstIndex, lastIndex, true));
    },
    
    getVisibleSlideCapacity: function() {
        return Math.floor(this.sizeOfClipper/this.sizeOfSlide);
    },
    

    _clear: function() {
        this.mover.style[this._sizeType] = '';
        this.mover.style[this._posType] = '';
    },
    
    _remove: function(shift) {
        this.mover.style[this._sizeType] = (this._getMoverSize() - this.sizeOfSlide) + 'px';
        this.mover.style[this._posType] = (this.mover.positionedOffset()[this._posType] + (shift ? this.sizeOfSlide : 0)) + 'px';
    },
    
    _add: function(unshift) {
        this.mover.style[this._sizeType] = (this._getMoverSize() + this.sizeOfSlide) + 'px';
        this.mover.style[this._posType] = (this.mover.positionedOffset()[this._posType] - (unshift ? this.sizeOfSlide : 0)) + 'px';
    },
    
    _newSlide: function(content) {
        var elem = new Element('DIV', {'class': this.CSS.slide});
        elem.style[this._sizeType] = this.sizeOfSlide + 'px';
        elem.insert(content);
        return elem;
    },
    
    _getClipperSize: function() {
        return this.clipper.getWidth();
    },
    
    _getMoverSize: function() {
        return this.mover.getWidth();
    },
    
    _navigate: function(by, options) {
		var self = this;
        if(this._locked) {
            return;
        }
        if(!by && !(by === 0)) {
            return;
        }

        this._locked = true;
        var endValue, 
			maxValue = 0, 
			minValue = -((this.slides.size()-1) * this.sizeOfSlide),
			slideViewer = this,
			mover = this.mover,
			units = 'px',
			duration,
			_options = {
				transition: true,
				onComplete: Prototype.emptyFunction
			},
			_onComplete;
			
		Object.extend(_options, options);
		
		_onComplete = function() {
			  slideViewer._locked = false;
			  if(Object.isFunction(_options.onComplete)) {
				  _options.onComplete();
			  }
			  self.callbacks.each(function(cb){
				  cb({type: 'end',index: self._getFirstVisibleSlideIndex()});
			  });
		};
			
        endValue = this.mover.positionedOffset()[this._posType] - (by * this.sizeOfSlide);
        endValue = (endValue < minValue ? minValue : endValue);
        endValue = (endValue > maxValue ? maxValue : endValue);

        
        
        if(!_options.transition) {
        	mover.style[this._posType] = endValue + units;
        	_onComplete();
			this.callbacks.each(function(cb){
				cb({type: 'direct',index: self._getFirstVisibleSlideIndex(endValue)});
			});
        	return;
        }

		this.callbacks.each(function(cb){
			cb({type: 'start',index: self._getFirstVisibleSlideIndex(endValue)});
		});
        
		if(mover.CSSTransitionsSupported()) {
			

			mover.addClassName('transitioning');
			

			duration = parseFloat(mover.getStyle('-moz-transition-duration') || mover.getStyle('-webkit-transition-duration'));
			

			mover.style[this._posType] = endValue + units;
			
			(function(){

					mover.removeClassName('transitioning');
					

					_onComplete();
			}.delay(duration));
			
		} else {
			
			this.mover.setStylePeriodically({
					property:     this._posType,
					endValue:     endValue,
					increment:    this.speed,
					units:        units,
					onComplete:   _onComplete
			});
			
		}
    },
    
    _getFirstVisibleSlideIndex: function(pos) {
        var currentPos = (pos != null ? pos : this.mover.positionedOffset()[this._posType]);
        return Math.round(Math.abs(currentPos/this.sizeOfSlide));
    },
    

    CSS: {
        slide: 'slide',
        mover: 'mover',
        clipper: 'clipper'
    }
        
});
// --------------------------------------------------------------------------------
// global.js: end JavaScript file: '/js/SlideViewer.js'

// global.js: begin JavaScript file: '/js/input/TextWithDefault.js'
// ================================================================================
Ss.input = window.Ss.input || {};

Ss.input.TextWithDefault = Class.create({
        initialize: function(args) {
            this.textField = args.textField;
            this.defaultValue = args.defaultValue;
            this.defaultCSS = args.defaultCSS || 'default';
            
            this.textField.observe('focus', this.focus.bind(this));
            this.textField.observe('blur', this.blur.bind(this));
        },
        focus: function(evt) {
            if(!this.textField.hasClassName(this.defaultCSS)) {
                return;
            }
            this.textField.removeClassName(this.defaultCSS);
            this.textField.clear();
        },
        blur: function(evt) {
           if(this.textField.getValue().strip().empty()) {
                this.textField.addClassName('default');
                this.textField.value = this.defaultValue;
            }
        }
});
// --------------------------------------------------------------------------------
// global.js: end JavaScript file: '/js/input/TextWithDefault.js'

// global.js: begin JavaScript file: '/js/input/PassWithDefault.js'
// ================================================================================



Ss.input = window.Ss.input || {};

Ss.input.PassWithDefault = Class.create({
        initialize: function(args) {
            this.textField = args.textField;
            this.fieldName = args.fieldName || this.textField.name;
            
            this.textField.observe('focus', this.focus.bind(this));
            this.passwordField = null; // will be created/inserted when needed
        },
        focus: function(evt) {
            this.showPasswordField();
            this.passwordField.focus();
        },
        blur: function(evt) {
            if(this.passwordField.getValue().strip().empty()) {
                this.showTextField();
            }   
        },
        showPasswordField: function() {
            if(!this.passwordField) {
                this.makePasswordField();
            }
            this.passwordField.show();
            this.textField.hide();
        },
        showTextField: function() {
            this.textField.show();
            this.passwordField.hide();
        },
        makePasswordField: function() {
            this.passwordField = new Element('input', {
                    type:   'password',
                    name:   this.fieldName
            })
            .observe('blur', this.blur.bind(this));
            this.textField.insert({after: this.passwordField});
            this.textField.name = '';
        }
});
// --------------------------------------------------------------------------------
// global.js: end JavaScript file: '/js/input/PassWithDefault.js'

// global.js: begin JavaScript file: '/js/input/InFieldLabel.js'
// ================================================================================
Ss = window.Ss || {};
Ss.input = window.Ss.input || {};

Ss.input.InFieldLabel = Class.create({
		
		initialize: function(args) {
			

			this.label = args.label;
			this.field = args.field;
			

            this.options = Object.extend(Ss.input.InFieldLabel.defaultOptions, args.options || {});
			

			this.showing = true;
			

			var base = this;
			(function(){

				if(!base.field.getValue().strip().empty()) {
					base.label.hide();
					base.showing = false;
				}
			}).delay(.25);
			
			this._events();
			
		},
		
		showFocus: function() {
			if(this.showing){
				this.setOpacity(this.options.fadeOpacity);
			}
		},
		
		setOpacity: function(opacity) {
			this.label.setOpacity(opacity);
			this.showing = (opacity > 0.0);
		},

		checkForEmpty: function(blur) {
			if(this.field.getValue().strip().empty()){
				if(!this.showing) {
					this.label.show();
				}
				this.setOpacity( blur ? 1 : this.options.fadeOpacity );
			} else {
				this.setOpacity(0.0);
			}
		},
		
		hideOnChange: function(e) {
			if(
				(e.keyCode == 16) || // Skip Shift
				(e.keyCode == 9) // Skip Tab
			  ) return; 
			
			if(this.showing){
				this.label.hide();
				this.showing = false;
			}
		},
		
		subscribe: function(observer) {



			var eventTypes = ['keyup', 'blur', 'change'],
				subject = this;
			
			if(Object.isFunction(observer.update)) {
				eventTypes.each(
					function(eventType) {
						subject.field.observe(eventType, function(evt) {
							observer.update();
						});
					}
				);
			}

		},
		
		
		update: function() {
			this.checkForEmpty(true);
		},
		
		_events: function() {
			var base = this;
			
			this.field.observe('focus', function(){
				base.checkForEmpty();
				base.showFocus();
				base.field.addClassName(base.options.focusCSS);
			});
			
			this.field.observe('blur', function(){
				base.checkForEmpty(true);
				base.field.removeClassName(base.options.focusCSS);
			});
			
			this.field.observe('keydown', function(e) {
				base.hideOnChange(e);
			});


			this.field.observe('change', function(e){
				base.checkForEmpty();
			});

			if(typeof (this.field.onpropertychange) == "object") {
				this.field.observe('propertychange', function() {
					base.checkForEmpty();
				});
			}

			this.label.observe('click', function() {
				( function(){ base.field.focus(); } ).defer(); // deferment is needed to support explorer
			});
			


			Event.observe(window, 'load', function() {
				if(!base.field.getValue().strip().empty()) {
					base.label.hide();
					base.showing = false;
				}
			});
		}
		
});

Ss.input.InFieldLabel.defaultOptions = {
	fadeOpacity: 0.5, // Once a field has focus, how transparent should the label be
	focusCSS: ''// Once a field has focus, what css class do we write in
};


// --------------------------------------------------------------------------------
// global.js: end JavaScript file: '/js/input/InFieldLabel.js'

// global.js: begin JavaScript file: '/js/storage/storage.js'
// ================================================================================
Ss = window.Ss || {};
Ss.storage = {};

Ss.storage.session = {

    _purgeProofKeys: ['_keys', 'search', 'active_lightbox_html', 'active_lightbox_size', 'active_lightbox_id', 'pending_event', 'search_announcement_seen'],
    
    _clearProofKeys: ['active_lightbox_html', 'active_lightbox_size', 'active_lightbox_id',  'pending_event', 'search_announcement_seen'],
    
    getItem: function(key) {
		if (window.location.protocol === "https:") {
			key = "_s_" + key;
		}
        var value = window.sessionStorage.getItem(key);
        if(Object.isString(value) && value.isJSON()) {
            value = value.evalJSON()
        }
        return value;
    },
    
    setItem: function(key, value) {
		if (key.match('^_s_')) {

			return;
		}
		if (window.location.protocol === "https:") {

			key = "_s_" + key;
		}
        var keys = this.getKeys();
        
        if(!Object.isString(value)) {
            value = Object.toJSON(value);
        } 
        
        try {
            
            window.sessionStorage.setItem(key, value);
            keys.push(key);
            window.sessionStorage.setItem('_keys', keys.join(','));
            
        } catch(e) {

            if (e.name === 'QUOTA_EXCEEDED_ERR' || e.name === 'NS_ERROR_DOM_QUOTA_REACHED') {
            
                var excludeKeys = Ss.storage.session._purgeProofKeys;
                var tempKey = '';
                var deleteCount = (window.sessionStorage.length/2).round();

                while(deleteCount > 0) {
					var noSkip = window.location.protocol === 'https:' ? '^_s_.*' : '^(?!_s_).*$';
                    tempKey = keys.shift();
					if (!tempKey.match(noSkip)) {
						continue;
					}
                    if(excludeKeys.include(tempKey)) {
                        keys.push(tempKey);
                        continue;
                    }
                    window.sessionStorage.removeItem(tempKey);
                    deleteCount--;
                }
                window.sessionStorage.setItem(key, value);
                keys.push(key);
                window.sessionStorage.setItem('_keys', keys.join(','));

            }
            
        }
    },
    
    removeItem: function(key, arg) {
		if (arg && arg.noPrefix) {

		} else {
			if (window.location.protocol === "https:") {
				key = "_s_" + key;
			}
		}
        var keys = this.getKeys();
        window.sessionStorage.removeItem(key);
        window.sessionStorage.setItem('_keys', keys.without(key).join(','));
    },
    
    clear: function() {
        var excludeKeys = this._clearProofKeys,
            excludeItems = new Hash();
		var noSkip = window.location.protocol === 'https:' ? '^_s_.*' : '^(?!_s_).*$';
        excludeKeys.each(
            function(key) {
                var item = window.sessionStorage.getItem(key);
                if(item) {
                    excludeItems.set(key, item);
                }
            }
        );
        

		try {
			this.getKeys().each(
				function(key) {
					if (key.match(noSkip)) {
						Ss.storage.session.removeItem(key, {noPrefix: 1});
					} 
				}
			);
		} catch (e) {

		}
        
        excludeItems.each(
            function(entry) {
                window.sessionStorage.setItem(entry.key, entry.value);
            }
        );
        
    },
    
    getKeys: function() {
        var keys = [];
        var sKeys = window.sessionStorage.getItem('_keys');
        if(sKeys) {
            keys = sKeys.split(',');
        }
        return keys;
    },
    
    supported: function() {
    	return 'sessionStorage' in window;
    }
    
};
/*
    notes: 150 thumb pages max out at about page 53.
    
    1. improve strategy around 'what' to remove
    
    2. move expired items to a javascript structure instead of just removing them
    
    3. improve prefetching strategy
    
    4. support _exclude keys manipulation via an arg to setItem
    
*/

Ss.storage.Memory = Class.create({
        
    MAX_STORED: 50,
        
    initialize: function() {
        this.storage = new Hash();
        this.sequentialKeys = [];
    },
    
    getItem: function(key) {
        return this.storage.get(key);
    },
    
    setItem: function(key, value) {
        this.storage.set(key, value);
        this.sequentialKeys.push(key);
        

        if(this.storage.size() > this.MAX_STORED) {
            this.removeItem(this.sequentialKeys.shift());
        }
    },
    
    removeItem: function(key) {
        this.storage.unset(key);
        this.sequentialKeys = this.sequentialKeys.without(key);
    },
    
    clear: function() {
        var storage = this.storage;
        storage.keys().each(
            function(key) {
                storage.remove(key);
            }
        );
        this.sequentialKeys.length = 0;
    }
    
});
// --------------------------------------------------------------------------------
// global.js: end JavaScript file: '/js/storage/storage.js'

// global.js: begin JavaScript file: '/js/search/search.js'
// ================================================================================
Ss = window.Ss || {};

Ss.search = {
    
    enhanced: Ss.enhancedSearchSupported && 'onhashchange' in window, // set by mason in header.mhtml before global javascript file
    
    currentPage: null,
    
    _initialized: false,
    
    criteria: {},
    
    resultsCallbacks: [],
    
    detailCallbacks: [],
    
    initialize: function(args) {

        if(!this.enhanced) {
            Ss.search.nextButton.initialize();
            Ss.image.grid.initialize();
            return;
        }
        
        this.initialPage =  parseInt(args.initialPage);
        this.totalPages =   parseInt(args.totalPages);
        this.setCriteria(args.criteria);
        this.srcID =        args.searchSrcID;
        this.currentPage =  this.initialPage;
        this.text = args.text || { download: 'download', lightbox: 'lightbox' };
        
		this.elements = {
		    container:      $('cat_container'),
		    picContainer:   $('pic_container'),
		    min:            $('image_min_range'),
		    max:            $('image_max_range'),
            body:			$$('body')[0]
		};


		Ss.search.history.initialize();
		Ss.image.grid.initialize();
		Ss.search.nextButton.initialize(this.initialPage, this.totalPages);
		Ss.search.GridPager.initialize(this.initialPage, this.totalPages);
		

		if(Ss.Browser.isIEVersion(9)) {
			this.elements.picContainer.onclick = this.clickDetailPage.bind(this);
		} else {
			this.elements.picContainer.observe('click', this.clickDetailPage.bind(this));
		}
		
		this._initialized = true;
    },
    
    initialized: function() {
        return this._initialized;
    },
    
    setCriteria: function(sCriteria) {
        var criteria = sCriteria.toQueryParams();
        Object.keys(criteria).each(
            function(key){
                this.criteria[key] = decodeURIComponent(escape(criteria[key]));
            }.bind(this)
        );
    },
    
    showDetail: function(pageContent) {
        this.elements.picContainer.update(pageContent);
        this.elements.picContainer.show();
        this.elements.container.hide();
        
        document.body.scrollTo(); 
        

        Ss.AdvancedSearch.resetMenusAndKeywordInput();
        

        Ss.SearchAlerts.disableAdd();
        

        this.elements.body.removeClassName('search_results');
        this.elements.body.addClassName('photo_detail');
        
        this._postDetail(pageContent);
    },
    
    clickDetailPage: function(evt) {
    	
		var selectors = [
			'#pic_info_column .thumb_image_container img',
			'#pic_info_column a.thumb-caption',
			'#more_similar_images a'
		],
		target = Event.findElement(evt),
		anchor,
		href = '',
		photoId = '';
		
		if(selectors.any(function(selector){ return target.match(selector);})) {
			


			if(Ss.Browser.isIEVersion(9)) {
				window.event.returnValue = false;
			} else {
				Event.stop(evt);
			}
			
			anchor = target.match('a') ? target : target.up('a');
			href = anchor.href;
			photoId = href.match(/pic-(\d+)/)[1];
			
			window.location = Ss.search.getHashURL(photoId, href);
		}
				
    },
    
    showResults: function() {
        Ss.AdvancedSearch.sync();
        this.elements.picContainer.hide();
        this.elements.container.show();
        

        this.elements.body.removeClassName('photo_detail');
        this.elements.body.addClassName('search_results');
        
        this._postResults();
    },
    

    resultsVisible: function() {
        return this.elements.container.visible();
    },
    
    update: function(response) {
        var min = Ss.search.elements.min,
            max = Ss.search.elements.max,
            currentPage = parseInt(response.page),
            totalPages = Ss.search.totalPages;
            

        this.currentPage = currentPage;
        this.srcID = response.searchSrcID;      


        Ss.image.grid.update(response);

        document.body.scrollTo();
        
		if(min && max) {
			min.innerHTML = response.min;
			max.innerHTML = response.max;
		}
		Ss.search.nextButton.update(currentPage, totalPages);
		Ss.search.GridPager.update(currentPage, totalPages);
		Ss.AdvancedSearch.sync();
		Ss.search._postResults(response);
		if(!Ss.search.resultsVisible()) {
			Ss.search.showResults();
		}
        
    },
    
    getInitialPage: function() {
        return parseInt(this.initialPage);
    },
    
    getCurrentPage: function() {
        return parseInt(this.currentPage);
    },
    
    getSrcID: function() {
        return this.srcID;
    },
    
    
    getTotalPages: function() {
        return parseInt(this.totalPages);
    },
    
    getCriteria: function() {
        return this.criteria;
    },
    
    getParams: function() {
        var params = {};
        params = Object.extend(params, this.criteria);
        params = Object.extend(params, this.getDisplayPrefs());
        return params;
    },
    
    toQueryString: function() {
        return Object.toQueryString(this.getParams()); //TODO: pluck out page if it's there
    },
    
    getDisplayPrefs: function() {
    	var preferences = {};
		
		var f = function(input) {
    			preferences[input.name] = $F(input);
    		}

		var form = $('grid_options_top');
    	form.select('input[checked]').each(f);

		var overlay = $('display_preferences_overlay');
		if(overlay && !overlay.descendantOf(form)){
			overlay.select('input[checked]').each(f);
		}


        preferences.results_per_chapter = preferences.images_per_page;

        return preferences;
    },
    
    showLoading: function() {
        var container = (this.elements ? this.elements.container : $('cat_container'));
        container.addClassName(this.CSS.loading); // added to support loading state for non-ajax enhanced browsers
    },
    
    hideLoading: function() {
        this.elements.container.removeClassName(this.CSS.loading);
    },
    
    getHashURL: function(id, href) {
        var params = {
        	id: id
        };

        if(href.include('?')) {
        	Object.extend( params, href.toQueryParams());
        }
        return '#' + Object.toQueryString(params);
    },
    
    CSS: {
        loading: 'search_loading'
    },
    

    lightboxes: {
        
        render: function() {
            var lightboxElem = $('main-lightbox-cell'),
                lightboxContent = Ss.storage.session.getItem('active_lightbox_html'),
                lightboxOpen = (lightboxPreviewSize != 'closed' && lightboxPreviewSize != 'minimized'),
                lightboxDisplayed = $('lightbox-preview-container');

            if(!lightboxContent && lightboxDisplayed) {
                setLightboxPreviewSize('closed');
            }
            else if(lightboxElem && lightboxOpen) {
                lightboxElem.innerHTML = lightboxContent;
                lightboxElem.innerHTML.evalScripts();
                lightboxContentsPopulated = true;
                enhanced_lightboxContentsPopulated = true;
                var sessionId = Ss.storage.session.getItem('active_lightbox_id');
                var box = lightboxes.get(sessionId);
                if(sessionId && box) {
                    activeLightboxId = sessionId;
                    activeLightbox = new Hash(box);
                    refreshLightboxListings();
                    this.updateCount();
                }
            }

        },
        
        updateCount: function() {

            var titleElem = $('select_lightbox').down('.pulldown_trigger strong'),
                contents = $$('#lightbox-contents-table .floated-image'),
                visibleThumbs = [];
            if(contents && titleElem) {
                visibleThumbs = contents.select(
                    function(elem) {
                        return elem.visible();
                    }
                );
                Ss.Lightbox.switcher.pulldown.replaceTrigger('<strong>' + titleElem.innerHTML + '</strong> (' + visibleThumbs.size() + ')');
            }
        },
        
        getSize: function() {
            return Ss.storage.session.getItem('active_lightbox_size');
        },
        
        store: function(activeLightboxHTML) {
            Ss.storage.session.setItem('active_lightbox_html', activeLightboxHTML);
            Ss.storage.session.setItem('active_lightbox_id', activeLightboxId);
        },
        
        storeSize: function(size) {
            Ss.storage.session.setItem('active_lightbox_size', size);
        },
        
        clearLightboxSessionData: function() {
            Ss.storage.session.removeItem('active_lightbox_html');
            Ss.storage.session.removeItem('active_lightbox_id');
            Ss.storage.session.removeItem('active_lightbox_size');
        }
        
    },
    
    addResultsCallback: function(f) {
    	Object.isFunction(f) && this.resultsCallbacks.push(f);
    },
    
    addDetailCallback: function(f) {
    	Object.isFunction(f) && this.detailCallbacks.push(f);
    },
    

    _postResults: function(response) {
        this.resultsCallbacks.each(
        	function(f) {
        		f(response);
        	}
        );
    },
    _postDetail: function(response) {
        this.detailCallbacks.each(
        	function(f) {
        		f(response);
        	}
        );
    }
    
};

/**************************
 * Legacy Global Functions
 **/


    var selectedPhotoId;
    var searchSourceId;
    
    function getSelectedPhotoSrc(photoId) {
        
        if(window.selectedPhotoSrc) { // try to just return the legacy global.. ajax mode or pic.mhtml may be setting it.
            return selectedPhotoSrc;
        }
                                                                                                   
        if(window.searchSourceId && photoId) { // if cat.mhtml sets the search source id, use it and append the "photoPosition"
            var thumbs = $('grid').select('.gc');
            var thumb = $('gc_' + photoId);
            var photoIndex = thumbs.indexOf(thumb);
            if(photoIndex != -1) {
                if(Ss.search.enhanced) {
                    return Ss.search.getSrcID() + '-' + Ss.search.getCurrentPage() + "-" + photoIndex; // TODO: use the cell object instead of finding the index manually
                }
                return searchSourceId + "-" + photoIndex;
            }
        }
        
        return "";
    }
    
    var getLocationHash = function() {
        var i, href;
        href = top.location.href;
        i = href.indexOf("#");
        return i >= 0 ? href.substr(i + 1) : null;
    }
    

    function showAddToLightboxList(id) {
        var gridCell, placeholder;
        

        dropdownDialogShowing = true;
        selectedPhotoId = id;
        

        gridCell = $('gc_' + id);
        

        placeholder = gridCell.down('.result-dropdown-placeholder');
        
        if(!placeholder) {

            placeholder = new Element('DIV'); 
            placeholder.addClassName('result-dropdown-placeholder');
            

            gridCell.down('td').insert(placeholder);
        }
        

        $(placeholder).insert(Ss.Lightbox.multipleAdder.getPulldown().getElement());
        Ss.Lightbox.multipleAdder.getPulldown().expand();
        

        if(Ss.user.loggedIn) {
            Ss.Lightbox.multipleAdder.refresh(getAllLightboxes());
        }
        
    }
// --------------------------------------------------------------------------------
// global.js: end JavaScript file: '/js/search/search.js'

// global.js: begin JavaScript file: '/js/search/history.js'
// ================================================================================
Ss = window.Ss || {};
Ss.search = window.Ss.search || {};



Ss.search.history = {
    
    _loading: false,
    
    _fetchKey: null,
    
    photoStorage: new Ss.storage.Memory(),
    
    initialize: function() {
        Event.observe(window, 'hashchange', this.load.bind(this));
        

        if(Ss.search.toQueryString() != Ss.storage.session.getItem('search')) {
            Ss.storage.session.clear();
            Ss.storage.session.setItem('search', Ss.search.toQueryString());
        }
        
        
        if(Ss.search.initialized()) {
            this._preFetch();
        } else {

            var _pe = new PeriodicalExecuter(
                function(pe) {
                    if(Ss.search.initialized()) {
                        this._preFetch();
                        pe.stop();
                    }
                }.bind(this), 0.025
            );
        }
    },
    
    load: function(evt, onComplete) {
        

        if(this._loading) {
            return;
        }
        

        var photoId = this.getHashParam('id');
        if(photoId) {
            this.loadDetail(photoId);
            return;
        }
        


        var reqPage = this.getHashParam('page');
        if(!reqPage) {
            Ss.search.showResults();
            if(Ss.search.getCurrentPage() != Ss.search.getInitialPage()) {
                location.hash = 'page=' + Ss.search.getInitialPage();
            }
            return;
        } else {
            reqPage = parseInt(reqPage);
        }
        
        


        var key = 'page=' + reqPage;
        var startPage = Ss.search.getCurrentPage();
        
        if(!this.loadFromStorage(key, onComplete)) {
            
            this._loading = true;
            Ss.search.showLoading();
            
            if(this.fetching(key)) {

                new PeriodicalExecuter(
                    function(pe) {     
                        if(Ss.search.history.loadFromStorage(key, onComplete)) {
                            pe.stop();
                            Ss.search.history._preFetch(startPage, reqPage);
                            Ss.search.hideLoading();
                            Ss.search.history._loading = false;
                        }
                    }, .0015);
                    
                return;
            }
                
            new Ajax.Request('/show_component.mhtml', {
                    
                    method: 'GET',
                    
                    parameters: this.getRequestParams(),
        
                    onComplete: function(response) {
                        var responseText = response.responseText
                        var responseObj = responseText.evalJSON();
                        Ss.search.update(responseObj);
                        if(onComplete && Object.isFunction(onComplete)) {// TODO: implement setCallback method
                            onComplete(responseObj);
                        }
                        Ss.search.hideLoading();

                        Ss.storage.session.setItem(key, responseText);
                        this._loading = false;
                        
                    }.bind(this)
                    
            });
        }
        this._preFetch(startPage, reqPage);
    },
    
    

    loadDetail: function(photoId) {
        
        cancelPreview(); // TODO: legacy function.. but this is to close any open image previews while going to the detail page
        
        var params = {
            component_path: 'pic.mhtml'
        };
        params = Object.extend(params, this.getHashParams());
        params.time = new Date().getTime(); // for browser ajax cache busting
        
        var pageContent = this.photoStorage.getItem(photoId);
        if(pageContent) {
            Ss.search.showDetail(pageContent);
        } else {
            Ss.search.showLoading();
            new Ajax.Request('/show_component.mhtml', {
            		
            	method: 'GET',
            	
                parameters: params,
                
                evalScripts: true,
                
                onSuccess: function(response) {
                    pageContent = response.responseText;
                    Ss.search.history.photoStorage.setItem(photoId, pageContent)
                    Ss.search.showDetail(pageContent);
                    Ss.search.hideLoading();
                    $('temp_style') && $('temp_style').remove(); // temp hack
                },
                
                onFailure: function() {
                    alert($t('DYNAMIC_RESULTS_ERROR_PHOTO_DETAILS', "Unfortunately there was a technical error getting details about this photo"));
                }
                
            });
        }
    },

    _preFetch: function(startPage, reqPage) {
        
        var params = this.getRequestParams(),
            fetchPage = (params.page ? parseInt(params.page) : Ss.search.getInitialPage()),
            next,
            prev;
            
        if(!startPage || !reqPage) {
            next = true;
        } else {
            next = (reqPage == (startPage+1)),
            prev = (reqPage == (startPage-1))
        }
        
        if(!prev && !next) {
            return;
        }
        
        next && fetchPage++;
        prev && fetchPage--;
        
        if(fetchPage > Ss.search.getTotalPages() || fetchPage < 1 ) {
            return;
        }
        
        window.setTimeout(
            function() {

                var key = 'page=' + fetchPage,
                    cachedPage = Ss.storage.session.getItem(key);
                    
                params.page = fetchPage;
                
                if(cachedPage) {
                    this._cacheThumbs(cachedPage.results);
                    return;
                }
                
                this._fetchKey = key;
                new Ajax.Request('/show_component.mhtml', {
                        
                        method: 'GET',
                        
                        parameters: params,
            
                        onComplete: function(response) {
                            var responseText = response.responseText;
                            var responseObj = responseText.evalJSON();
                            

                            Ss.storage.session.setItem(key, responseText);
                            

                            this._cacheThumbs(responseObj.results);
                            
                            this._fetchKey = null;
                            
                        }.bind(this)
                        
                });
            }.bind(this), 0);
    },                                      
    
    fetching: function(key) {
        return (this._fetchKey && this._fetchKey == key);
    },
    
    _cacheThumbs: function(results) {
        results.pluck('thumb_url').each(
            function(url){
                var img = new Image();
                img.src = url;
            }
        );
    },

    loadFromStorage: function(key, onComplete) {
        var cachedResponse = Ss.storage.session.getItem(key);
        if(cachedResponse) {
            Ss.search.update(cachedResponse);
            if(onComplete && Object.isFunction(onComplete)) {// TODO: implement setCallback method
                onComplete(cachedResponse);
            }
        }
        return cachedResponse;
    },
    
    getRequestParams: function() {
        var params = {
            component_path: '/search/get_results.md'
        };
        params = Object.extend(params, Ss.search.getParams());
        params = Object.extend(params, this.getHashParams());
        return params;
    },
    
    hasHashParams: function() {
        return !window.location.hash.empty();
    },
    
    getHashParams: function() {
        return this.extractHashQueryString().toQueryParams();
    },
    
    getHashParam: function(name) {
        if(this.hasHashParams()) {
            return this.getHashParams()[name];
        }
    },
    
    extractHashQueryString: function() {
        return window.location.hash.split('#')[1] || '';
    }
    
};


if(Ss.search.enhanced) {
    (function(){
    
        var idParam = Ss.search.history.getHashParam('id'), pageParam = Ss.search.history.getHashParam('page');
        

        if(pageParam || idParam) {
            

            if(Ss.search.getInitialPage() == pageParam && !idParam) {
                return;
            }
            

            document.write('<style id="temp_style">#bodyContentCenter, #ui_widgets, select, .gc_thumb img { visibility: hidden !important; } #bodyContent{background: url("/images/loading_icon_2.gif") no-repeat 25px 25px;}</style>');
            
            Event.observe(document, 'dom:loaded', function(evt) {

                    var ajaxLoad = function() {
                        Ss.search.history.load(
                            null,
                            function(){
                                $('temp_style').remove();
                            }
                        );
                    };
                    
                    if(Ss.search.initialized()) {
                        ajaxLoad();
                        return;
                    }


                    var _pe = new PeriodicalExecuter(
                        function(pe) {
                            if(Ss.search.initialized()) {
                                ajaxLoad();
                                pe.stop();
                            }
                        }, 0.025
                    );

            });
            
        }
    
    })();
}
// --------------------------------------------------------------------------------
// global.js: end JavaScript file: '/js/search/history.js'

// global.js: begin JavaScript file: '/js/search/nextButton.js'
// ================================================================================
Ss = window.Ss || {};
Ss.search = window.Ss.search || {};


Ss.search.nextButton = {

    initialize: function(cPage, totalPages) {
        this.element = $('search-results-next-button');
        
        if(this.element) {
            this._events();
        }
        
        if(cPage && totalPages) {
            this.update(cPage, totalPages);
        }
    },
    
    update: function(cPage, totalPages) {
        if(!this.element) {
            return;
        }
        this.updateLink(cPage, totalPages);

    },
    
    updateLink: function(cPage, totalPages) {
        
        if(cPage >= totalPages) {
            this.element.href = 'javascript:void(0)';
            this.element.hide();
        } else {
            this.element.show();
            this.element.href = '#page=' + (cPage + 1);
        }

    },
    

    updateDisplay: function() {
        var aloneOnRow = (this.element.offsetLeft <= Ss.search.nextButton.MIN_DISTANCE_X);
        (aloneOnRow ? this.hide() : this.show());
    },


    show: function() {
        this.element.setStyle({
                visibility: 'visible',
                marginBottom: '0'
        });
    },

    hide: function() {
        this.element.setStyle({
                visibility: 'hidden',
                marginBottom: -(this.element.getHeight()) + 'px'
        });
    },

    _events: function() {

        Event.observe(window, 'load', this.updateDisplay.bind(this));
        

        Event.observe(window, 'resize', this.updateDisplay.bind(this));
    },
    


    MIN_DISTANCE_X: 10
  
};
// --------------------------------------------------------------------------------
// global.js: end JavaScript file: '/js/search/nextButton.js'

// global.js: begin JavaScript file: '/js/image/grid/grid.js'
// ================================================================================
Ss.image = window.Ss.image || {};

Ss.image.grid = {
    
    cells: [],
    
    element: null,
    
    _isMiddleClick: false,
    
    recycleElements: (Ss.user.browser == 'chrome' || Ss.user.browser == 'safari'), 

    initialize: function() {
        
        this.element = $('grid_cells');
        
        if(!this.element) {
            return;
        }
        
        if(Ss.search.enhanced) {

            this.element.select('.gc').each( 
                function(elem, index) {
                    
                    var imageLink = elem.down('.gc_thumb'),
                        image = elem.down('.gc_thumb img'),
                        descriptionLink = elem.down('.gc_desc'),
                        picIcon = elem.down('.pic_btn'),
                        id = elem.id.replace('gc_', '');
                    
                    if(this.recycleElements) {
                        
                        this.cells.push(
                            new Ss.image.Cell({
                                index: index,
                                elements: {
                                    container:          elem,
                                    imageLink:          imageLink,
                                    image:              image,
                                    descriptionLink:    descriptionLink,
                                    picIcon:        picIcon
                                },
                                id: id
                            })
                        );
                        
                    }
                    
                }.bind(this));
        }
        
        this._events();
    },
    
    update: function(response) {
        
        if(this.recycleElements) {
            this.cells.each(
                function(cell, i) {
                    var result = response.results[i];
                    result ? cell.update(result) : cell.clear();
                }
            );
        } else {
            this.element.innerHTML = this.getHTML(response.results);
        }
        
    },
    
    getHTML: function(results) {
		var output = [], result, id, href;
		for(var i=0, len=results.length; i<len; i++) {
			result = results[i];
			id = result.id;
			href = result.photo_detail_link;
			output.push('<table class="gc" id="gc_' + id + '"><tr><td>');
			output.push('<a class="gc_thumb" href="'+ href + '" style="height: ' + result.thumb_height + 'px; width: ' + result.thumb_width +'px;">');
			output.push('<img src="' + result.thumb_url + '" />')
			output.push('</a>');
			output.push('<a class="gc_desc" href="' + href + '">')
			output.push(result.display_description);
			output.push('</a>');
			output.push('<a class="lbx_btn" title="' + Ss.search.text.lightbox + '"></a>');
			output.push('<a class="pic_btn" title="' + Ss.search.text.download + '" href="' + href + '"></a>');
			output.push('</td></tr></table>');
		}
		return output.join('');
    },
    
    _events: function() {


    	if(Ss.Browser.isIEVersion(9)) {
    		this.element.onclick = this.click.bind(this);
    	} else {
    		this.element.observe('click', this.click.bind(this));
    	}
    	





    	this.element.observe('mousedown', function(evt) {
    		Ss.image.grid._isMiddleClick = evt.isMiddleClick() || evt.which == 2;
    	});
    },
    
    click: function(evt) {
		var elem = Event.findElement(evt),
			cell = Event.findElement(evt, '.gc'),
			thumb,
			description,
			picIcon,
			href = '',
			photoId = '';
		

		if(this._isMiddleClick || evt.metaKey || evt.shiftKey || evt.altKey || evt.ctrlKey) {
				return;
		}
		

		if(cell && !cell.hasClassName('gc')) {
			cell = Event.element(evt).ancestors().find(
				function(elt) {
					return elt.hasClassName('gc');
				}
			);
		}
		
		if(!cell) {
			return;
		}
		
		if(elem.hasClassName('lbx_btn')) {
			this.showAddToLightboxList(elem.up('.gc'));
			return;
		}
		
		thumb = cell.down('.gc_thumb');
		description = cell.down('.gc_desc');
		picIcon = cell.down('.pic_btn');
		
		if(Ss.search.enhanced) {
			if(elem.isElementOrDescendantOf(thumb) || elem.isElementOrDescendantOf(description) || elem.isElementOrDescendantOf(picIcon)) {
				href = thumb.href;
				photoId = cell.id.replace('gc_', '');
				


				if(evt.preventDefault) {
					evt.preventDefault();
				} 
				else if(evt.returnValue) {
					evt.returnValue = false;
				} 
				else {
					window.event.returnValue = false;
				}
				window.location = Ss.search.getHashURL(photoId, href);
				return false;
			} 
		}
		
    },
    
    previewOn: function() {
    	var element = this.element || $('grid');
        if(element) {
			element.observe('mouseover', function(evt) {
					var thumb = Event.findElement(evt, 'img');
					thumb && previewThumb(thumb);
			});
			element.observe('mouseout', function(evt) {
					var thumb = Event.findElement(evt, 'img');
					thumb && cancelPreview(thumb);
			});
        }
    },
    
    previewOff: function() {
        this.element.stopObserving('mouseover');
        this.element.stopObserving('mouseout');
    },
    
    showDescriptions: function() {
    	$('cat_container').addClassName('descriptions_on');
    	this.writeDescriptions();
    },
    
    writeDescriptions: function() {
		this.element.select('.gc_desc').each(
			function(desc) {
				if(desc.title && !desc.title.empty()) {
					desc.update(desc.title);
					desc.title = '';
				}
			}
		);
    },
    
    hideDescriptions: function() {
    	$('cat_container').removeClassName('descriptions_on');
    },
    
    hasDescriptions: function() {
    	return $('cat_container').hasClassName('descriptions_on')
    },
    
    showAddToLightboxList: function(cellElem) {
        
        if(!cellElem || !cellElem.id) {
            return;
        }
        

        dropdownDialogShowing = true;
        selectedPhotoId = cellElem.id.replace('gc_', '');
        

        

        var placeholder = cellElem.down('.result-dropdown-placeholder');
        if(!placeholder) {

            placeholder = new Element('DIV');
            placeholder.addClassName('result-dropdown-placeholder');
            

            cellElem.down('td').insert(placeholder); // for div
        }
        

        placeholder.insert(Ss.Lightbox.multipleAdder.getPulldown().getElement());
        Ss.Lightbox.multipleAdder.getPulldown().expand();
        

        if(Ss.user.loggedIn) {
            Ss.Lightbox.multipleAdder.refresh(getAllLightboxes());
        }
        
    }
    
};

Ss.image.Cell = Class.create({
        
        initialize: function(args) {
            this.index = args.index;
            this.elements = args.elements;
            
            this.elements.image.onload = function(evt) {
            	this.setStyle({
            			visibility: 'visible'
            	});
            };
        },
        
        update: function(result) {
        	

            var container = this.elements.container,
                imageLink = this.elements.imageLink,
                image = this.elements.image,
                descriptionLink = this.elements.descriptionLink,
                picIcon = this.elements.picIcon;


            if(image.src === result.thumb_url) {
                return;
            }
        	

            container.id = 'gc_' + result.id;
            

            imageLink.style.cssText = 'width: ' + result.thumb_width + 'px; ' + 'height: ' + result.thumb_height + 'px';
            

            image.setStyle({
            		visibility: 'hidden'
            });
            

            image.src = result.thumb_url;
            

            descriptionLink.innerHTML = result.display_description;
            

            picIcon.href = imageLink.href = descriptionLink.href = result.photo_detail_link;
            

            !container.visible() && container.show();
            
        },
        
        clear: function() {
            this.elements.container.hide();
        }
        
});
// --------------------------------------------------------------------------------
// global.js: end JavaScript file: '/js/image/grid/grid.js'

// global.js: begin JavaScript file: '/js/search/GridPager.js'
// ================================================================================
Ss = window.Ss || {};
Ss.search = window.Ss.search || {};


Ss.search.GridPager = {
    
    initialize: function(cPage, totalPages) {
        
        var topE= $('grid_pager_top'), 
            botE = $('grid_pager_bottom');
        
        this.topPager = new Ss.search.Pager({
                            input:  topE.down('input[type=text]'),
                            prev:   topE.down('.grid_pager_button_prev'),
                            next:   topE.down('.grid_pager_button_next'),
                            firstCSS: 'grid_pager_button_prev_disabled',
                            lastCSS: 'grid_pager_button_next_disabled'
                        });
        
        this.bottomPager = new Ss.search.Pager({
                            input:  botE.down('input[type=text]'),
                            prev:   botE.down('.grid_pager_button_prev'),
                            next:   botE.down('.grid_pager_button_next'),
                            firstCSS: 'grid_pager_button_prev_disabled',
                            lastCSS: 'grid_pager_button_next_disabled'
                        });
        
        this.update(cPage, totalPages);
    },
    
    update: function(cPage, totalPages) {
        this.topPager.update(cPage, totalPages);
        this.bottomPager.update(cPage, totalPages);
    }
};



Ss.search.Pager = Class.create({
    
    initialize: function(args) {
        this.elements = {
            input: args.input,
            prev: args.prev,
            next: args.next
        };
        this.firstCSS = args.firstCSS;
        this.lastCSS = args.lastCSS;
        
        this._prevDisabled = this.elements.prev.hasClassName(this.firstCSS);
        this._nextDisabled = this.elements.prev.hasClassName(this.lastCSS);
        
        this._events();
    },
    

    update: function(cPage, totalPages) {
        var next = this.elements.next,
            prev = this.elements.prev,
            input = this.elements.input,
            nothing = 'javascript:void(0)',
            last = (cPage >= totalPages),
            first = (cPage < 2);
        
        this.setValue(cPage);
        
        prev.href = (first ? nothing : '#page=' + (cPage - 1));
        next.href = (last ? nothing : '#page=' + (cPage + 1));
        

        if(this._nextDisabled && !last) {
            next.removeClassName(this.lastCSS);
            this._nextDisabled = false;
        }


        if(last) {
            next.addClassName(this.lastCSS);
            this._nextDisabled = true;
        }        
        
        

        if(this._prevDisabled && !first) {
            prev.removeClassName(this.firstCSS);
            this._prevDisabled = false;
        }
        

        if(first) {
            prev.addClassName(this.firstCSS);
            this._prevDisabled = true;
        }
    },
    
    setValue: function(value) {
        var input = this.elements.input;
        input.value = value;
        if(input.value.length > input.size) {
            input.size = input.value.length;
        }
    },
    
    _events: function() {
        var input = this.elements.input;
        input.up('form').observe('submit',
            function(evt) {
                Event.stop(evt);
                var page = parseInt($F(input)),
                    totalPages = Ss.search.getTotalPages();
                if(page < 1) {
                    page = 1;
                }
                if(page > totalPages) {
                    page = totalPages;
                }
                this.setValue(page);
                window.location.hash = '#page=' + page;
            }.bind(this)
        );
    }
        
});
// --------------------------------------------------------------------------------
// global.js: end JavaScript file: '/js/search/GridPager.js'

// global.js: begin JavaScript file: '/js/image/Preview.js'
// ================================================================================
/* Copyright (c) 2008 Shutterstock Images LLC */
Ss = window.Ss || {};
Ss.image = window.Ss.image || {};

Ss.image.Preview = Class.create({
        
        initialize: function(args) {
            
            this.elements = args.elements;
    
            this.showing = false;
    
            this.locks = {
                thumbLoad: {}
            };
    
            this.activeResultSlot = {};
            

            this.elements.previewImage = new Image();
            $(this.elements.previewImage).hide();
            this.elements.thumb.parentNode.insertBefore(
                this.elements.previewImage,
                this.elements.thumb
            );
        
            this.elements.thumb.observe('load', this.show.bind(this));
        },
    
        populate: function(args) {






            var resultSlot = args.resultSlot;

            this.showing = true;





            var thumb = resultSlot.elements.thumb;
            var previewThumb = this.elements.thumb;
            var previewImage = this.elements.previewImage;
            var previewDescription = this.elements.description;
            var descriptionText = args.descriptionText;
            var showDescription = !Object.isUndefined(args.descriptionText);


            var imageSrc = resultSlot.previewThumbSrc
                ? resultSlot.previewThumbSrc
                : ['http:/', resultSlot.result.host, 'photos', 'display_pic_with_logo', resultSlot.result.set_name, resultSlot.result.filename].join('/');


            if (previewImage.src == imageSrc) {
                this.show();
            } else {
                this.locks[resultSlot.id] = true;


                var hqAnnotationHeight = 20;

                var dimensionMax = 450;


                var aspectRatio = args.resultSlot.result.aspect_ratio;
                var displayHeight = aspectRatio < 1
                    ? dimensionMax
                    : Math.round(dimensionMax / aspectRatio);
                var displayWidth = aspectRatio < 1
                    ? Math.round(dimensionMax * aspectRatio)
                    : dimensionMax;


                var previewContainer = previewThumb.parentNode;
                previewContainer.style.height = displayHeight + 'px';
                previewContainer.style.width = displayWidth + 'px';
                previewContainer.style.overflow = 'hidden';
                

                if(previewDescription && showDescription) {
                	previewDescription.style.width = displayWidth + 'px';
                	previewDescription.update(descriptionText);
                }


                var iframeBacking = this.elements.iframeBacking;
                if (iframeBacking) {
                    iframeBacking.style.height = (displayHeight + 65) + 'px';
                    iframeBacking.style.width = (displayWidth + 65) + 'px';
                }


                this.position( {
                    resultSlot: resultSlot,
                    placement: args.placement,
                    fixedContainer: args.fixedContainer,
                    fixedAncestor: args.fixedAncestor,
                    showDescription: showDescription,
                    cellMax: args.cellMax
                } );


                previewThumb.show();
                previewImage.hide();



                previewImage.src = imageSrc;
                previewImage.height = displayHeight + hqAnnotationHeight;
                previewImage.width = displayWidth;

                previewImage.onload = function () {

                    if (this.locks[resultSlot.id]) {
                        toggleCursorState('revert', this.activeResultSlot.elements.thumb, 'local');
                        this.locks[resultSlot.id] = false;
                    }
                    previewThumb.hide();
                    previewImage.show();
                }.bind(this);




                if(!thumb) {

                    return;
                }
                previewThumb.src = thumb.src;
                previewThumb.height = aspectRatio <= 1
                    ? dimensionMax
                    : dimensionMax * (thumb.naturalHeight || thumb.height) / (thumb.naturalWidth || thumb.width);
                previewThumb.width = aspectRatio <= 1
                    ? dimensionMax * (thumb.naturalWidth || thumb.width) / (thumb.naturalHeight || thumb.height)
                    : dimensionMax;
                



                this.elements.container.style.display = 'block';
            }



            setTimeout(function() {
                if (this.locks[resultSlot.id]) {
                    toggleCursorState('progress', thumb, 'local');
                }
            }.bind(this), 1000);

        },

        unPopulate: function(args) {
            if (!args) args = {};
            this.locks[this.activeResultSlot.id] = false;
            this.elements.container.style.display = 'none';	
            this.showing = false;
            if (this.activeResultSlot.elements && !args.dontClearCursor) {
                toggleCursorState('revert', this.activeResultSlot.elements.thumb, 'local'); 
            }

            newlyActiveThumb = false;
        },

        position: function(args) { // resultSlot, placement

            this.activeResultSlot = args.resultSlot;

            var thumbPosition;

            if (args.fixedContainer && args.fixedAncestor) {
                thumbPosition = getFixedPosition(args.fixedContainer, args.resultSlot.elements.container, args.fixedAncestor);
            } else {
                thumbPosition = getElementScreenPosition(args.resultSlot.elements.container);
            }

            var viewportDimensions = document.viewport.getDimensions();
            var viewportOffsets = document.viewport.getScrollOffsets();


            var padding = (args.cellMax ? 6 : 10);
            var previewPadding = (args.cellMax ? 35 : 60);
            var descriptionHeight = 25;


            var dimensions = args.resultSlot.getDimensions(450);
            var previewHeight = dimensions.height + previewPadding;
            var previewWidth = dimensions.width + previewPadding;
            


            var cellMax = args.cellMax;

			var thumbWidth, thumbHeight;
			
			try {
				thumbWidth = parseInt(this.activeResultSlot.elements.container.style.width);
				thumbHeight = parseInt(this.activeResultSlot.elements.container.style.height);
			} catch (e) {
            	thumbWidth = thumbHeight = activeThumbSize.max_dimension_pixels;
			}
			
            var berth = {
                top: thumbPosition.top - previewHeight,
                left: thumbPosition.left - previewWidth,
                bottom: viewportDimensions.height - thumbPosition.top - previewHeight,
                right: viewportDimensions.width - thumbPosition.left - thumbWidth - previewWidth
            }; 

            var bestPlacement;
            $H(berth).keys().each( function(placement) {
                bestPlacement = bestPlacement ? bestPlacement : placement;
                bestPlacement = berth[placement] > berth[bestPlacement] ? placement : bestPlacement;
            } );

            bestPlacement = bestPlacement ? bestPlacement : 'bottom';
            var previewPosition, diffThumbCellX, diffThumbCellY;

            switch (bestPlacement) {
                case 'top':
                    previewPosition = {
                        top: thumbPosition.top - previewHeight - padding,
                        left: thumbPosition.left + (thumbWidth / 2) - (previewWidth / 2)
                    };
                    
					if(cellMax) {
						diffThumbCellY = cellMax - thumbHeight;
						previewPosition.top -= (diffThumbCellY + previewPadding - padding);
					}
                    break;
                case 'left':
                    previewPosition = {
                        top: thumbPosition.top + (thumbHeight / 2) - (previewHeight / 2),
                        left: thumbPosition.left - previewWidth - padding - 3
                    };
					
					if(cellMax) {
						diffThumbCellX = cellMax - thumbWidth;
						previewPosition.left -= (diffThumbCellX/2).round();
					}
                    break;
                case 'bottom':
                    previewPosition = {
                        top: thumbPosition.top + thumbHeight + padding,
                        left: thumbPosition.left + (thumbWidth / 2) - (previewWidth / 2)
                    };
                    
                    if(cellMax) {
                    	


                    	previewPosition.top += cellMax - Ss.image.grid.calculateTopMargin(thumbHeight) - thumbHeight;
                    	

                    	if(Ss.image.grid.hasDescriptions()) {
                    		previewPosition.top += descriptionHeight;
                    	}
                    	
                    }
                    break;
                case 'right':
                    previewPosition = {
                        top: thumbPosition.top + (thumbHeight / 2) - (previewHeight / 2),
                        left: thumbPosition.left + thumbWidth + padding
                    };
                    
					if(cellMax) {
						diffThumbCellX = cellMax - thumbWidth;
						previewPosition.left += (diffThumbCellX/2).round();
					}
                    break;
            }

            var containerStyle = this.elements.container.style;
            $w('top left').each( function(dimension) {
                if (previewPosition[dimension] !== null) {

                    if ((bestPlacement == 'left' || bestPlacement == 'right') && (dimension  == 'top')
                        || (bestPlacement == 'top' || bestPlacement == 'bottom') && (dimension  == 'left')) {
                    
                        if (previewPosition[dimension] < 0) {
                            previewPosition[dimension] = padding;

                        } else if (dimension == 'left' && previewPosition[dimension] + previewWidth > viewportDimensions.width) {
                            previewPosition[dimension] = viewportDimensions.width - previewWidth - padding;

                        } else if (dimension == 'top' && previewPosition[dimension] + previewHeight > viewportDimensions.height) {
                            previewPosition[dimension] = viewportDimensions.height - previewHeight - padding;
                        }
    
                    }

                    if (dimension == 'top' || dimension == 'bottom') {
                        containerStyle[dimension] = previewPosition[dimension] + viewportOffsets.top + 'px';
                    } else {
                        containerStyle[dimension] = previewPosition[dimension] + 'px';
                    }
                
                } else {
                    containerStyle[dimensions] = null;
                }
            } );

        },
        show: function() {
            if (!this.showing) return;
            this.elements.container.style.display = 'block';
        }
});

Ss.image.Preview.initialize = function() {

    window.resultPreview = new Ss.image.Preview( { 
        elements: {
            container: $('photo-details-container'),
            thumb: $('photo-comp-thumb'),
            iframeBacking: document.getElementById('photo-details-iframe-backing'),
            description: $('photo-details-description')
        }
    });
    
    window.activeThumbSize = { max_dimension_pixels: 100 };
};


/************************************************************
 * Dependencies/functions moved from other deprecated classes
 **/
var legacyActiveThumb; // dump this out into the global namespace for show_image.mh
var initialElementCursorStyles = {};

function previewThumb(e, descriptionText, cellMax) {
	legacyActiveThumb = e;
	if (!Object.isElement(legacyActiveThumb)) {
	    return;
	}
	setTimeout( function() {
		if (legacyActiveThumb && legacyActiveThumb.src == e.src) {



			var ancestors = $(legacyActiveThumb).ancestors(),
				container;
				

			container = ancestors.find(
				function(ancestor) {
					return ancestor.hasClassName('thumb_image_container') || ancestor.hasClassName('gc_c');
				}
			);
			

			container = container || legacyActiveThumb.up('a');

			

			if(!container) {
				return;
			}
			

			var thumbWidth = parseInt(container.style.width);
			var thumbHeight = parseInt(container.style.height);

			var fakeResultSlot = {
				elements: {
					container: container,
					thumb: legacyActiveThumb
				},
				getDimensions: function(maxDimensionPixels) {
					
					var multiplier = maxDimensionPixels / Math.max(thumbWidth, thumbHeight);
					return { 
						width: thumbWidth * multiplier, 
						height: thumbHeight * multiplier
					};
				},
				result: {
					aspect_ratio: (thumbWidth / thumbHeight)
				},
				previewThumbSrc: legacyActiveThumb.src.replace(/\/thumb_(small|large)\//, '/display_pic_with_logo/')
			};

			if (window.resultPreview) {
				var lightboxContainer = document.getElementById('lightbox-contents-table');
				var lightboxFixedAncestor = document.getElementById('lightbox-preview-container');
				var fixedContainer = lightboxContainer && Element.extend(legacyActiveThumb).descendantOf(lightboxContainer) ? lightboxContainer: null;
				resultPreview.populate( { resultSlot: fakeResultSlot, fixedContainer: fixedContainer, fixedAncestor: lightboxFixedAncestor, descriptionText: descriptionText, cellMax: cellMax } );
				resultPreview.show();
			}
		}
	}, 250);
}

function cancelPreview(e) {
	legacyActiveThumb = null;
	if (window.resultPreview) {
		resultPreview.unPopulate();
	}
}

function toggleCursorState(state, e, scope) {

    if (this.isSafari) {
        
        var cursorIndicator = $('cursor-indicator');

        if (state == 'revert') {
            cursorIndicator.hide();	

        } else {
            this.showingCursorIndicator = true;
            this.positionCursorIndicator();
            cursorIndicator.show();
        }

    } else {

        if (state == 'revert') {
            if (e && e.style.cursor == 'progress') {
                e.style.cursor = initialElementCursorStyles[e.id];
            }
            if (scope != 'local') {
                document.body.style.cursor = 'auto';
            }

        } else {
            if (e && e.style.cursor != 'progress') {
                initialElementCursorStyles[e.id] = e.style.cursor;
            }

            if (e) e.style.cursor = state;

            if (scope != 'local') {
                document.body.style.cursor = state;
            }
        }
    }

}

function getElementScreenPosition(e, fixedOffsets) {

    var elementPagePosition = getElementPosition(e);
    var scrollOffsets = fixedOffsets ? fixedOffsets : document.viewport.getScrollOffsets();

    return { 
        left: elementPagePosition.left - scrollOffsets.left,
        top: elementPagePosition.top - scrollOffsets.top
    };

}

function getFixedPosition(container, element, fixedAncestor) {

    var elementPosition = getElementPosition(element, container.id);

    var elementAdjustedTop = elementPosition.top - container.scrollTop;
    var elementAdjustedLeft = elementPosition.left - container.scrollLeft;
    
    var containerPosition;
    if (Element.getStyle(fixedAncestor, 'position') == 'fixed') {
        containerPosition = getElementPosition(container);
    } else {
        containerPosition = getElementScreenPosition(container);
    }

    var elementScreenTop = elementAdjustedTop + containerPosition.top;
    var elementScreenLeft = elementAdjustedLeft + containerPosition.left;

    return { top: elementScreenTop, left: elementScreenLeft };

}

function getElementPosition(obj, containerId) {

        var left = 0, top = 0;
        if (obj.offsetParent) {
                do {
                    if (containerId && obj.id == containerId) {
                        break;
                    }
                    left += obj.offsetLeft;
                    top += obj.offsetTop;
                } while (obj = obj.offsetParent);
        }
    
    return { left: left, top: top };

}
// --------------------------------------------------------------------------------
// global.js: end JavaScript file: '/js/image/Preview.js'

// global.js: begin JavaScript file: '/js/search_feedback_forms/ContentGapForm.js'
// ================================================================================
Ss.ContentGapForm = {
    
    DISABLED_CSS: 'content_gap_disabled',
    
    CLOSE_DELAY: 3,
    
    _seen: false,
    
    initialize: function() {
        

        this.elements = {
            
            container: $('content_gap_feedback'),
            
            form: $('content_gap_form'),
            
            buttons: {
                found: $('result_found_button'),
                notFound: $('result_not_found_button'),
                notFoundMeta: $('result_not_found_meta_button')
            },
            
            notFoundMeta: $('result_not_found_meta'),
            notFoundMetaText:  $('result_not_found_meta_text'),
            resultStatus: $('result_status'),
            inlineThanks: $('inline_thanks'),
            complete: $('content_gap_form_complete')
        };
        

        Ss.FlyoutLayer.write(this.elements.container);
        
        this._events();
        



		if(Ss.search.enhanced) {
			Ss.search.addResultsCallback(function(response) {
				
				if(Ss.FlyoutLayer.hidden()) {
					Ss.FlyoutLayer.show();
				}
				
				if(response && response.page) {
					
					var page = parseInt(response.page);
					
					if(page) {
					

						if (!readCookie('search_flyout_seen')) {
							
							Ss.ContentGapForm.setSeenInSessionOnOpen();
							
							if (page >= 2) {
								Ss.ContentGapForm.enableAutoOpen($('grid_navigation_bottom'));
							}
							
						}
						
					}
				}
			});
			
			Ss.search.addDetailCallback(function(response) {
				if(Ss.FlyoutLayer.visible()) {
					Ss.FlyoutLayer.hide();
				}
			});
		}

    },

    reset: function() {

        this.elements.container.removeClassName(this.DISABLED_CSS);
        this.elements.inlineThanks.hide();
        this.elements.notFoundMetaText.clear();
        this.elements.notFoundMeta.hide();
        Ss.FlyoutLayer.write(Ss.ContentGapForm.elements.container);
    },
    
    showComplete: function() {
        function reset(evt) {
            if(evt.type == 'close') {
                Ss.ContentGapForm.reset();
                window.clearTimeout(autoCloseTimerId);
                Ss.FlyoutLayer.unsubscribeObserver(reset);
            }
        }
        
        Ss.FlyoutLayer.write(Ss.ContentGapForm.elements.complete);
        
        Ss.FlyoutLayer.subscribeObserver(reset);
        
        var autoCloseTimerId = (function(){
                if(Ss.FlyoutLayer.isOpen()) { // user may have closed it
                    Ss.FlyoutLayer.close();
                }
        }).delay(this.CLOSE_DELAY);
        
    },
    
    submit: function(e) {
			var params = {};


			if (window.dynamicResultsVersion) {
				params.search_query = Object.toQueryString(browser.pageArgs);
				params.page         = browser.pageArgs.page || 1;
			}
            this.elements.form.request({
                method: 'POST',
                parameters: params,
                onComplete: function(response) {

                }
            });
    },
    
    showNotFoundMeta: function() {

        this.elements.container.addClassName(this.DISABLED_CSS);
        this.elements.inlineThanks.show();
        

        this.elements.notFoundMeta.show();
        
        Ss.FlyoutLayer.updateYPosition();
    },

    setSeenInSessionOnOpen: function() {
        var seenObserver = function(evt) {

            if(evt.type == 'open' && !this.wasSeen()) {
                
                this.setSeen(true);
                
				if (!readCookie('search_flyout_seen')) {
					createCookie({
						name:  'search_flyout_seen',
						value: 1,
						days:  30
					});
				}
                Ss.FlyoutLayer.unsubscribeObserver(seenObserver);

            }
            
        }.bind(this);
        
        Ss.FlyoutLayer.subscribeObserver(seenObserver);
    },

    enableAutoOpen: function(target) {
        Ss.FlyoutLayer.enableAutoOpen(target);
    },

    disableAutoOpen: function() {
        Ss.FlyoutLayer.disableAutoOpen();
    },

    setSeen: function(seen) {
        this._seen = seen;
    },

    wasSeen: function() {
        return this._seen;
    },

    _events: function() {
        this.elements.buttons.found.observe('click', this._submitYes.bind(this));
        this.elements.buttons.notFound.observe('click', this._submitNo.bind(this));
        this.elements.buttons.notFoundMeta.observe('click', this._submitNotFoundMeta.bind(this));
    },
    
    _submitNo: function() {
        

        this.elements.resultStatus.value = '0';
        

        this.showNotFoundMeta();
        

        this.submit();
    },
    
    _submitYes: function() {

        this.elements.resultStatus.value = '1';
        

        this.submit();
        

        this.showComplete();
    },
    
    _submitNotFoundMeta: function() {
        

        this.submit();
        

        this.showComplete();
    }
    
};
// --------------------------------------------------------------------------------
// global.js: end JavaScript file: '/js/search_feedback_forms/ContentGapForm.js'

// global.js: begin JavaScript file: '/js/search_ui/grid/GridNavigation.js'
// ================================================================================
Ss = window.Ss || {};

Ss.GridNavigation = {
	
	initialize: function() {
		
		var self = this;
			
		var labelClickHandler = function(evt) {
		

			if(this.hasClassName('button_white_beveled_selected') || this.hasClassName('selected')) {
				Event.stop(evt);
				return;
			}
			

			var inp = this.down('input');
			inp.checked = true; 
			(self.behaviors[inp.name] || self._default_behavior).call(self, inp, inp.name);
		};
		
		this.elements = {
			grid_options_top:$('grid_options_top'),
			prefs_wrapper:$('display_preferences_overlay_wrapper'),
			prefs_div:$('display_preferences_overlay'),
			image_previews: {on: $('image_previews_on'), off: $('image_previews_off')},
			show_descriptions: {on: $('descriptions_on'), off: $('descriptions_off')}
		};


		this.elements.grid_options_top.select('label').invoke('observe', 'click', labelClickHandler);
		this.elements.prefs_div.select('label').invoke('observe', 'click', labelClickHandler);
		
		var	toggle_div = $('toggle_display_preferences'),
		 	sc_opts = {modal:false, position: {target:toggle_div, type:'bottom', offsetY:4}, 
					closeButton:{type:''}
				}
			;


		Ss.ShadowContainer.Stateful.register({
			key:'display_prefs', 
			content: this.elements.prefs_div, 
			sc_opts:sc_opts
		});
			
		toggle_div.observe('click', function(evt) {
			Ss.ShadowContainer.Stateful.toggle('display_prefs');
		});

		this.initBehaviors();
	},
	initBehaviors:function(){
		var non_refresh = function(inp, name){ 
				var pref = inp.value;
				if(pref == 'on'){
					this.elements[name].off.removeClassName('selected');
					this.elements[name].on.addClassName('selected');
				}else{
					this.elements[name].off.addClassName('selected');
					this.elements[name].on.removeClassName('selected');
				}
				var parameters = {component_path: 'set_display_prefs.md'};
				parameters[name] = pref;
				new Ajax.Request( '/show_component.mhtml', {
					method: 'GET',
					parameters:parameters
				});
				if(name == 'show_descriptions'){
					Ss.image.grid[(pref === 'on' ? 'showDescriptions' : 'hideDescriptions') ]();
				}else if(name == 'image_previews'){
					(pref == 'on') ? Ss.image.grid.previewOn() : Ss.image.grid.previewOff();
				}
				Ss.ShadowContainer.Stateful.toggle('display_prefs');
				/* change is instance, showing loading for visual feedback */
				this.show_loading_right();
				var self = this;
				setTimeout(function(){
					self.hide_loading_right();
				}, 300);
			}
			
		var load_right = function(inp, name){
			var form = inp.up('form'),
				pageInput;
				
			this.set_loading_right();
			/* hack for safari timing, so loading image apears before submit freezes the page */
			setTimeout(
				function(){
					if(Ss.search.enhanced) {


						pageInput = form.down('input[name=page]');
						if(pageInput.type == 'hidden') {
							pageInput.value = Ss.search.getCurrentPage();
						}
					}
					
					form.submit(); 			// submit the form
				}, 1
			);
			Ss.ShadowContainer.Stateful.toggle('display_prefs');
		}

		this.behaviors = {image_previews: non_refresh, show_descriptions: non_refresh,
				images_per_page: load_right, thumb_size: load_right, safesearch: load_right 
			}
	},
	_default_behavior: function(inp, name){
		this.set_loading();
		/* hack for safari timing, so loading image apears before submit freezes the page */
		setTimeout(function(){
			inp.up('form').submit(); 			// submit the form
		},1);
	},
	set_loading: function(){
	    var s = $("sort_text");
		s.setStyle({width: s.getWidth()+'px'});
		$("sort_text_msg").hide();
		$("sort_loading").show();
		Ss.search.showLoading();
	},
	set_loading_right: function(){
		this.show_loading_right();
		Ss.search.showLoading();
	},
	show_loading_right: function(){
		$("toggle_display_preferences").addClassName('toggle_display_preferences_active');
	},
	hide_loading_right: function(){
		$("toggle_display_preferences").removeClassName('toggle_display_preferences_active');
	}
};
// --------------------------------------------------------------------------------
// global.js: end JavaScript file: '/js/search_ui/grid/GridNavigation.js'

// global.js: begin JavaScript file: '/js/AutoSuggest.js'
// ================================================================================
var AutoSuggest = Class.create(Autocompleter.Local, {

	initialize: function($super, element, update, options) {
		this.element = element;
		this.currentStr = this.element.value;
		this.urlBase = "http://autocomplete.shutterstock.com/ac/";
		this._super = {};

		options = options || {};
		options.partialSearch = false;

		$super(element, update, [], options);
	},

	processSuggestion: function (results) {
		if (results[0] !== this.currentStr) {
			return;
		} else {
			this.options.array = [];
			var that = this;
			if (results[1]) {
				results[1].each(function(s) {
					if (s && s.length) {
						var term = that.currentStr + s[0];
						that.options.array.push(term);
					}
				});
			}
		}


		if (this._super.getUpdatedChoices) {
			this._super.getUpdatedChoices();
		}
	},

	getUpdatedChoices: function($super) {
		this.currentStr = this.element.value;


		var script = document.createElement("script");
		script.type = "text/javascript";
		script.src = this.urlBase + this.currentStr;
		this.currentStr = this.currentStr;
		$$("head")[0].appendChild(script);

		this._super.getUpdatedChoices = $super;
	}
});
// --------------------------------------------------------------------------------
// global.js: end JavaScript file: '/js/AutoSuggest.js'

// global.js: begin JavaScript file: '/js/feature_announcement/SearchAnnouncement.js'
// ================================================================================
var Ss = window.Ss || {};

Ss.SearchAnnouncement = {

	initialize: function() {
		this.elements = {
			body: $$('body')[0],
			announcement: $('feature_announcement'),
			placeholder: $('feature_announcement_placeholder'),
			seeWhatsNew: $('user_see_whats_new')
		};
		
		this._events(); // register event handlers for opening / closing / mousing over
		this._onlyShowOnce(); // make sure to only show it automatically once
		this._hideOnPicPage(); // make sure that it gets hidden when the user navigates to the pic page
	},
	
	getFeatureElements: function() {
		return Object.values(this.features).pluck('elementId').map(
			function(id) {
				return $(id);
			}
		);
	},
	
	getFeatureElement: function(feature) {
		return $(feature.elementId);
	},
	
	showSingleFeature: function(feature) {
		this.getFeatureElements().invoke('removeClassName', 'active_feature');
		this.getFeatureElement(feature).addClassName('active_feature'); 	// highlight this feature
		Ss.Bubbles.showSingleBubble(feature.bubbleId);  // show the help bubble
		this._positionBubble(feature); // position the help bubble next to the feature
	},
	
	showAllFeatures: function() {
		Object.values(this.features).each(
			function(feature) {
				this.getFeatureElement(feature).addClassName('active_feature'); 	// highlight this feature
				Ss.Bubbles.showBubble(feature.bubbleId);  // show the help bubble
				this._positionBubble(feature); // position the help bubble next to the feature
			}.bind(this)
		);
	},
	
	show: function() {
		if(this.elements.announcement.CSSTransitionsSupported()) {
			this.elements.body.addClassName(this.CSS.open);
		} else {
			this._transitionIn();
		}
	},
	
	hide: function() {
		if(this.elements.announcement.CSSTransitionsSupported()) {
			this.elements.body.removeClassName(this.CSS.open);
		} else {
			this._transitionOut();
		}
	},
	
	CSS: {
		open: 'feature_announcement_open'
	},
	
	features: { // this will be made dynamic/have an interface when we have more than one set of feature announcements
		feature_display_preferences: {
			itemId:		'feature_display_preferences',
			bubbleId: 	'bubble_display_preferences',
			elementId: 	'toggle_display_preferences',
			offset:		{ x: 25, y: 2 }
		},
		feature_grid_layout: {
			itemId:		'feature_grid_layout',
			bubbleId: 	'bubble_grid_layout',
			elementId: 	'grid',
			offset:		{ x: 115, y: 175 }
		}
	},
	
	_positionBubble: function(feature) {
		var featureElement = this.getFeatureElement(feature),
			featureOffset = featureElement.cumulativeOffset(),
			featureDim = featureElement.getDimensions(),
			bubbleElement = Ss.Bubbles.getBubble(feature.bubbleId),
			bubbleDim = bubbleElement.getDimensions(),
			bubblePos = Ss.Bubbles.getPosition(feature.bubbleId),
			notchDim = { height: 20, width: 33 },
			top = featureOffset.top + feature.offset.y,
			left = featureOffset.left - (bubbleDim.width/2).round() - notchDim.width + feature.offset.x;
			
		if( bubblePos === 'bottom' ) {
			top = top - bubbleDim.height - notchDim.height;
		} else {
			top = top + notchDim.height + featureDim.height;
		}
		

		bubbleElement.setStyle({
			left: 	left + 'px',
			top: 	top + 'px'
		});
	},
	
	_onlyShowOnce: function() {
		if (!readCookie('search_announcement_v2_seen')) {
			Event.observe(window, 'load', function(evt) {
				Ss.SearchAnnouncement.show.bind(Ss.SearchAnnouncement).defer();
				createCookie({
					name:  'search_announcement_v2_seen',
					value: 1,
					days:  1000
				});
			});
		}
	},
	

	_events: function() {
		

		this.elements.announcement.observe('click', function(evt) {
			

			var target = Event.element(evt),
				item = (target.match('#feature_announcement li') ? target : target.up('li'));
				
			if( target.match('#feature_close_announcement') ) {
				this.hide();
				return;
			}

			if( item ) {
				Event.stop(evt); // prevent the 'click away to close' behavior
			}
	
		}.bind(this));
		

		this.elements.announcement.observe('mouseover', function(evt) {


			var target = Event.element(evt),
				item = (target.match('#feature_announcement li') ? target : target.up('li')),
				feature;
			
			if(!item) {
				return;
			}
			
			if(this.features[item.id]) {
				this.showSingleFeature(this.features[item.id]);
			} else {
				this.showAllFeatures();
			}
			
		}.bind(this));
		

		this.elements.seeWhatsNew.observe('click', this.show.bind(this));
		

		Event.observe(window, 'resize', function(evt) {
			Object.values(Ss.SearchAnnouncement.features).each(
				function(feature) {
					Ss.SearchAnnouncement._positionBubble(feature); // position the help bubble next to the feature
				}
			);
		});
	},
	
	_hideOnPicPage: function() {

		Ss.search.addDetailCallback(function(response) {
			Ss.SearchAnnouncement.hide();
			Ss.SearchAnnouncement.elements.seeWhatsNew.hide();
		});
		Ss.search.addResultsCallback(function(response) {
			Ss.SearchAnnouncement.elements.seeWhatsNew.show();
		});
	},
	

	_transitionIn: function() {
		var body = this.elements.body;

		this.elements.placeholder.setStylePeriodically({
				property: 'height',
				endValue: 48,
				units: 'px',
				increment: 2
		});

		this.elements.announcement.setStylePeriodically({
				property: 'top',
				endValue: 0,
				units: 'px',
				increment:2,
				onComplete: function() {
					body.addClassName(Ss.SearchAnnouncement.CSS.open);
				}
		});
	},
	

	_transitionOut: function() {
		var body = this.elements.body;

		this.elements.placeholder.setStylePeriodically({
				property: 'height',
				endValue: 0,
				units: 'px',
				increment: 2
		});

		this.elements.announcement.setStylePeriodically({
				property: 'top',
				endValue: -48,
				units: 'px',
				increment: 2,
				onComplete: function() {
					body.removeClassName(Ss.SearchAnnouncement.CSS.open);
				}
		});
	}

};
// --------------------------------------------------------------------------------
// global.js: end JavaScript file: '/js/feature_announcement/SearchAnnouncement.js'

// global.js: begin JavaScript file: '/js/Anim.js'
// ================================================================================
(function(ns){
   ns.Anim = {};
   ns.Anim = function(set, duration, interval, callback, easing){
	    this.set = set;
		this.increment = 1 / (duration / interval);
		this.callback = callback || null;
		this.interval = interval || 30;
		this.easing = easing || ns.Anim.easing.in_out;
		this.val = 0;
		this._stop = null;
		this._timeout = null;
   };

   ns.Anim.easing = {
		'in': function (t) {
			return t*t;
		},
		'out': function (t) {
			return -1*t*(t-2);
		},
		'in_out': function (t) {
			if ((t/=1/2) < 1) return 1/2*t*t;
			return -1/2 * ((--t)*(t-2) - 1);
		},
		'none': function(t){ return t; }
   };

   ns.Anim.prototype = {
		start: function(){ this._run()},
		stop: function(){ this._stop = true;},
	    _run: function(){
		     var self = this;
			 if(this._stop){
				 this._stop = null;
				 return;
			 }
			 var val = this.val + this.increment;
			 var easeval = this.easing(this.val);
			 if(val >= 1){
			   this.set(1);
			   this.val = 1;
			   if(this.callback){ this.callback()}
			 }else{
			   this.set(easeval);
			   this._timeout = setTimeout(function(){ self._run()}, this.interval);
			   this.val = val;
			 }
	   }
   }
})(window);
// --------------------------------------------------------------------------------
// global.js: end JavaScript file: '/js/Anim.js'


// Cache Key Counter: 353



