//
// 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(event) {
          event.stop();
          Help.loadContent(this.href);
        });
    });
  },
  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() {
    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;
      },
      '#pane div.submit a:click' : function(e) {
        e.stop();
        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;
  }

});



//
// Simulates a real application menu
//
var Menu = Class.create({

  initialize: function(list) {
    this.list = $(list);
    if (this.list.nodeName.toLowerCase() != 'ul')
      throw 'ArgumentError: Menu must be initialized with a UL element';

    this.selectedItemClass = 'selected_menu_item';
    this.setupEvents();
  },

  deselectAll: function() {
    this.list.select('li').each( this.deselectItem.bind(this) );
  },

  deselectItem: function(listItem) {
    $(listItem).removeClassName(this.selectedItemClass);
  },

  hasSelectedItem: function() {
    return this.list.select( 'li.' + this.selectedItemClass ).length > 0;
  },

  selectItem: function(listItem) {
    this.deselectAll();
    $(listItem).addClassName(this.selectedItemClass);
  },

  setupEvents: function() {
    document.observe('click', function(event) {
      this.deselectAll();
    }.bind(this));

    document.observe('keydown', function(event) {
      if (event.keyCode == Event.KEY_ESC)
        this.deselectAll();
    }.bind(this));

    this.list.select('li').each( function(li) {

      // Hack because Prototype incorrectly selects elements if
      // the #select above is changed to the more appropriate '> li'
      if (li.parentNode != this.list)
        return;

      if (li.select('ul').length > 0)
        li.addClassName('has_menu');

      li.observe('click', function(event) {
        event.stopPropagation();
        li.hasClassName(this.selectedItemClass) ? this.deselectItem(li) : this.selectItem(li);
      }.bind(this));

      li.observe('mouseover', function(event) {
        if (this.hasSelectedItem())
          this.selectItem(li);
      }.bind(this));

    }.bind(this));
  }

});



//
// Adds behavior to either specific selector-based
// events or just generically on DOM ready.
//
Event.onReady(function() {
  // XXX Move this into an addBehvaior object below. Everything inside the Each.
  $$('input.inline-calendar').each( function(e) {
    Calendar.setup({
      // change to 'this'
      dateField: e,
      parentElement: $(e).up().next('div.calendar-div')
    });
  });
});

Event.addBehavior({


  'th.sortable:click': function(event) {
    var col = this.id.replace('_head', '');
    SortController.sort(col);
  },

  'th.sortable a:click': function(event) {
    event.preventDefault();
  },

  'th.sortable:mouseover': function(event) {
    this.addClassName("sfover");
  },

  'th.sortable:mouseout': function(event) {
    this.removeClassName("sfover");
  },

  'a.popup:click': function(event) {
    Popup.open(this.href);
    event.stop();
  },

  '#hlink:click': function(event) {
    Help.toggle();
    event.stop();
  },

  '#primary_nav > ul': function(event) {
    window.menu = new Menu(this);
  }

});


function delete_link (e) {
    e.preventDefault();
    var delete_id = this.readAttribute('data-delete_id');
        input = $(delete_id + '-delete'),

    new_value = $F(input) == '1' ? 0 : 1;
    input.writeAttribute('value', new_value);
    input.up('#' + delete_id).toggleClassName('deleted');
}

function destroy_link (e) {
  e.preventDefault();
  $(this.readAttribute('data-delete_id')).remove();
}

function add_link (e) {
    e.preventDefault();
    var a = this,
        id = a.readAttribute('data-add-nested'),
        regexp = new RegExp(a.readAttribute('data-add-nested-replace') || id, "g"),
        html = $(id).innerHTML;
    var output = html.replace(regexp, (new Date).getTime())
    Element.insert(a, { before: output });
    Event.addBehavior.reload();
    findAndUpdateSelectBoxes(bindChangeHandler);
    // Event.fire(this, 'nested-content-added');
}

function toggle_link (e) {
  if (e) e.preventDefault();
  if (this.readAttribute('data-toggle-class')) {
    var target = this.readAttribute('data-toggle-class-on'),
        className = this.readAttribute('data-toggle-class');
    $(target).toggleClassName(className);
  } else if (this.readAttribute('data-toggle')) {
    var targets = this.readAttribute('data-toggle').split(',');
    targets.each(function(el, i) { $(el).toggle(); });
  }
}

function setup_popup_calendar (event) {
  Calendar.setup({
    dateField: this,
    eventName: 'focus',
    triggerElement: this

  });
}

Event.addBehavior({
  'a[data-add-nested]:click': add_link,
  '.delete_link:click': delete_link,
  '.destroy_link:click': destroy_link,
  '.toggle_link:click': toggle_link,
  '.popup-calendar': setup_popup_calendar
});

Event.onReady(function() {
  $$('.toggle_link.click_on_load').each(function(e, i) {
    if (e.up('.new_object_form')) return;
    toggle_link.call(e);
  });

  // $$('input.popup-calendar').each( function(e) { setup_popup_calendar(e) });
});

