/**
 * Pyncer base library
 *
 * Author: Blake Hancock
 * http://torology.com
 *
 * Copyright 2009 Blake Hancock
 **/

(function(){
    if (typeof $ == "undefined") {
        $ = {};
    }

    function Pyncer() {
        this._modules = [];
        this.libUrl = false;
        this.baseUrl = false;
        this.pageX = 0;
        this.pageY = 0;
    }

    Pyncer.prototype = {
        init : function(args) {
            if (!this.libUrl) {
                this.libUrl = this.scriptURL("pyncer/pyncer.js", true);
            }

            for (key in args) {
                if (key == "modules") {
                    args[key].walk(this, function(k,v) {
                        v = v.trim();
                        if (this._modules.contains(v) === false) {
                            if (typeof $.p[v] == "undefined") { // If the module isnt loaded already (compressed into one .js file)
                                if (v == "swfu") {
                                    document.write('<sc'+'ript type="text/javascript" src="' + this.libUrl + 'swfupload/swfupload.js"></sc'+'ript>\n');
                                    document.write('<sc'+'ript type="text/javascript" src="' + this.libUrl + 'pyncer/pyncer_swfupload.js"></sc'+'ript>\n');
                                }
                                else if (v == "swfo") {
                                    document.write('<sc'+'ript type="text/javascript" src="' + this.libUrl + 'swfobject/swfobject.js"></sc'+'ript>\n');
                                }
                                else {
                                    document.write('<sc'+'ript type="text/javascript" src="' + this.libUrl + 'pyncer/pyncer_' + v + '.js"></sc'+'ript>\n');
                                }
                            }
                            this.modules.push(v);
                        }
                    });
                }
                else if (key == "baseUrl" && this.baseUrl === false) {
                    this.baseUrl = args[key];
                }
            }

            if (this.baseUrl === false) {
                this.baseUrl = this.scriptURL("scripts/pyncer/pyncer.js", true);
            }
        },

        element : function(id) {
            if(typeof(id) == "string") {
                id = document.getElementById(id);
            }
            return id;
        },

        create : function(type) {
            return document.createElement(type);
        },

        round : function(value, digits) {
            if (!digits) { digits = 0; }

            var mul = Math.pow(10, digits);
            value = Math.round(value * mul);
            value = value / mul;
            return value;
        },

        ensureNumeric : function(value, defval, decimals, min, max) {
            if (!decimals) { decimals = 0; }
            if (!min) { min = false; }
            if (!max) { max = false; }

            var re = new RegExp("^[-]?\\d+([.]\\d*)?$");
            if (value.match(re)) {
                if (min != false) {
                    value = Math.max(value, min);
                }
                if (max != false) {
                    value = Math.min(value, max);
                }
                if (decimals >= 0) {
                    value = this.round(value, decimals).toString();
                }
                else { value = value.toString(); }

                if (decimals > 0) {
                    var pos = value.lastIndexOf(".");
                    if (pos == -1) {
                           value += ".";
                           value = value.padRight("0", decimals);
                    }
                    else {
                        var len = decimals - (value.length - pos - 1);
                        value = value.padRight("0", len);
                    }
                }
                return value;
            }
            else {
                if (defval) { return defval; }
                else { return 0; }
            }
        },

        cookie : function(name, value) {
            if (typeof value == "undefined") {
                var start, end;
                if (document.cookie.length > 0) {
                    start = document.cookie.indexOf(name + "=");
                    if (start != -1) {
                        start = start + name.length + 1;
                        end = document.cookie.indexOf(";", start);
                        if (end == -1) { end = document.cookie.length; }
                        return unescape(document.cookie.substring(start, end));
                    }
                }
                return false;
            }
            else {
                throw "Not implemented!";
            }
        },

        redirect : function (url, delay) {
            if (delay) {
                setTimeout(function() { $.p.redirect(url); }, delay);
            }
            else {
                window.location = url;
            }
        },

        scriptURL : function(script, rel) {
            var src

            $("script").each(function(i){
                if (this.src && this.src.indexOf(script) != -1) {
                    src = this.src;
                    if (rel) {
                        src = src.substring(0, src.lastIndexOf(script));
                    }
                    else {
                        src = src.substring(0, src.lastIndexOf('/') + 1);
                    }
                    return false;
                }
            });

            if (src) {
                return src;
            }

            return false;
        },

        importStyleSheet : function(css, rel) {
            var exists;

            if (!rel && !css.startsWith(this.baseUrl)) {
                css = this.baseUrl + css;
            }

            $("link").each(function(i){
                if (rel) {
                    if (this.href && this.href.lastIndexOf(css) != -1) {
                        exists = true;
                        return false;
                    }
                }
                else if (this.href && this.href == css) {
                    exists = true;
                    return false;
                }
            });

            // Has the stylesheet been already added
            if (exists) { return; }

            if (rel) {
                if (rel.startsWith(this.baseUrl)) {
                    css = rel + css;
                }
                else {
                    css = this.baseUrl + rel + css;
                }
            }

            if (typeof document.createStyleSheet == "undefined") {
                var e, head = $("head").get()[0];
                e = $.p.create("link");
                e.rel = "stylesheet";
                e.href = css;

                if (head && head[0]) {
                    head.insertBefore(e, head.firstChild);
                }
            }
            else {
                document.createStyleSheet(css);
            }
        },

        disableSelection : function(id) {
            id = $.p.element(id);
            if (typeof id == "undefined") { return; }

            if (typeof id.onselectstar != "undefined") { // IE, Chrome
                id.onselectstart = function(){ return false; };
            }
            else if (typeof id.unselectable != "undefined") { // Opera (IE as well)
                id.unselectable = "on";
            }
            else if (typeof id.style.MozUserSelect != "undefined") { //Firefox route
                id.style.MozUserSelect = "none";
            }
        },

        enableSelection : function(id) {
            id = $.p.element(id);
            if (typeof id == "undefined") { return; }

            if (typeof id.onselectstart != "undefined") { // IE, Chrome
                id.onselectstart = null;
            }
            else if (typeof id.unselectable != "undefined") { // Opera (IE as well)
                id.unselectable = "off";
            }
            else if (typeof id.style.MozUserSelect != "undefined") { // Firefox
                id.style.MozUserSelect = "";
            }
        },

        absoluteUrl : function(url, base) {
            //alert(url);
            if (url.indexOf("://") < 0) {
                //alert("base: " + this.baseUrl);
                return (base ? base : this.baseUrl) + url;
            }
            return url;
        },

        extend : function(dest, src, override) {
            if (!dest) { dest = {}; }

            for (var key in src) {
                if (override || typeof dest[key] == "undefined") {
                    dest[key] = src[key];
                }
            }
            return dest;
        },

        match : function(obja, objb) {
            var key, c1 = 0, c2 = 0;
            for (key in obja) {
                if (obja[key] !== objb[key]) {
                    return false;
                }
                c1++; // Number of items obja
            }
            for (key in objb) {
                c2++; // Number of items objb
            }
            // If not the same number of items, then are not equal
            if (c1 != c2) { return false; }

            return true;
        },

        offset : function (id) {
            id = this.element(id);
            var offset = $(id).offset();
            return new $.p.Point(offset.left, offset.top);
        },

        pageOffset : function() {
            var x = (window.pageXOffset ? window.pageXOffset : (document.documentElement ? document.documentElement.scrollLeft : document.body.scrollLeft));
            var y = (window.pageYOffset ? window.pageYOffset : (document.documentElement ? document.documentElement.scrollTop : document.body.scrollTop));
            var w = (window.pageXOffset ? window.innerWidth : (document.documentElement ? document.documentElement.offsetWidth : document.body.offsetWidth));
            var h = (window.pageYOffset ? window.innerHeight : (document.documentElement ? document.documentElement.offsetHeight : document.body.offsetHeight));
            return new $.p.Rect(x, y, w, h);
        },

        hasQuery : function(url) {
            return (url.indexOf("?") != -1);
        },

        uniqueId : function (prefix) {
            if (prefix) { prefix = "p__" + prefix + "_"; }
            else { prefix = "p__id_"; }

            var id = prefix + Math.floor(Math.random() * 99999);
            while (this.element(id)) {
                id = prefix + Math.floor(Math.random() * 99999);
            }
            return id;
        },

        decodeJSON : function(json) {
            var jdata, pos = json.indexOf("{");
            if (pos >= 0) {
                json = json.substr(pos);
            }
            eval("jdata = " + json);
            return jdata;
        }
    };

    $.p = new Pyncer();
    $.p._ = {}; // Var storage

    // $.p.Class.extend is heavily based off of http://ejohn.org/blog/simple-javascript-inheritance/
    $.p._.Class = { // Store some required values outside of class
        initializing : false,
        fnTest : /xyz/.test(function(){xyz;}) ? /\b_base\b/ : /.*/
    }

    $.p.Class = function(){};

    // Create a new Class that inherits from this class
    $.p.Class.extend = function(prop) {
        var _base = this.prototype;

        // Instantiate a base class without initializing
        $.p._.Class.initializing = true;
        var prototype = new this();
        $.p._.Class.initializing = false;

        // Copy the properties over onto the new prototype
        for (var name in prop) {
          // Check if we're overwriting an existing function
          prototype[name] = typeof prop[name] == "function" && typeof _base[name] == "function" && $.p._.Class.fnTest.test(prop[name]) ?
            (function(name, fn) {
              return function() {
                var tmp = this._base;

                // Add a new ._base() method that is the same method but on the base-class
                this._base = _base[name];

                // The method only need to be bound temporarily, so we remove it when we're done executing
                var r = fn.apply(this, arguments);
                this._base = tmp;

                return r;
              };
            })(name, prop[name]) :
            prop[name];
        }

        function Class() { // The dummy class constructor
            if (!$.p._.Class.initializing && this.init) {
                this.init.apply(this, arguments);
            }
        }

        Class.prototype = prototype;        // Populate our constructed prototype object
        Class.constructor = Class;          // Enforce the constructor to be what we expect
        Class.extend = arguments.callee;    // And make this class extendable

        return Class;
    };

    $.p.EventHandler = $.p.Class.extend({
        // TODO: Extend to hold multiple event handlers for a single event
        init : function(object, events) {
            this._object = object;
            if (events) {
                this._events = events;
            }
            else { this._events = {}; }
        },

        call : function(name, args) {
            if (this.isEventSet(name)) {
                var tmp = this._events[name].call(this._object, args);
                if (typeof tmp == "undefined" || tmp !== false) {
                    return true;
                }
                else { return false; }
            }
            return true;
        },

        add : function(name, func) {
            this._events[name] = func;
        },

        remove : function(name) {
            delete(this._events[name]);
        },

        isEventSet : function(name) {
            if (this._events[name] && typeof(this._events[name] == "function")) {
                return true;
            }
            return false;
        }
    });

    $.p.Url = $.p.Class.extend({
        init : function(url) {
            if (typeof url == "object") {
                if (url.href) { // anchors, links, and window.location
                    url = url.href;
                }
                else if (url.action) { // Forms
                    url = url.action;
                }
                else {
                    url = url.toString();
                }
            }

            $.p.extend(this, this._parse(url), true);
        },

        hasAnchor : function() {
            return (this.anchor ? true : false);
        },

        hasQuery : function() {
            return (this.queryString ? true : false);
        },

        query : function(key, value) {
            var q, n;

            if (typeof key != "undefined") {
                q = this._parseQuery(this.queryString);
                if (typeof value != "undefined") {
                    if (!key) {
                        n = this._parseQuery(value);
                        $.p.array.walk(n, this, function(k, v) {
                            q[k] = v;
                        });
                    }
                    else {
                        if (value === false) {
                            delete q[key];
                        }
                        else {
                            q[key] = value;
                        }
                    }

                    this.queryString = this._combineQuery(q);
                }

                return q[key];
            }

            return this.queryString;
        },

        filterQuery : function (keys) {
            var q1 = this._parseQuery(this.queryString), q2 = {};

            $.p.array.walk(q1, this, function(k, v) {
                if (keys.contains(k) !== false) {
                    q2[k] = v;
                }
            });

            this.queryString = this._combineQuery(q2);
        },

        makeUrl : function() {
            var combine = '';

            if (this.url !== '') {
                combine += this.url;
            }

            if (this.queryString !== '') {
                combine += "?" + this.queryString;
            }

            if (this.anchor !== '') {
                combine += "#" + this.anchor;
            }

            return combine;
        },

        _parse : function(url) {
            var pos, parsed = {}, d;
            // url: http://sub.domain/path/index.php?key=value#anchor

            // Get hash
            pos = url.indexOf("#");
            if (pos >= 0) {
                parsed.anchor = url.substr(pos + 1);
                url = url.substr(0, pos);
                // url: http://sub.domain/path/index.php?key=value
            }
            else {
                parsed.anchor = '';
            }

            // Get query
            pos = url.indexOf("?");
            if (pos >= 0) {
                parsed.queryString = url.substr(pos + 1);
                url = url.substr(0, pos);
                // url: http://sub.domain/path/index.php
            }
            else {
                parsed.queryString = '';
            }

            // Remaining value is url
            parsed.url = url;

            // Relative or absolute based on whether the protocol exists or not
            pos = url.indexOf("://");
            if (pos == 0) {
                throw "Invalid protocol!";
            }
            else if (pos > 0) {
                parsed.relative = false;
                parsed.protocol = url.substr(0, pos);

                url = url.substr(pos + 3);
                // url: sub.domain/path/index.php
            }
            else {
                parsed.relative = true;
                parsed.protocol = "http";
            }

            // Parse out filename
            pos = url.lastIndexOf("/");
            if (pos >= 0) {
                d = url.substr(pos); // Get part after domain to check for file extension
                if (d.lastIndexOf(".") >= 0) {
                    parsed.filename = url.substr(pos + 1);
                    url = url.substr(0, pos + 1);
                    // url: sub.domain/path/
                }
                else if (!url.endsWith("/")) {
                    parsed.filename = '';
                    url += "/";
                    parsed.url += "/";
                }
            }
            else {
                parsed.filename = '';
                url += "/";
                parsed.url += "/";
            }

            // Parse out username and password
            pos = url.indexOf("@");
            if (pos >= 0) {
                d = url.substr(0, pos);
                url = url.substr(pos + 1);
                d = d.split(d);

                parsed.username = d[0];
                parsed.password = (typeof d[1] != "undefined" ? d[1] : '');
            }
            else {
                parsed.username = '';
                parsed.password = '';
            }

            // If no protocol and a sub or top level domain,
            // make it no logner relative
            if (parsed.relative) {
                pos = url.indexOf(".");
                if (pos >= 0) {
                    parsed.relative = false;
                    parsed.url = "http://" + parsed.url;
                }
            }

            // Parse out hostname and path
            if (parsed.relative) {
                parsed.hostname = '';
                parsed.path = url;
            }
            else {
                pos = url.indexOf("/"); // Path position

                parsed.path = url.substr(pos + 1);

                url = url.substr(0, pos);

                // Port
                pos = url.indexOf(":");
                if (pos >= 0) {
                    parsed.hostname = url.substr(0, pos);
                    parsed.port = url.substr(pos + 1);
                }
                else {
                    parsed.hostname = url;
                    parsed.port = '';
                }
            }

            // Remove any sub domains
            /*pos = url.lastIndexOf(".");
            pos = url.lastIndexOf(".", url.length - pos);
            if (pos >= 0) {
                url = url.substr(pos + 1);
            }*/

            return parsed;
        },

        _parseQuery : function(query) {
            var pos, q = {}, s, p;

            s = query.split("&");

            s.walk(this, function(k, v) {
                if (v) {
                    p = v.split("=");
                    if (p.length == 2) {
                        q[p[0]] = p[1];
                    }
                }
            });

            return q;
        },

        _combineQuery : function(query) {
            var q = '', key;

            for (key in query) {
                if (query[key] !== '' || query[key] !== false) {
                    if (!q) {
                        q = key + "=" + query[key];
                    }
                    else {
                        q += "&" + key + "=" + query[key];
                    }
                }
            }

            return q;
        }
    });

    $.p.image = {
        /**
         * Returns a $.p.Size object with the specified width and height
         * resized to fit the specified max width and height
         */
        resize : function(w, h, mw, mh) {
            var rw, rh, tmp;

            if (w > mw) {
                tmp = $.p.round(w * mh / mw);
                if (tmp < h) {
                    rh = mh;
                    rw = $.p.round(rh * w / h);
                }
                else {
                    rw = mw;
                    rh = $.p.round(rw * h / w);
                }
            } // size > maxsize (height)
            else if (h > mh) {
                rh = mh;
                rw = $.p.round(mh * w / h);
            }
            else {
                rw = w;
                rh = h;
            }

            return new $.p.Size(rw, rh);
        },

        preload : function(img) {
            for(var i = 0; i < arguments.length; ++i) {
                $("<img>").attr("src", arguments[i]);
            }
        }
    }

    $.p.Point = $.p.Class.extend({
        init : function(x, y) {
            this[0] = x;
            this[1] = y;
            this.left = x;
            this.top = y;
            this.x = x;
            this.y = y;
        }
    });

    $.p.Size = $.p.Class.extend({
        init : function(w, h) {
            this[0] = w;
            this[1] = h;
            this.width = w
            this.height = h;
        }
    });

    $.p.Rect = $.p.Point.extend({
        init : function(x, y, w, h) {
            this._base(x, y);

            this[2] = w;
            this[3] = h;
            this.right = x + w;
            this.bottom = y + h;
            this.width = w;
            this.height = h;
        },

        containsPoint : function(p) {
            if (p[0] >= this.left && p[0] <= this.right && p[1] >= this.top && p[1] <= this.bottom) {
                return true;
            }
            return false;
        },

        containsRect : function(r) {
            if (r.left >= this.left && r.right <= this.right && r.top >= this.top && r.bottom <= this.bottom) {
                return true;
            }
            return false;
        }
    });

    $.p.Position = $.p.Point.extend({
        init : function (x, y, r) {
            // Ensure the position fits inside the rect
            x = Math.min(Math.max(x, r.x), r.width);
            y = Math.min(Math.max(y, r.y), r.height);

            this._base(x, y);

            this.rect = r;
        },

        leftMost : function() {
            if (this.x < this.rect.width / 2) {
                return true;
            }

            return false;
        },

        rightMost : function() {
            return !this.leftMost();
        },

        topMost : function() {
            if (this.y < this.rect.height / 2) {
                return true;
            }

            return false;
        },

        bottomMost : function() {
            return !this.topMost();
        }
    });

    //  __noclear will not change in value
    //  __checked will make radio and checkboxes checked
    //  __form:name allows formData to distinguish forms within a form
    $.p.forms = {
        clear : function(f) {
            var elements, elm, i;
            f = $.p.element(f);

            elements = f.getElementsByTagName('input');
            for(i = 0; elm=elements.item(i++);) {
                if (!elm.className.match("__noclear")) {
                    if (elm.getAttribute("type") == "text") {
                        elm.value = "";
                    }
                    else if (elm.getAttribute("type") == "checkbox" || elm.getAttribute("type") == "radio") {
                        if (elm.className.match("__checked")) {
                            elm.checked = true;
                        }
                        else {
                             elm.checked = false;
                        }
                    }
                }
            }

            elements = f.getElementsByTagName("select");
            for(i = 0; elm=elements.item(i++); ) {
                elm.options.selectedIndex = 0;
            }
        },

        check : function(f) {
            var elements, elm, i;
            f = $.p.element(f);

            elements = f.getElementsByTagName('input');
            for(i = 0; elm=elements.item(i++); ) {
                if (elm.getAttribute("type") == "checkbox" || elm.getAttribute("type") == "radio") {
                    if (!elm.className.match("__noclear")) {
                        if (elm.checked == false) {
                            elm.click();
                        }
                    }
                }
            }
        },

        uncheck : function(f) {
            var elements, elm, i;
            f = $.p.element(f);

            elements = f.getElementsByTagName('input');
            for(i = 0; elm=elements.item(i++); ) {
                if (elm.getAttribute("type") == "checkbox" || elm.getAttribute("type") == "radio") {
                    if (!elm.className.match("__noclear")) {
                        if (elm.checked == true) {
                            elm.click();
                        }
                    }
                }
            }
        },

        reverseCheck : function(f) {
            var elements, elm, i;
            f = $.p.element(f);

            elements = f.getElementsByTagName('input');
            for(i = 0; elm=elements.item(i++); ) {
                if (elm.getAttribute("type") == "checkbox" || elm.getAttribute("type") == "radio") {
                    if (!elm.className.match("__noclear")) {
                        //elm.checked = !elm.checked;
                        elm.click();
                    }
                }
            }
        },

        formData : function(f, form) {
            var elements, elm, i, tmp, value

            f = $.p.element(f);
            elements = f.getElementsByTagName('input');

            for(i = 0; elm = elements.item(i); i++) {
                var type = elm.getAttribute("type");
                if (type != "button" && type != "submit" && type != "reset") {
                    if(!((type == "checkbox" || type == "radio") && elm.checked == false)) {
                        if (!form || elm.className.match("__form:" + form)) {
                            value = elm.value;

                            if (!tmp) {
                                tmp = elm.name + "=" + escape(encodeURIComponent(value));
                            }
                            else {
                                tmp += "&" + elm.name + "=" + escape(encodeURIComponent(value));
                            }
                        }
                    }
                }
            }

            elements = f.getElementsByTagName('select');
            for(i = 0; elm=elements.item(i); i++ ) {
                if (!form || elm.className.match("__form:" + form)) {
                    var value = elm.options[elm.selectedIndex].value
                    if (!tmp) {
                        tmp = elm.name + "=" + escape(encodeURIComponent(value));
                    }
                    else {
                        tmp += "&" + elm.name + "=" + escape(encodeURIComponent(value));
                    }
                }
            }

            elements = f.getElementsByTagName('textarea');
            for(i = 0; elm=elements.item(i); i++ ) {
                if (!form || elm.className.match("__form:" + form)) {
                    value = elm.value;

                    if (!tmp) {
                        tmp = elm.name + "=" + escape(encodeURIComponent(value));
                    }
                    else {
                        tmp += "&" + elm.name + "=" + escape(encodeURIComponent(value));
                    }
                }
            }
            if (!tmp) {
                tmp = false;
            }
            return tmp;
        },

        selectByValue : function(e, v) {
            e = $.p.element(e);

            var f = false;
            for (var i = 0; i < e.options.length; i++) {
                var option = e.options[i];

                if (option.value == v) {
                    option.selected = true;
                    f = true;
                }
                else {
                    option.selected = false;
                }
            }

            return f;
        },

        selectByText : function(e, t) {
            e = $.p.element(e);

            var f = false;
            for (var i = 0; i < e.options.length; i++) {
                var option = e.options[i];

                if (option.text == t) {
                    option.selected = true;
                    f = true;
                }
                else {
                    option.selected = false;
                }
            }

            return f;
        },

        selectValue : function(e) {
            e = $.p.element(e);
            return e.options[e.selectedIndex].value;
        },

        selectText : function(e) {
            e = $.p.element(e);
            return e.options[e.selectedIndex].text;
        },

        isNumericKey : function(e, neg) {
            var charCode = (e.which ? e.which : (typeof(event) != "undefined" ? event.keyCode : 0));
            if (charCode > 31 && charCode != 45 && charCode != 46 && (charCode < 48 || charCode > 57)) {
                return false;
            }
            else {
                if (!neg && charCode == 45) {
                    return false;
                }
                else {
                    return true;
                }
            }
        },

        ensureValueIsNumeric : function(id, decimals, min, max, empty) {
            var input = $.p.element(id), val = input.value.trim();
            if (!(val == "" && empty)) {
                val = $.p.ensureNumeric(val, input.defaultValue, decimals, min, max);
            }
            input.value = val;
        }
    }

    $.p.forms.Form = $.p.Class.extend({
        init : function(id, form) {
            var id = $.p.element(id);
            //alert(id.tagName);
            if (id.tagName == "FORM") {
                this._element = id;
                this._form = form;
            }
            else { throw "Element not a form!"; }
        },

        element : function(name) {
            var e = this._element.elements[name];
            if (e) {
                switch(e.tagName) {
                    case "INPUT":
                        if (e.getAttribute("type") == "checkbox" || e.getAttribute("type") == "radio") {
                            return new $.p.forms.CheckFormElement(e);
                        }
                        else { return new $.p.forms.FormElement(e); }
                        break;
                    case "TEXTAREA":
                        return new $.p.forms.FormElement(e);
                        break;
                    case "SELECT":
                        return new $.p.forms.SelectFormElement(e);
                        break;
                    default:
                        return e;
                }
            }
            else { throw "Form element does not exist in the form!"; }
        },

        clear : function() {
            return $.p.forms.clear(this._element);
        },

        formData : function() {
            return $.p.forms.formData(this._element, this._form);
        }
    });

    $.p.forms.FormElement = $.p.Class.extend({
        init : function(id) {
            this._element = $.p.element(id);
        },

        value : function(v) {
            if (typeof v != "undefined") {
                this._element.value = v;
            }
            return this._element.value;
        },

        element : function() {
            return this._element;
        }
    });

    $.p.forms.CheckFormElement = $.p.forms.FormElement.extend({
        init : function(id) {
            this._base(id);
        },

        checked : function(v) {
            if (typeof v != "undefined") {
                if (this._element.checked != v) {
                    this._element.click();
                }
            }
            return this._element.checked;
        }
    });

    $.p.forms.SelectFormElement = $.p.forms.FormElement.extend({
        init : function(id) {
            this._base(id);
        },

        value : function(v) {
            if (typeof v != "undefined") {
                $.p.forms.selectByValue(this.element, v)
            }
            return $.p.forms.selectValue(this.element);
        },

        selectByValue : function(v) {
            return $.p.forms.selectByValue(this.element, v);
        },

        selectByText : function(v) {
            return $.p.forms.selectByText(this.element, v);
        }
    });

    $.p.array = {
        contains : function(ary, value) {
            if ($.isArray(ary)) {
                for (var i = 0; i < ary.length; i++) {
                    if (ary[i] == value) { return i; }
                }
            }
            else {
                for (var key in ary) {
                    if (ary[key] == value) { return key; }
                }
            }

            return false;
        },

        removeItem : function(ary, key) {
            var nary;

            if ($.isArray(ary)) {
                nary = [];
                ary.walk(ary, function(k, v) {
                    if (k != key) {
                        nary.push(v);
                    }
                });
            }
            else {
                nary = {};

                for (var k in ary) {
                    if (k != key) {
                        nary[k] = ary[k];
                    }
                }
            }
            return nary;
        },

        removeEmpty : function(ary) {
            var nary;

            if ($.isArray(ary)) {
                nary = [];
                ary.walk(ary, function(k, v) {
                    if (v !== '') {
                        nary.push(v);
                    }
                });
            }
            else {
                nary = {};

                for (var key in ary) {
                    if (ary[key] !== '') {
                        nary[key] = ary[key];
                    }
                }
            }
            return nary;
        },

        walk : function(ary, o, fn, rtrn) {
            var i, tmp;
            if ($.isArray(ary)) {
                var len = ary.length;
                for (i = 0; i < len; i++) {
                    tmp = fn.call(o, i, ary[i]);
                    if (rtrn) {
                        if (typeof tmp != "undefined") {
                            return tmp;
                        }
                    }
                    else {
                        if (typeof tmp != "undefined") {
                            ary[i] = tmp;
                        }
                    }
                }
            }
            else {
                for (i in ary) {
                    tmp = fn.call(o, i, ary[i]);
                    if (rtrn) {
                        if (typeof tmp != "undefined") {
                            return tmp;
                        }
                    }
                    else {
                        if (typeof tmp != "undefined") {
                            ary[i] = tmp;
                        }
                    }
                }
            }
            if (rtrn) {
                return false;
            }
        }
    }

    Array.prototype = $.p.extend(Array.prototype, {
        contains : function(value) {
            return $.p.array.contains(this, value);
        },

        removeItem : function(key) {
            return $.p.array.removeItem(this, key);
        },

        removeEmpty : function() {
            return $.p.array.removeEmpty(this);
        },

        walk : function(o, fn, rtrn) {
            return $.p.array.walk(this, o, fn, rtrn);
        }
    });

    String.prototype = $.p.extend(String.prototype, {
        padLeft : function(value, times) {
            var str = "";
            for (var i = 0; i < times; i++) {
                str += value;
            }
            str += this;
            return str;
        },

        padRight : function(value, times) {
            var str = this;
            for (var i = 0; i < times; i++) {
                str += value;
            }
            return str;
        },

        trim : function() {
            return this.replace(/^\s\s*/, '').replace(/\s\s*$/, '');
        },

        trimLong : function() {
            var str = this;
            str = str.replace(/^\s+/, '');
            for (var i = str.length - 1; i > 0; i--) {
                if (/\S/.test(str.charAt(i))) {
                    str = str.substring(0, i + 1);
                    break;
                }
            }
            return str;
        },

        startsWith : function(sub) {
            if (this.substr(0, sub.length) == sub) {
                return true;
            }
            return false;
        },

        endsWith : function(sub) {
            if (this.substr(this.length - sub.length, sub.length) == sub) {
                return true;
            }
            return false;
        },

        parseOut : function(start, end, inc, count) {
            var posa, posb = 0, posc, posd, stra = '', strb = [];

            if (count === 0) { // Skip all the work if count is 0
                return [start, strb];
            }

            posa = this.indexOf(start);

            while (posa >= 0) {
                posc = this.indexOf(start, posa + 1); // Next start
                posd = this.indexOf(end, posa + 1); // Next end

                if (posc == posd) { // If start is the same as end go get next
                    posc = this.indexOf(start, posc + 1);
                }

                if (posd < 0) { break; } // No end item
                else if (posc < 0 || posd < posc) { // End item before next start item
                    // Add non parsed out string to stra
                    stra += this.substr(posb, posa - posb);
                    posb = posd + end.length;

                    if (inc) { posd = posb; }
                    else { posa += start.length; }

                    strb.push(this.substr(posa, posd - posa));

                    if (count && count <= strb.length) {
                        break;
                    }
                }

                posa = posc;
            }

            stra += this.substr(posb);

            return [stra, strb];
        }
    });

    $().mousemove(function(e){
        if ($.p) {
            $.p.distanceX = e.pageX - $.p.pageX;
            $.p.distanceY = e.pageY - $.p.pageY;
            $.p.pageX = e.pageX;
            $.p.pageY = e.pageY;
        }
    });
})();

(function($){
    // Gets the index of the parent element
    $.fn.parentIndex = function() {
        return this.parent().parent().children().index(this.parent());
    };

    // Gets the index of the closest parent element
    $.fn.closestIndex = function(query) {
        return this.closest(query).parent().children().index(this.closest(query));
    };

    // Mouse enter event with information on where the mouse entered the element
    $.fn.mouseenterposition = function(cb){
        return this.mouseenter(function(){
            var x, y, pos, r;

            pos = $(this).offset();
            x = $.p.pageX - pos["left"];
            y = $.p.pageY - pos["top"];
            r = new $.p.Rect(0, 0, $(this).width() + 1, $(this).height() + 1);

            pos = new $.p.Position(x, y, r);

            cb.call(this, pos);
        });
    };

})(jQuery);