﻿/* Speed, Tiny, 
 * @author kares, be similar with jQuery`s framework
 * @date 2010.11.26
 */
(function(window, undefined){

//get Obj with prototype
if(typeof Object.pro !== "function"){
	Object.pro = function(obj){
		var Func = function(){};
		Func.prototype = obj;
		return new Func();
	}
}

//type map
var class2type = {};



// sandbox
var ST = (function(){
	
/* use to select a DOM directly
 * @param {String} selector, exp, "#topTab"
 */
var ST = function(selector){
		return new ST.fn.init(selector);
	},
	
	// Is it a simple ID selector 
	isSimpleId = /^#([\S]+)$/,
	// Used for trimming whitespace
	trimLeft = /^\s+/,
	trimRight = /\s+$/,
	rwhite = /\s/,
	
	// Save a reference to some core methods
	toString = Object.prototype.toString,
	hasOwn = Object.prototype.hasOwnProperty,
	push = Array.prototype.push,
	slice = Array.prototype.slice,
	trim = String.prototype.trim,
	indexOf = Array.prototype.indexOf;
	

	
ST.fn = ST.prototype = {
	/* init by selector
	 * @param {String} selector, exp, "#topTab". noly one Id
	 */
	init : function(selector){
			var match,
				//use to record exec rank in list.
				execStep = 0,
				//use to record state string
				state = [],
				STPrototype = function (){},
				newSTPrototype,
				obj = function (){},
				returnObj,
				extendObj,
				bridgeObj = ST.fn.initBridgejQuery ? ST.fn.initBridgejQuery(selector) : null;
				
			extendObj = {
				getStep : function (){
					return execStep;
				},
				shiftState : function (){
					execStep -= 1;
					return state.shift();
				},
				pushState : function (value){
					execStep += 1;
					state.push(value);
				}
			};
			if (bridgeObj){// prototype from bridgeObj
				STPrototype.prototype = bridgeObj;
				var newSTPrototype = new STPrototype;
				ST.extend(ST.fn, newSTPrototype);
				obj.prototype =  newSTPrototype;
				returnObj = new obj;
			}
			else{
				match = isSimpleId.exec(selector);
				obj.prototype = ST.fn;
				returnObj = new obj;
				returnObj[0] = document.getElementById(match[1]);
				returnObj.selector = selector;
				returnObj.length = 1;
			}
			ST.extend(extendObj, returnObj);
			return returnObj;
	},
	
	ST : "0.1Alpha"

}

//"new ST.fn.init" could create a new ST  
ST.fn.init.prototype = ST.fn; 

// Verify that \s matches non-breaking spaces
// (IE fails on this test)
if ( !rwhite.test( "\xA0" ) ) {
	trimLeft = /^[\s\xA0]+/;
	trimRight = /[\s\xA0]+$/;
}

/* to extend ST and ST.fn
 * @param {Object} mergeObject, mergeObject`s property will merge to ST/ST.fn
 */
ST.extend = ST.fn.extend = function(merge, tar){
	if(merge != null){
		var src, copy, name;
		var target = tar == null ? this : tar;
		for(name in merge){
			copy = merge[name];	
			// Prevent never-ending loop
			if ( target === copy ) {
				continue;
			}
			if(copy !== undefined){
				target[name] = copy;
			}
		}
		return target;
	}
}

ST.extend({
	/* create a bridge to connect jQuery and ST
	 * 
	 */
	bridgeTojQuery : function(){
		ST.fn.extend({
			initBridgejQuery: function(selector){
				return new jQuery.fn.init(selector);
			}
		});	
	},
	
	/* create a bridge to connect HighSlide and ST
	 * 
	 */
	bridgeToHighSlide : function(){
		//do some hack

	},
	
	
	//use native trim function if there is enable
	trim: trim ?
		function( text ) {
			return text == null ?
				"" :
				trim.call( text );
		} :
		// Otherwise use our own trimming functionality
		function( text ) {
			return text == null ?
				"" :
				text.toString().replace( trimLeft, "" ).replace( trimRight, "" );
		},
	/* check if it is a window
	 * @param {Object} obj
	 */
	isWindow : function(obj){
		return obj && typeof obj === "object" && "setInterval" in obj;
	},
	
	/* check if it is a PlainObject(no sub property)
	 * @param {Object} obj
	 */
	/* *********************************
	isPlainObject : function(obj){
		// From jQuery.
		// Must be an Object.
		// Because of IE, we also have to check the presence of the constructor property.
		// Make sure that DOM nodes and window objects don't pass through, as well
		if (!obj || ST.type(obj) !== "object" || obj.nodeType || ST.isWindow(obj)) {
			return false;
		}
		// Not own constructor property must be Object
		if ( obj.constructor &&
			!hasOwn.call(obj, "constructor") &&
			!hasOwn.call(obj.constructor.prototype, "isPrototypeOf") ) {
			return false;
		}
		
		// Own properties are enumerated firstly, so to speed up,
		// if last one is own, then all properties are own.
		var key;
		for (key in obj) {}
		return key === undefined || hasOwn.call( obj, key );
	},
	********************************* */
	
	/* check if is a Array
	 * @param {Object} obj
	 */
	isArray: Array.isArray || function(obj) {
		return ST.type(obj) === "array";
	},
	
	/* check if is a Function
	 * @param {Object} obj
	 */
	isFunction: function( obj ) {
		return ST.type(obj) === "function";
	},

	/* check type
	 * @param {Object} obj
	 */
	type: function( obj ) {
		return obj == null ?
			String( obj ) :
			class2type[ toString.call(obj) ] || "object";
	}
});

//export ST to global.
return (window.ST = window.$ = ST);
})();

//some Regexp
var rspaces = /\s+/,
	rclass = /[\n\t]/g;

ST.extend({
	
	//cross event obj
	eventUtil : {
		/* add event handle
		 * @param {DOM} obj, which DOM attached to, or a ST obj
		 * @param {String} type, type of event, exp, mouseover(no "on" at front)
		 * @param {Function} fn, callback
		 */
		addEvent : function (obj, type, fn){
			//only one elem in ST obj
			var elem = obj && obj.ST ? obj[0] : obj;
			if(elem){
				if ( elem.addEventListener ) {
					elem.addEventListener( type, fn, false );
				}
				else if ( elem.attachEvent ) {
					elem.attachEvent( "on" + type, fn );
				}
			}
		},
		
		getEvent : function (event){
			return event ? event : window.event;
		},
		getTarget : function (event){
			return event.target || event.srcElement;
		},
		preventDefault : function (event){
			if(event.preventDefault){
				event.preventDefault();
			}
			else {
				event.returnValue = false;
			}
		}
	},
	
	
	/* args is for internal usage only (from jQuery)
	 * @param {Object} object, ST or jQuery object
	 * @param {Function} callback
	 * @param {Array} args
	 */
	each: function( object, callback, args ) {
		var name, i = 0,
			length = object.length,
			isObj = length === undefined || ST.isFunction(object);

		if ( args ) {
			if ( isObj ) {
				for ( name in object ) {
					if ( callback.apply( object[ name ], args ) === false ) {
						break;
					}
				}
			} else {
				for ( ; i < length; ) {
					if ( callback.apply( object[ i++ ], args ) === false ) {
						break;
					}
				}
			}

		// A special, fast, case for the most common use of each
		} else {
			if ( isObj ) {
				for ( name in object ) {
					if ( callback.call( object[ name ], name, object[ name ] ) === false ) {
						break;
					}
				}
			} else {
				for ( var value = object[0];
					i < length && callback.call( value, i, value ) !== false; value = object[++i] ) {}
			}
		}

		return object;
	},
	
	/* closure catch.
	 * @param {Object} that
	 * @param {Function} callback
	 * @param {Array} args
	 * @return {Function}
	 */
	catClosure : function (that, callback, args){
		return function (){
			callback.apply(that, args);
		};
	}
});


// Populate the class2type map
ST.each("Boolean Number String Function Array Date RegExp Object".split(" "), function(i, name) {
	class2type[ "[object " + name + "]" ] = name.toLowerCase();
});


//event toggle
ST.each("mouseover mouseup mousedown mouseout click".split(" "), function(i, name){
	ST.fn.extend(function(eventName){
		var extendObj = {};
		extendObj["on" + eventName] = function(func){
			if(ST.isFunction(func)){
				ST.eventUtil.addEvent(this, eventName, func);
			}
			else{
				this.pushState(eventName);
				return this;
			}
		};
		return extendObj;
	}(name));
});

ST.fn.extend({
	/* add class
	 * @param {String} value
	 */
	addClass: function( value ) {
		if (this.getStep()){
			var elem,
				that,
				length = this.length;
			for(var n = 0; n < length; n++){
				if(this[n]){
					elem = this[n];
					that = this;
					ST.eventUtil.addEvent(elem, this.shiftState(), ST.catClosure(that, ST.fn.addClass, [value]));
				}
			}
		}
		else if ( value && typeof value === "string" ) {
			var classNames = (value || "").split( rspaces ),
				elem;
			
			for (var i = 0, l = this.length; i < l; i++) {
				elem = this[i];
				
				if (elem.nodeType === 1) {
					if (!elem.className) {
						elem.className = value;
					}
					else {
						var className = " " + elem.className + " ", setClass = elem.className;
						
						for (var c = 0, cl = classNames.length; c < cl; c++) {
							if (className.indexOf(" " + classNames[c] + " ") < 0) {
								setClass += " " + classNames[c];
							}
						}
						elem.className = ST.trim(setClass);
					}
				}
			}
		}
		return this;
	},
	
	/* hello world
	 * @param {String} value
	 */
	removeClass: function( value ) {
		if (this.getStep()){
			var elem,
				that,
				length = this.length;
			for(var n = 0; n < length; n++){
				if(this[n]){
					elem = this[n];
					that = this;
					ST.eventUtil.addEvent(elem, this.shiftState(), ST.catClosure(that, ST.fn.removeClass, [value]));
				}
			}
		}
		else if ( (value && typeof value === "string") || value === undefined ) {
			var classNames = (value || "").split( rspaces ),
				elem;

			for (var i = 0, l = this.length; i < l; i++) {
				elem = this[i];
				if (elem.nodeType === 1 && elem.className) {
					if (value) {
						var className = (" " + elem.className + " ").replace(rclass, " ");
						for (var c = 0, cl = classNames.length; c < cl; c++) {
							className = className.replace(" " + classNames[c] + " ", " ");
						}
						elem.className = ST.trim(className);
					}
					else {
						elem.className = "";
					}
				}
			}
		}
		return this;
	},
	
	/* toggle class
	 * @param {String} value, classname
	 */
	toggleClass : function (value){
		if (this.getStep()){
			var elem,
				that,
				length = this.length;
			for(var n = 0; n < length; n++){
				if(this[n]){
					elem = this[n];
					that = this;
					ST.eventUtil.addEvent(elem, this.shiftState(), ST.catClosure(that, ST.fn.toggleClass, [value]));
				}
			}
		}
		else if( (value && typeof value === "string") || value === undefined ) {
			var elem,
				className,
				classNamesL;
			for (var i = 0, l = this.length; i < l; i++) {
				elem = this[i];
				className = elem.className;
				if(elem.nodeType === 1){
					classNamesL = elem.className.length;
					if (classNamesL ===
						(elem.className = elem.className.replace(value, "")).length){
						elem.className +=  " " + value;
					}
				}
			}
		}
	}
	
	/* event state machine
	 * 
	 */
	/*
	onmouseover : function (func){
		if(ST.isFunction(func)){
			ST.eventUtil.addEvent(this, "mouseover", func);
		}
		else{
			this.pushState("mouseover");
			return this;
		}
	},
	onmouseout : function (func){
		if(ST.isFunction(func)){
			ST.eventUtil.addEvent(this, "mouseout", func);
		}
		else{
			this.pushState("mouseout");
			return this;
		}
	},
	onmouseup : function (func){
		if(ST.isFunction(func)){
			ST.eventUtil.addEvent(this, "mouseup", func);
		}
		else{
			this.pushState("mouseup");
			return this;
		}
	},
	onmousedown : function (func){
		if(ST.isFunction(func)){
			ST.eventUtil.addEvent(this, "mousedown", func);
		}
		else{
			this.pushState("mousedown");
			return this;
		}
	}*/
});


})(window);



