var PublicMediaLibrary = Class.create({
  initialize: function(triggerContainer, mediaContainer, options) {
    this.triggerContainer = $(triggerContainer);
    this.mediaContainer = $(mediaContainer);
    this.options = options || {};
    
    this.listItems = this.triggerContainer.select('li');
    this.listItems.each(function(li) {
      var anchor = li.down('a');
      anchor.observe('click', this.onclick.curry(li).bind(this));
    }.bind(this));
    
    this.observePaginationLinks();
  },
  
  observePaginationLinks: function() {
    $$('.pagination a').each(function(anchor) {
      anchor.observe('click', this.paginationOnclick.curry(anchor).bind(this));
    }.bind(this))
  },
  
  onclick: function(li, event) {
    event.stop();
    
    if (li.hasClassName('selected')) return;
    
    var url = li.down('a').getAttribute('href');
    new Ajax.Request(url, Object.extend({
      method: 'get',
      onCreate: function() { showStatus('Loading&hellip;') },
      onComplete: this.oncomplete.curry(li).bind(this),
    }, this.options));
  },
  
  oncomplete: function(li, request, paginationUpdateUrl) {
    if (!paginationUpdateUrl) paginationUpdateUrl = li.down('a').getAttribute('href') + '_update_pagination';
    
    this.updatePaginationDisplay(paginationUpdateUrl);
    this.updateDisplayFromRequest(li, request);
    this.updateDisplay(li);
    hideStatus();
  },
  
  paginationOnclick: function(anchor, event) {
    event.stop();
    
    var url = anchor.getAttribute('href');
    var li = this.listItems.detect(function(item) {
      if (item.hasClassName('selected')) return item;
    });
    
    new Ajax.Request(url, Object.extend({
      method: 'get',
      onCreate: function() { showStatus('Loading&hellip;') },
      onComplete: this.paginationOncomplete.curry(anchor, li).bind(this),
    }, this.options));
  },
  
  paginationOncomplete: function(anchor, li, request) {
    var page = anchor.getAttribute('href').match(/page=(\d+)/)[1];
    var url = li.down('a').getAttribute('href') + '_update_pagination?page=' + page;
    
    this.oncomplete(li, request, url);
  },
  
  updateDisplay: function(li) {
    this.listItems.invoke('removeClassName', 'selected');
    li.addClassName('selected');
    
    var url = li.down('a').getAttribute('href');
    var mediaType = url.split('/').last();
    ['books', 'dvds', 'music', 'video_games'].each(function(type) { this.mediaContainer.removeClassName(type) }.bind(this));
    this.mediaContainer.addClassName(mediaType);
  },
  
  updateDisplayFromRequest: function(li, request) {
    var text = request.responseText;
    var roundCorners = function() {
      this.mediaContainer.setStyle('-moz-border-radius-bottomleft:8px; -webkit-border-bottom-left-radius:8px');
    }.bind(this);
    
    if (text.blank()) {
      text = "<div class='no_media'>No Media</div>";
      if (li.hasClassName('last')) this.mediaContainer.setStyle('-moz-border-radius-bottomleft:0px; -webkit-border-bottom-left-radius:0px');
      else roundCorners();
    } else roundCorners();
    
    this.mediaContainer.update(text);
  },
  
  updatePaginationDisplay: function(url) {
    new Ajax.Request(url, Object.extend({
      method: 'get',
      asynchronous: false,
      onComplete: function(paginationRequest) {
        var text = paginationRequest.responseText;
        $$('.pagination_holder').each(function(div) { div.update(text) });
        if (!text.blank()) this.observePaginationLinks();
      }.bind(this),
    }, this.options));
  }
});

/*
 *  status.js
 *
 *  dependencies: prototype.js, effects.js, lowpro.js
 *  
 *  --------------------------------------------------------------------------
 *  
 *  Allows you to display a status message when submiting a form. To use,
 *  simply add the following to application.js:
 *  
 *    Event.addBehavior({'form': Status.FormBehavior()});
 *  
 *  And then add an "onsubmit_status" to each form that you want to display
 *  a status message on submit for:
 *  
 *    <form onsubmit_status="Saving changes" ...>
 *  
 *  Based on code from popup.js.
 *  
 *  --------------------------------------------------------------------------
 *  
 *  Copyright (c) 2008, John W. Long
 *  Portions copyright (c) 2008, Five Points Solutions, Inc.
 *  
 *  Permission is hereby granted, free of charge, to any person obtaining a
 *  copy of this software and associated documentation files (the "Software"),
 *  to deal in the Software without restriction, including without limitation
 *  the rights to use, copy, modify, merge, publish, distribute, sublicense,
 *  and/or sell copies of the Software, and to permit persons to whom the
 *  Software is furnished to do so, subject to the following conditions:
 *  
 *  The above copyright notice and this permission notice shall be included in
 *  all copies or substantial portions of the Software.
 *  
 *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 *  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 *  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
 *  THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 *  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 *  FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 *  DEALINGS IN THE SOFTWARE.
 *  
 */

var Status = {
  CornerThickness: 12,
  SpinnerImage: '/images/ajax-loader.gif',
  SpinnerImageWidth: 32,
  SpinnerImageHeight: 32,
  BackgroundImage: '/images/status_background.png',
  TopLeftImage: '/images/status_top_left.png',
  TopRightImage: '/images/status_top_right.png',
  BottomLeftImage: '/images/status_bottom_left.png',
  BottomRightImage: '/images/status_bottom_right.png'
};

Status.BackgroundImages = function() {
  return $A([
    Status.SpinnerImage,
    Status.BackgroundImage,
    Status.TopLeftImage,
    Status.TopRightImage,
    Status.BottomLeftImage,
    Status.BottomRightImage
  ]);
}

Status.preloadImages = function() {
  if (!Status.imagesPreloaded) {
    Status.BackgroundImages().each(function(src) {
      var image = new Image();
      image.src = src;
    });
    Status.preloadedImages = true;
  }
}

Status.FormBehavior = Behavior.create({
  initialize: function() {
    var attr = this.element.attributes['onsubmit_status']
    if (attr) this.status = attr.value; 
    if (this.status) this.element.observe('submit', function() { showStatus(this.status) }.bind(this));
  }
});

Status.Window = Class.create({
  initialize: function() {
    Status.preloadImages();
    this.buildWindow();
  },

  buildWindow: function() {
    this.element = $div({'class': 'status_window', style: 'display: none; padding: 0 ' + Status.CornerThickness + 'px; position: absolute'});
    
    this.top = $div({style: 'background: url(' + Status.BackgroundImage + '); height: ' + Status.CornerThickness + 'px'});
    this.element.insert(this.top);
    
    var outer = $div({style: 'background: url(' + Status.BackgroundImage + '); margin: 0px -' + Status.CornerThickness + 'px; padding: 0px ' + Status.CornerThickness + 'px; position: relative'});
    this.element.insert(outer);
    
    this.bottom = $div({style: 'background: url(' + Status.BackgroundImage + '); height: ' + Status.CornerThickness + 'px'});
    this.element.insert(this.bottom);
    
    var topLeft = $div({style: 'background: url(' + Status.TopLeftImage + '); height: ' + Status.CornerThickness + 'px; width: ' + Status.CornerThickness + 'px; position: absolute; left: 0; top: -' + Status.CornerThickness + 'px'});
    outer.insert(topLeft);
    
    var topRight = $div({style: 'background: url(' + Status.TopRightImage + '); height: ' + Status.CornerThickness + 'px; width: ' + Status.CornerThickness + 'px; position: absolute; right: 0; top: -' + Status.CornerThickness + 'px'});
    outer.insert(topRight);
    
    var bottomLeft = $div({style: 'background: url(' + Status.BottomLeftImage + '); height: ' + Status.CornerThickness + 'px; width: ' + Status.CornerThickness + 'px; position: absolute; left: 0; bottom: -' + Status.CornerThickness + 'px'});
    outer.insert(bottomLeft);
    
    var bottomRight = $div({style: 'background: url(' + Status.BottomRightImage + '); height: ' + Status.CornerThickness + 'px; width: ' + Status.CornerThickness + 'px; position: absolute; right: 0; bottom: -' + Status.CornerThickness + 'px'});
    outer.insert(bottomRight);
    
    this.content = $div({'class': 'status_content'});
    outer.insert(this.content);
    
    this.spinner = $img({src: Status.SpinnerImage, width: Status.SpinnerImageWidth, height: Status.SpinnerImageHeight, alt: ''});
    this.status = $div()
    
    var table = $table({border: 0, cellpadding: 0, cellspacing: 0},
      $tr(
        $td(this.spinner),
        $td({style: 'padding-left: ' + Status.CornerThickness + 'px'}, this.status)
      )
    );
    this.content.insert(table);
    
    var body = $$('body').first();
    body.insert(this.element);
  },
  
  setStatus: function(value) {
    this.status.update(value)
  },
  
  getStatus: function() {
    return this.status.innerHTML();
  },
  
  show: function(options) {
    if (Prototype.Browser.IE) {
      // IE fixes
      var width = this.element.getWidth() - (Status.CornerThickness * 2);
      this.top.setStyle("width:" + width + "px");
      this.bottom.setStyle("width:" + width + "px");
    }
    if (options.hideSpinner) this.spinner.hide();
    else this.spinner.show();
    
    this.centerWindowInView();
    this.element.show();
  },
  
  hide: function() {
    this.element.hide();
  },
  
  toggle: function() {
    if (this.visible()) {
      this.hide();
    } else {
      this.show();
    }
  },
  
  visible: function() {
    return this.element.visible();
  },
  
  centerWindowInView: function() {
    var offsets = document.viewport.getScrollOffsets();
    this.element.setStyle({
      left: parseInt(offsets.left + (document.viewport.getWidth() - this.element.getWidth()) / 2) + 'px',
      top: parseInt(offsets.top + (document.viewport.getHeight() - this.element.getHeight()) / 2.2) + 'px'
    });
  }
});

// Setup the modal status window onload
Event.observe(window, 'load', function() {
  Status.window = new Status.Window();
});

// Sets the status to string
function setStatus(string) {
  Status.window.setStatus(string);
  if (Status.window.visible()) Status.window.centerWindowInView();
}

// Sets the status to string and shows the modal status window
function showStatus(string, options) {
  setStatus(string);
  Status.window.show(options || {});
}

// Hides the modal status window
function hideStatus() {
  Status.window.hide();
}

Event.addBehavior.reassignAfterAjax = true;
Event.addBehavior({
  'form[onsubmit_status]': Status.FormBehavior()
});

// rating_stars.js
//  
// dependencies: prototype.js, lowpro.js
//  
// ====================================
//  
// Rating stars is a javascript library to easily allow you to having a rating
// widget (or many rating widgets) on a page, and submit that rating back to a
// server via Ajax.
//  
// Usage
//  
//   <div id="widget"></div>
//    
//   <script type="text/javascript">
//     new RatingStars.Widget({
//       'holder': 'widget',
//       'url': '/url/for/submission'
//     });
//   </script>
//  
// or, to use the built in behavior just class a div with 'rating'
//  
//   <div class="rating" href="/url/for/submission"></div>
//  
// ====================================
//  
// Copyright (c) 2009, Steve Iannopollo
// http://github.com/siannopollo/rating_stars
//  
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following conditions:
//  
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//  
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
//  
// The stars for this widget were taken from the Silk icon collection
// (http://www.famfamfam.com/lab/icons/silk/) and used under the Creative
// Commons Attribution 2.5 License.


var RatingStars = {
  StarsImage: '/images/stars.png',
  StarFrameImage: '/images/star_frame.png',
  StarMaskImage: '/images/mask.png',
  height: 20,
  width: 104,
  padding: 10, // Padding on either side of the stars
  starWidth: 84 // The width taken up by the stars
}

RatingStars.Widget = Class.create({
  initialize: function(options) {
    var options = Object.extend({
      initialMessage: 'Click to submit',
      submittedMessage: 'Thanks for voting!',
      rating: 0,
      readOnly: false,
      parameters: {}
    }, options);
    
    this.holder = $(options.holder);
    this.widgetHolder = $span();
    this.messageHolder = $span(options.initialMessage);
    this.messageHolder.addClassName('message_holder');
    this.stars = $img({'src': RatingStars.StarsImage, 'height': RatingStars.height, 'width': RatingStars.width});
    this.frame = $img({'src': RatingStars.StarFrameImage, 'height': RatingStars.height, 'width': RatingStars.width});
    this.mask = $div();
    
    this.url = options.url
    this.rating = options.rating;
    this.message = options.initialMessage;
    this.submitted = options.readOnly;
    this.submittedMessage = options.submittedMessage;
    this.defaultParameters = options.parameters;
    
    this._placeElements();
    this._initializeStyles();
    
    if (this.rating > 0) this._updateMaskFromRating();
    
    if (!this.submitted) {
      this.widgetHolder.observe('mousemove', this.onmousemove.bind(this));
      this.widgetHolder.observe('mouseover', this.onmouseover.bind(this));
      this.widgetHolder.observe('mouseout', this.onmouseout.bind(this));
      this.widgetHolder.observe('click', this.onclick.bind(this));
    } else this._updateMessage();
  },
  
  onclick: function(event) {
    if (!this.submitted) {
      this.submitted = true;
      this.update(event.clientX, { updateMessage: false });
      
      new Ajax.Request(this.url, {
        method: 'post',
        evalJS: false,
        parameters: this.parameters(),
        onSuccess: this._finalize.bind(this),
        onComplete: this.onmouseout.bind(this)
      });
    };
  },
  
  onmousemove: function(event) {
    if (!this.submitted) this.update(event.clientX);
  },
  
  onmouseover: function(event) {
    if (!this.submitted) {
      this.holder.setStyle('cursor:pointer');
      this.stars.setStyle('opacity:1; filter:alpha(opacity=100)');
    };
  },
  
  onmouseout: function(event) {
    this.holder.setStyle('cursor:default');
    this.stars.setStyle('opacity:0.7; filter:alpha(opacity=70)');
  },
  
  parameters: function() {
    return Object.extend({ rating: this.rating }, this.defaultParameters);
  },
  
  update: function(x, options) {
    var options = Object.extend({ updateMessage: true }, options);
    
    var offsetX = x - this.holder.positionedOffset().left;
    this._updateMask(offsetX);
    this._updateRating(offsetX);
    if (options.updateMessage) this._updateMessage();
  },
  
  _placeElements: function() {
    this.widgetHolder.insert(this.stars);
    this.widgetHolder.insert(this.frame);
    this.widgetHolder.insert(this.mask);
    this.holder.insert(this.widgetHolder);
    this.holder.insert(this.messageHolder);
  },
  
  _initializeStyles: function() {
    this.holder.setStyle('position:relative');
    this.stars.setStyle('position:absolute; z-index:2; left:0; opacity:0.7; filter:alpha(opacity=70)');
    this.mask.setStyle(
      'display:inline; background:url(' + RatingStars.StarMaskImage + ') repeat;' +
      'height:' + RatingStars.height + 'px; width:0px; position:absolute; z-index:3; left:0px'
    );
    this.frame.setStyle('position:absolute; z-index:4; left:0');
    this.messageHolder.setStyle(
      'display:inline; height:' + RatingStars.height + 'px;' +
      'padding-left:' + RatingStars.width + 'px; font-size:70%; vertical-align:5%'
    );
  },
  
  _finalize: function(response) {
    this._updateMessage(response.responseText);
  },
  
  _updateMask: function(x) {
    var width = RatingStars.width - x;
    var left = x;
    this.mask.setStyle('width:' + width + 'px; left:' + left + 'px');
  },
  
  _updateMaskFromRating: function() {
    var calculatedClientX = Math.round(((this.rating / 100) * RatingStars.starWidth) + RatingStars.padding);
    this._updateMask(calculatedClientX);
  },
  
  _updateMessage: function(optionalMessage) {
    if (optionalMessage && !optionalMessage.blank()) {
      this.message = optionalMessage.strip();
    } else {
      if (this.submitted) this.message = this.submittedMessage + ' (' + this.rating + '%)';
      else this.message = this.rating + '% Rating';
    }
    
    this.messageHolder.innerHTML = this.message
  },
  
  _updateRating: function(x) {
    if (x <= RatingStars.padding) this.rating = 0;
    if (x > RatingStars.padding && (x < (RatingStars.width - RatingStars.padding)))
      this.rating = Math.round(((x - RatingStars.padding) / RatingStars.starWidth) * 100);
    if (x >= (RatingStars.width - RatingStars.padding)) this.rating = 100;
  }
})

var RatingStarsBehavior = Behavior.create({
  initialize: function() {
    var options = { 'holder': this.element };
    
    var url = this.element.getAttribute('href');
    var initialMessage = this.element.getAttribute('initial_message');
    var submittedMessage = this.element.getAttribute('submitted_message');
    var readOnly = this.element.getAttribute('read_only');
    var rating = this.element.getAttribute('rating');
    var parameters = this.element.getAttribute('parameters');
    
    if (url != null) options['url'] = url;
    if (initialMessage != null) options['initialMessage'] = initialMessage;
    if (submittedMessage != null) options['submittedMessage'] = submittedMessage;
    if (readOnly != null) options['readOnly'] = readOnly;
    if (rating != null) options['rating'] = rating;
    if (parameters != null) {
      var parametersHash = {};
      parameters.split('//').each(function(key_and_value) {
        var key = key_and_value.split(',').first()
        var value = key_and_value.split(',').last()
        parametersHash[key] = value;
      });
      options['parameters'] = parametersHash;
    }
    
    this.widget = new RatingStars.Widget(options);
  }
});

Event.addBehavior.reassignAfterAjax = true;
Event.addBehavior({
  'div.rating': RatingStarsBehavior()
});

var InputHintBehavior = Behavior.create({
  initialize: function() {
    this.hint = this.element.getAttribute('alt')
    if (this.element.getAttribute('type') == 'password') this.passwordField = true;
    this.originalColor = this.element.getStyle('color');
    if (this.originalColor.blank()) this.originalColor = '#000';
    
    this.onblur();
    if (!this.passwordField) this.element.up('form').observe('submit', this.onfocus.bind(this));
  },
  
  onblur: function() {
    if (this.element.getValue().blank()) {
      if (this.passwordField) this.element.setAttribute('type', 'text');
      this.element.setValue(this.hint);
      this.element.setStyle('font-style:italic; color:#999');
    }
  },
  
  onfocus: function() {
    if (this.element.getValue() == this.hint) {
      if (this.passwordField) this.element.setAttribute('type', 'password');
      this.element.setValue('');
      this.element.setStyle('font-style:inherit; color:' + this.originalColor)
    }
  }
});

if (Prototype.Browser.IE) {
  Event.addBehavior({
     'input[type="text"][alt], textarea[alt]': InputHintBehavior()
  });
} else {
  Event.addBehavior({
    'input[alt], textarea[alt]': InputHintBehavior()
  });
}


/*
 *  popup.js
 *
 *  dependencies: prototype.js, effects.js, lowpro.js
 *
 *  --------------------------------------------------------------------------
 *  
 *  Allows you to open up a URL inside of a Facebook-style window. To use
 *  simply assign the class "popup" to a link that contains an href to the
 *  HTML snippet that you would like to load up inside a window:
 *  
 *    <a class="popup" href="window.html">Window</a>
 *
 *  You can also "popup" a specific div by referencing it by ID:
 *
 *    <a class="popup" href="#my_div">Popup</a>
 *    <div id="my_div" style="display:none">Hello World!</div>
 *  
 *  You will need to install the following hook:
 *  
 *    Event.addBehavior({'a.popup': Popup.TriggerBehavior()});
 *
 *  --------------------------------------------------------------------------
 *  
 *  Copyright (c) 2008, John W. Long
 *  Portions copyright (c) 2008, Five Points Solutions, Inc.
 *  
 *  Permission is hereby granted, free of charge, to any person obtaining a
 *  copy of this software and associated documentation files (the "Software"),
 *  to deal in the Software without restriction, including without limitation
 *  the rights to use, copy, modify, merge, publish, distribute, sublicense,
 *  and/or sell copies of the Software, and to permit persons to whom the
 *  Software is furnished to do so, subject to the following conditions:
 *  
 *  The above copyright notice and this permission notice shall be included in
 *  all copies or substantial portions of the Software.
 *  
 *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 *  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 *  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
 *  THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 *  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 *  FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 *  DEALINGS IN THE SOFTWARE.
 *  
 */

var Popup = {
  BorderThickness: 8,
  BorderImage: '/images/popup_border_background.png',
  BorderTopLeftImage: '/images/popup_border_top_left.png',
  BorderTopRightImage: '/images/popup_border_top_right.png',
  BorderBottomLeftImage: '/images/popup_border_bottom_left.png',
  BorderBottomRightImage: '/images/popup_border_bottom_right.png'
};

Popup.borderImages = function() {
  return $A([
    Popup.BorderImage,
    Popup.BorderTopLeftImage,
    Popup.BorderTopRightImage,
    Popup.BorderBottomLeftImage,
    Popup.BorderBottomRightImage
  ]);
}

Popup.preloadImages = function() {
  if (!Popup.imagesPreloaded) {
    Popup.borderImages().each(function(src) {
      var image = new Image();
      image.src = src;
    });
    Popup.preloadedImages = true;
  }
}

Popup.TriggerBehavior = Behavior.create({
  initialize: function() {
    var href = this.element.getAttribute('href');
    var matches = href.match(/\#(.+)$/);
    if (matches) {
      this.window = new Popup.Window($(matches[1]));
    } else {
     this.window = new Popup.AjaxWindow(href);
    }
    if (this.element.hasClassName('now')) this.window.show();
  },
  
  onclick: function(event) {
    this.popup();
    event.stop();
  },
  
  popup: function() {
    this.window.show();
  }
});

Popup.AbstractWindow = Class.create({
  initialize: function() {
    Popup.preloadImages();
    this.buildWindow();
  },
  
  buildWindow: function() {
    this.element = new Element('table', {className: 'popup_window', style: 'display: none; position: absolute; border-collapse: collapse; padding: 0px; margin: 0px;'});
    var tbody = new Element('tbody');
    this.element.insert(tbody)
    
    var top_row = $tr();
    top_row.insert($td({style: 'background: url(' + Popup.BorderTopLeftImage + '); height: ' + Popup.BorderThickness + 'px; width: ' + Popup.BorderThickness + 'px; padding: 0px'}));
    top_row.insert($td({style: 'background: url(' + Popup.BorderImage + '); height: ' + Popup.BorderThickness + 'px; padding: 0px'}))
    top_row.insert($td({style: 'background: url(' + Popup.BorderTopRightImage + '); height: ' + Popup.BorderThickness + 'px; width: ' + Popup.BorderThickness + 'px; padding: 0px'}));
    tbody.insert(top_row);
    
    var content_row = $tr();
    content_row.insert($td({style: 'background: url(' + Popup.BorderImage + '); width: ' + Popup.BorderThickness + 'px; padding: 0px'}, ''));
    this.content = $td({style: 'background-color: white; padding: 0px'});
    content_row.insert(this.content);
    content_row.insert($td({style: 'background: url(' + Popup.BorderImage + '); width: ' + Popup.BorderThickness + 'px; padding: 0px'}, ''));
    tbody.insert(content_row);
    
    var bottom_row = $tr();
    bottom_row.insert($td({style: 'background: url(' + Popup.BorderBottomLeftImage + '); height: ' + Popup.BorderThickness + 'px; width: ' + Popup.BorderThickness + 'px; padding: 0px'}));
    bottom_row.insert($td({style: 'background: url(' + Popup.BorderImage + '); height: ' + Popup.BorderThickness + 'px; padding: 0px'}))
    bottom_row.insert($td({style: 'background: url(' + Popup.BorderBottomRightImage + '); height: ' + Popup.BorderThickness + 'px; width: ' + Popup.BorderThickness + 'px; padding: 0px'}));
    tbody.insert(bottom_row);

    var body = $$('body').first();
    body.insert(this.element);
  },
  
  makeDraggable: function() {
    if (!this.draggable)
      this.draggable = new Draggable(this.element.identify(), {handle: 'h3.title', scroll: window});
  },
  
  show: function() {
    this.beforeShow();
    this.element.show();
    this.content.select('*').each(function(element) {
      element.toggleClassName('render');
    });
    this.afterShow();
  },
  
  hide: function() {
    this.element.hide();
    if (this.draggable) {
      this.draggable.destroy();
      this.draggable = null;
    }
  },
  
  toggle: function() {
    if (this.element.visible()) {
      this.hide();
    } else {
      this.show();
    }
  },
  
  focus: function() {
    var form = this.element.down('form');
    if (form) {
      var elements = form.getElements().reject(function(e) { return e.type == 'hidden' });
      var element = elements[0] || form.down('button');
      if (element) element.focus();
    }
  },
  
  beforeShow: function() {
    this.centerWindowInView();
  },
  
  afterShow: function() {
    if (this.element.down('.popup.draggable')) this.makeDraggable();
    this.focus();
  },

  centerWindowInView: function() {
    var offsets = document.viewport.getScrollOffsets();
    this.element.setStyle({
      left: parseInt(offsets.left + (document.viewport.getWidth() - this.element.getWidth()) / 2) + 'px',
      top: parseInt(offsets.top + (document.viewport.getHeight() - this.element.getHeight()) / 2.2) + 'px'
    });
  }
});

Popup.Window = Class.create(Popup.AbstractWindow, {
  initialize: function($super, element, options) {
    $super(options);
    element.remove();
    this.content.update(element);
    element.show();
  }
});

Popup.AjaxWindow = Class.create(Popup.AbstractWindow, {
  initialize: function($super, url, options) {
    $super();
    options = Object.extend({reload: true}, options);
    this.url = url;
    this.reload = options.reload;
  },
  
  show: function($super) {
    if (!this.loaded || this.reload) {
      new Ajax.Updater(this.content, this.url, {asynchronous: false, method: "get", evalScripts: true, onComplete: $super});
      this.loaded = true;
    } else {
      $super();
    }
  }
});

Popup.Alert = Class.create(Popup.AbstractWindow, {
  initialize: function($super, message, options) {
    $super();
    this.options = Object.extend({
      title: 'Alert'
    }, (options || {}));
    this.message = message;
  },
  
  beforeShow: function($super) {
    var titleBar = $h3({'class':'title'}, this.options.title);
    var buttonBar = $div({'class':'buttons'});
    var popup = $div({'class':'popup'},
      titleBar,
      $div({'class':'popup_content'},
        $p(this.message),
        buttonBar
      )
    );
    this.content.insert(popup);
    
    this.okButton = $a({href:'#ok'}, 'OK');
    this.okButton.observe('click', this.close.bindAsEventListener(this));
    buttonBar.insert(this.okButton);
    
    if (this.options.beforeShow) this.options.beforeShow(this);
    $super();
  },
  
  close: function(event) {
    this.element.remove();
    event.stop();
  }
});
Popup.alert = function(message, options) {
  new Popup.Alert(message, options).show();
};

// Element extensions
Element.addMethods({
  closePopup: function(element) {
    $(element).up('table.popup_window').hide();
    return $(element);
  }
});

Event.addBehavior.reassignAfterAjax = true;
Event.addBehavior({
  'a.popup, button.popup': Popup.TriggerBehavior()
});

// Place your application-specific JavaScript functions and classes here
// This file is automatically included by javascript_include_tag :defaults

Element.addMethods({
  fadeIn: function(element) {
    if (element.visible()) return;
    
    element.setOriginal('opacity', element.getStyle('opacity'));
    
    var effect = new Fx.Style(element, 'opacity', {
      onComplete: function() {
        element.setStyle('display:block; opacity:' + element.originalOpacity);
      },
      duration: 100
    });
    effect.set(0);
    effect.custom(0, element.originalOpacity);
  },
  
  fadeOut: function(element) {
    if (!element.visible()) return;
    
    element.setOriginal('opacity', element.getStyle('opacity'));
    
    var effect = new Fx.Style(element, 'opacity', {
      onComplete: function() {
        element.setStyle('display:none; opacity:' + element.originalOpacity);
      },
      duration: 100
    });
    effect.set(element.originalOpacity);
    effect.custom(element.originalOpacity, 0);
  },
  
  // Rotate elements, in the format of element.rotate({from: angle1, to: angle2})
  rotate: function(element, options) {
    var from = options.from || 0,
        to = options.to;
    
    var interval = setInterval(function() {
      if (from < to) {
        from += 3
        if (from >= to) clearInterval(interval);
      } else {
        from -= 3
        if (from <= to) clearInterval(interval);
      }
      element.setStyle('-moz-transform:rotate(' + from + 'deg); -webkit-transform:rotate(' + from + 'deg)');
    }, 10)
  },
  
  setRotate: function(element, angle) {
    element.setStyle('-moz-transform:rotate(' + angle + 'deg); -webkit-transform:rotate(' + angle + 'deg)');
  },
  
  // Show elements in a smooth fashion
  slideDown: function(element, options) {
    if (element.visible()) return;
    
    options = options || {};
    element.setOriginal('height', element.getHeight());
    element.setOriginal('overflow', element.getStyle('overflow'));
    
    var effect = new Fx.Style(element, 'height', {
      duration: 500 + (element.originalHeight / 6),
      onStart: function() {
        element.setStyle('height:0px; display:block; overflow:hidden');
        if (options.onStart) options.onStart();
      },
      onComplete: function() {
        element.setStyle('overflow:' + element.originalOverflow);
        if (options.onComplete) options.onComplete();
      }
    });
    effect.set(0);
    effect.custom(0, element.originalHeight);
  },
  
  slideToggle: function(element, options) {
    if (element.visible()) element.slideUp(options);
    else element.slideDown(options);
  },
  
  // Hide elements in a smooth fashion
  slideUp: function(element, options) {
    if (!element.visible()) return;
    
    options = options || {};
    element.setOriginal('height', element.getHeight());
    element.setOriginal('overflow', element.getStyle('overflow'));
    
    var effect = new Fx.Style(element, 'height', {
      duration: 500 + (element.originalHeight / 6),
      onStart: function() {
        element.setStyle('overflow:hidden');
        if (options.onStart) options.onStart();
      },
      onComplete: function() {
        element.setStyle('display:none; overflow:' + element.originalOverflow);
        if (options.onComplete) options.onComplete();
      }
    });
    effect.set(element.originalHeight);
    effect.custom(element.originalHeight, 0);
  },
  
  setOriginal: function(element, property, value) {
    if (!element['original' + property.capitalize()]) element['original' + property.capitalize()] = value;
  }
})

var Timezone = {
  set : function() {
    var date = new Date();
    var timezone = "timezone=" + -date.getTimezoneOffset() * 60;
    date.setTime(date.getTime() + (1000*24*60*60*1000));
    var expires = "; expires=" + date.toGMTString();
    document.cookie = timezone + expires + "; path=/";
  }
}
Event.observe(document, 'dom:loaded', function() { Timezone.set() });