/*
Script: extensions.js
  
Copyright:
  copyright (c) 2007 wollzelle GmbH, <http://www.wollzelle.com>

Group: extensions
  collects several helper functions to make work with the DOM easier
*/

Object.callAllMethods = function(object) {
  $each(object, function(method) {
    if ($type(method) == 'function') method();
  });
}

document.redraw = function() { //ugly, but it works, Safari2 only
  window.resizeBy(-1,0);
  window.resizeBy(1,0);
}

Array.implement({
  unique: function() {
    var o = {};
    for (var i = 0 ; i < this.length; i++)
    o[this[i]] = true;
    var tmp = new Array();
    for (var i in o) tmp[tmp.length] = i;
    return tmp;
  }
});

String.implement({
  truncate: function(length, truncation) {
    length = length || 30;
    truncation = truncation === undefined ? '...' : truncation;
    return this.length > length ? this.slice(0, length - truncation.length) + truncation : String(this);
  },
  
  toDateString: function() {
    date = new Date(this);
    var day = (date.getDate() < 10) ? '0'+date.getDate() : date.getDate();
    var month = (date.getMonth()+1 < 10) ? '0'+(date.getMonth()+1) : date.getMonth()+1;
    return day + '.' + month + '.' + date.getFullYear();
  },
  
  toHumanString: function() {
    date = new Date(this);
    today = new Date();
    
    var day = date.getDate() + '.';
    var month = Date.$months[date.getMonth()];
    if(date.compare(today) == 0)
      return 'Heute';
    else
      return day + ' ' + month + ' ' + date.getFullYear();
  },
  isValidDate: function(format){
    var dateStr = this.toString();
    if (format == null) { format = "DMY"; }
    
    format = format.toUpperCase();
    if (format.length != 3) { format = "MDY"; }
    if ( (format.indexOf("M") == -1) || (format.indexOf("D") == -1) || (format.indexOf("Y") == -1) ) { format = "MDY"; }
    if (format.substring(0, 1) == "Y") { // If the year is first
      var reg1 = /^\d{2}(\-|\/|\.)\d{1,2}\1\d{1,2}$/
      var reg2 = /^\d{4}(\-|\/|\.)\d{1,2}\1\d{1,2}$/
    } else if (format.substring(1, 2) == "Y") { // If the year is second
      var reg1 = /^\d{1,2}(\-|\/|\.)\d{2}\1\d{1,2}$/
      var reg2 = /^\d{1,2}(\-|\/|\.)\d{4}\1\d{1,2}$/
    } else { // The year must be third
      var reg1 = /^\d{1,2}(\-|\/|\.)\d{1,2}\1\d{2}$/
      var reg2 = /^\d{1,2}(\-|\/|\.)\d{1,2}\1\d{4}$/
    }
    // If it doesn't conform to the right format (with either a 2 digit year or 4 digit year), fail
    if ( (reg1.test(dateStr) == false) && (reg2.test(dateStr) == false) ) { return false; }
    var parts = dateStr.split(RegExp.$1); // Split into 3 parts based on what the divider was
    // Check to see if the 3 parts end up making a valid date
    if (format.substring(0, 1) == "M") { var mm = parts[0]; } else 
    if (format.substring(1, 2) == "M") { var mm = parts[1]; } else { var mm = parts[2]; }
    if (format.substring(0, 1) == "D") { var dd = parts[0]; } else 
    if (format.substring(1, 2) == "D") { var dd = parts[1]; } else { var dd = parts[2]; }
    if (format.substring(0, 1) == "Y") { var yy = parts[0]; } else 
    if (format.substring(1, 2) == "Y") { var yy = parts[1]; } else { var yy = parts[2]; }
    if (parseFloat(yy) <= 50) { yy = (parseFloat(yy) + 2000).toString(); }
    if (parseFloat(yy) <= 99) { yy = (parseFloat(yy) + 1900).toString(); }
    var dt = new Date(parseFloat(yy), parseFloat(mm)-1, parseFloat(dd), 0, 0, 0, 0);
    if (parseFloat(dd) != dt.getDate()) { return false; }
    if (parseFloat(mm)-1 != dt.getMonth()) { return false; }
    return true;
  },
  isValidTime: function(){
    var re = /^\d{1,2}[:]\d{2}([:]\d{2})?( [aApP][mM]?)?$/;
    if (!re.test(this)) { return false; }
    var values = this.split(":");
    if ( (parseFloat(values[0]) < 0) || (parseFloat(values[0]) > 23) ) { return false; }
    if ( (parseFloat(values[1]) < 0) || (parseFloat(values[1]) > 59) ) { return false; }
    return true;
  }
});


Number.implement({

	/*
	Property: numberFormat
		Format a number with grouped thousands.

	Arguments:
		decimals, optional - integer, number of decimal percision; default, 2
		dec_point, optional - string, decimal point notation; default, '.'
		thousands_sep, optional - string, grouped thousands notation; default, ','

	Returns:
		a formatted version of number.

	Example:
		>(36432.556).numberFormat()  // returns 36,432.56
		>(36432.556).numberFormat(2, '.', ',')  // returns 36,432.56
	*/

	numberFormat : function(decimals, dec_point, thousands_sep) {
		decimals = Math.abs(decimals) + 1 ? decimals : 2;
		dec_point = dec_point || '.';
		thousands_sep = thousands_sep || ',';

		var matches = /(-)?(\d+)(\.\d+)?/.exec((isNaN(this) ? 0 : this) + ''); // returns matches[1] as sign, matches[2] as numbers and matches[2] as decimals
		var remainder = matches[2].length > 3 ? matches[2].length % 3 : 0;
		return (matches[1] ? matches[1] : '') + (remainder ? matches[2].substr(0, remainder) + thousands_sep : '') + matches[2].substr(remainder).replace(/(\d{3})(?=\d)/g, "$1" + thousands_sep) + 
				(decimals ? dec_point + (+matches[3] || 0).toFixed(decimals).substr(2) : '');
	},
  
  toDateString: function() {
    date = new Date(this);
    var day = (date.getDate() < 10) ? '0'+date.getDate() : date.getDate();
    var month = (date.getMonth()+1 < 10) ? '0'+(date.getMonth()+1) : date.getMonth()+1;
    return day + '.' + month + '.' + date.getFullYear();
  },
  
  toHumanString: function() {
    date = new Date(this);
    today = new Date();
    
    var day = date.getDate() + '.';
    var month = Date.$months[date.getMonth()];
    if(date.compare(today) == 0)
      return 'Heute';
    else
      return day + ' ' + month + ' ' + date.getFullYear();
  }

});

// TODO: Safari 2 rendering bug:
// extend Fx.Base Class with an onAfterIncrease method which calls document.redraw

Element.implement({
    deactivate: function() {
        if (this.hasDeactivator) {
            this.hasDeactivator.show();
        }
        else {
            var dims = this.getCoordinates();
            this.hasDeactivator = new Element('div').setStyles({
                'top': this.offsetTop,
                'left': this.offsetLeft,
                'width': dims.width,
                'height': dims.height,
                'background': '#fff',
                'z-index': 1000000,
                'position': 'absolute'
            }).setOpacity(0.7).injectInside(this);
        }
    },
    activate: function() {
        if (!this.hasDeactivator)
            return;
        else
            this.hasDeactivator.hide();
    },
    hide: function() {
        this.setStyle('display', 'none');
        return this;
    },
    show: function() {
        this.setStyle('display', '');
        return this;
    },
    toggle: function() {
        this[this.isVisible() ? 'hide' : 'show']();
        return this;
    },
    isVisible: function() {
        return this.getStyle('display') != 'none';
    },
    isVisibleChild: function(parent) {
        var chk = this;
        var child = this;
        while ((chk = chk.parentNode) && (chk != parent) && (chk.parentNode)) {
            if (child.offsetTop > (chk.offsetTop + chk.offsetHeight))
                return false;
            child = chk;
        };
        return (chk == parent);
    },
    getHeight: function() {
        return this.getSize().y;
        //return this.getStyle('height').toInt();
    },
    getWidth: function() {
        return this.getSize().x;
        //return this.getStyle('width').toInt();
    },
    getRelativePosition: function(relative) {
        var offset = this.getOffsets();
        var scroll = this.getScrolls();
        //console.log(offset);
        //console.log(scroll);		
        var position = { x: offset.x - scroll.x, y: offset.y - scroll.y };
        var relativePosition = $(document.body).getPosition();
        relativeDelta = { x: relativePosition.x, y: relativePosition.y };
        if (Browser.Engine.trident) {
            var relativeScrolls = (relative && (relative = $(relative))) ? relative.getScrolls() : { x: 0, y: 0 };
            relativeDelta = {
                x: relativePosition.x - relativeScrolls.x,
                y: relativePosition.y - relativeScrolls.y
            }
        }
        return { x: position.x - relativeDelta.x, y: position.y - relativeDelta.y };
    },
    shake: function(amount) {
        var start = this.getStyle('margin-left').toInt();
        var amount = amount;

        if (!amount) amount = 20;

        var f = new Fx.Tween(this, { duration: 80 });

        f.start('margin-left', start, start + amount).chain(function() {
            f.start('margin-left', start + amount, start - amount);
        }).chain(function() {
            f.start('margin-left', start - amount / 2, start + amount / 2);
        }).chain(function() {
            f.start('margin-left', start + amount / 4, start - amount / 4);
        }).chain(function() {
            f.start('margin-left', start - amount / 8, start + amount / 8);
        }).chain(function() {
            f.start('margin-left', start + amount / 8, start);
        });
    },
    nod: function(amount) {
        var start = this.getStyle('margin-top').toInt();
        var amount = amount;

        if (!amount) amount = 20;

        var a = start - amount;
        var b = start + amount;
        var f = this.effect('margin-top', { duration: 250 });
        
        f.start(start, b).chain(function() {
            f.start(b, a);
        }).chain(function() {
            f.start(a, b);
        }).chain(function() {
            f.start(b, start);
        });
    },
    enable: function() {
        this.disabled = false;
        return this;
    },
    disable: function() {
        this.disabled = true;
        return this;
    },
	setFocus: function(index) {
		this.setAttribute('tabIndex',index || 0);
		this.focus();
	}
});

// Numberic input handler
// Allows only number values to be entered in inputs with "num-only" or "time-only" classname
(function() {
  document.addEvents({
    'keyup': function(event){
      var input = $(event.target);
      if(input.tagName.capitalize() != 'INPUT' || !input.hasClass('num-only')) return;
      
      var oldValue = '';
      var value = input.get('value').toInt();
      input.set('value', $type(value) ? value : oldValue);
    }
  });
})();

/*
Function: $ES
	Returns a collection of Elements that match the selector passed in limited to the scope of the optional filter.
	See Also: <Element.getElements> for an alternate syntax.
	Returns as <Elements>.

Returns:
	an array of dom elements that match the selector within the filter

Arguments:
	selector - string; css selector to match
	filter - optional; a DOM element to limit the scope of the selector match; defaults to document.

Examples:
	>$ES("a") //gets all the anchor tags; synonymous with $$("a")
	>$ES('a','myElement') //get all the anchor tags within $('myElement')
*/
function $ES(selector, filter){
	return ($(filter) || document).getElements(selector);
};

/*
Function: $E
	Selects a single (i.e. the first found) Element based on the selector passed in and an optional filter element.
	Returns as <Element>.

Arguments:
	selector - string; the css selector to match
	filter - optional; a DOM element to limit the scope of the selector match; defaults to document.

Example:
	>$E('a', 'myElement') //find the first anchor tag inside the DOM element with id 'myElement'

Returns:
	a DOM element - the first element that matches the selector
*/

function $E(selector, filter){
	return ($(filter) || document).getElement(selector);
};

Event.implement({
  findElement: function(expression) {
    var element = $(this.event.target);
    if (!expression) return element;
    var elements = [element].concat(element.getParents(expression));
    return elements.map(function(el){
      return el.match(expression) ? el : null;
    }).getLast();
  }
});

/*
Fx.Morph = Fx.Styles.extend({
	start: function(className) {
    var to = {};
 
		$each(document.styleSheets, function(style) {
			var rules = style.rules || style.cssRules;
			$each(rules, function(rule) {
				if (!rule.selectorText.test('\.' + className + '$')) return;
				Fx.CSS.Styles.each(function(style) {
					if (!rule.style || !rule.style[style]) return;
					var ruleStyle = rule.style[style];
					to[style] = (style.test(/color/i) && ruleStyle.test(/^rgb/)) ? ruleStyle.rgbToHex() : ruleStyle;
				});
			});
		});
		return this.parent(to);
	}
});
 
Fx.CSS.Styles = [
  "backgroundColor", "backgroundPosition", "color", "width", "height", "left", "top", "bottom", "right", 
  "fontSize", "letterSpacing", "lineHeight", "textIndent", "opacity"
];
 
Fx.CSS.Styles.extend(Element.Styles.padding);
Fx.CSS.Styles.extend(Element.Styles.margin);
 
Element.Styles.border.each(function(border) {
	['Width', 'Color'].each(function(property) {
		Fx.CSS.Styles.push(border + property);
	});
});
*/
window.getInnerDimensions = function() {
  if (self.innerHeight) 
    return { height: self.innerHeight, width: self.innerWidth }; // != IE
  else if (document.documentElement && document.documentElement.clientHeight) // IE7/IE6 Strict
    return { height: document.documentElement.clientHeight, width: document.documentElement.clientWidth };
  else if (document.body) // IE
    return { height: document.body.clientHeight, width: document.body.clientWidth };
}

