﻿// contains Aris.js, Aris.DOM.js, Aris.Event.js
var Aris = new function() {
    var _overwrites = {};
    var _guid = 0;
    return {
        guid: function(id) {
            return ((id || '') + (_guid++));
        },

        register: function(obj, name) {
            if (obj) {
                if (name) {
                    var old = window[name];
                    window[name] = obj;
                    if (typeof old != 'undefined') _overwrites[name] = old;
                }
                if (typeof obj.init == 'function') { obj.init(); obj.init = function() { } };
            }
            return this;
        },

        restore: function(name/*,...*/) {
            Array.forEach(arguments, function(n) {
                if (n in _overwrites) {
                    window[n] = _overwrites[n];
                    delete _overwrites[n];
                }
            });
            return this;
        },

        create: function(sNamespace) {
            var s = sNamespace.split('.'), obj = window, i = 0;
            obj = window;
            while (i < s.length) {
                if (typeof obj[s[i]] == 'undefined')
                    obj[s[i]] = {};
                obj = obj[s[i++]];
            }
            return obj;
        }
    };
};


Aris.Utils = {
    override: function(obj, src/*, val*/) {
        if (arguments.length == 3) {
            var val = arguments[2];
            var ancestor = obj[src];
            if (ancestor && typeof val == 'function' && src != 'base') {
                var method = val;
                val = function() {
                    var old = this.base;
                    this.base = ancestor;
                    var rv = method.apply(this, arguments);
                    this.base = old;
                    return rv;
                };
                val.ancestor = ancestor;
            }
            obj[src] = val;
        } else {
            for (var key in src) if (!Object.prototype[key]) {
                $override(obj, key, src[key]);
            }
        }
        return obj;
    },

    extract: function(obj, prop/*,...*/) {
        var rv = {};
        if (obj) {
            for (var i = 1, p; p = arguments[i]; i++) {
                if (p in obj) {
                    rv[p] = obj[p];
                }
            }
        }
        return arguments.length == 2 ? rv[prop] : rv;
    },

    merge: function(dest, src) {
        dest = dest || {};
        if (src) {
            for (var prop in src) {
                dest[prop] = src[prop];
            }
        }
        return dest;
    },

    remove: function(obj, prop/*,...*/) {
        var rv = {};
        if (obj) {
            for (var i = 1, p; p = arguments[i]; i++) {
                if (p in obj) {
                    rv[p] = obj[p];
                    delete obj[p];
                }
            }
        }
        return arguments.length == 2 ? rv[prop] : rv;
    },

    serialize: function(obj, s1, s2) {
        s1 = s1 || '=';
        s2 = s2 || '&';
        var rv = [];
        forEach(obj, function(val, name) {
            rv.push(name + s1 + (val.join ? val.join(',') : val));
        });
        return rv.join(s2);
    },

    defined: function(obj) {
        var dummy; return obj !== dummy;
    }
};

for (var key in Aris.Utils) {
    Aris.register(Aris.Utils[key], '$' + key);
}

// array-like enumeration
if (!Array.forEach) { // mozilla already supports this
    Array.forEach = function(obj, callback, context) {
        for (var i = 0, len = obj.length; i < len; i++) {
            if ($defined(obj[i])) callback.call(context, obj[i], i, obj);
        }
    };
}
Aris.Utils.forEach = Array.forEach;

Function.prototype.method = function(n, fn) {
    $override(this.prototype, n, fn);
    return this;
};
Function.method('forEach', function(obj, callback, context) {
    for (var key in obj) {
        if (typeof this.prototype[key] == "undefined") {
            callback.call(context, obj[key], key, obj);
        }
    }
    return obj;
}).
method('extend', function(_inst, _static) {
    _inst = _inst || {};
    //var proto = new this;
    var members = ['constructor', 'toString', 'valueOf'];
    var proto = $extract.apply(this, [this.prototype].concat(members));
    var key, i = 0;
    while (key = members[i++]) if (_inst[key] != Object.prototype[key]) {
        $override(proto, key, _inst[key]);
    }
    $override(proto, this.prototype);
    $override(proto, _inst);

    var constructor = proto.constructor;
    var klass = function() {
        return constructor.apply(this, arguments);
    };
    klass.extend = this.extend;
    klass.ancestor = this;
    klass.prototype = proto;
    klass.prototype.constructor = klass;
    klass.toString = this.toString;
    $override(klass, _static);
    if (typeof klass.init == 'function') klass.init();
    return klass;
}).
method('bind', function(/* context, arguments... */) {
    var __method = this, args = $A(arguments), obj = args.shift();
    return function() { return __method.apply(obj, args); };
}).
method('delay', function(/* ms, context, arguments...*/) {
    var args = $A(arguments), ms = args.shift();
    return setTimeout(this.bind.apply(this, args), ms);
}).
method('inherits', function(src) {
    var dest = this;
    var i = 0;
    while (src = arguments[i]) {
        forEach(src, function(obj, name) {
            var over = true;
            if (name.charAt(0) == '@') {
                name = name.slice(1);
                over = !(name in dest.prototype);
            }
            if (over) { dest.method(name, obj); }
        });
        i++;
    }
    return dest;
});

// globally resolve forEach enumeration
var forEach = function(obj, callback, context) {
    if (obj) {
        var resolve = Object; // default
        if (obj instanceof Function) {
            // functions have a "length" property
            resolve = Function;
        } else if (obj.forEach instanceof Function) {
            // the object implements a custom forEach method so use that
            obj.forEach(callback, context);
            return;
        } else if (typeof obj.length == "number") {
            // the object is array-like
            resolve = Aris.Utils;
        }
        resolve.forEach(obj, callback, context);
        return;
    }
};

var $A = Array.from = function(enumerable) {
    return [].map.call(enumerable, function(v) { return v; });
};


Array.inherits({
    //javascript 1.6 array functions forEach, every, some, filter
    '@forEach': function(callback, context) {
        for (var i = 0, len = this.length; i < len; i++) {
            if (i in this)
                callback.call(context, this[i], i, this);
        }
    },

    '@filter': function(callback, context) {
        var rv = [];
        for (var i = 0, len = this.length; i < len; i++) {
            if (i in this) {
                if (callback.call(context, this[i], i, this))
                    rv.push(this[i]);
            }
        }
        return rv;
    },

    '@every': function(callback, context) {
        var rv = true;
        for (var i = 0, len = this.length; i < len; i++) {
            if (i in this && !callback.call(context, this[i], i, this))
                return false;
        }
        return true;
    },

    '@some': function(callback, context) {
        for (var i = 0, len = this.length; i < len; i++) {

            if (i in this && callback.call(context, this[i], i, this))
                return true;
        }
        return false;
    },

    '@map': function(callback, context) {
        var len = this.length;
        var rv = new Array(len);
        for (var i = 0; i < len; i++) {
            if (i in this)
                rv[i] = callback.call(context, this[i], i, this);
        }
        return rv;
    },

    '@indexOf': function(obj, start) {
        var len = this.length;
        var i = start || 0;
        if (i < 0) i += len;
        while (start < len) {
            if (i in this && this[i] === obj)
                return i;
            i++;
        }
        return -1;
    }
});

Aris.BOM = new function() {
    var ua = navigator.userAgent.toLowerCase();
    this.xpath = !!(document.evaluate);
    this.xml = !!(document.implementation && document.implementation.hasFeature('XML', '1.0'));
    if (window.ActiveXObject) { this.ie = this[window.XMLHttpRequest ? 'ie7' : 'ie6'] = true; }
    else if (document.childNodes && !document.all && !navigator.taintEnabled) { this.khtml = this.webkit = this[this.xpath ? 'webkit420' : 'webkit419'] = true; }
    else if (document.getBoxObjectFor !== null) { this.gecko = true; }
    this.mac = /mac/.test(ua);
    this.winCE = /windows ce/.test(ua);

    this.opera = /opera/.test(ua);

    this.is = function() {
        return [].every.call(arguments, function(condition) {
            return this[condition] === true;
        }, this);
    };
    this.has = function(name) {
        var n = name.replace('window.', '').split('.');
        var root = window;
        var i = 0, l = n.length;
        var rv = true;
        while (i < l) {
            if (!(n[i] in root)) {
                rv = false;
                break;
            }
            root = root[n[i++]];
        }
        return rv;
    };
    if (this.is('ie6')) try { document.execCommand("BackgroundImageCache", false, true); } catch (e) { };
};

Aris.Observer = function(owner) {
    this.fns = [];
    this.locked = false;
    this.expired = false;
    this.owner = owner;
}
Aris.Observer.inherits({
    add: function(fn, thisObj) {
        if (typeof fn != 'function') return;
        if (this.expired) {
            this.fns = [{ fn: fn, context: thisObj}];
            this.fire(this.owner, {});
        }
        else if (!this.fns.some(function(obj) { return obj.fn === fn; }))
            this.fns.push({ fn: fn, context: thisObj });
        return this;
    },
    remove: function(fn) {
        this.fns = this.fns.filter(function(el) {
            if (typeof fn == 'undefined' || (el.fn && el.fn !== fn)) {
                delete el;
                return false;
            }
            return true;
        });
        return this;
    },
    fire: function(sender, args) {
        var i = this.fns.length, el;
        if (i == 0) return this;
        while (el = this.fns[--i]) {
            if (el.fn.call(el.context, sender, args) === false)
                break;
        }
        return this;
    },
    expire: function() {
        this.expired = true;
        return this;
    }
});
Aris.Event = new function() {
    var _cache = [],
		_timer = null;
    var Observer = Aris.Observer;
    var _add = function() {
        if (document.addEventListener) {
            return function(element, type, handler) {
                element.addEventListener(type, handler, false);
            };
        } else if (document.attachEvent) {
            return function(element, type, handler) {
                element.attachEvent("on" + type, handler);
            };
        } else {
            return function() { }; // not supported
        }
    } ();

    var _remove = function() {
        if (document.removeEventListener) {
            return function(element, type, handler) {
                element.removeEventListener(type, handler, false);
            };
        } else if (document.detachEvent) {
            return function(element, type, handler) {
                element.detachEvent("on" + type, handler);
            };
        } else {
            return function() { };
        }
    } ();

    function _getCacheIndex(el, eType, fn) {
        for (var i = 0, len = _cache.length; i < len; i++) {
            var li = _cache[i];
            if (li && li[2] == fn && li[0] == el && li[1] == eType) return i;
        }
        return -1;
    };

    function _ready() {
        var AE = Aris.Event;
        if (!AE.pageIsReady) {
            AE.pageIsReady = true;
            AE.onDOMLoaded.fire(Aris.Event).expire();
            _ready = function() { };
        }
    };

    return {
        onDOMLoaded: new Observer(this),
        onPageUnloading: new Observer(this),
        onPageLoad: new Observer(this),
        pageIsReady: false,

        add: function(el, eType, fn, scope) {
            if (!el || !fn || !fn.call) return this;

            if (typeof el == "string") {
                if (this.pageIsReady) {
                    return this.add(document.getElementById(el), eType, fn, scope);
                }
                else {
                    this.onDOMReady.add(this.add.bind(this, el, eType, fn, scope));
                    return this;
                }
            }

            var wFn = (scope) ? this.bind(fn, scope, el) : this.bind(fn, el);
            var li = [el, eType, fn, wFn, scope];
            _cache.push(li);
            _add(el, eType, wFn);
            return this;
        }, //end add

        bind: function(fn, obj, orig) {
            var __method = fn;
            return function(e) {
                return __method.call(obj, e || window.event, orig);
            };
        },

        init: function() {
            var d = document, ie = /*@cc_on!@*/false;
            // for Mozilla/Opera 9
            if (d.addEventListener) {
                d.addEventListener("DOMContentLoaded", _ready, false);
            }

            // polling for no erros on ie
            if (ie) (function() {
                try {
                    // throws errors until after ondocumentready
                    d.documentElement.doScroll('left');
                } catch (e) {
                    setTimeout(arguments.callee, 100);
                    return;
                }
                // no errors, fire
                _ready();
            })();

            //for Safari/KTHML based browsers
            if (/KHTML/i.test(navigator.userAgent)) { // sniff
                _timer = setInterval(function() {
                    if (/loaded|complete/.test(document.readyState)) {
                        _ready(); // call the onload handler
                    }
                }, 100);
            }

            if (ie) this.add(window, 'unload', this.unLoad, this);
            this.add(window, 'load', this.load, this);
            return this;
        },

        load: function() {
            _ready();
            this.onPageLoad.fire(this).expire();
        },

        preventDefault: function(ev) {
            if (ev.preventDefault) { ev.preventDefault(); }
            else { ev.returnValue = false; }
        },

        remove: function(el, eType, fn, idx) {
            if (!el || !fn || !fn.call) return false;
            var cacheItem = null;
            if (typeof idx == "undefined") idx = _getCacheIndex(el, eType, fn);
            if (idx >= 0) cacheItem = _cache[idx];
            if (cacheItem) {
                _remove(el, eType, cacheItem[3]);
                delete cacheItem[3];
                delete cacheItem[2];
                delete cacheItem;
            }
            return this;
        },

        stopEvent: function(ev) {
            this.stopPropagation(ev);
            this.preventDefault(ev);
        },

        stopPropagation: function(ev) {
            if (ev.stopPropagation)
                ev.stopPropagation();
            else
                ev.cancelBubble = true;
        },

        unLoad: function(e) {
            this.onPageUnloading.fire(this);
            _cache.forEach(function(l, idx) {
                this.remove(l[0], l[1], l[2], idx);
            }, this);
        }
    };
};
Aris.register(Aris.Event);

Aris.DOM = new function() {
    var BOM = Aris.BOM;
    var ie = BOM.is('ie');
    var css = {
        float: ie ? 'styleFloat' : 'cssFloat'
    }
    var getCSS = function(prop) { return css[prop] || prop; };
    return {

        addClass: function(o, c1) {
            if (!this.hasClass(o, c1)) {
                o.className += o.className ? ' ' + c1 : c1;
            }
            return o;
        },

        adopt: function(o, a) {
            o.appendChild(a.parentNode.removeChild(a));
            return o;
        },

        clear: function(el) {
            while (el && el.hasChildNodes()) {
                var r = el.removeChild(el.firstChild);
            }
            return el;
        },

        create: function(htmlJson) {
            var elName = typeof htmlJson == 'string' ? htmlJson : htmlJson.nodeName;
            var props = arguments.length > 1 ? arguments[1] : htmlJson.props;
            if (BOM.is('ie') && props) {
                var el = [elName];
                ['name', 'type', 'checked'].forEach(function(val) {
                    if (props[val]) { el.push(val + '="' + $remove(props[val]) + '"'); }
                });
                if (el.length > 1) { elName = '<' + el.join(' ') + '>'; }
            }
            var el = document.createElement(elName);
            if (props) {
                var children = $remove(props, 'children');
                forEach(props, function(val, a) {
                    if (a == 'style') { DOM.setStyle(el, val); }
                    else { el[a] = val; }
                });
                if (typeof children == 'string') children = [children];
                if (children) Array.from(children).forEach(function(c) {
                    if (typeof c == 'string') { el.appendChild(document.createTextNode(c)); }
                    else if (c.nodeType == 3) { el.appendChild(document.createTextNode(c.nodeValue)); }
                    else if (c.nodeType == 1) { el.appendChild(c); }
                    else { el.appendChild(DOM.create(c)); }
                });
            }
            return el;
        },

        get: function(el) {
            if (typeof el == 'string')
                el = document.getElementById(el);
            return el;
        },

        getElementsByTagName: function(root, tag) {
            var node = root || document;
            tag = tag || '*';
            var els = node.getElementsByTagName(tag);
            if (!els.length && (tag == '*' && node.all)) els = node.all;
            return els;
        },

        getElementsByClass: function(searchClass, node, tag) {
            var cEls = [];
            var els = this.getElementsByTagName(node, tag);
            var elsLen = els.length;
            for (var i = 0, j = 0; i < elsLen; i++) {
                if (this.hasClass(els[i], searchClass)) { cEls[j++] = els[i]; }
            }
            return cEls;
        },

        getStyle: function() {
            if (document.defaultView && document.defaultView.getComputedStyle) {
                return function(o, prop) {
                    if (!o) return;
                    prop = getCSS(prop);
                    var s = document.defaultView.getComputedStyle(o, '');
                    return s ? s[prop] : o.style[prop];
                };
            }
            else if (BOM.is('ie')) {
                return function(o, prop) {
                    if (!o) return;
                    if (prop == 'opacity') {
                        var val = 100;
                        try { // will error if no DXImageTransform
                            val = o.filters['DXImageTransform.Microsoft.Alpha'].opacity;
                        } catch (e) {
                            try { // make sure its in the document
                                val = o.filters('alpha').opacity;
                            } catch (e) { }
                        }
                        return val / 100;
                    }
                    else {
                        prop = getCSS(prop);
                        return o.style[prop] || o.currentStyle[prop];
                    }
                };
            }
            else {
                return function(o, prop) { if (!o) return; return o.style[prop] };
            }
        } (),

        getStyles: function(o) {
            var rv = {};
            for (var i = 1, prop; prop = arguments[i]; i++) {
                rv[prop] = DOM.getStyle(o, prop);
                if (!rv[prop]) delete rv[prop];
            }
            return rv;
        },

        hasClass: function(o, c1) {
            return new RegExp('(^|\\s)' + c1 + '(\\s|$)').test(o.className)
        },

        removeClass: function(o, c1) {
            var rep = o.className.match(' ' + c1) ? ' ' + c1 : c1;
            o.className = o.className.replace(rep, '');
            return o;
        },

        setOpacity: function() {
            if (window.ActiveXObject) {
                return function(o, val) {
                    o.style.filter = "alpha(opacity:" + val * 100 + ")";
                    if (!o.hasLayout) o.style.zoom = 1;
                }
            }
            else {
                return function(o, val) {
                    o.style.opacity = val < 1 ? val : .999;
                };
            }
        } (),

        setStyle: function(el, style, val) {
            if (!el) return;
            if (arguments.length == 3)
                style == "opacity" ? DOM.setOpacity(el, val) : el.style[getCSS(style)] = val;
            else {
                if (typeof style == 'string') { el.style.cssText = style; }
                else for (var s in style) {
                    s == "opacity" ? DOM.setOpacity(el, style[s]) : el.style[getCSS(s)] = style[s];
                }
            }
            return el;
        },

        swapClass: function(o, c1, c2) {
            o.className = !this.hasClass(o, c1) ? o.className.replace(c2, c1) : o.className.replace(c1, c2);
            return o;
        },

        getPos: function(tEl, asArray) {
            var el = Aris.DOM.get(tEl);
            var pos = { top: 0, left: 0 };
            do {
                pos.top += el.offsetTop;
                pos.left += el.offsetLeft;
            } while (el = el.offsetParent);
            return asArray ? [pos.top, pos.left] : pos;
        },

        getDim: function(tEl, asArray) {
            var tEl = DOM.get(tEl);
            var dim = {};
            dim.width = tEl.offsetWidth;
            dim.height = tEl.offsetHeight;
            return asArray ? [dim.width, dim.height] : dim;
        },

        getLayout: function(el) {
            return $merge(this.getPos(el), this.getDim(el));
        }

    };
};