﻿/*
    1.浏览器判断,Expo对象和id生成器
    2.dom ready逻辑:替代window.onload
    3.面向对象机制
    4.跨浏览器dom事件对象
    5.js事件类
		
*/
Expo = {
    version: '1.0',
    idSeed: 0,
    id : function(el, prefix){
        prefix = prefix || "expo";
        el = $(el);
        var id = prefix + (++Expo.idSeed);
        return el ? (el.id ? el.id : (el.id = id)) : id;
    }
}
window["undefined"] = window["undefined"];
if(typeof $ === 'undefined'){
    function $(id, doc){
        doc = doc || document;
        return typeof id === 'string' ? doc.getElementById(id) : id;
    }
}
var ua = navigator.userAgent.toLowerCase();
var isStrict = document.compatMode == "CSS1Compat",
    isOpera = ua.indexOf("opera") > -1,
    isChrome = ua.indexOf('chrome') > -1,
    isSafari = (/webkit|khtml/).test(ua) && !isChrome,
    isSafari3 = isSafari && ua.indexOf('webkit/5') != -1 && !isChrome,
    isIE = !isOpera && ua.indexOf("msie") > -1,
    isIE7 = !isOpera && ua.indexOf("msie 7") > -1,
     isIE8 = !isOpera && ua.indexOf("msie 8") > -1,
    isGecko = !isSafari && ua.indexOf("gecko") > -1,
    isGecko3 = !isSafari && ua.indexOf("rv:1.9") > -1,
    isGecko2 = isGecko && !isGecko3,
    isAir = (ua.indexOf("adobeair") != -1),
    isBorderBox = isIE && !isStrict,
    isWindows = (ua.indexOf("windows") != -1 || ua.indexOf("win32") != -1),
    isMac = (ua.indexOf("macintosh") != -1 || ua.indexOf("mac os x") != -1),    
    isLinux = (ua.indexOf("linux") != -1),
    isSecure = window.location.href.toLowerCase().indexOf("https") === 0;
	if(isIE && !isIE7){
        try{
            document.execCommand("BackgroundImageCache", false, true);
        }catch(e){}
    }
function emptyFn(){}
function apply(o, c, defaults){
    if(defaults){
        apply(o, defaults);
    }
    if(o && c && typeof c == 'object'){
        for(var p in c){
            o[p] = c[p];
        }
    }
    return o;
};
function applyIf(o, c){
    if(o && c){
        for(var p in c){
            if(typeof o[p] == "undefined"){ o[p] = c[p]; }
        }
    }
    return o;
}
//------------------    javascript扩展  --------------------//
apply(Function.prototype, {
    bind : function(o) {
	    var fn = this;	   
	    return function() {
	        //var args2 = Array.apply(null, arguments).addRange(args);
		    return fn.apply(o, arguments);
	    }
    },
    defer : function(defer, o, args) {
	    var fn = this;
	    return setTimeout(function(){
	        fn.apply(o, args || []);
	    }, defer);
    }
});    
apply(Array.prototype, {
    add: Array.prototype.enqueue = function(item){    
        this[this.length] = item;
        return this;
    },
    addRange: function(array){        
		for (var i = 0, j = array.length; i < j; i++) this[this.length] = array[i];
		return this;        
    },
    clear: function(){
        this.length = 0;
    },
    clone: function(){
        if (this.length === 1) {
            return [this[0]];
        }
        else {
            return Array.apply(null, this);
        }    
    },
    contains: function(item){
        return (this.indexOf(item) >= 0);
    },
	indexOf: function(item, from){
		var len = this.length;
		for (var i = (from < 0) ? Math.max(0, len + from) : from || 0; i < len; i++){
			if (this[i] === item) return i;
		}
		return -1;
	},    
    dequeue: function(){
        return this.shift();
    },
    insert: function(index,item){
        this.splice(index, 0, item);        
    },
    remove: function(item){
        var index = this.indexOf(item);    
        if (index >= 0) {
            this.splice(index, 1);
        }
        return (index >= 0);
    },
    removeAt: function(index){
        var ritem = this[index];
        this.splice(index, 1);
        return ritem;    
    },
    each: function(method, instance) {
        for (var i = 0, l = this.length; i < l; i++) {
            var elt = this[i];
            if (typeof(elt) !== 'undefined') {
                if(method.call(instance, elt, i, this) === false) break;
            }
        }
    },
    rgbToHex : function(array){
	    if (this.length < 3) return false;
	    if (this.length == 4 && this[3] == 0 && !array) return 'transparent';
	    var hex = [];
	    for (var i = 0; i < 3; i++){
		    var bit = (this[i] - 0).toString(16);
		    hex.push((bit.length == 1) ? '0' + bit : bit);
	    }
	    return array ? hex : '#' + hex.join('');
    },
    hexToRgb : function(array){
	    if (this.length != 3) return false;
	    var rgb = [];
	    for (var i = 0; i < 3; i++){
		    rgb.push(parseInt((this[i].length == 1) ? this[i] + this[i] : this[i], 16));
	    }
	    return array ? rgb : 'rgb(' + rgb.join(',') + ')';
    }
});
applyIf(String, {
    escape : function(string) {
        return string.replace(/('|\\)/g, "\\$1");
    },
    leftPad : function (val, size, ch) {
        var result = new String(val);
        if(!ch) {
            ch = " ";
        }
        while (result.length < size) {
            result = ch + result;
        }
        return result.toString();
    },
    format : function(format){
        var args = Array.prototype.slice.call(arguments, 1);
        return format.replace(/\{(\d+)\}/g, function(m, i){
            return args[i];
        });
    }
});
apply(String.prototype,{
    rgbToHex : function(array){
	    var rgb = this.match(/\d{1,3}/g);
	    return (rgb) ? rgb.rgbToHex(array) : false;
    },
    hexToRgb : function(array){
	    var hex = this.match(/^#?(\w{1,2})(\w{1,2})(\w{1,2})$/);
	    return (hex) ? hex.slice(1).hexToRgb(array) : false;
    },
    contains : function(string, s){
	    return (s) ? (s + this + s).indexOf(s + string + s) > -1 : this.indexOf(string) > -1;
    }
});
String.prototype.trim = function(){
    var re = /^\s+|\s+$/g;
    return function(){ return this.replace(re, ""); };
}();

Expo.onDomLoad = function(fn) {
    if(window.__ready) {
        fn();
        return;
    }
    if(!window.__load_events) window.__load_events = [];
    window.__load_events.push(fn); 
    var init = function (){    
        if (arguments.callee.done) return; 
        arguments.callee.done = true; 
        
        if(isGecko || isOpera){
            document.removeEventListener("DOMContentLoaded", init, false);
        }
        if (window.__load_timer){
            clearInterval(window.__load_timer);
            window.__load_timer = null; 
        }
        if(!window.__ready){
            window.__ready = true;
            for (var i=0;i < window.__load_events.length;i++) {
                window.__load_events[i](); 
            } 
            window.__load_events = null; 
        }
    }
    if(isIE){        
        document.write("<scr"+"ipt id='__ie_onload' defer src='void(0)'><\/scr"+"ipt>"); 
        var s =document.getElementById("__ie_onload"); 
        s.onreadystatechange = function() {         
            if(this.readyState == "complete" || this.readyState == "loaded") {
                s.onreadystatechange = null;
                s.parentNode.removeChild(s);                
                init.defer(100);
            }
        };
    }else if(isGecko || isOpera){
        document.addEventListener("DOMContentLoaded", init, false);
    }else if(isSafari){
        window.__load_timer = setInterval(function() { 
            if(/loaded|complete/.test(document.readyState)) { 
                init();
            }
        },10);
    }
}
Expo.onDomLoad(function(){
    Expo.util.Event.fireEvent(window, 'domload');
});
//----------------      面向对象    ------------------//
function override (origclass, overrides){
    if(overrides){
        var p = origclass.prototype;
        for(var method in overrides){
            p[method] = overrides[method];
        }
    }
}
function namespace(){
    var a=arguments, o=null, i, j, d, rt;
    for (i=0; i<a.length; ++i) {
        d=a[i].split(".");
        rt = d[0];
        eval('if (typeof ' + rt + ' == "undefined"){' + rt + ' = {};} o = ' + rt + ';');
        for (j=1; j<d.length; ++j) {
            o[d[j]]=o[d[j]] || {};
            o=o[d[j]];
        }
    }
}
var extend = function(){    
    var io = function(o){
        for(var m in o){
            this[m] = o[m];
        }
    };
    var oc = Object.prototype.constructor;

    return function(sb, sp, overrides){
        if(typeof sp == 'object'){
            overrides = sp;
            sp = sb;
            sb = overrides.constructor != oc ? overrides.constructor : function(){sp.apply(this, arguments);};
        }
        var F = function(){}, sbp, spp = sp.prototype;
        F.prototype = spp;
        sbp = sb.prototype = new F();
        sbp.constructor=sb;
        sb.superclass=spp;
        if(spp.constructor == oc){
            spp.constructor=sp;
        }
        sb.override = function(o){
            override(sb, o);
        };
        sbp.override = io;
        override(sb, overrides);
        sb.extend = function(o){extend(sb, o);};
                
        return sb;
    };
}();

Expo.ns = Expo.namespace = namespace;
Expo.extend = extend;
Expo.ns('Expo.util');
//-----------------------跨浏览器事件对象-----------------------//
Expo.util.Event = function(evt) {
    if(evt.rawEvent) return evt;
    var e = this.rawEvent = evt;
    apply(this, e);
    // mac metaKey behaves like ctrlKey
    this.ctrlKey = e.ctrlKey || e.metaKey;
    
    if (typeof(e.button) !== 'undefined') {
        var mb = Expo.util.Event.MouseButton;
        this.button = (typeof(e.which) !== 'undefined') ? e.button :
            (e.button === 4) ? mb.middle :
            (e.button === 2) ? mb.right:
            mb.left;
    }
    
    if (e.type === 'keypress') {
        this.charCode = e.charCode || e.keyCode || 0;
    }
    else if (e.keyCode && (e.keyCode === 46)) {
        this.keyCode = 127;
    }    
    this.target = this.resolveTextNode(e.target || e.srcElement);
    this.trigger = this.target;
    this.xy = [this.getX(), this.getY()];    
}

Expo.util.Event.prototype = {
    getScroll: function() {
        var dd = document.documentElement, db = document.body;
        if (dd && (dd.scrollTop || dd.scrollLeft)) {
            return [dd.scrollTop, dd.scrollLeft];
        } else if (db) {
            return [db.scrollTop, db.scrollLeft];
        } else {
            return [0, 0];
        }
    },
    getX: function(ev) {
        ev = ev ? ev.rawEvent || ev : this.rawEvent;
        var x = ev.pageX;
        if (!x && 0 !== x) {
            x = ev.clientX || 0;

            if (document.all) {
                x += this.getScroll()[1];
            }
        }
        return x;
    },
    getY: function(ev) {
        ev = ev ? ev.rawEvent || ev : this.rawEvent;
        var y = ev.pageY;
        if (!y && 0 !== y) {
            y = ev.clientY || 0;
            if (document.all) {
                y += this.getScroll()[0];
            }
        }
        return y;
    },
    getRelatedTarget: function(ev) {
        ev = ev ? (ev.rawEvent || ev) : this.rawEvent;
        var t = ev.relatedTarget;
        if (!t) {
            if (ev.type == "mouseout") {
                t = ev.toElement;
            } else if (ev.type == "mouseover") {
                t = ev.fromElement;
            }
        }
        return this.resolveTextNode(t);
    },    
    resolveTextNode: function(d){
        return (d && d.nodeType == 3) ? d.parentNode : d;
    },
    getWheelDelta : function(){
        var e = this.rawEvent;
        var delta = 0;
        if(e.wheelDelta){
            delta = e.wheelDelta/120;
        }else if(e.detail){ 
            delta = -e.detail/3;
        }
        return delta;
    },
    within : function(el, related){
        var t = related ? this.getRelatedTarget() : this.target;
        return t && DomHelper.contains(el, t);
    },    
    stop: function(){
        this.stopDefault();
        this.stopEvent();
    },
    stopDefault: function() {
        if (this.rawEvent.preventDefault) {
            this.rawEvent.preventDefault();
        }else if (window.event) {
            window.event.returnValue = false;
        }
    },
    stopEvent: function() {       
        if (this.rawEvent.stopPropagation) {        
            this.rawEvent.stopPropagation();
        }else if (window.event) {
            window.event.cancelBubble = true;
        }
    }
}
//-----------------------跨浏览器事件操作:增,删,差,清除-----------------------//
apply(Expo.util.Event, {
    MouseButton: {
        left: 0,
        middle: 1,
        right: 2
    },
    evHash : {},
    EVENTS : {
        dragstart: 1,
        dragmove: 1,
        dragcomplete: 1,
        dragenter: 1,
        dragover: 1,
        dragout: 1,
        dragdrop: 1    
    },
    addListener : function(el, type, fn, scope) {
        el = $(el);
        var id = Expo.id(el);
        var ides = this.evHash[id];
        if(!ides){
            ides = this.evHash[id] = {dom: el};
        }
        var tes = ides[type];
        if(!tes) tes = ides[type] = [];
        
        scope = scope || el;
        //findListener判断,同一个函数,只能绑定一次!
        if(this.findListener(el, type, fn, scope)) return false;
               
        var method = function(e) {        
            return fn.call(scope, new Expo.util.Event(e || window.event));
        };
        
        tes.add([fn, method, scope]);
        
        if(el == window && type == 'domload') return;
        if(this.EVENTS[type]) return;
        if (window.addEventListener) {
            el.addEventListener(type, method, false);
        } else if (window.attachEvent) {
            el.attachEvent("on" + type, method);
        }
        return true;
    },
    removeListener : function(el, type, fn, scope){
        scope = scope || el;
        var f = this.findListener(el, type, fn, scope);
        if(!f) return false;
        this.evHash[Expo.id(el)][type].remove(f);
        
        if(this.EVENTS[type]) return;
        if (window.removeEventListener) {
            el.removeEventListener(type, f[1], false);
        } else if (window.detachEvent) {
            el.detachEvent("on" + type, f[1]);
        }
        return true;
    },
    findListener : function(el, type, fn, scope){        
        el = $(el);
        var id = Expo.id(el);
        var ides = this.evHash[id];
        if(!ides) return;
        var tes = ides[type];
        if(!tes) return;
        scope = scope || el;
        for(var i=0,l=tes.length; i<l; i++){
            var f = tes[i];
            if(f[0] === fn && f[2] === scope) return f;
        }
    },
    fireEvent: function(el, name){    
        el = $(el);
        var id = Expo.id(el);    
        var events = this.evHash[id];
        if(!events) return;
        name = name.toLowerCase();
        var event = events[name];
        if(event){
            var args = Array.apply(null, arguments);
            args.shift();
            args.shift();
            event.each(function(fn){
                fn[0].apply(fn[2], args);
            });
        }
    },
    clearEvent : function(el, type){
        el = $(el);
        var id = Expo.id(el);
        var ides = this.evHash[id];
        if(!ides) return;
        for(var name in ides){
            if((type && type == name) || !type){
                var tes = ides[name];
                while(tes.length){
                    var cache = tes[0];
                    this.removeListener(el, name, cache[0], cache[2]);
                }
            }
        }        
        delete this.evHash[id];
    },
    //阻止事件,是否停止默认行为
    swallowEvent : function(el, eventName, preventDefault){        
        var fn = function(e){
            e.stopPropagation();
            if(preventDefault){
                e.preventDefault();
            }
        };
        if(eventName instanceof Array){
            for(var i = 0, len = eventName.length; i < len; i++){
                 this.on(el, eventName[i], fn);
            }         
        }else{
            this.on(el, eventName, fn);
        }
    }
});
Expo.util.Event.on = Expo.util.Event.addListener;
Expo.util.Event.un = Expo.util.Event.removeListener;

//---------------------------事件基类---------------------------
Observable = function(){
    if(!this.events) this.events = {};
    var listeners = this.listeners;    
    for(var name in listeners){
        this.on(name, listeners[name]);
    }
    delete this.listeners;
}
Observable.prototype = {
    addListener: function(name, fn, scope){
        name = name.toLowerCase();
        var event = this.events[name];
        if(event){
            scope = scope || this;
            if(!this.hasListener(name, fn, scope)) event.add([fn, scope]);
        }
        return this;
    },
    removeListener: function(name, fn, scope){
        name = name.toLowerCase();
        var event = this.events[name];
        if(event){           
            scope = scope || this; 
            var f = this.hasListener(name, fn, scope);
            if(f){
                event.remove(f);
            }
        }
        return this;
    },
    hasListener: function(name, fn, scope){
        name = name.toLowerCase();
        var event = this.events[name];
        scope = scope || this;
        if(event){            
            for(var i=0,l=event.length; i<l; i++){
                var f = event[i];
                if(f[0] === fn && f[1] === scope) return f;
            }                   
        }
    },
    addEvents: function(){
        if(!this.events) this.events = {};
        var events = this.events;
        
        var args = Array.apply(null, arguments);
        args.each(function(name){
            var event = events[name];
            if(!event) events[name] = [];
        });
        return this;
    },
    fireEvent: function(name){    
        name = name.toLowerCase();
        var event = this.events[name];
        if(event){
            var args = Array.apply(null, arguments);
            args.shift();
            var v;
            event.each(function(fn){
                var r = fn[0].apply(fn[1] || this.scope || this, args);
                if(v !== false) v = r;                
            }, this);
            return v;
        }
    },
    clearEvent: function(){
        this.events = {};
        return this;
    }
}
Observable.prototype.on = Observable.prototype.addListener;
Observable.prototype.un = Observable.prototype.removeListener;
Observable.prototype.dispatchEvent = Observable.prototype.fireEvent;

Expo.util.Observable = Observable;
if(!window.XMLHttpRequest){
    window.XMLHttpRequest = function(){
        var progIDs = ['Msxml2.XMLHTTP','Microsoft.XMLHTTP'];
        for(var i=0; i<progIDs.length; i++){
            try{
                var xmlHttp = new ActiveXObject(progIDs[i]);
                return xmlHttp;
            }catch(ex){
            }
        }
        return null;
    }
}
function emptyFn(){}
(function(){
function getUrlParam( hs ) {
    var s = [];
    for ( var j in hs) {
        var v = hs[j];
        if(typeof(v) !== 'string'){
            v = Json.encode(v);
        }
        //s.push(encodeURIComponent(j) + "=" + encodeURIComponent(v));
		s.push((j) + "=" + (v));
    }
    return s.join("&");
}    
function apply(o, c, defaults){
    if(defaults){
        apply(o, defaults);
    }
    if(o && c && typeof c == 'object'){
        for(var p in c){
            o[p] = c[p];
        }
    }
    return o;
};
function abort(){   
    if(this._timer){
        window.clearTimeout(this._timer);
        this._timer = null;
    }
    var http = this.request;
    if(http){       
        http.onreadystatechange = emptyFn;
        http.abort();
        //this.request = null;
        http.onFail(0, this);                   //out失败:0                          
    }
}
function onreadystatechange(http){
    var http = this.request;
    if (http.readyState === 4) {
        if(http.status === 200) {           
            this.onSuccess(http.responseText, this);        
        }else{
            this.onFail(http.status, this);
        }
        http.onreadystatechange = emptyFn;  
    }
}
Ajax = {
    request: function(config){
        var xmlHttp = new XMLHttpRequest();               
        var c = apply({
            type: "POST",
            url: null,
            timeout: 0,
            contentType: "application/x-www-form-urlencoded",    
            async: true,
            params: null,     //key-value形式,value必须为字符串对象(jsonString).
            onSuccess: emptyFn,
            onFail: emptyFn,
            onOut: emptyFn,
            request: xmlHttp,
            abort: abort
        }, config);
                
        var data = getUrlParam(c.params);
        
        if (c.type.toLowerCase() == "post"){
            xmlHttp.open(c.type, c.url, c.async);
            xmlHttp.setRequestHeader("Content-Type", c.contentType);
        }else{
            if(c.params) c.url += ((c.url.indexOf("?") > -1) ? "&" : "?") + data;
			if(c.url.indexOf(".xml")==-1)
            	c.url += ((c.url.indexOf("?") > -1) ? "&" : "?") + '_$nocache='+new Date().getTime();
            xmlHttp.open(c.type, c.url, c.async);
        }
        xmlHttp.onreadystatechange = function(){
            onreadystatechange.call(c);
        }        
        
	    if (c.timeout > 0){
		    c._timer = window.setTimeout(function(){
                c.onOut(c);
		    }, c.timeout);
        }
	    try {	    
		    xmlHttp.send(data);		    
	    } catch(e) {
            c.onFail(1000, c);//没有正确调用成功 1000	        
	    }
	    return c;
    }
}    
})();

if(window.Expo) Expo.util.Ajax = Ajax;
/*
    Anim:动画类.
        开放动画过程事件
        动画算法
        动画时间
        频率
*/    
function emptyFn(){}
Anim = function(options){    
    this.options = apply({
		//事件
		onStart: emptyFn,
		onStep: emptyFn,
		onComplete: emptyFn,
		//属性配置
		transition: function(p){
			return -(Math.cos(Math.PI * p) - 1) / 2;
		},//Anim.Transitions.Back.easeIn,
		duration: 500,
		fps: 50
    }, options);
}
Anim.prototype = {
    //动画操作方法
    start: function(time){              //开始
        this.stopTimer();
        this.time = time || new Date().getTime();
        
        this.options.onStart();                       
        this.timer = setInterval(this.step.bind(this), Math.round(1000/this.options.fps));
    },
    stop: function(cancel){            
        this.stopTimer();
        
        this.options.onComplete(cancel);
        var t = new Date().getTime() - this.time; 
        this.time = null;
        
        return t;
    },
    step: function(){
	    var time = new Date().getTime();
	    var s = this.options.onStep;
	    if (time < this.time + this.options.duration){
		    var delta = this.options.transition((time - this.time) / this.options.duration);
		    s(delta);
	    } else {
		    s(1);
		    this.stop(false);
	    }
    },
    stopTimer: function(){
        clearInterval(this.timer);
        this.timer = null;
    }
}

Anim.compute = function(from, to, delta){
    return (to - from) * delta + from;
};
Anim.parseColorArray = function(v){
    if(v instanceof Array) return v;
    else if (v.contains('rgb')){
        v = v.split('rgb').splice(1,4).map(function(color){
	        return color.rgbToHex();
        }).join(' ');
    }
    return v.hexToRgb(true);        
}
Anim.getStyle = function(el, p){
    var dh = Expo.util.Dom;
    var v = dh.getStyle(el, p);
    if(p.contains('olor')){
        while(v == 'transparent' && p != document){            
            el = el.parentNode;
            v = dh.getStyle(el, p);
        }
        if(v == 'transparent'){
	        v = '#fff';
	    }	   
    }
    return v;        
}
    
//动画算法
Anim.Transition = function(transition, params){
	params = params ? (params instanceof Array ? params : [params]) : [];
	return apply(transition, {
		easeIn: function(pos){
			return transition(pos, params);
		},
		easeOut: function(pos){
			return 1 - transition(1 - pos, params);
		},
		easeInOut: function(pos){
			return (pos <= 0.5) ? transition(2 * pos, params) / 2 : (2 - transition(2 * (1 - pos), params)) / 2;
		}
	});
};

Anim.Transitions = {
	linear: function(){
		return arguments[0];
	}
};

Anim.Transitions.extend = function(transitions){
	for (var transition in transitions) Anim.Transitions[transition] = new Anim.Transition(transitions[transition]);
};
Anim.Transitions.extend({
	Pow: function(p, x){
		return Math.pow(p, x[0] || 6);
	},

	Expo: function(p){
		return Math.pow(2, 8 * (p - 1));
	},

	Circ: function(p){
		return 1 - Math.sin(Math.acos(p));
	},

	Sine: function(p){
		return 1 - Math.sin((1 - p) * Math.PI / 2);
	},

	Back: function(p, x){
		x = x[0] || 1.618;
		return Math.pow(p, 2) * ((x + 1) * p - x);
	},

	Bounce: function(p){
		var value;
		for (var a = 0, b = 1; 1; a += b, b /= 2){
			if (p >= (7 - 4 * a) / 11){
				value = - Math.pow((11 - 6 * a - 11 * p) / 4, 2) + b * b;
				break;
			}
		}
		return value;
	},

	Elastic: function(p, x){
		return Math.pow(2, 10 * --p) * Math.cos(20 * p * Math.PI * (x[0] || 1) / 3);
	}

});

['Quad', 'Cubic', 'Quart', 'Quint'].each(function(transition, i){
	Anim.Transitions[transition] = new Anim.Transition(function(p){
		return Math.pow(p, [i + 2]);
	});
});

Cookie = {
    get: function(sName){
	    //获取单个cookies
	    // cookies are separated by semicolons
	    var aCookie = document.cookie.split("; ");
	    for (var i=0; i < aCookie.length; i++)
	    {
	    // a name/value pair (a crumb) is separated by an equal sign
	    var aCrumb = aCookie[i].split("=");
	    if (sName == aCrumb[0]){
		    return decodeURI(aCrumb[1]);}
	    }
	    // a cookie with the requested name does not exist
	    return null;

    },
    set: function(name, value){  
        var argv = arguments; 
        var argc = arguments.length; 
        var expires = (argc > 2) ? argv[2] : null; 
        if(expires!=null) 
        { 
            var LargeExpDate = new Date (); 
            LargeExpDate.setTime(LargeExpDate.getTime() + (expires*1000*3600*24));         
        } 
        document.cookie = name + "=" + escape (value)+((expires == null) ? "" : ("; expires=" +LargeExpDate.toGMTString()))+";path=/"; 
    },
    del: function delCookie(name) { 
        var expdate = new Date(); expdate.setTime(expdate.getTime() - (86400 * 1000 * 1)); Cookie.set(name, "", expdate); 
    }
}
if(window.Expo) Expo.util.Cookie = Cookie;

/*
    DataView三要素:
        1.tpl:  模板
        2.url:  数据请求地址
        3.render: 容器
        
        增加错误提示
*/
DataView = function(o){
    apply(this, o);
    
    this.addEvents(
        'beforerender',
        'render',//
        'loading',
        'load',
        'fail'
    );
    DataView.superclass.constructor.call(this);
}
extend(DataView, Observable, {
    tpl: null,
    render: null,
    
    refresh: function(data){
        if(this.tpl && this.render){
            if(typeof this.tpl === 'string') this.tpl = new Template(this.tpl);
            this.render = $(this.render);            
            this.data = data;
            if(this.data){
                if(this.fireEvent('beforerender', this) !== false){
                    var html = this.tpl.run(this.data);
                    this.render.innerHTML = html;
                    this.fireEvent('render', this)
                }
            }
        }
    }
});

/*
    1.css操作
    2.style操作
    3.dom的增删改操作
    4.其他
*/
(function(){
var propCache = {};
var camelRe = /(-[a-z])/gi;
var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
var view = document.defaultView;

var classCache = {};

function _f1(){};
function _f2(){return false;};

window.Dom = {    
    repaint : function(el){        
        this.addClass(el, "x-repaint");
        setTimeout(function(){
            DomHelper.removeClass(el, "x-repaint");
        }, 1);        
    },
    selectable: function(el, selected){       
        if(!!selected){
            this.removeClass(el, 'e-unselectable');
            if(isIE) el.unselectable = "off";                
            else{                
                el.style.MozUserSelect = '';
                el.style.KhtmlUserSelect = '';
                el.style.UserSelect = '';
                
            }
            el.onmousedown = _f1;            
        }else{
            this.addClass(el, 'e-unselectable');
            if(isIE) el.unselectable = 'on';            
            else{                                
                el.style.MozUserSelect = 'none';				
                el.style.UserSelect = 'none';
                el.style.KhtmlUserSelect = 'none';		                
            }
            el.onmousedown = _f2;
        }
    },    
    getStyle : function(){
        return view && view.getComputedStyle ?
            function(el, prop){
                var v, cs, camel;
                if(prop == 'float'){
                    prop = "cssFloat";
                }
                if(v = el.style[prop]){
                    return v;
                }
                if(cs = view.getComputedStyle(el, "")){
                    if(!(camel = propCache[prop])){
                        camel = propCache[prop] = prop.replace(camelRe, camelFn);
                    }
                    return cs[camel];
                }
                return null;
            } : 
            function(el, prop){
                var v, cs, camel;
                if(prop == 'opacity'){
                    return this.getOpacity(el);
                }else if(prop == 'float'){
                    prop = "styleFloat";
                }
                if(!(camel = propCache[prop])){
                    camel = propCache[prop] = prop.replace(camelRe, camelFn);
                }
                if(v = el.style[camel]){
                    return v;
                }
                if(cs = el.currentStyle){
                    return cs[camel];
                }
                return null;
            };
    }(),
    setStyle : function(el, prop, value){
        if(typeof prop == "string"){
            var camel;
            if(!(camel = propCache[prop])){
                camel = propCache[prop] = prop.replace(camelRe, camelFn);
            }
            switch(camel){
                case 'opacity': this.setOpacity(el, value); break;
                case 'float': camel = isIE ? 'styleFloat' : 'cssFloat';
                default:
                    el.style[camel] = value;
            }                
        }else{
            for(var style in prop){
                if(typeof prop[style] != "function"){
                   this.setStyle(el, style, prop[style]);
                }
            }
        }
        return this;
    },
    applyStyles : function(el, styles){
        if(styles){           
           if(typeof styles == "string"){
                var re = /\s?([a-z\-]*)\:\s?([^;]*);?/gi;
                var matches;
                while ((matches = re.exec(styles)) != null){
                    this.setStyle(el, matches[1], matches[2]);
                }
           }else if (typeof styles == "object"){
                for (var style in styles){
                    this.setStyle(el, style, styles[style]);
                }
           }else if (typeof styles == "function"){
                this.applyStyles(el, styles.call());
           }
        }
    },
    getStyles : function(el){
        var a = arguments, len = a.length, r = {};
        for(var i = 1; i < len; i++){
            r[a[i]] = this.getStyle(el, a[i]);
        }
        return r;
    },
    clearOpacity : function(el){
        var s = el.style;
        if (isIE) {
            if(typeof s.filter == 'string' && (/alpha/i).test(s.filter)){
                s.filter = "";
            }
        } else {
            s.opacity = "";
            s["-moz-opacity"] = "";
            s["-khtml-opacity"] = "";
        }            
    },
    setOpacity : function(el, opacity){
        opacity = parseFloat(opacity);
        var s = el.style;
        if(isIE){
            s.zoom = 1;
            s.filter = (s.filter || '').replace(/alpha\([^\)]*\)/gi,"") +
                       (opacity == 1 ? "" : " alpha(opacity=" + opacity * 100 + ")");
        }else{
            s.opacity = opacity;
        }
    },
    getOpacity: function(el){
        var s = el.style;
        if(typeof s.filter == 'string'){
            var m = s.filter.match(/alpha\(opacity=(.*)\)/i);
            if(m){
                var fv = parseFloat(m[1]);
                if(!isNaN(fv)){
                    return fv ? fv / 100 : 0;
                }
            }
            return 1;    
        }else{
            return parseFloat(s.opacity) || 1;
        }        
    },
    //--------------    class操作   ------------------//
    addClass: function(el, className){
        if(className && !this.hasClass(el, className)){
            el.className = el.className + " " + className;
        }
    },
    removeClass: function(el, className){
        if(!className || !el.className){
            return
        }
        if(this.hasClass(el, className)){
            var re = classCache[className];
            if (!re) {
               re = new RegExp('(?:^|\\s+)' + className + '(?:\\s+|$)', "g");
               classCache[className] = re;
            }
            el.className = el.className.replace(re, " ");
        }
    },
    hasClass: function(el, className){
        return className && (' '+el.className+' ').indexOf(' '+className+' ') != -1;
    },
    toggleClass: function(el, className){
        if(hasClass(el, className)){
            this.removeClass(el, className);
        }else{
            this.addClass(el, className);
        }    
    },
    replaceClass : function(el, o, n){
        this.removeClass(el, o);
        this.addClass(el, n);        
    },    
    hover : function(el, overFn, outFn, scope){
        var preOverFn = function(e){
            if(!e.within(this, true)){
                overFn.apply(scope || this, arguments);
            }
        };
        var preOutFn = function(e){
            if(!e.within(this, true)){
                outFn.apply(scope || this, arguments);
            }
        };
        this.on(el, "mouseover", preOverFn, el);
        this.on(el, "mouseout", preOutFn, el);        
    },
    addClassOnOver : function(el, className){
        this.hover(
            el,
            function(){
                this.addClass(el, className);
            },
            function(){
                this.removeClass(el, className);
            },
            this
        );        
    },
    addClassOnFocus : function(el, className){
        this.on(el, "focus", function(){
            this.addClass(el, className);
        }, this);
        this.on(el, "blur", function(){
            this.removeClass(el, className);
        }, this);        
    },
    addClassOnClick : function(el, className){
        this.on(el, "mousedown", function(){
            this.addClass(el, className);            
            var fn = function(){
                this.removeClass(el, className);
                this.un(document, "mouseup", fn);
            };
            this.on(document, "mouseup", fn, this);
        }, this);
    },
    remove : function(n){
        if(n && n.parentNode && n.tagName != 'BODY'){
            n.parentNode.removeChild(n);
        }
    },
    insertElement: function(p, el, where){
        where = where.toLowerCase()
        if(p.insertAdjacentElement){
            p.insertAdjacentElement(where, el);          
        }else{
            switch(where){
            case "beforebegin":
                p.parentNode.insertBefore(el,p);
                break;
            case "afterbegin": 
                p.insertBefore(el, p.firstChild);
                break;
            case "beforeend":
                p.appendChild(el);
                break;
            case "afterend":
                p.nextSibling ? p.parentNode.insertBefore(el,p.nextSibling) : p.parentNode.appendChild(el);		            
                break;
            }
        }
        switch(where){
            case "beforebegin":
                return p.previousSibling;
            case "afterbegin":
                return p.firstChild;
            case "beforeend":
                return p.lastChild;
            case "afterend":
                return p.nextSibling;
            default:
                throw new Error();                
        }
    },
    insertHtml : function(el, html, where){
        where = where.toLowerCase();
        if(el.insertAdjacentHTML){                
            switch(where){
                case "beforebegin":
                    el.insertAdjacentHTML('BeforeBegin', html);
                    return el.previousSibling;
                case "afterbegin":
                    el.insertAdjacentHTML('AfterBegin', html);
                    return el.firstChild;
                case "beforeend":
                    el.insertAdjacentHTML('BeforeEnd', html);
                    return el.lastChild;
                case "afterend":
                    el.insertAdjacentHTML('AfterEnd', html);
                    return el.nextSibling;
            }
            throw 'Illegal insertion point -> "' + where + '"';
        }
        var range = el.ownerDocument.createRange();
        var frag;
        switch(where){
         case "beforebegin":
            range.setStartBefore(el);
            frag = range.createContextualFragment(html);
            el.parentNode.insertBefore(frag, el);
            return el.previousSibling;
         case "afterbegin":
            if(el.firstChild){
                range.setStartBefore(el.firstChild);
                frag = range.createContextualFragment(html);
                el.insertBefore(frag, el.firstChild);
                return el.firstChild;
            }else{
                el.innerHTML = html;
                return el.firstChild;
            }
        case "beforeend":
            if(el.lastChild){
                range.setStartAfter(el.lastChild);
                frag = range.createContextualFragment(html);
                el.appendChild(frag);
                return el.lastChild;
            }else{
                el.innerHTML = html;
                return el.lastChild;
            }
        case "afterend":
            range.setStartAfter(el);
            frag = range.createContextualFragment(html);
            el.parentNode.insertBefore(frag, el.nextSibling);
            return el.nextSibling;
        }
        throw 'Illegal insertion point -> "' + where + '"';
    },
    insert: function(p, c, where){
        return this[typeof c == 'string' ? 'insertHtml' : 'insertElement'](p, c, where);
    },
    append: function(el, c){
        return this.insert(el, c, 'beforeend');
    },
    preend: function(el, c){
        return this.insert(el, c, 'afterbegin');
    },
    after: function(el, c){
        return this.insert(el, c, 'afterend');
    },
    before: function(el, c){
        return this.insert(el, c, 'beforebegin');
    },
    overwrite : function(p, o){
        p.innerHTML = '';
        return this.append(p, o);
    },
    replace: function(sel,tel){     //tel替换到sel的位置,sel被删除        
        if(sel.parentNode) sel.parentNode.replaceChild(tel,sel);
    },
    empty: function(el){
        if(el.innerHTML) el.innerHTML = '';
        else el.value = '';
    },
    contains : function(p, c) {
        p = $(p);
        c = $(c);
        if (!p || !c) {
            return false;
        }
        if(p === c) return true;
        if (p.contains && !isSafari) {
            return p.contains(c);
        } else if (p.compareDocumentPosition) {
            return !!(p.compareDocumentPosition(c) & 16);
        } else {
            var parent = c.parentNode;
            while (parent) {
                if (parent == p) {
                    return true;
                }
                else if (!parent.tagName || parent.tagName.toUpperCase() == "HTML") {
                    return false;
                }
                parent = parent.parentNode;
            }
            return false;
        }
    },
    mask : function(el, msg, msgCls){
        if(this.getStyle(el, "position") == "static" && el != document.body){
            this.setStyle(el, "position", "relative");
        }
        this.unmask(el);

        el._mask = this.append(el, '<div class="mask-cover"></div>');
        this.addClass(el, "masked");        
        
//        if(isIE && !(isIE7 && isStrict) && this.getStyle(el, 'height') == 'auto'){ // ie will not expand full height automatically
//            this._mask.setSize(this.dom.clientWidth, this.getHeight());
//        }
    
        //el._mask.style.height = Math.max(document.body.clientHeight, document.body.scrollHeight)+'px';
        return el._mask;
    },
    unmask : function(el){
        if(el==null) return;
        if(el._mask){
            if(el._maskMsg){
                this.remove(el._maskMsg);
                el._maskMsg = null;
            }
            this.remove(el._mask);
            el._mask = null;
        }
        this.removeClass(el, "masked");
    },
    isMasked : function(el){
        return el._mask && el._mask.style.display != 'none';
    }
};    
})();
if(window.Expo) Expo.util.Dom = Dom;
var Fx = {
    
}

Fx.Base = function(options){
    this.options = apply({		
		onStart: this.onStart.bind(this),
		onStep: this.onStep.bind(this),
		onComplete: this.onComplete.bind(this),		
		//transition: Anim.Transitions.Back.easeIn,
		duration: 500,
		fps: 50,
		unit: 'px',
		wait: true
    }, options);    
    
    this.listeners = this.options.listeners;
    this.addEvents(
        'start',
        'step',
        'complete',
        'chaincomplete'
    );
    Fx.Base.superclass.constructor.call(this, this.options);
    
}
extend(Fx.Base, Observable,{
	chain: function(fn){	
		this.chains = this.chains || [];
		this.chains.push(fn);
		return this;
	},
	callChain: function(){
		if (this.chains && this.chains.length) {
		    var c = this.chains.shift()//.defer(1, this);
		    c.call(this);
        }		    
		return this;
	},
	clearChain: function(){
		this.chains = [];
		return this;
	},
	hasChain: function(){
	    return this.chains && this.chains.length > 0;
	},
    
    start: function(o){
        //wait为true,则表示一定将当前效果执行完后才可以继续
        if(this.anim && this.options.wait) return false;
        this.stop();
        
        this.fromTo = o || {};
        
        this.anim = new Anim(this.options);
        this.anim.start();
        return this;
    },
    stop: function(){
        if(this.anim) {
            var t = this.anim.stop();
            this.anim = null;
            return t;
        }
    },
    
    onStart: function(){    
        this.fireEvent('start', this);
    },
    onStep: function(delta){        
        this.fromTo = this.compute(delta);
        this.fireEvent('step', delta, this);                
    },
    compute: function(delta){
        var fromTo = this.fromTo;
        for(var p in fromTo){
            var v = fromTo[p];
            var value = v[0];
            v[1] = v[1] || this.options.unit;
            if(value[0] instanceof Array){
                var now = [];
                for(var i=0,l=value[0].length; i<l; i++){
                    now[i] = parseInt(Anim.compute(value[0][i], value[1][i], delta));
                    if(now[i] < 0) now[i] = 0;
                    if(now[i] > 255) now[i] = 255;
                }
                value[2] = now;
            }else{
                value[2] = Anim.compute(value[0], value[1], delta);            
            }
        }
        return fromTo;
    },
    onComplete: function(cancel){
        this.fireEvent('complete', cancel, this);
        this.anim = null;
        if(this.hasChain()){
            this.callChain();
        }else{
            this.fireEvent('chaincomplete', cancel, this);
        }
        
    }
});

/*
    Fx.Style:
        1.保存style配置对象
        2.
*/
Fx.Style = extend(Fx.Base, {
    onStart: function(){
        //如果没有配置初始值,则从元素上获取此值
        //debugger
        var ft = this.fromTo;
        var el = this.options.el;        
        for(var p in ft){
            var v = ft[p];
            //if(p == 'color') debugger
            var from = Anim.getStyle(this.options.el, p);
            
            if(!(v[0] instanceof Array)){
                v[0] = [from,v[0]];
            }else if(v[0].length < 2){
                v[0] = [from,v[0][0]];
            }
            
            var value = v[0];
            if(p.contains('olor')){            
                value[0] = Anim.parseColorArray(value[0]);
                value[1] = Anim.parseColorArray(value[1]);
            }else{
                value[0] = parseFloat(value[0]);
                value[1] = parseFloat(value[1]);      
            }
        }
        Fx.Style.superclass.onStart.call(this);
    },
    onStep: function(delta){                
        Fx.Style.superclass.onStep.call(this, delta);
        
        var ft = this.fromTo;
        var el = this.options.el;
        var dh = Expo.util.Dom;
        var el = this.options.el;
        for(var p in ft){
            var v = ft[p];
            var value = parseInt(v[0][2]);
            if(p.contains('olor')){
                value = value.rgbToHex();
            }else{
                value += v[1];
            }
            dh.setStyle(el, p, value);                        
        }        
    },    
    onComplete: function(cancel){
        
        
        Fx.Style.superclass.onComplete.call(this, cancel);
    }
});
/*

    var fx = new Fx({
    });
    
    fx.chain(function(){
        this.start();
    }).chain(function(){
        this.start();
    }).start({
    });
*/
if(window.Expo) Expo.util.Fx = Fx;
/*
    <form id="history-form" style="display:none;">
        <input type="hidden" id="history-field" />
        <iframe id="history-frame"></iframe>
    </form>
*/
var History;
(function(){
    var iframe, hiddenField;
    var ready = false;
    var currentToken;
    
    function getHash(){
        var href = top.location.href, i = href.indexOf('#');
        return i >= 0 ? href.substr(i+1) : null;
    }
    function doSave(){
        hiddenField.value = currentToken;
    }
    function handleStateChange(token){
        currentToken = token;
        History.fireEvent('change', token);
    }
    function updateIFrame(token){
    //if(History.doWork){         //控制是否增加历史记录
        var html = ['<html><body><div id="state">',token,'</div><script type="text/javascript">try{document.domain="'+document.domain+'";}catch(e){}</script></body></html>'].join('');
        try{
            var doc = iframe.contentWindow.document;
            doc.open();
            doc.write(html);
            doc.close();
            return true;
        }catch(e){
            return false;
        }
    }
    function checkIFrame(){
        try{
            if(!iframe.contentWindow || !iframe.contentWindow.document){
                setTimeout(checkIFrame, 10);
                return;
            }
        }catch(e){
            setTimeout(checkIFrame, 10);
            return;
        }
        
        var doc = iframe.contentWindow.document;
        var elem = doc.getElementById('state');
        var token = elem ? elem.innerText : null;
        
        var hash //= getHash();
        
        //定时检查hash标记,如果不一样,则激发历史change事件
        setInterval(function(){
            doc = iframe.contentWindow.document;
            elem = doc.getElementById('state');
            
            var newtoken = elem ? elem.innerText : null;
            
            var newHash = getHash();
            
            if(newtoken !== token){
                token = newtoken;
                handleStateChange(token);
                top.location.hash = token;      //更新历史标记
                hash = token;
                doSave();
            }else if(newHash !== hash){     //如果hash变化,则通过改变iframe,在IE下获得历史改变.在下个定时中,就可以得到token的不同,从而激发(缓了一次)
                hash = newHash;                
                updateIFrame(newHash);                
            }
        }, 25);
        
        ready = true;
        
        History.fireEvent('ready', History);
    }
    
    function startUp(){
        currentToken = hiddenField.value;
        if(isIE){
            checkIFrame();
        }else{
            var hash //= getHash();
            setInterval(function(){
                var newHash = getHash();
                if (newHash !== hash) {
                    hash = newHash;
                    handleStateChange(hash);
                    doSave();
                }
            }, 50);
            ready = true;
            History.fireEvent('ready', History);
        }
    }
    
    History = {
        doWork: true,
        fieldId: 'history-field',
        iframeId: 'history-frame',        
        init: function(proxy){        
            var el = document.createElement('form');
            el.id = "history-form";
            el.style.display = 'none';
            document.body.appendChild(el);
            var s = '';
            if(isIE){
                s = '<iframe id="history-frame" src="'+proxy+'"></iframe>';
            }
            el.innerHTML = '<input type="hidden" id="history-field" />'+s;
            hiddenField = document.getElementById(this.fieldId);
            iframe = document.getElementById(this.iframeId);
            this.addEvents('change');
            startUp();
        },
        add: function(token, preventDup){
            if(preventDup !== false){
                if(this.getToken() == token){
                    return true;
                }
            }
            if (isIE) {
                return updateIFrame(token);
            } else {
                top.location.hash = token;
                return true;
            }
        },
        back: function(){
            history.go(-1);
        },
        forward: function(){
            history.go(1);
        },
        getToken: function(){
            return ready ? currentToken : getHash();
        }
    }
    apply(History, new Observable());            
})();

if(window.Expo) Expo.util.History = History;
Json = new (function(){
    var useHasOwn = !!{}.hasOwnProperty;

    // crashes Safari in some instances
    //var validRE = /^("(\\.|[^"\\\n\r])*?"|[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t])+?$/;

    var pad = function(n) {
        return n < 10 ? "0" + n : n;
    };

    var m = {
        "\b": '\\b',
        "\t": '\\t',
        "\n": '\\n',
        "\f": '\\f',
        "\r": '\\r',
        '"' : '\\"',
        "\\": '\\\\'
    };

    var encodeString = function(s){
        if (/["\\\x00-\x1f]/.test(s)) {
            return '"' + s.replace(/([\x00-\x1f\\"])/g, function(a, b) {
                var c = m[b];
                if(c){
                    return c;
                }
                c = b.charCodeAt();
                return "\\u00" +
                    Math.floor(c / 16).toString(16) +
                    (c % 16).toString(16);
            }) + '"';
        }
        return '"' + s + '"';
    };

    var encodeArray = function(o){
        var a = ["["], b, i, l = o.length, v;
            for (i = 0; i < l; i += 1) {
                v = o[i];
                switch (typeof v) {
                    case "undefined":
                    case "function":
                    case "unknown":
                        break;
                    default:
                        if (b) {
                            a[a.length] = ',';
                        }
                        a[a.length] = v === null ? "null" : Json.encode(v);
                        b = true;
                }
            }
            a[a.length] = "]";
            return a.join("");
    };

    this.encodeDate = function(o){
        return '"' + o.getFullYear() + "-" +
                pad(o.getMonth() + 1) + "-" +
                pad(o.getDate()) + "T" +
                pad(o.getHours()) + ":" +
                pad(o.getMinutes()) + ":" +
                pad(o.getSeconds()) + '"';
    };
    this.encode = function(o){
        if(typeof o == "undefined" || o === null){
            return "null";
        }else if(o instanceof Array){
            return encodeArray(o);
        }else if(o.getFullYear){
            return Json.encodeDate(o);
        }else if(typeof o == "string"){
            return encodeString(o);
        }else if(typeof o == "number"){
            return isFinite(o) ? String(o) : "null";
        }else if(typeof o == "boolean"){
            return String(o);
        }else {
            var a = ["{"], b, i, v;
            for (i in o) {
                if(!useHasOwn || o.hasOwnProperty(i)) {
                    v = o[i];
                    switch (typeof v) {
                    case "undefined":
                    case "function":
                    case "unknown":
                        break;
                    default:
                        if(b){
                            a[a.length] = ',';
                        }
                        a[a.length] = this.encode(i) + ":" + (v === null ? "null" :this.encode(v));
                        b = true;
                    }
                }
            }
            a[a.length] = "}";
            return a.join("");
        }
    };
    this.decode = function(s){
        //return eval("(" + json + ')');
        if(s){		    
    	    var reg = /[\"\'](\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})[\"\']/g;
		    var exp = s.replace(reg, "new Date($1,$2-1,$3,$4,$5,$6)");    	    
            return eval('(' + exp + ')');
        }
    };
})();

if(window.Expo) Expo.util.Json = Json;
(function(){
    var re = /^\s+|\s+$/g;
    String.prototype.trim = function() {
        return this.replace(re, '');
    }
})();

Array.prototype.addRange = function(array){    
	for (var i = 0, j = array.length; i < j; i++) this[this.length] = array[i];
	return this;        
}

Template = function(tpl){
    if(tpl) this.set(tpl);
}
Template.id = 1000;
Template.prototype = {
    exprs: {'=':1,'if':1,'else if':1,'else':1,'for':1,'switch':1,'case':1,'default':1,'macro':1,'function':1,'/if':1,'/for':1,'/switch':1,'/macro':1,'/function':1},
    scriptRe: /(?:<script([^>]*)?>)((\n|\r|.)*?)(?:<\/script>)/ig,
    styleRe: /(?:<style([^>]*)?>)((\n|\r|.)*?)(?:<\/style>)/ig,
    emptyRe: /(?:<--)((\n|\r|.)*?)(?:-->)/ig,
    //将tpl分解成一个数组:htmlstring + evalContext
    set: function(tpl){
        tpl = String(tpl) || '';
        
        tpl = tpl.trim();
        
        this.compiled = false;
        
        var tplArray = [];//
        var evals = {};
        
        var start = -1;
        var end = -1;
        
        var clause = '';
        
        //分离出script,style
        this.scripts = tpl.match(this.scriptRe) || [];
        this.styles = tpl.match(this.styleRe) || [];
        tpl = tpl.replace(this.scriptRe, '');
        tpl = tpl.replace(this.styleRe, '');
        tpl = tpl.replace(this.emptyRe, '');
        //alert(tpl);
        
        function isSpace(str){
            var arr = str.split('');
            for(var i=0,l=arr.length; i<l; i++){
                var c = arr[i];
                if(c == '\r' || c == ' ' || c == '\n') continue;
                return false;
            }
            return true;
        }
        
        var arr = tpl.split('');
        var evalStart = false;
        for(var i=0,l=arr.length; i<l; i++){
            var c = arr[i];            
            if(c == '{' && arr[i-1] != '\\'){
                if(start != i){
                    var clause = tpl.substring(start, i);
                    if(!isSpace(clause)){
                        tplArray[tplArray.length] = clause;
                    }
                }
                start = i;
                evalStart = true;
            }else if(c == '}' && arr[i-1] != '\\'){
                var ai = tplArray.length;
                tplArray[ai] = tpl.substring(start+1, i);
                evals[ai] = true;
                
                start = i+1;
                evalStart = false;
            }else{
                if(!evalStart && start == -1){
                    start = i;
                }
            }
        }
        if(start != arr.length) tplArray[tplArray.length] = tpl.substring(start, arr.length);//tpl.substring(start+1, arr.length);
        
        this.tplArray = tplArray.addRange(this.styles).addRange(this.scripts);
       
        this.evals = evals;
        
        if(!this.compiled) this.compile();
    },
    //给有逻辑的节点,生成evalFn方法,比如组织和for,if等
    compile: function(){        
    //debugger
        this.compiled = true;
        
        var exprs = this.exprs;
        function getExpr(str){
            for(var i=9; i>=0; i--){
                var s = str.substr(0, i);
                if(exprs[s]) return s;                
            }
            return '';
        }
                
        var evals = this.evals;
        var tplArray = this.tplArray;
        var tplTree = [];

        //if(this.fnId) delete window[this.fnId];
        this.fnId = '___TEMPLATE_FN_'+new Date().getTime()+Template.id++;        
        
        var sb = ['window["'+this.fnId+'"] = function (tplArray){\n ',
                    //'var sb = [scripts.join(""),styles.join("")];\n'
                    'var _sb = [];\n'
                    ];
        for(var i=0,l=tplArray.length; i<l; i++){
            var clause = tplArray[i];
            if(evals[i]){              
                var expr = getExpr(clause);
                switch(expr){
                case "=":                    
                    sb[sb.length] = '_sb[_sb.length] ' + clause + ';\n';
                break;
                case "if":
                    sb[sb.length] = clause + '{\n';
                break;                
                case "else if":
                    sb[sb.length] = '}'+clause+'{\n';
                break;
                case "else":
                    sb[sb.length] = '}else{\n';
                break;                
                case "/if":
                    sb[sb.length] = '}\n';
                break;
                case "for":
                    sb[sb.length] = clause + '{\n';
                break;                
                case "/for":
                    sb[sb.length] = '}\n';
                break;
                case "switch":
                    sb[sb.length] = clause + '{\n';
                break;
                case "case":
                    sb[sb.length] = clause + '\n';
                break;
                case "default":
                    sb[sb.length] = 'default\n';
                break;
                case "/switch":
                    sb[sb.length] = '}\n';
                break;
                case 'macro':
                case 'function':                
                    clause = clause.replace('macro', 'function');
                    sb[sb.length] = clause + '{\n var _sb = [];\n';
                break;
                case '/macro':
                case "/function":
                    sb[sb.length] = 'return _sb.join("");\n}\n';
                break;
                default:
                    sb[sb.length] = clause + '\n';
                }
            }else{
                sb[sb.length] = '_sb[_sb.length] = tplArray[' + i+'];\n';
            }
        }
        
        sb[sb.length] = 'return _sb.join("");\n}';        
        var fn = sb.join('');        
        
        eval(fn);                
    },
    callMacro: function(name){
        
    },
    //加入数据,直接依次遍历调用树枝的evalFn方法,拼接字符串返回
    run: function(data){
        if(!this.compiled) this.compile();
        return window[this.fnId].call(data, this.tplArray);
    }
}

if(window.Expo) Expo.util.Template = Template;
window.XMLDOM = function(markup) {
    if (!window.DOMParser) {
        var progIDs = [ 'Msxml2.DOMDocument.3.0', 'Msxml2.DOMDocument' ];
        for (var i = 0; i < progIDs.length; i++) {
            try {
                var xmlDOM = new ActiveXObject(progIDs[i]);
                xmlDOM.async = false;
                xmlDOM.loadXML(markup);
                xmlDOM.setProperty('SelectionLanguage', 'XPath');
                return xmlDOM;
            }
            catch (ex) {
            }
        }
        return null;
    }
    else {
        try {
            var domParser = new window.DOMParser();
            return domParser.parseFromString(markup, 'text/xml');
        } catch (ex) {
            return null;
        }
    }
    return null;
}

//andy:targetClass目标类实例， 将xml转换成制定对象的实例
function xmlTojson(text,targetInstance){
    if (!text) return null;
    if(typeof text !== 'string') return text;

    xml = new XMLDOM(text);
    
    var root = xml.documentElement;
    if(!root) return targetInstance || null;
    
    function filterNodes(cs){
        var children = [];
        for(var i=0,l=cs.length; i<l; i++){
            var c = cs[i];
            if(c.nodeType == 1 || c.nodeType == 4) {    //判断是否是<>标签节点,或者是内容节点都可以
                children.push(c);
            }
        }
        cs = children.length > 0 ? children : cs;
        return cs;
    }
    function parseNode(n, o){        
        o = o || {};
        //1)attributes        
        var as = n.attributes;
        for(var i=0, l=as.length; i<l; i++){
            var a = as[i];
            o[a.nodeName] = a.nodeValue;
        }
        //2)childNodes
        var cs = filterNodes(n.childNodes);
        for(var i=0, l=cs.length; i<l; i++){
            var c = cs[i];
                       
            var name = c.nodeName;
            if(c.nodeType ==3 || c.nodeType == 4) {
                return c.nodeValue;
            }else if(c.nodeType == 1){
                var value = parseNode(c);
                var old = o[name];
                if(!old) o[name] = value;
                else if(old){            
                    if(!(old instanceof Array)) {
                        o[name] = [old];
                    }
                    o[name].push(value);
                }            
            }
        }
        if(cs.length == 0) return '';
        return o;
    }    
    
    var json = parseNode(root);
    
    if(targetInstance!=null){
        // 开始遍历 
        for(var p in json){  
            // 方法 
            if(typeof(json[p])=="function"){  
                //obj[p](); 
            }else{  
                targetInstance[p]=json[p]
            }  
        }
        return  targetInstance;
    }else{
        return json;
    }
}/*  |xGv00|83e6ea0eba7356f966b9326b5b6065b4 */
