/*** Prototype extensions ***/

Prototype.Browser.IE6 = (Prototype.Browser.IE
  && parseInt(navigator.userAgent.substring(navigator.userAgent.indexOf('MSIE')+5))==6);
  // Use this only for presentational changes. If you need to change actual
  // behavior, use feature detection, not browser detection.



/*** Effects ***/

// Modify script.aculo.us defaults
Effect.DefaultOptions.duration=0.25;



/*** Standard BloomBox JS ***/

var BB={}; // BloomBox namespace

BB.createAll=function(selector,klass){
  // Uses the Prototype JS framework.
  // Allows for quickly instantiating classes based on CSS selectors.
  //
  // Syntax:
  //   BB.createAll(css_selector, BB_class_name[, arguments])
  //
  // Example:
  //
  //   Before:
  //   $$('a[rel=external]').each(function(a){
  //     new BB.PopUpControl(a, arg1, arg2);
  //   });
  //
  //   After:
  //   BB.createAll('a[rel=external]', 'PopUpControl', arg1, arg2);
  var args=$A(arguments).slice(2);
  $$(selector).each(function(el){ new BB[klass](el, args) });
};

BB.DynamicSubmitButton=Class.create({
  // Uses the Prototype JS framework.
  // For generating form submit buttons that are disabled and change to an
  // alternate text value when the form is submitted. For example, a button
  // labeled "Save" can be disabled and changed to "Saving..." on submit.

  initialize:function(button){
    this.button=button;
    this.model={ oldText:$F(this.button), newText:'' };
    this.observers={
      disableButton:this.disableButton.bindAsEventListener(this),
      enableButton:this.enableButton.bindAsEventListener(this)
    };
    if(this.form=this.button.up('form')){
      this.form.observe('submit',this.observers.disableButton);
      if(typeof BB.ValidatedForm!='undefined'){
        this.form.observe('BB:ValidatedForm:submitFailure',this.observers.enableButton);
      }
    }
  },
  disableButton:function(ev){
    if(this.model.oldText.match(/^Log[\s]?in$/)){
      this.model.newText='Logging in...';
    }else if(this.model.oldText=='Save' || this.model.oldText.match(/^Save /)){
      this.model.newText='Saving...';
    }
    if(!this.model.newText.blank()){this.button.value=this.model.newText}
    this.button.disabled='disabled';
    if(this.alt=this.button.next('span.alt')){this.alt.hide()}
  },
  enableButton:function(ev){
    this.button.removeAttribute('disabled');
    this.button.value=this.model.oldText;
    if(this.alt){this.alt.show()}
  },
  destroy:function(){
    this.form.stopObserving('submit',this.observers.disableButton);
    this.form.stopObserving('BB:ValidatedForm:submitFailure',this.observers.enableButton);
  }
});

BB.FormWithDefaultValueTextFields=Class.create({
  // Uses the Prototype JS framework.
  // For automatically generating text input fields that are pre-filled with
  // default values, which disappear and reappear based on user clicks and
  // input. Uses the BB.DefaultValueTextField class.

  // NOTE: This is currently incompatible with BB.ValidatedForm.

  // Usage (two steps):
  // 1. Add HTML: <form id="new-comment-form">
  //                <fieldset>
  //                  <input class="default-value" title="Name" />
  //                  <input class="default-value" title="E-mail address" />
  //                  <input class="default-value" title="Comment" />
  //                </fieldset>
  //              </form>
  // 2. Add JS:   new BB.FormWithDefaultValueTextFields('new-comment-form');
  // This automatically uses each input's title as the default value.

  // Options:
  // - focusColor (default: '#000000')
  // - focusBackgroundColor (default: '#ffffff')
  // - blurColor (default: '#5d5d5d')
  // - blurBackgroundColor (default: '#bebebe')

  initialize:function(form, opts){
    this.form=$(form);
    this.inputs=this.form.select('input.default-value, textarea.default-value');
    this.enabledInputs=[];

    this.inputs.each(function(inp){
      this.enabledInputs.push(new BB.DefaultValueTextField(inp, opts));
    }.bind(this));
    this.form.observe('submit',this.onsubmit.bindAsEventListener(this));
  },
  onsubmit:function(ev){
    this.enabledInputs.each(function(inp){
      if($F(inp.input)==inp.defaultText){ inp.setInputValue('') }
    });
  }
});

BB.DefaultValueTextField=Class.create({
  // Uses the Prototype JS framework.
  // For generating text input fields that are pre-filled with default values,
  // which disappear and reappear based on user clicks and input. Use the
  // BB.FormWithDefaultValueTextFields class instead; the caution below
  // explains why.

  // CAUTION: Be careful using this with an input that writes to the database!
  // If the input field is not changed, the default string will be saved.
  // Instead, instantiate a BB.FormWithDefaultValueTextFields object, which
  // will clear these fields before submission.

  // Usage (two steps):
  // 1. Add HTML: <input id="users-new-username" title="username" />
  // 2. Add JS:   new BB.DefaultValueTextField('users-new-username');
  // This automatically uses the input's title attribute as the default value.

  // See BB.FormWithDefaultValueTextFields for color options.

  initialize:function(input, opts){
    this.input=$(input);
    this.opts=opts||{};
    this.colors={
      focus:    this.opts.focusColor || '#000000',
      focusBg:  this.opts.focusBackgroundColor || '#ffffff',
      blur:     this.opts.blurColor || '#5d5d5d',
      blurBg:   this.opts.blurBackgroundColor || '#bebebe'
    };
    this.defaultText=this.input.title;

    this.input.title='';
    this.reset();
    this.input.observe('focus',this.onfocus.bindAsEventListener(this));
    this.input.observe('blur',this.onblur.bindAsEventListener(this));
  },
  setInputValue:function(newText){
    if(this.input.tagName.toLowerCase()=='textarea'){
      this.input.update(newText);
    }else{
      this.input.value=newText;
    }
  },
  reset:function(){
    if($F(this.input).blank()){
      this.input.setStyle({
        color:this.colors.blur,
        backgroundColor:this.colors.blurBg
      });
      this.setInputValue(this.defaultText);
    }
  },
  onfocus:function(ev){
    if($F(this.input)==this.defaultText){
      this.input.setStyle({
        color:this.colors.focus,
        backgroundColor:this.colors.focusBg
      });
      this.setInputValue('');
      this.input.focus();
    }
  },
  onblur:function(ev){ this.reset() }
});

BB.MaxLengthTextField=Class.create({
  // Uses the Prototype JS framework.
  // For generating text input fields that have maximum lengths.

  // Usage (two steps):
  // 1. Add HTML: <textarea id="new-comment-text"></textarea>
  // 2. Add JS:   new BB.MaxLengthTextField('new-comment-text',200);

  initialize:function(input,limit){
    this.input=$(input); // ID to Element
  this.counter=$('counter-1');
    this.model={
      limit:parseInt(limit),
      inputType:this.input.tagName.toLowerCase()
    };
    this.observers={truncate:this.truncate.bindAsEventListener(this)}

    switch(this.model.inputType){
      case 'input':
        this.input.writeAttribute('maxlength',this.model.limit);break;
      case 'textarea':
        this.input.observe('keyup',  this.observers.truncate);
        this.input.observe('keydown',this.observers.truncate);
        this.input.observe('blur',   this.observers.truncate);
        break;
    }
    this.truncate();
  },
  truncate:function(){
    this.input.value=$F(this.input).truncate(this.model.limit,'');
    if (this.counter) {
      this.counter.update("(" + this.input.value.length + "/150)");
    };
  },
  destroy:function(){
    if(this.model.inputType=='textarea'){
      this.input.stopObserving('keyup',  this.observers.truncate);
      this.input.stopObserving('keydown',this.observers.truncate);
      this.input.stopObserving('blur',   this.observers.truncate);
    }
  }
});

BB.PopUpControl=Class.create({
  // Pass the element that should trigger the popup when clicked;
  //   the url of the page that should be opened in a new window
  //   a hash of options that can be passed to window.open.
  //   The two keys that we look for specifically at this point
  //   are title and params. Title is the title of the popup page,
  //   and params is a string containing comma separated key value
  //   pairs that window.open accepts.
  initialize:function(cntrl, opts){
    this.cntrl=cntrl;
    this.opts=$H(opts) || $H({});
    this.cntrl.observe('click',this.pop.bindAsEventListener(this));
  },
  pop:function(event){
    event.stop();
    window.open(this.href(), this.name(), this.params());
  },
  href:function(){ return this.cntrl.readAttribute('href') },
  name:function(){ return this.opts.get('name') || '_blank' },
  params:function(){ return this.opts.get('params') || '' }
});

BB.ValidatedForm=Class.create({
  // Uses the Prototype JS framework.
  // Adds JS (not Ajax) validation to specified inputs in a form. Based on
  // model validations in Ruby on Rails.

  // Usage (two steps):
  // 1. Add HTML: <form id="users-new-form">
  //                <fieldset>
  //                  <ol>
  //                    <li><input type="text" name="user[username]" /></li>
  //                    <li><input type="text" name="user[email]" /></li>
  //                    <li><input type="checkbox" name="user[accept_terms]" /></li>
  //                  </ol>
  //                </fieldset>
  //              </form>
  // 2. Add JS:   var vf=new BB.ValidatedForm($('users-new-form'));
  //              vf.validates('presenceOf', 'input[name="user[username]"]',{
  //                message: "Dude, you need a username."
  //              });
  //              vf.validates('formatOf', 'input[name="user[email]"]',{
  //                format: BB.ValidatedForm.formats.email
  //              });
  //              vf.validates('acceptanceOf', 'input#user_accept_terms');
  // Note: The JS parameters are CSS selector strings. This allows you to
  // validate form inputs not only by name, but also by ID, class, etc.

  // validates() options for all validators:
  // - message: String to display in case of error (default: empty string)
  //   - Note: Unlike RoR, validators have no default messages, as these are
  //     typically not helpful or user-friendly enough.
  // - [TODO] if: Boolean function telling whether to run the validator
  // For validator-specific options, see BB.ValidatedForm.validators.

  // NOTE: This is currently incompatible with
  // BB.FormWithDefaultValueTextFields.

  initialize:function(form){ // ValidatedForm
    this.form=form; // <form>
    this.inputs=$H(); // {id:ValidatedFormInput, id:ValidatedFormInput, ...}
    this.observers={
      blur:this.onblur.bindAsEventListener(this),
      change:this.onchange.bindAsEventListener(this),
      submit:this.onsubmit.bindAsEventListener(this)
    };
    this.form.observe('submit',this.observers.submit);
  },
  validates:function(validator, selector, options){ // void
    this.form.select(selector).each(function(inp){
      var id=inp.identify();

      if(!this.inputs.get(id)){
        this.inputs.set(id,new BB.ValidatedFormInput(this,inp,[]));
      }
      this.inputs.get(id).addValidation(BB.ValidatedForm.validators[validator], options);

      inp.observe('blur',this.observers.blur);
      inp.observe('change',this.observers.change);
    }.bind(this));
  },
  onblur:function(ev){ // void
    window.setTimeout(function(){ this.onchange(ev) }.bind(this), 500);
  },
  onchange:function(ev){ // void
    var vfi=this.inputs.get(ev.element().readAttribute('id'));
    vfi.validate();
  },
  onsubmit:function(ev){ // void
    var anyErrors=false;

    this.inputs.each(function(i){
      if(!i[1].validate()){ anyErrors=true }
    }.bind(this));

    if(anyErrors){
      ev.stop();
      this.form.fire('BB:ValidatedForm:submitFailure');
      if(this.form.down('li.error')){ this.form.down('li.error input, li.error textarea').activate() }
    }
  }
});
BB.ValidatedForm.validators={
  // Class functions that should all return booleans. If a required option
  // isn't defined, the validator returns true so that the user isn't locked
  // out of submitting the form. (Ideally, this wouldn't happen because the
  // brilliant developer already tested the form validation.)

  presenceOf:function(input){
    return !$F(input).blank();
  },
  formatOf:function(input, opts){
    // Options:
    // - format:regex (e.g., /^[0-9]+$/) (required)

    var format=$H(opts).get('format');
    return !format||format.test($F(input));
  },
  lengthOf:function(input, opts){
    // Options:
    // - minimum:integer
    // - maximum:integer
    // - is:integer

    // Obvious constraints (e.g., min < max) are intentionally not tested here
    // to save processing time.

    var opts=$H(opts), s=$F(input), len=s.length,
        min=opts.get('minimum'), max=opts.get('maximum'), is=opts.get('is');
    if(!is&&!min&&!max){ return true }
    if(is&&len!=is || min&&len<min || max&&len>max){ return false }
    return true;
  },
  acceptanceOf:function(input){ return input.checked },
  confirmationOf:function(confirmationInput){
    // Usage:
    // 1. Add HTML: <input type="password" name="user[password]" />
    //              <input type="password" name="user[password_confirmation]" />
    //                <!-- Names should match except for suffix "_confirmation" -->
    // 2. Add JS:   var vf=BB.ValidatedForm(...);
    //              vf.validates('confirmationOf','input[name="user[password_confirmation]"]);
    //                // NOTE: Contrary to what you may expect, the validator
    //                // should be applied to the confirmation input, not the
    //                // main input.

    var input=confirmationInput.up('form').down('input[name="'+confirmationInput.readAttribute('name').gsub(/_confirmation/,'')+'"]');
    return $F(input)==$F(confirmationInput);
  }
};
BB.ValidatedForm.formats={
  numeric:/^[0-9]+$/,
  alphanumericWithUnderscores:/^[a-z0-9_]+$/i,
  email:/^([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})$/i
};

BB.ValidatedFormInput=Class.create({
  // Uses the Prototype JS framework.
  // Helper class for BB.ValidatedForm. A BB.ValidatedForm has a collection
  // of BB.ValidatedFormInputs, and each BB.ValidatedFormInput has a
  // collection of validator functions and their respective options.

  initialize:function(form,input,validators){ // ValidatedFormInput
    this.form=form; // BB.ValidatedForm
    this.input=input; // <input>
    this.validations=[]; // [{validator:function, options:Hash}, {...}, ...]
  },
  addValidation:function(validator,opts){ // void
    this.validations.push({ validator:validator, options:$H(opts||{}) });
  },
  validate:function(){ // boolean
    // Returns true if valid, false otherwise.

    var noErrors=true;
    for(var i=0,len=this.validations.length;i<len;i++){
      var v=this.validations[i], isValid=this.isValid(v);
      this.updateWrapper(v, isValid);
      if(!isValid){ noErrors=false;break }
    }
    return noErrors;
  },
  isValid:function(validation){ // boolean
    // Parameters:
    // - validation: {validator:booleanFunction, options:Hash}
    return validation.validator(this.input, validation.options);
  },
  updateWrapper:function(validation, isValid){ // void
    // Parameters:
    // - validation: {validator:booleanFunction, options:Hash}
    // - isValid: boolean

    var wrapper=this.input.up('li'),
        message=validation.options.get('message'), errorAction;
    if(isValid){
      wrapper.removeClassName('error');
    }else{
      wrapper.addClassName('error');
    }

    if(message && !message.blank()){
      var error=this.input.next('span.error-message');
      if(!error){
        error=new Element('span',{"class":"error-message"}).hide();
        wrapper.insert({bottom:error});
      }
      error.update(message);
      if(isValid){ error.hide() }else{ error.show() }
    }
  }
});

BB.MenuTextInput=Class.create({
  // Uses the Prototype JS framework.
  // Adds an "Other..." option to a <select> menu such that, when the option
  // is selected, a text input appears for text entry. The <select>'s "name"
  // attribute is automatically transferred to the text input field, and is
  // transferred back if the user selects another option from the menu.

  // Usage (two steps):
  // 1. Add HTML: <form>
  //                <fieldset>
  //                  <ol>
  //                    <li>
  //                      <label>Favorite color</label>
  //                      <select id="color-select" name="color">
  //                        <option value="red">red</option>
  //                        <option value="green">green</option>
  //                        <option value="blue">blue</option>
  //                      </select>
  //                    </li>
  //                  </ol>
  //                </fieldset>
  //              </form>
  // 2. Add JS:   new BB.MenuTextInput('color-select');

  // Options:
  // - menuAltText: string (default: 'Other...')
  // - menuAltValue: string (default: 'other')

  initialize:function(menuInput,opts){
    this.menuInput=$(menuInput); // <select>
    this.inputName=this.menuInput.readAttribute('name');
    this.textInput=new Element('input',{type:"text"});
    this.opts=$H({ menuAltText:"Other...", menuAltValue:"other" }).merge(opts);
    this.observers={
      toggleTextInput:this.toggleTextInput.bindAsEventListener(this)
    };

    this.menuInput.insert({
      bottom:"<option value=\""+this.opts.get('menuAltValue')+"\">"
        +this.opts.get('menuAltText')+"</option>"
    });
    this.menuInput.insert({ after:this.textInput.hide() });
    this.menuInput.observe('change',this.observers.toggleTextInput);
  },
  toggleTextInput:function(ev){
    if($F(this.menuInput)==this.opts.get('menuAltValue')){
      this.showTextInput();
    }else{
      this.hideTextInput();
    }
  },
  showTextInput:function(){
    this.menuInput.writeAttribute('name','');
    this.textInput.writeAttribute('name',this.inputName).show();
  },
  hideTextInput:function(){
    this.menuInput.writeAttribute('name',this.inputName);
    this.textInput.writeAttribute('name','').hide();
  },
  destroy:function(){
    this.menuInput.stopObserving('change',this.observers.toggleTextInput);
    this.menuInput.down('option[value="'+this.opts.get('menuAltValue')+'"]').remove();
    this.menuInput.writeAttribute('name',this.inputName);
    this.textInput.remove();
  }
});



/*** Custom JS ***/

var RemoteForm = Class.create({
  // Uses the Prototype JS framework.
  // Basic xhr form enabler.
  // Parameters: a form dom element
  // Inherit this class and overwrite renderSuccess and renderFailure like so:
  //
  // var MyRemoteForm = Class.create(RemoteForm, {
  //   renderSuccess: function($super, xhr) {
  //     var response = $super(xhr);
  //     // Response is a hash ob, so do thing to it like so:
  //     this.form.insert({after: response.message});
  //   },
  //   renderFailure: function($super, xhr) {
  //     var error = $super(xhr);
  //     // Note: this.errorContainer is another RemoteForm class method
  //     // you can override to customize error display.
  //     this.form.insert({after: this.errorContainer.update(error.message)});
  //   }
  // });
  initialize: function(form) {
    this.form = form;
    if (!this.form) {return false};
    this.form.observe('submit', this.act.bindAsEventListener(this));
  },
  act: function(ev) {
    ev.stop();

    new Ajax.Request(this.url(), {
      parameters: this.params(),
      method: this.method(),
      onSuccess: this.renderSuccess.bind(this),
      onFailure: this.renderFailure.bind(this)
    });
  },
  // callbacks -
  renderSuccess: function(xhr) {
    return xhr.responseText.evalJSON();
  },
  renderFailure: function(xhr) {
    return xhr.responseText.evalJSON();
  },
  // xhr options - not recommended to overwrite
  url: function() {
    return this.form.readAttribute('action');
  },
  params: function() {
    alert('params: '+this.form.serialize(true));
    return this.form.serialize(true);
  },
  method: function() {
    return this.form.readAttribute('method');
  },
  // default opts - inherit and overwrite!
  messageContainer: function() {
    return new Element('div').setStyle(this.containerStyles());
  },
  containerStyles: function() {
    return {};
  },
  successContainer: function() {
    return this.messageContainer().addClassName(this.successContainerClass());
  },
  successContainerClass: function() {
    return 'success';
  },
  errorContainer: function() {
    return this.messageContainer().addClassName(this.errorContainerClass());
  },
  errorContainerClass: function() {
    return 'errors';
  }
});

var FlashPlayer=Class.create({
  initialize:function(fp){
    this.wrapper=fp;
    this.model={ state:null, prevState:null };
  },
  setState:function(newState){
    newState=newState.toLowerCase();
    if(newState=='completed'){
      // Reset to same state as before starting the video
      this.model.prevState=null; this.model.state=null;
    }else{
      this.model.prevState=this.model.state;
      this.model.state=newState;
    }
    return this;
  },
  updateCounter:function(type){
    var wrapperID=this.wrapper.readAttribute('id'),
        trackeeID=wrapperID.gsub(/^flash-/,'');
    new Ajax.Request('/player_trackers/'+trackeeID+'/'+type,{
      method:"put",
      parameters:{ authenticity_token:authToken() }
      // Not user-triggered, so do nothing onSuccess or onFailure
    });
  }
});
FlashPlayer.listeners={
  model:{
    state:function(obj){
      var flashPlayer=jwObjectToFlashPlayer(obj);
      switch(obj.newstate.toLowerCase()){
        // case 'idle':break;
        // case 'buffering':break;
        case 'paused':
          flashPlayer.setState('paused'); break;
        case 'playing':
          flashPlayer.setState('playing');
          if(flashPlayer.model.prevState==null){ flashPlayer.updateCounter('start') }
          break;
        case 'completed':
          flashPlayer.setState('completed').updateCounter('complete'); break;
      }
    }
  },
  view:{},
  controller:{}
};

// function playerReady(obj){ // Special hook for JW Player
//   // Element IDs:
//   // - Wrapper div: flash-###-movie / flash-###-flv (used as flashPlayers hash keys)
//   // - Flash object/embed: ###-movie / ###-flv (used to grab jwplayer object)
//
//   var jwplayer=$(obj['id']), id=stripJWPlayerObjectId(obj);
//   if(jwplayer){
//     setTimeout(function(){
//       // Inside playerReady, we need a bit more time before player is actually ready
//       // console.log(jwplayer.getPlaylist());
//       var fp=flashPlayers.get(id);
//       if(fp){ fp.jwplayer=jwplayer }
//       fp.jwplayer.addModelListener('STATE','FlashPlayer.listeners.model.state');
//     },500);
//   }
// };
function jwObjectToFlashPlayer(obj){
  return flashPlayers.get(stripJWPlayerObjectId(obj));
};
function stripJWPlayerObjectId(obj){ return obj['id'].gsub(/^flash-/,'') };

//Add cookie support to prototype
var Cookie = {
  set: function(name, value, daysToExpire) {
    var expire = '';
    if (daysToExpire != undefined) {
      var d = new Date();
      d.setTime(d.getTime() + (86400000 * parseFloat(daysToExpire)));
      expire = '; expires=' + d.toGMTString();
    }
    return (document.cookie = escape(name) + '=' + escape(value || '') + expire);
  },
  get: function(name) {
    var cookie = document.cookie.match(new RegExp('(^|;)\\s*' + escape(name) + '=([^;\\s]*)'));
    return (cookie ? unescape(cookie[2]) : null);
  },
  erase: function(name) {
    var cookie = Cookie.get(name) || true;
    Cookie.set(name, '', -1);
    return cookie;
  },
  accept: function() {
    if (typeof navigator.cookieEnabled == 'boolean') {
      return navigator.cookieEnabled;
    }
    Cookie.set('_test', '1');
    return (Cookie.erase('_test') === '1');
  }
};

function IE6Hover(){
  if(!ieMajorVersion || ieMajorVersion > 6){ return; }

  $$('.site-nav li.section').each(function(el){
    el.observe('mouseover',function(){
      this.addClassName('hover');
      if(this.match('li.section:last-child')){
        this.addClassName('last-hover');
      }
    });
    el.observe('mouseout', function(){
      this.removeClassName('hover');
      if(this.match('li.section:last-child')){
        this.removeClassName('last-hover');
      }
    });
  });
}


/*** Miscellaneous ***/

flashPlayers=$H(); // global

document.observe('dom:loaded',function(){
  BB.createAll('input[type=submit], input[type=image]', 'DynamicSubmitButton');
  BB.createAll('a[rel=external]', 'PopUpControl');
  BB.createAll('div.bookmark li a', 'PopUpControl', {
    params:'toolbar=no,width=700,height=500'
  });

  BB.createAll('div.new-user-question textarea','MaxLengthTextField',150,"counter-1");

  $$('body:not(.admin) div.flash-video').each(function(fv){
    flashPlayers.set(fv.identify(), new FlashPlayer(fv));
  });

  $$('textarea.embed').invoke('observe','click',function(ev){
    ev.element().activate();
  });

  var siteWarning=$('site-warning');
  if(siteWarning && Cookie.get('hide_warning')){
    var p=siteWarning.down('p'); p.hide();
  }

  IE6Hover();

  (function(){
    var questionsList = $$('div.categorized-questions-list')[0],
        fullList;

    if(!questionsList){ return; }

    fullList = questionsList.down('div.full-list');
    if(!!fullList){
      // See also application.css. This creates two separate controls so that
      // multiple elements (the full list and its toggles) can be hidden/shown
      // via CSS and toggling a single class, rather than programmatically
      // toggling each element or changing a single toggle's HTML/text.
      fullList.insert({
        before: [
          '<div class="toggle-full-list show-full-list">',
            '<a href="#" rel="details"><span>See all questions</span></a>',
          '</div>'
        ].join(''),
        after: [
          '<div class="toggle-full-list hide-full-list">',
            '<a href="#" rel="details"><span>See fewer questions</span></a>',
          '</div>'
        ].join('')
      });
      questionsList.down('.show-full-list a').observe('click', function(ev){
        questionsList.addClassName('full-list-visible');
        ev.stop();
      });
      questionsList.down('.hide-full-list a').observe('click', function(ev){
        questionsList.removeClassName('full-list-visible');
        ev.stop();
      });
    }
  })();

  if(!!$$('body#landmarks-show')[0]){
    // Enable collapsible question video grids
    $$('div.video-grid').each(function(gridWrapper){
      if(!gridWrapper.down('ul.more')){ return; } // No more videos to show

      var gridHeader = gridWrapper.down('h3');

      // Add toggle
      gridHeader.insert({
        bottom: [
          '<a href="#" class="toggle toggle-more" rel="more">',
            'See more videos</a>',
          '<a href="#" class="toggle toggle-less" rel="less">',
            'See fewer videos</a>'
        ].join('')
      });

      gridHeader.down('a[rel=more]').observe('click', function(ev){
        // See also application.css for showing/hiding sets of elements.
        gridWrapper.addClassName('full-list-visible');
        ev.stop();
      });
      gridHeader.down('a[rel=less]').observe('click', function(ev){
        gridWrapper.removeClassName('full-list-visible');
        ev.stop();
      });
    });
  }

  // Put any additional JavaScript here that doesn't fit the sections above.
});
