/*  Extensions for the Prototype JavaScript framework
 *  (c) 2006, misc. developers
 *
 *  Collection of extra methods that extend the basic behavior of the library.
 *  Prototype is freely distributable under the terms of an MIT-style license.
 *  For details, see the Prototype web site: http://prototype.conio.net/
 *
/*--------------------------------------------------------------------------*/

(function() {
    if((typeof Prototype=='undefined') || 
       (typeof Element == 'undefined') || 
       (typeof Element.Methods=='undefined') ||
       parseFloat(Prototype.Version.split(".")[0] + "." +
                  Prototype.Version.split(".")[1]) < 1.5)
       throw("This requires the Prototype JavaScript framework >= 1.5.0");
})();

// Ensure that Node constants are available (for IE)
if (typeof(window.Node) == 'undefined') {
	window.Node = {
		/* Node types */
		ELEMENT_NODE: 1,
		ATTRIBUTE_NODE: 2,
		TEXT_NODE: 3,
		CDATA_SECTION_NODE: 4,
		ENTITY_REFERENCE_NODE: 5,
		ENTITY_NODE: 6,
		PROCESSING_INSTRUCTION_NODE: 7,
		COMMENT_NODE: 8,
		DOCUMENT_NODE: 9,
		DOCUMENT_TYPE_NODE: 10,
		DOCUMENT_FRAGMENT_NODE: 11,
		NOTATION_NODE: 12,

		DOCUMENT_POSITION_DISCONNECTED: 1,
		DOCUMENT_POSITION_PRECEDING: 2,
		DOCUMENT_POSITION_FOLLOWING: 4,
		DOCUMENT_POSITION_CONTAINS: 8,
		DOCUMENT_POSITION_CONTAINED_BY: 16,
		DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC: 32
	};
}

/*--------------------------------------------------------------------------*/

// Position.getPageSize()
// Returns object with page width, height and window width, height
// Core code from http://quirksmode.org/

Object.extend(Position, {
	getPageSize: function() {
		var xScroll, yScroll;

		if (window.innerHeight && window.scrollMaxY) {  
			xScroll = document.body.scrollWidth;
			yScroll = window.innerHeight + window.scrollMaxY;
		} else if (document.body.scrollHeight > document.body.offsetHeight) { // all but Explorer Mac
			xScroll = document.body.scrollWidth;
			yScroll = document.body.scrollHeight;
		} else { // Explorer Mac...would also work in Explorer 6 Strict, Mozilla and Safari
			xScroll = document.body.offsetWidth;
			yScroll = document.body.offsetHeight;
		}

		var windowWidth, windowHeight;
		if (self.innerHeight) { // all except Explorer
			windowWidth = self.innerWidth;
			windowHeight = self.innerHeight;
		} else if (document.documentElement && document.documentElement.clientHeight) { // Explorer 6 Strict Mode
			windowWidth = document.documentElement.clientWidth;
			windowHeight = document.documentElement.clientHeight;
		} else if (document.body) { // other Explorers
			windowWidth = document.body.clientWidth;
			windowHeight = document.body.clientHeight;
		} 

		// for small pages with total height less then height of the viewport
		pageHeight = Math.max(windowHeight, yScroll);

		// for small pages with total width less then width of the viewport
		pageWidth = Math.max(windowWidth, xScroll);

		return { page: { width: pageWidth, height: pageHeight }, window: { width: windowWidth, height: windowHeight } };
	},

	// Set the inner size of the current window
	setWindowSize: function(width, height) {
		var size = Position.getPageSize();
		
		// Where the window is positioned on the screen
		var winPositionX = self.screenLeft || self.screenX;
		var winPositionY = self.screenTop || self.screenY;	
		
		// The size of thw windows
		var dWidth = width - size.window.width;	
		var dHeight = height - size.window.height;	

        // How much space are available
        var screenWidth = self.screen.availWidth;
        
        // Do the window exceed the screen Boundaries
        var heightExceeded = (winPositionY + height) - self.screen.availHeight;
        if( heightExceeded > 0 )
        {
            var chromeHeight = 40; // approximated chrome height
            window.moveBy(0, -1 * (heightExceeded + chromeHeight));
            
            // No support for the positioning the window on Y axel, cause there are some issues!!
        }
							
		window.resizeBy(dWidth, dHeight);
	}
});

/*--------------------------------------------------------------------------*/

// Event.onDOMReady()
// Source: http://www.vivabit.com/bollocks/2006/06/21/a-dom-ready-extension-for-prototype

Object.extend(Event, {
  _domReady : function() {
    if (arguments.callee.done) return;
    arguments.callee.done = true;

    if (this._timer)  clearInterval(this._timer);
    
    this._readyCallbacks.each(function(f) { f() });
    this._readyCallbacks = null;
  },
  onDOMReady : function(f) {
    if (!this._readyCallbacks) {
      var domReady = this._domReady.bind(this);

      if (document.addEventListener)
        document.addEventListener("DOMContentLoaded", domReady, false);

      /*@cc_on @*/
      /*@if (@_win32)
        document.write("<script id=__ie_onload defer src=javascript:void(0)><\/script>");
        document.getElementById("__ie_onload").onreadystatechange = function() {
          if (this.readyState == "complete") domReady();
        };
      /*@end @*/

      if (/WebKit/i.test(navigator.userAgent)) { 
        this._timer = setInterval(function() {
          if (/loaded|complete/.test(document.readyState)) domReady(); 
        }, 10);
      }

      Event.observe(window, 'load', domReady);
      this._readyCallbacks =  [];
    }
    this._readyCallbacks.push(f);
  }
});

/*--------------------------------------------------------------------------*/

// Decorator pattern / Aspect oriented programming.
// Modified from:
// http://beppu.lbox.org/articles/2006/08/22/the-decorator-pattern-for-javascript
// http://beppu.lbox.org/articles/2006/09/06/actsasaspect

var Aspect = new Object();
Aspect.decorate = function(object) {
  object.yield = null;
  object.rv = { };

  object.before = function(method, f) {
    var original = this[method];
    this[method] = function() {
      f.apply(this, arguments);
      return original.apply(this, arguments);
    };
  };

  object.after = function(method, f) {
    var original = this[method];
    this[method] = function() {
      this.rv[method] = original.apply(this, arguments);
      return f.apply(this, arguments);
    }
  };

  object.around = function(method, f) {
    var original = this[method];
    this[method] = function() {
      this.yield = original;
      return f.apply(this, arguments);
    }
  };

  // [thm] return the object to enable chain of control
  return object;
}

/*--------------------------------------------------------------------------*/

// Harald Martin Ström » Rails’ time extensions ported to JavaScript
// Source: http://burnfield.com/martin/2006/09/15/rails-time-extensions-ported-to-javascript

Object.extend(Number.prototype, {
  seconds: function() {
    return this * 1000;
  },
  
  minutes: function() {
    return this * (60).seconds();
  },
  
  hours: function() {
    return this * (60).minutes();
  },
  
  days: function() {
    return this * (24).hours();
  },
  
  weeks: function(args) {
    return this * (7).days();
  },
  
  fortnights: function() {
    return this * (2).weeks();
  },
  
  months: function() {
    return this * (30).days();
  },
  
  years: function() {
    return parseInt(this * (365.25).days())
  },
  
  since: function(time) {
    time = time || new Date();
    if (time instanceof Date) time = time.getTime();
    return this + time;
  },
  
  ago: function(time) {
    time = time || new Date();
    if (time instanceof Date) time = time.getTime();
    return time - this;
  },
  
  toDate: function() {
    var date = new Date();
    date.setTime(this);
    return date;
  }
});

// Aliases
Object.extend(Number.prototype, {
  second:    Number.prototype.seconds,
  minute:    Number.prototype.minutes,
  hour:      Number.prototype.hours,
  day:       Number.prototype.days,
  week:      Number.prototype.weeks,
  fortnight: Number.prototype.fortnights,
  month:     Number.prototype.months,
  year:      Number.prototype.years,
  from_now:  Number.prototype.since,
  fromNow:   Number.prototype.since,
  until:     Number.prototype.ago
});

/*--------------------------------------------------------------------------*/

// Number percentage and 'human size' implemented in javascript - Rails Weenie
// Original Source: http://rails.techno-weenie.net/tip/2006/5/21/number_percentage_and_human_size_implemented_in_javascript
// Modified by THM 2007
// *Note:* You have to use (1).megabyte() as opposed to just 1.megabyte()

['byte', 'kilobyte', 'megabyte', 'gigabyte', 'terabyte', 'petabyte', 'exabyte'].inject(null, function(prev, meth) {
  var mul = (prev) ? prev.apply(1024) : 1;
  return Number.prototype[meth] = Number.prototype[meth+'s'] = function() { return this * mul; };
});

Number.prototype.toPrecision = function() {
  var precision = arguments[0] || 2;
  var s         = Math.round(this * Math.pow(10, precision)).toString();
  var pos       = s.length - precision;
  var last      = s.substr(pos, precision);
  return s.substr(0, pos) + (last.match("^0{" + precision + "}$") ? '' : '.' + last);
}

// (1/10).toPercentage()
// # => '10%'
Number.prototype.toPercentage = function() {
  return (this * 100).toPrecision() + '%';
}

Number.prototype.toHumanSize = function() {
  if(this < (1).kilobyte())  return this + " Bytes";
  if(this < (1).megabyte())  return (this / (1).kilobyte()).toPrecision()  + ' KB';
  if(this < (1).gigabytes()) return (this / (1).megabyte()).toPrecision()  + ' MB';
  if(this < (1).terabytes()) return (this / (1).gigabytes()).toPrecision() + ' GB';
  if(this < (1).petabytes()) return (this / (1).terabytes()).toPrecision() + ' TB';
  if(this < (1).exabytes())  return (this / (1).petabytes()).toPrecision() + ' PB';
                             return (this / (1).exabytes()).toPrecision()  + ' EB';
}

/*--------------------------------------------------------------------------*/

/**
* Return the value of the given radio button
* @param mixed frm form ID or object reference
* @param mixed name radio name (value in name="...")
* @return mixed null if no radio button is selected
*/
function $FR(frm, name)
{
  var rb = $(frm).getInputs('radio', name).find(function(obj) { return obj.checked; });
  return rb ? $F(rb) : null;
}
