/**
 * fusionLib JavaScript library implementing methods for manipulating style.
 *
 * THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED
 * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
 *
 * Copyright (c) 2007 - 2016 fusionLib. All rights reserved.
 */

(function() {
	/**
	 * Private function to trim white space from the start and end of a string.
	 *
	 * @param string s The string to trim.
	 * @return string The trimmed string.
	 */
	function trim(s) {
		return s.replace(/^\s*|\s*$/g, '');
	}

	/**
	 * Get the class for the element.
	 *
	 * @param DOMElement elem The element.
	 * @returns string The elements class.
	 */
	function getClass(elem) {
		return elem.getAttribute && elem.getAttribute('class') || '';
	}

	fusionLib.extend({
		/**
		 * Add a CSS class to the elements.
		 *
		 * @param string cls The class to add.
		 * @return fusionLib This object.
		 */
		addClass: function(cls) {
			r = new RegExp('\\s*' + cls + '\\s*');
			return this.each(function() {
				this.setAttribute('class', trim(getClass(this).replace(r, ' ') + ' ' + cls));
			});
		},

		/**
		 * Remove a CSS class from the elements.
		 *
		 * @param string cls The class to remove.
		 * @return fusionLib This object.
		 */
		removeClass: function(cls) {
			r = new RegExp('\\s*' + cls + '\\s*');
			return this.each(function() {
				this.setAttribute('class', trim(getClass(this).replace(r, ' ')));
			});
		},

		/**
		 * Test if any element has the given class.
		 *
		 * @param string cls The class to test for.
		 * @return bool true if the class is present; else false.
		 */
		hasClass: function(cls) {
			var res = false,
				rt = new RegExp('\\b' + cls + '\\b');
			this.each(function() {
				if(rt.test(getClass(this))) {
					res = true;
					return false;
				}
			});
			return res;
		},

		/**
		 * Toggle a class on the elements.
		 *
		 * @param string cls The class to toggle.
		 * @return fusionLib This object.
		 */
		toggleClass: function(cls) {
			rt = new RegExp('\\b' + cls + '\\b');
			r = new RegExp('\\s*' + cls + '\\s*');
			return this.each(function() {
				var className = getClass(this);
				if(rt.test(className))
					className = trim(className.replace(r, ' '));
				else
					className = trim(className + ' ' + cls);
				this.setAttribute('class', className);
			});
		},

		/**
		 * Set a CSS property or gets the property of the first element if the value is undefined.
		 *
		 * @param string prop The property name e.g. background-color.
		 * @param string val The value to set or if not give the current value will be read.
		 * @return fusionLib This object of the property value of the 1st element.
		 */
		css: function(prop, val) {
			if(val === undefined) {
				if(document.defaultView && document.defaultView.getComputedStyle)
					return document.defaultView.getComputedStyle(this[0], '').getPropertyValue(prop);
				else {
					prop = prop.replace(/\-([a-z])/g, function(s, m) { return m.toUpperCase(); });
					return this[0].currentStyle[prop];
				}
			}
			else {
				prop = prop.replace(/\-([a-z])/g, function(s, m) { return m.toUpperCase(); });
				return this.each(function() { this.style[prop] = val; });
			}
		},

		/**
		 * Get width of the 1st element or set the width of all elements.
		 *
		 * @param int w The width in pixels or ignore to get the width.
		 * @return int The width in pixels or this object.
		 */
		width: function(w) {
			var elem = this[0];
			if(arguments.length) {
				w += 'px';
				return this.each(function() { this.style.width = w; });
			}
			else
				if(this[0] == window)
					return window.innerWidth || document.documentElement.offsetWidth;
				else
					return this[0].offsetWidth -
						(parseFloat(this.css('padding-left')) || 0) -
						(parseFloat(this.css('padding-right')) || 0) -
						(parseFloat(this.css('border-left-width')) || 0) -
						(parseFloat(this.css('border-right-width')) || 0);
		},

		/**
		 * Get height of the 1st element or set the height of all elements.
		 *
		 * @param int h The height in pixels or ignore to get the height.
		 * @return int The height in pixels or this object.
		 */
		height: function(h) {
			if(arguments.length) {
				h += 'px';
				return this.each(function() { this.style.height = h; });
			}
			else {
				if(this[0] == window)
					return window.innerHeight || document.documentElement.offsetHeight;
				else
					return this[0].offsetHeight -
						(parseFloat(this.css('padding-top')) || 0) -
						(parseFloat(this.css('padding-bottom')) || 0) -
						(parseFloat(this.css('border-top-width')) || 0) -
						(parseFloat(this.css('border-bottom-width')) || 0);
			}
		}
	});
})();