// 
// Adds element storage to Prototype 1.6.0.3
// This will come standard in 1.6.1.
// http://prototypejs.org/2009/2/16/pimp-my-code-1-element-storage
//

Element.Storage = {
  UID: 1
};
 
Element.addMethods({
  getStorage: function(element) {
    if (!(element = $(element))) return null;
    
    if (Object.isUndefined(element._prototypeUID))
      element._prototypeUID = Element.Storage.UID++;
      
    var uid = element._prototypeUID;
    
    if (!Element.Storage[uid])
      Element.Storage[uid] = $H();
      
    return Element.Storage[uid];
  },
  
  store: function(element, key, value) {
    if (!(element = $(element))) return null;
    element.getStorage().set(key, value);
    return element;
  },
  
  retrieve: function(element, key, defaultValue) {
    if (!(element = $(element))) return null;
    
    var hash = element.getStorage(), value = hash.get(key);
    
    if (Object.isUndefined(value)) {
      hash.set(key, defaultValue);
      value = defaultValue;
    }
    
    return value;
  }
});



/**
 * Event.simulate(@element, eventName[, options]) -> Element
 * 
 * - @element: element to fire event on
 * - eventName: name of event to fire (only MouseEvents and HTMLEvents interfaces are supported)
 * - options: optional object to fine-tune event properties - pointerX, pointerY, ctrlKey, etc.
 *
 *    $('foo').simulate('click'); // => fires "click" event on an element with id=foo
 *
 **/
(function(){
  
  var eventMatchers = {
    'HTMLEvents': /^(?:load|unload|abort|error|select|change|submit|reset|focus|blur|resize|scroll)$/,
    'MouseEvents': /^(?:click|mouse(?:down|up|over|move|out))$/
  };
  var defaultOptions = {
    pointerX: 0,
    pointerY: 0,
    button: 0,
    ctrlKey: false,
    altKey: false,
    shiftKey: false,
    metaKey: false,
    bubbles: true,
    cancelable: true
  };
  
  Event.simulate = function(element, eventName) {
    var options = Object.extend(defaultOptions, arguments[2] || { });
    var oEvent, eventType = null;
    
    element = $(element);
    
    for (var name in eventMatchers) {
      if (eventMatchers[name].test(eventName)) { eventType = name; break; }
    }
 
    if (!eventType)
      throw new SyntaxError('Only HTMLEvents and MouseEvents interfaces are supported');
 
    if (document.createEvent) {
      oEvent = document.createEvent(eventType);
      if (eventType == 'HTMLEvents') {
        oEvent.initEvent(eventName, options.bubbles, options.cancelable);
      }
      else {
        oEvent.initMouseEvent(eventName, options.bubbles, options.cancelable, document.defaultView, 
          options.button, options.pointerX, options.pointerY, options.pointerX, options.pointerY,
          options.ctrlKey, options.altKey, options.shiftKey, options.metaKey, options.button, element);
      }
      element.dispatchEvent(oEvent);
    }
    else {
      options.clientX = options.pointerX;
      options.clientY = options.pointerY;
      oEvent = Object.extend(document.createEventObject(), options);
      element.fireEvent('on' + eventName, oEvent);
    }
    return element;
  };
  
  Element.addMethods({ simulate: Event.simulate });
})();



// 
// A simple implementation of class-level events.
// 
var ClassEvents = {
  
  fire: function(key) {
    if (!this._events || !this._events[key]) return false;
    this._events[key].each( function(func) { func.apply(); });
    return true;
  },
  
  observe: function(key, func) {
    this._events = this._events || {};
    this._events[key] = this._events[key] || $A();
    this._events[key].push( func.bind(this) );
    return this;
  }
  
};



// Controls the help system
Help = {
  WITH_HELP_CLASS : "whelp",
  HELP_LINK : "hlink",
  CONTAINER : "container",
  show : function(url) {
    $('help').show();
    $(Help.CONTAINER).addClassName(Help.WITH_HELP_CLASS);
    $(Help.HELP_LINK).update("<span>Close</span>");
    Help.loadContent(url || $(Help.HELP_LINK).href);
  },
  hide : function() {
    $(Help.CONTAINER).removeClassName(Help.WITH_HELP_CLASS);
    $('help').hide();
    $(Help.HELP_LINK).update("<span>Help</span>");
  },
  toggle : function(url) {
    if ($(Help.CONTAINER).hasClassName(Help.WITH_HELP_CLASS)) Help.hide();
    else Help.show(url);
  },
  _attachLinkEvents : function() {
    var links = $('help').getElementsByTagName('a');
    $A(links).each(function(link) {
      if (link.href.indexOf('/help') > -1) 
        $(link).observe('click', function() {
          Help.loadContent(this.href);
          return false;
        });
    });
  },
  loadContent : function(url) {
    new Ajax.Updater($('help'), url, { onComplete : Help._attachLinkEvents });
  }
};

// Generic loading indicator
Loader = {
  show : function() {
    if (!Loader.loader) {
      Loader.loader = document.createElement('span'); 
      Loader.loader.id = 'doing';
      $('logout').insertBefore(Loader.loader, $('logout').firstChild);
    }
    $(Loader.loader).show();
  },
  hide : function() {
    $(Loader.loader).hide();
  }
};

Ajax.Responders.register({ onCreate: Loader.show, onComplete: Loader.hide });

// For inserting text into text fields.
Form.Element.insertAtCursor = function(el, text) {
  el = $(el); el.focus();
  if (document.selection)
    document.selection.createRange().text = text;

  if (typeof el.selectionStart != 'undefined') {
	  var start = el.selectionStart;
    el.value = el.value.substring(0, start) + 
    text + el.value.substring(el.selectionEnd, el.value.length);
	  el.selectionStart = el.selectionEnd = start + text.length;
  }
};

// Popup windows in admin section
Popup = {
  
  onClose : Prototype.K,
  
  open : function(url, options) {
    var self = this;
    this._createHTML();
    $('container').visualEffect('scroll_to', { duration : 0.5, afterFinish : function() {
      self._showOverlay();
      self._loadContent(url, options);
    } });
  },
  
  close : function() {
    this.isOpen = false;
    this.content.hide();
    this._hideOverlay();
  },
  
  _createHTML : function() {
    if (!this.overlay) {
      document.body.appendChild($div({ id : 'lb'}, $div({ id : 'pane' })));
      this.overlay = $('lb');
      this.content = $('pane');
      Element.hide('lb', 'pane'); 
      this._setOverlayHeight();
      Event.observe(window, 'resize', this._setOverlayHeight.bind(this));
      Popup.content.hide();
    }
  },
  
  _setOverlayHeight : function() {
    var windowHeight = window.innerHeight || document.documentElement.clientHeight;
    var documentHeight = document.documentElement.offsetHeight;
    this.overlay.style.height = ((documentHeight > windowHeight) ? 
      documentHeight : windowHeight - 13) + "px";
  },
  
  _showOverlay : function() {
    this.overlay.show();
  },
  
  _hideOverlay : function() {
    this.overlay.hide();
    this.onClose();
  },
  
  _attachEvents : function() {
    new Insertion.After('submit', ' <input type="button" id="close" value="Cancel" />');
    Event.addBehavior({
      '#pane form:submit' : function(e) {
        new Ajax.Updater('pane', this.action, { 
          parameters : Form.serialize(this), 
          evalScripts : true,
          onComplete : function() {
            Popup._attachEvents();  
          }
        });
        return false;
      },
      '#close:click' : function() {
        Popup.close();
      }
    });
  },
  
  _loadContent : function(url, options) {
    options = options || {};
    var attachEvents = this._attachEvents.bind(this);
    new Ajax.Updater(this.content, url, Object.extend({
      method: 'GET',
      evalScripts : true,
      onComplete : function() {
        if (!Popup.isOpen) {
          Popup.content.show();
          Popup.isOpen = true;
        }
        
        attachEvents();
      }
    }, options));
  }
  
};

ActionBar = {
  id : 'action',
  checked : 0,
  open : function() {
    $(this.id).addClassName('booby');
    $A($(this.id).getElementsByTagName('input')).each(function(input) {
      $(input).enable();
    });
  },
  close : function() {
    $(this.id).removeClassName('booby');
    $A($(this.id).getElementsByTagName('input')).each(function(input) {
      $(input).disable();
    });
  },
  update : function() {
    if (this.checked==0) this.close();
    else this.open();
  },
  reset : function() {
    this.checked = 0;
    this.close();
  },
  updateCheck : function(check) {
    var row =  $(check.parentNode.parentNode);
  	var alt = row.hasClassName('alt');
    this._highlightRow(check);
    
  	if (check.checked) {
  		ActionBar.checked++;
  	} else {
  		ActionBar.checked--;
  	}
  },
  init : function(check) {
    if (check.checked) {
  		this._highlightRow(check);
  		ActionBar.checked++;
  	}
  },
  _highlightRow : function(check) {
     var row =  $(check.parentNode.parentNode);
     var alt = row.hasClassName('alt');
     if (check.checked)
       row.addClassName( (alt) ? 'checkedalt' : 'checked' );
     else
       row.removeClassName( (alt) ? 'checkedalt' : 'checked' );
  }
};

SortController = {
  form : 'f',
  sortDirField : 'sort_dir',
  sortColField : 'sort_col',
  filterField : 'filterbox',
  cancelField : 'showcanc',
  sort : function(col) {
    var sortCol = $(this.sortColField), sortDir = $(this.sortDirField);
    if (sortCol.value == col) {
      sortDir.value = (sortDir.value == 'asc') ? 'desc' : 'asc';
    } else {
      sortCol.value = col;
      sortDir.value = 'asc';
    }
    this.update();
  },
  update : function() {
    var form = $(this.form);
    new Ajax.Request(form.action, {asynchronous:true, evalScripts:true, parameters : Form.serialize(form)});
  }
};



//
// Allows for a 24 hour time selector to be 
// easily switched to a 12 hour time selector. 
//
var HourPicker = Class.create(ClassEvents, {
  
  // Arguments:
  // - Element:hour24Select   A select element with option values from 0 - 23.
  initialize: function(hour24Select) {
    HourPicker.Instances.push(this);
    hour24Select.store('HourPicker', this);
    
    this.hour24Setup(hour24Select);
    this.hour12Setup();
    this.meridianSetup();
    this.setCurrentHour(this.hour24.value);
    this.show24Hour();
  },
  
  hour12Change: function() {
    this.setCurrentHour( (this.meridian.value == 'am') ? this.hour12.selectedIndex : this.hour12.selectedIndex + 12 );
  },
  
  hour12Setup: function() {
    this.hour12 = new Element('select');
    this.hour12
      .addClassName(this.hour24.className)
      .addClassName('hour12')
      .removeClassName('hour24')
      .update('<option value="0">12</option><option value="1">1</option><option value="2">2</option><option value="3">3</option><option value="4">4</option><option value="5">5</option><option value="6">6</option><option value="7">7</option><option value="8">8</option><option value="9">9</option><option value="10">10</option><option value="11">11</option>')
      .observe('change', this.hour12Change.bind(this));
    this.hour24.insert({ after: this.hour12 });
  },
  
  hour24Change: function() {
    this.setCurrentHour(this.hour24.selectedIndex);
  },
  
  hour24Setup: function(hour24Select) {
    this.hour24 = hour24Select;
    this.hour24.addClassName('hour24');
    this.hour24.observe('change', this.hour24Change.bind(this));
  },
  
  meridianChange: function() {
    this.setCurrentHour( (this.meridian.value == 'am') ? this._currentHour - 12 : this._currentHour + 12 );
  },
  
  meridianSetup: function() {
    this.meridian = new Element('select');
    this.meridian
      .addClassName(this.hour24.className)
      .addClassName('meridian')
      .removeClassName('hour24')
      .update('<option value="am">AM</option><option value="pm">PM</option>')
      .observe('change', this.meridianChange.bind(this));
    this.hour24.up().select('select:last-child')[0].insert({ after: this.meridian });
  },
  
  setCurrentHour: function(hour) {
    this._currentHour = parseFloat(hour);
    if (this._currentHour < 0)  this._currentHour = 0;
    if (this._currentHour > 23) this._currentHour = 23;
    
    this.hour24.selectedIndex = this._currentHour;
    this.hour12.selectedIndex = (this._currentHour < 12) ? this._currentHour : this._currentHour - 12;
    this.meridian.value       = (this._currentHour < 12) ? 'am' : 'pm';
    
    this.fire('currenthourchange');
  },
  
  show12Hour: function() {
    this.hour24.hide();
    this.hour12.show();
    this.meridian.show();
  },
  
  show24Hour: function() {
    this.hour24.show();
    this.hour12.hide();
    this.meridian.hide();    
  }
  
});

// 
// Holds a reference to all the HourPickers
// for easy manipulation.
// 
HourPicker.Instances = $A();
Object.extend(HourPicker.Instances, {
  
  show12Hour: function() {
    this.each( function(hp) { hp.show12Hour(); });
  },
  
  show24Hour: function() {
    this.each( function(hp) { hp.show24Hour(); });
  }
  
});



// 
// Controls start & end dates and provides 
// hooks to tie into when they change.
// 
var StartEndController = Class.create(ClassEvents, {
  
  initialize: function(options) {
    options = options || {};
    if ( !(options instanceof Object) )
      throw 'ArgumentError: options must be passed as an object';
    
    this.setStart(options.start);
    this.setEnd( options.end || this._hourShift(1, this.start) );
  },
  
  _getDate: function(date) {
    return new Date( date || new Date() );
  },
  
  _hourShift: function(hours, date) {
    return new Date( (new Date(date)).getTime() + (hours * 3600000) );
  },
  
  _isValidDate: function(date) {
    return !isNaN( date.getDate() );
  },
  
  _setDate: function(when, date, setOtherDate) {
    date = this._getDate(date);
    if ( !this._isValidDate(date) )
      return false;
    
    this[when] = date;
    this.fire(when + 'change');
    return true;
  },
  
  setEnd: function(date) {
    if ( !this._setDate('end', date) )
      return false;
    
    var newStart = this._hourShift(-1, this.end);
    if (newStart < this.start)
      this.setStart(newStart);
    
    return true;
  },
  
  setStart: function(date) {
    if ( !this._setDate('start', date) )
      return false;
    
    var newEnd = this._hourShift(1, this.start);
    if (newEnd > this.end)
      this.setEnd(newEnd);
    
    return true;
  }
  
});



// 
// Runs when the DOM is ready
// 
Event.onReady(function() {

  // 
  // Set up a calendar overlay for each 
  // field meant to be a date/time.
  // 
  $$('input.datetime').each( function(el) {
    Calendar.setup({ 
      inputField:  el, 
      ifFormat:    '%b %e, %Y', 
      showsTime:   false, 
      weekNumbers: false,
      timeFormat:  '24',
      onUpdate:    function(cal) {
        el.simulate('change');
      }
    });
  });
  
});
