(function () {
    var Slatebox = function (_options) {
        var _sb = this;
        var slate = null;

        if (!(_sb instanceof Slatebox))
            return new Slatebox(_options);

        if (_sb.slate === undefined) {
            alert("You have not included a reference to Slatebox.slate.js!");
        }

        _sb.slates = new Array();
        _sb._options = _options;

        window.Slatebox.instance = _sb;
    };

    Slatebox.trim = function (str) {
        var str1 = str.replace(/^\s\s*/, ''),
        ws = /\s/,
        i = str1.length;
        while (ws.test(str1.charAt(-i)));
        return str1.slice(0, i + 1);
    };

    Slatebox.windowSize = function () {
        var w = 0;
        var h = 0;

        //IE
        if (!window.innerWidth) {
            //strict mode
            if (!(document.documentElement.clientWidth == 0)) {
                w = document.documentElement.clientWidth;
                h = document.documentElement.clientHeight;
            }
            //quirks mode
            else {
                w = document.body.clientWidth;
                h = document.body.clientHeight;
            }
        }
        //w3c
        else {
            w = window.innerWidth;
            h = window.innerHeight;
        }
        return { width: w, height: h };
    };

    Slatebox.clone = function (obj) {
        return JSON.parse(JSON.stringify(obj));

        /*
        if (obj == null || typeof (obj) != 'object')
        return obj;

        var temp = obj.constructor(); // changed

        for (var key in obj)
        temp[key] = Slatebox.clone(obj[key]);

        return temp;
        */
    };

    Slatebox.isEqualTo = function (obj1, obj2) {
        for (p in obj2) {
            if (typeof (obj1[p]) == 'undefined') { return false; }
        }
        for (p in obj2) {
            if (obj2[p]) {
                switch (typeof (obj2[p])) {
                    case 'object':
                        if (!obj2[p].equals(obj1[p])) { return false }; break;
                    case 'function':
                        if (typeof (obj1[p]) == 'undefined' || (p != 'equals' && obj2[p].toString() != obj1[p].toString())) { return false; }; break;
                    default:
                        if (obj2[p] != obj1[p]) { return false; }
                }
            } else {
                if (obj1[p]) {
                    return false;
                }
            }
        }
        for (p in obj1) {
            if (typeof (obj2[p]) == 'undefined') { return false; }
        }
        return true;
    }

    // stripped from jQuery, thanks John Resig 
    Slatebox.each = function (obj, fn) {
        if (!obj) { return; }

        var name, i = 0, length = obj.length;

        // object
        if (length === undefined) {
            for (name in obj) {
                if (fn.call(obj[name], name, obj[name]) === false) { break; }
            }

            // array
        } else {
            for (var value = obj[0];
			    i < length && fn.call(value, i, value) !== false; value = obj[++i]) {
            }
        }

        return obj;
    };

    Slatebox.isElement = function (o) {
        return (
            typeof HTMLElement === "object" ? o instanceof HTMLElement : //DOM2
            typeof o === "object" && o.nodeType === 1 && typeof o.nodeName === "string"
        );
    };

    Slatebox.isFunction = function (x) {
        return Object.prototype.toString.call(x) === "[object Function]";
    };

    Slatebox.isArray = function (o) {
        return Object.prototype.toString.call(o) === "[object Array]";
    };

    // convenience
    Slatebox.el = function (id) {
        return document.getElementById(id);
    };

    // used extensively. a very simple implementation. 
    Slatebox.extend = function (to, from, skipFuncs) {
        if (typeof from != 'object') { return to; }

        if (to && from) {
            Slatebox.each(from, function (name, value) {
                if (!skipFuncs || typeof value != 'function') {
                    to[name] = value;
                }
            });
        }

        return to;
    };

    // var arr = select("elem.className"); 
    Slatebox.select = function (query) {
        var index = query.indexOf(".");
        if (index != -1) {
            var tag = query.slice(0, index) || "*";
            var klass = query.slice(index + 1, query.length);
            var els = [];
            Slatebox.each(document.getElementsByTagName(tag), function () {
                if (this.className && this.className.indexOf(klass) != -1) {
                    els.push(this);
                }
            });
            return els;
        }
    };

    Slatebox.getKey = function (e) {
        var keyCode = 0;
        try { keyCode = e.keyCode; } catch (Err) { keyCode = e.which; }
        return keyCode;
    };

    // fix event inconsistencies across browsers
    Slatebox.stopEvent = function (e) {
        e = e || window.event;

        if (e.preventDefault) {
            e.stopPropagation();
            e.preventDefault();

        } else {
            e.returnValue = false;
            e.cancelBubble = true;
        }
        return false;
    };

    Slatebox.addEvent = function(obj, type, fn) {
        if (obj.attachEvent) {
            obj['e' + type + fn] = fn;
            obj[type + fn] = function () { obj['e' + type + fn](window.event); }
            obj.attachEvent('on' + type, obj[type + fn]);
        } else
            obj.addEventListener(type, fn, false);
    }
    Slatebox.removeEvent = function(obj, type, fn) {
        if (obj.detachEvent) {
            obj.detachEvent('on' + type, obj[type + fn]);
            obj[type + fn] = null;
        } else
            obj.removeEventListener(type, fn, false);
    }

    // push an event listener into existing array of listeners
    Slatebox.bind = function (to, evt, fn) {
        to[evt] = to[evt] || [];
        to[evt].push(fn);
    };

    Slatebox.imageExists = function (u, cb) {
        var _id = "temp_" + Slatebox.guid();
        var _img = document.body.appendChild(document.createElement("img"));
        _img.style.position = "absolute";
        _img.style.top = "-10000px";
        _img.style.left = "-10000px";
        _img.setAttribute("src", u);
        _img.setAttribute("id", _id);

        Slatebox.addEvent(_img, "load", function (e) {
            var d = Slatebox.getDimensions(_img);
            document.body.removeChild(_img);
            cb.apply(this, [true, d.width, d.height]);
        });

        Slatebox.addEvent(_img, "error", function (e) {
            document.body.removeChild(_img);
            cb.apply(this, [false]);
        });
    };

    Slatebox.urlExists = function (url) {
        var http = new XMLHttpRequest();
        http.open('GET', url, false);
        http.send();
        return http.status == 200;
    };

    Slatebox.ajax = function (u, f, d, v, x) {
        x = this.ActiveXObject;
        //the guid is essential to break the cache because ie8< seems to want to cache this. argh.
        u = [u, u.indexOf("?") === -1 ? "?" : "&", "guid=" + Slatebox.guid()].join("");
        x = new (x ? x : XMLHttpRequest)('Microsoft.XMLHTTP');
        var vx = d ? (v ? v : 'POST') : (v ? v : 'GET');
        x.open(vx, u, 1);
        x.setRequestHeader('Content-type', 'application/json; charset=utf-8');
        x.onreadystatechange = function () {
            x.readyState > 3 && f ? f(x.responseText, x) : 0
        };
        x.send(d);
    };

    var S4 = function () { return (((1 + Math.random()) * 0x10000) | 0).toString(16).substring(1); }
    Slatebox.guid = function () { return (S4() + S4() + S4()); }
    Slatebox.number = function () { return Math.floor(Math.random() * 9999) + 999; }

    var head = document.getElementsByTagName('head')[0], global = this;
    Slatebox.getJSON = function (url, callback) {
        id = S4() + S4();
        var script = document.createElement('script'), token = '__jsonp' + id;

        // callback should be a global function
        global[token] = callback;

        // url should have "?" parameter which is to be replaced with a global callback name
        script.src = url.replace(/\?(&|$)/, '__jsonp' + id + '$1');

        // clean up on load: remove script tag, null script variable and delete global callback function
        script.onload = function () {
            delete script;
            script = null;
            delete global[token];
        };
        head.appendChild(script);
    };

    Slatebox.positionedOffset = function (obj) {
        var curleft = 0;
        var curtop = 0;
        if (obj.offsetParent) {
            do {
                curleft += obj.offsetLeft;
                curtop += obj.offsetTop;
            } while (obj = obj.offsetParent);
        }
        return { left: curleft, top: curtop };
    };

    Slatebox.getDimensions = function (ele) {
        var width = 0, height = 0;
        if (typeof ele.clip !== "undefined") {
            width = ele.clip.width;
            height = ele.clip.height;
        } else {
            if (ele.style.pixelWidth) {
                width = ele.style.pixelWidth;
                height = ele.style.pixelHeight;
            } else {
                width = ele.offsetWidth;
                height = ele.offsetHeight;
            }
        }
        return { width: width, height: height };
    };

    Slatebox.isNumeric = function (n) {
        return !isNaN(parseFloat(n)) && isFinite(n);
    };

    Slatebox.isIE = function () {
        var version = 999; // we assume a sane browser
        if (navigator.appVersion.indexOf("MSIE") !== -1 && navigator.appVersion.indexOf("chromeframe") === -1)
            version = parseFloat(navigator.appVersion.split("MSIE")[1]);
        return version;
    };

    Slatebox.isIpad = function () {
        return navigator.userAgent.match(/iPad/i) !== null;
    };

    Slatebox.mousePos = function (e) {
        if (document.all) {
            mouseX = window.event.clientX; //document.body.scrollLeft; //(e.clientX || 0) +
            mouseY = window.event.clientY; //document.body.scrollTop;
        } else if (e.targetTouches) {
            if (e.targetTouches.length) {
                var t = e.targetTouches[0]; // touches.item(0);
                mouseX = t.clientX;
                mouseY = t.clientY;
                var _allTouches = [];
                for (var tx in e.targetTouches) {
                    _allTouches.push({ x: e.targetTouches[tx].clientX, y: e.targetTouches[tx].clientY });
                }
            }
            //}
        } else {
            mouseX = e.pageX;
            mouseY = e.pageY;
        }
        return { x: mouseX, y: mouseY, allTouches: _allTouches };
    };

    //    Slatebox.toJSON = function (obj) {
    //        var tmp = this.split("");
    //        for (var i = 0; i < tmp.length; i++) {
    //            var c = tmp[i];
    //            (c >= ' ') ?
    //			            (c == '\\') ? (tmp[i] = '\\\\') :
    //			            (c == '"') ? (tmp[i] = '\\"') : 0 :
    //		            (tmp[i] =
    //			            (c == '\n') ? '\\n' :
    //			            (c == '\r') ? '\\r' :
    //			            (c == '\t') ? '\\t' :
    //			            (c == '\b') ? '\\b' :
    //			            (c == '\f') ? '\\f' :
    //			            (c = c.charCodeAt(), ('\\u00' + ((c > 15) ? 1 : 0) + (c % 16)))
    //		            )
    //        }
    //        return '"' + tmp.join("") + '"';
    //    };

    Slatebox.ensureEle = function (el) {
        return (typeof el === 'string' ? document.getElementById(el) : el);
    };

    Slatebox.onOff = function (baseUrl, ele, callback) {
        var imgID = Slatebox.guid().replace('-', '').substring(0, 8);
        var _element = Slatebox.ensureEle(ele);
        _element.innerHTML = "<div style='cursor:pointer;overflow:hidden;width:53px;height:20px;'><img id='" + imgID + "' style='margin-top:0px;' src='" + baseUrl + "/public/images/checkbox-switch-stateful.png' alt='toggle'/>";
        Slatebox.el(imgID).onclick = function (e) {
            callback.apply(this, [imgID]);
        };
        return imgID;
    };

    Slatebox.isOn = function (ele) {
        var _ele = Slatebox.ensureEle(ele);
        if (_ele.style.marginTop === "0px") return false;
        return true;
    };

    Slatebox.toggleOnOff = function (ele) {
        var _ele = Slatebox.ensureEle(ele);
        if (_ele.style.marginTop === "0px") _ele.style.marginTop = "-22px";
        else _ele.style.marginTop = "0px";
    };

    Slatebox.div = function (p, x, y, w, h) {
        var _id = "temp_" + Slatebox.guid();
        var _div = p.appendChild(document.createElement("div"));
        _div.style.position = 'absolute';
        _div.style.top = y + "px";
        _div.style.left = x + "px";
        _div.style.width = w + "px";
        _div.style.height = h + "px";
        _div.style.border = "1px solid red";
        _div.style.backgroundColor = "#f8f8f8";
        _div.setAttribute("id", _id);
        return _id;
    };


    Slatebox.fn = Slatebox.prototype = {
        initNode: function () {
            var _node = this;
            $s.each($s.fn.node.fn, function () {
                if (Slatebox.isFunction(this)) {
                    if (arguments[0].substring(0, 1) === '_') {
                        this.apply(_node);
                        //delete Slatebox.fn.node.fn[arguments[0]];
                    }
                }
            });
        }
    };

    //helper methods
    if (!Array.prototype.filter) {
        Array.prototype.filter = function (fun /*, thisp */) {
            "use strict";

            if (this === void 0 || this === null)
                throw new TypeError();

            var t = Object(this);
            var len = t.length >>> 0;
            if (typeof fun !== "function")
                throw new TypeError();

            var res = [];
            var thisp = arguments[1];
            for (var i = 0; i < len; i++) {
                if (i in t) {
                    var val = t[i]; // in case fun mutates this
                    if (fun.call(thisp, val, i, t))
                        res.push(val);
                }
            }

            return res;
        };
    }

    window.Slatebox = Slatebox;
})();
(function ($s) {
    $s.fn.slate = function (_options) {
        if (!(this instanceof $s.fn.slate))
            return new $s.fn.slate(_options);

        var _slate = this;
        _slate.options = {
            id: $s.guid()
            , container: ''
            , instance: ''
            , name: ''
            , description: ''
            , containerStyle: {
                width: "auto"
                , height: "auto"
            }
            , viewPort: {
                allowDrag: true
                , originalWidth: 50000
                , width: 50000
                , height: 50000
                , left: 1000
                , top: 1000
                , zoom: { w: 50000, h: 50000, r: 1 }
            }
            , events: {
                onNodeDragged: null
            }
            , enabled: true
            , showBirdsEye: true
            , showMultiSelect: true
            , showZoom: true
            , showStatus: true
            , collaboration: { allow: true, allowPublic: false, key: "d7e6bb28-c9d3-4edc-a598-398d62afb4fd", showPanel: true, onCollaboration: null, panelContainer: null }
            , isPublic: false
        };

        //ensure indiv sections aren't wiped out by custom additions/changes
        var _iv = _slate.options.viewPort;
        var _cs = _slate.options.containerStyle;
        var _ie = _slate.options.events;
        var _c = _slate.options.collaboration;

        $s.extend(_slate.options, _options);
        $s.extend(_slate.options.collaboration, $s.extend(_c, _options.collaboration || {}));
        $s.extend(_slate.options.viewPort, $s.extend(_iv, _options.viewPort || {}));
        $s.extend(_slate.options.events, $s.extend(_ie, _options.events || {}));
        $s.extend(_slate.options.containerStyle, $s.extend(_cs, _options.containerStyle || {}));

        //ensure container is always an object
        if (!$s.isElement(_slate.options.container)) {
            _slate.options.container = $s.el(_slate.options.container);
        }

        var constants = {
            statusPanelAtRest: 33
            , statusPanelExpanded: 200
        };

        /*
        var comet = {
        client: fm.websync.client
        , util: fm.websync.utilities
        , json: fm.websync.utilities.json
        };
        */

        function url(opt) {
            return options.ajax.rootUrl + options.ajax.urlFlavor + opt
        };

        var glows = [];
        _slate.glow = function (obj) {
            glows.push(obj.glow());
            //setTimeout(function () { glows.length > 0 && _slate.unglow() }, 1000);
        };

        _slate.unglow = function () {
            $s.each(glows, function () {
                this.remove();
            });
            glows = [];
        };

        var tips = [];
        _slate.addtip = function (tip) {
            if (tip) tips.push(tip);
        };

        _slate.untooltip = function () {
            $s.each(tips, function () {
                this && this.remove();
            });
        };

        _slate.save = function () {
            alert(Slatebox.toJSON(_slate));
        };

        _slate.zoom = function (x, y, w, h, fit) {
            this.paper.setViewBox(x, y, w, h, fit);
        };

        _slate.zoomAround = function (pkg) {

            var zaxy = [], initX = _slate.options.viewPort.left, initY = _slate.options.viewPort.top, d = $s.getDimensions(_slate.options.container);
            for (var n in pkg.nodes) {
                var zNode = pkg.nodes[n];
                $s.each(_slate.nodes.allNodes, function () {
                    if (this.options.name === zNode) {
                        zaxy.push({ node: this, x: this.options.xPos - (d.width / 2), y: this.options.yPos - (d.height / 2), w: this.options.width, h: this.options.height });
                        $s.extend(zaxy[zaxy.length - 1], { origX: zaxy[zaxy.length - 1].x, origY: zaxy[zaxy.length - 1].y });
                        return;
                    }
                });
            };

            var val = _slate.options.viewPort.width;
            var zoom = [];
            $s.each(zaxy, function () {
                var targetVal = (Math.max(this.w, this.h) + 20) + 1100;
                zoom.push($s.extend(this, { startVal: val, endVal: targetVal, speed: this.speed || pkg.speed }));
                //zoom.push({ x: initX, y: initY, startVal: targetVal, endVal: val, speed: this.speed || pkg.speed });
            });

            var _step = 0;
            var animate = function () {
                if (_step < zoom.length) {
                    var z = zoom[_step];
                    var _val = z.startVal;
                    _slate.canvas.move({
                        x: z.x
                        , y: z.y
                        , dur: z.speed
                        , isAbsolute: true
                        , callbacks: {
                            after:
                                function () {
                                    _step++;
                                    var incr = 50, _done = false, _val = z.startVal;
                                    var interval = setInterval(function () {
                                        if (z.startVal > z.endVal) { _val -= incr; if (_val < z.endVal + incr) { clearInterval(interval); _done = true; } }
                                        else { _val += incr; if (_val > z.endVal + incr) { clearInterval(interval); _done = true; } }
                                        if (_done && _step <= zoom.length - 1) {

                                            //recalc the next move target
                                            var r = _slate.options.viewPort.originalWidth / _val;
                                            var origX = zoom[_step].x;
                                            zoom[_step].x = zoom[_step].x * r + d.width;
                                            zoom[_step].y = zoom[_step].y * r + d.height;

                                            var zx = _slate.options.viewPort.zoom;
                                            var vp = _slate.options.viewPort;
                                            vp.width = zx.w;
                                            vp.height = zx.h;
                                            vp.left = zx.l;
                                            vp.top = zx.t;

                                            zoom[_step].startVal = _val;
                                            var targetVal = (Math.max(zoom[_step].node.vect.attr("width"), zoom[_step].node.vect.attr("height")) + 20) + 1100;
                                            zoom[_step].endVal = targetVal;

                                            animate();
                                        } else {
                                            _slate.zoom(0, 0, _val, _val, false);
                                            _slate.canvas.resize(_val);
                                        }
                                    }, 10);
                                }
                            , during: function () {
                                /*
                                if (z.startVal > z.endVal && !_done) { _val -= incr; }
                                else if (!_done) { _val += incr; }
                                _slate.zoom(0, 0, _val, _val, false);
                                _slate.canvas.resize(_val);
                                */
                            }
                        }
                    });
                }
            };

            animate();
        };

        _slate.setSize = function (w, h) {
            this.paper.setSize(w, h);
        };

        _slate.loadJSON = function (_jsonSlate, blnPreserve) {
            var _enabled = this.options.enabled;
            if (blnPreserve === undefined) {
                this.paper.clear();
                _slate.nodes.allNodes = [];
            }

            var _loadedSlate = JSON.parse(_jsonSlate);
            $s.extend(this.options, _loadedSlate.options);

            var _deferredRelationships = [];
            $s.each(_loadedSlate.nodes, function () {
                var _boundTo = $s.instance.node(this.options);
                _slate.nodes.add(_boundTo);
                _deferredRelationships.push({ bt: _boundTo, json: this });
            });

            $s.each(_deferredRelationships, function () {
                var _bounded = this;
                _bounded.bt.addRelationships(_bounded.json, function (lines) {
                    _.invoke(lines, 'toFront');
                    _bounded.bt.vect.toFront();
                    _bounded.bt.text.toFront();
                    _bounded.bt.link.toFront();
                });
            });

            //zoom
            var _v = Math.max(this.options.viewPort.zoom.w, this.options.viewPort.zoom.h);
            this.zoom(0, 0, _v, _v, false);
            this.canvas.resize(_v);

            //reset disable if previously was disabled
            if (!_enabled) {
                _slate.disable();
            }
        };

        //the granularity is at the level of the node...
        _slate.exportDifference = function (compare) {
            var _difOpts = $s.extend({}, _slate.options);
            var _pc = _difOpts.collaboration.panelContainer;
            delete _difOpts.collaboration.panelContainer;
            delete _difOpts.container;
            delete _difOpts.events;
            var jsonSlate = { options: $s.clone(_difOpts), nodes: [] };

            $s.each(_slate.nodes.allNodes, function () {
                var _exists = false;
                var pn = this;
                $s.each(compare.nodes.allNodes, function () {
                    if (this.options.id === pn.options.id) {
                        _exists = true;
                        return;
                    }
                });
                if (!_exists) jsonSlate.nodes.push(pn.serialize());
            });

            _difOpts.collaboration.panelContainer = _pc;
            return JSON.stringify(jsonSlate);
        };

        _slate.exportJSON = function () {
            var _cont = _slate.options.container;
            var _pcont = _slate.options.collaboration.panelContainer || null;
            var _opts = _slate.options;
            delete _opts.container;
            delete _opts.collaboration.panelContainer;

            var jsonSlate = { options: $s.clone(_opts), nodes: [] };
            _slate.options.container = _cont;
            _slate.options.collaboration.panelContainer = _pcont;

            delete jsonSlate.options.events;
            delete jsonSlate.options.ajax;
            delete jsonSlate.options.container;

            var ma = [];
            $s.each(_slate.nodes.allNodes, function () {
                jsonSlate.nodes.push(this.serialize());
            });

            return JSON.stringify(jsonSlate);
        };

        _slate.snapshot = function () {
            var _snap = JSON.parse(_slate.exportJSON());
            _snap.nodes.allNodes = _snap.nodes;
            return _snap;
        };

        _slate.getOrientation = function (_nodesToOrient) {
            var orient = 'landscape', sWidth = _slate.options.viewPort.width, sHeight = _slate.options.viewPort.height, vpLeft = 0, vpTop = 0;

            var bb = new Array();
            bb['left'] = 99999; bb['right'] = 0; bb['top'] = 99999; bb['bottom'] = 0;

            var an = _nodesToOrient || _slate.nodes.allNodes;
            if (an.length > 0) {
                for (_px = 0; _px < an.length; _px++) {
                    //var sb = allNodes[_px].b.split(' ');
                    var sbw = 10;
                    //if (!isNaN(sb[0].replace('px', ''))) sbw = parseInt(sb[0].replace('px', ''));
                    var _bb = an[_px].vect.getBBox();

                    //var x = _bb.x + ((_bb.x / _slate.options.viewPort.zoom.r) - _bb.x);
                    var _r = _slate.options.viewPort.zoom.r || 1;
                    var x = _bb.x * _r;
                    var y = _bb.y * _r;
                    var w = _bb.width * _r;
                    var h = _bb.height * _r;

                    /*
                    var x = _bb.x;
                    var y = _bb.y;
                    var w = _bb.width;
                    var h = _bb.height;
                    */

                    bb['left'] = Math.abs(Math.min(bb['left'], x - sbw));
                    bb['right'] = Math.abs(Math.max(bb['right'], x + w + sbw));
                    bb['top'] = Math.abs(Math.min(bb['top'], y - sbw));
                    bb['bottom'] = Math.abs(Math.max(bb['bottom'], y + h + sbw));
                }

                var sWidth = bb['right'] - bb['left'];
                var sHeight = bb['bottom'] - bb['top'];

                if (sHeight > sWidth) {
                    orient = 'portrait';
                }
            }
            return { orientation: orient, height: sHeight, width: sWidth, left: bb['left'], top: bb['top'] };
        };

        _slate.resize = function (_size, dur, pad) {
            var _p = (pad || 0);
            if (_p < 6) _p = 6;
            _size = _size - ((_p * 2) || 0);
            var orx = _slate.getOrientation();
            var wp = (orx.width / _size) * _slate.options.viewPort.width;
            var hp = (orx.height / _size) * _slate.options.viewPort.height;
            var sp = Math.max(wp, hp);

            var _r = Math.max(_slate.options.viewPort.width, _slate.options.viewPort.height) / sp;
            var l = orx.left * _r - _p;
            var t = orx.top * _r - _p;

            _slate.zoom(0, 0, sp, sp, true);
            _slate.options.viewPort.zoom = { w: sp, h: sp, l: parseInt(l * -1), t: parseInt(t * -1), r: _slate.options.viewPort.originalWidth / sp };
            _slate.canvas.move({ x: l, y: t, dur: dur, isAbsolute: true });
        };

        _slate.stopEditing = function () {
            $s.each(_slate.nodes.allNodes, function () {
                this.editor && this.editor.end();
                this.images && this.images.end();
                this.links && this.links.end();
            });
        };

        var _prevEnabled;
        _slate.disable = function (exemptSlate) {
            $s.each(_slate.nodes.allNodes, function () {
                this.disable();
            });
            _prevEnabled = _slate.options.enabled;
            if (exemptSlate === undefined) {
                _slate.options.enabled = false;
                _slate.options.viewPort.allowDrag = false;
            }
        };

        _slate.enable = function () {
            $s.each(_slate.nodes.allNodes, function () {
                this.enable();
            });
            _slate.options.enabled = _prevEnabled;
            _slate.options.viewPort.allowDrag = true;
        };

        _slate.unMarkAll = function () {
            $s.each(_slate.nodes.allNodes, function () {
                this.unmark();
            });
        };

        var _changed = false;
        _slate.hasChanged = function () {
            return _changed;
        };

        _slate.setChanged = function (chg) {
            _changed = chg;
        };

        _slate.init = function (userName, userProfile, userIP, sizeOfBirdsEye) {

            //show zoom slider
            if (_slate.options.showZoom) {
                _slate.zoomSlider.show();
                _slate.zoomSlider.setValue(_slate.options.viewPort.width);
            }

            //show birdseye
            if (_slate.options.showBirdsEye) {
                if (_slate.birdseye.enabled()) {
                    _slate.birdseye.reload(_slate.exportJSON());
                } else {
                    _slate.birdseye.show({
                        size: sizeOfBirdsEye || 200
                        , onHandleMove: function (left, top) {
                        }
                    });
                }
            }

            //init collaboration
            if (_slate.options.collaboration && _slate.options.collaboration.allow) {
                //init collaboration
                _slate.websync.init(_slate.options.collaboration.key, userName, userProfile, userIP);
            }

            //init multi selection mode 
            if (_slate.options.showMultiSelect) {
                _slate.multiselection && _slate.multiselection.init();
            }

            //window.onerror = function (e) {
            //TODO: add error handling
            //};
        };

        //loads plugins
        $s.each($s.fn.slate.fn, function () {
            if ($s.isFunction(this)) {
                if (arguments[0].substring(0, 1) === '_') {
                    var p = arguments[0].replace("_", "");
                    _slate[p] = {};
                    _slate[p] = this.apply(_slate[p]);
                    _slate[p]._ = _slate; //_slate[p].parent = 
                    //delete _node["_" + p];
                }
            }
        });

        _slate.tempNodeId = $s.guid();

        if ($s.isFunction(_slate.options.onInitCompleted)) {
            _slate.options.onInitCompleted.apply(this);
        }

        return _slate;
    };
    $s.fn.slate.fn = $s.fn.slate.prototype = {};
})(Slatebox);(function ($s) {
    $s.fn.node = function (_options) {
        if (!(this instanceof $s.fn.node))
            return new $s.fn.node(_options);

        var _node = this, _marker;
        _node.options = {
            id: $s.guid()
            , name: ''
			, text: '' //text in the node
            , isPinned: false
            , isPinnedExact: false
            , pinnedRowCount: 5
			, image: '' //the image to show with the node
			, imageTiled: false
			, xPos: 0 //the initial x position relative to the node container
			, yPos: 0 //the initial y position relative to the node container
			, height: 10 //the height of the node
			, width: 10 //the width of the node
            , borderWidth: 2 //border width of the node
			, lineColor: '#000000' //line color
			, lineWidth: 3 //line width
            , lineOpacity: 1
			, allowDrag: true
            , allowMenu: true
			, backgroundColor: '#f8f8f8'
			, foregroundColor: '#000'
			, fontSize: 13
			, fontFamily: 'Trebuchet MS'
			, fontStyle: 'normal'
			, vectorPath: ''
			, rotationAngle: 0
			, scaleX: 1.0
			, scaleY: 1.0
            , link: { show: false, type: '', data: '', thumbnail: { width: 175, height: 175} }
        };

        $s.extend(_node.options, _options);

        _node.constants = {
            statusPanelAtRest: 33
			, statusPanelExpanded: 200
        };

        _node.refresh = function () {
            //_node.connectors.reposition();
            _node.relationships.refresh();
            _node.pinChildNodes(true);
            if (_node.options.isPinnedExact) {
                $s.each(_node.relationships.parents, function () {
                    this.parent.pinChildNodes(true);
                    this.parent.relationships.refresh();
                });
            }
        };

        _node.del = function () {
            var _reposParents = [], _unlinkId = _node.options.id;
            if (_node.options.isPinnedExact) {
                _reposParents = _node.relationships.parents;
            }

            _node.slate.nodes.closeAllMenus();
            _node.relationships.removeAll();

            _node.slate.options.viewPort.allowDrag = true;

            //unlink any links
            $s.each(_node.slate.nodes.allNodes, function () {
                if (this.options.link && this.options.link.show && this.options.link.data === _unlinkId) {
                    $s.extend(this.options.link, { show: false, type: '', data: '' });
                    this.link.hide();
                }
            });

            _node.slate.unMarkAll();
            _node.slate.nodes.remove(_node);

            //reposition parent's children if this was one of the deleted children...
            $s.each(_reposParents, function () {
                this.parent.pinChildNodes(true);
            });
        };

        function url(opt) {
            return _node.options.ajax.rootUrl + _node.options.ajax.urlFlavor + opt
        };

        _node.setStartDrag = function () {
            _node.slate.options.viewPort.allowDrag = false;
            _node.slate.stopEditing();
            _node.connectors && _node.connectors.reset();
            _node.context && _node.context.hide();
            if (_node.menu && _node.menu.isOpen()) {
                _node.menu.wasOpen = true;
            }
        };

        _node.setEndDrag = function () {
            if (_node.slate && _node.slate.options.enabled) //could be null in case of the tempNode
                _node.slate.options.viewPort.allowDrag = true;

            if (_node.menu && $s.isFunction(_node.menu.show) && _node.options.allowMenu)
                _node.menu.show();
        };

        _node.serialize = function () {
            var jsonNode = {};
            $s.extend(jsonNode, {
                options: _node.options
            });
            jsonNode.relationships = { parents: [], associations: [] }; //, children: []
            $s.each(_node.relationships.parents, function () {
                jsonNode.relationships.parents.push(bindRel(this));
            });
            $s.each(_node.relationships.associations, function () {
                jsonNode.relationships.associations.push(bindRel(this));
            });

            return jsonNode;
        };

        function bindRel(obj) {
            return {
                childId: obj.child.options.id
                , parentId: obj.parent.options.id
                , isStraightLine: obj.blnStraight
                , lineColor: obj.lineColor
                , lineOpacity: obj.lineOpacity
                , lineWidth: obj.lineWidth
                , showParentArrow: obj.showParentArrow || false
                , showChildArrow: obj.showChildArrow || false
            };
        };

        _node.addRelationships = function (json, cb) {
            //add parents
            var _lines = [];
            if (json.relationships) {
                if ($s.isArray(json.relationships.parents)) {
                    $s.each(json.relationships.parents, function () {
                        var _pr = this, _pn = null;
                        $s.each(_node.slate.nodes.allNodes, function () {
                            if (this['options']['id'] === _pr.parentId) {
                                _pn = this;
                                return;
                            }
                        });
                        if (_pn) {
                            var _conn = _node.relationships.addParent(_pn, _pr, false);
                            _lines.push(_conn.line);
                            _pn.pinChildNodes();
                            return;
                        }
                    });
                }

                //add associations
                if ($s.isArray(json.relationships.associations)) {
                    $s.each(json.relationships.associations, function () {
                        var _pr = this, _pn = null;
                        $s.each(_node.slate.nodes.allNodes, function () {
                            if (this.options.id === _pr.parentId && _node.options.id !== this.options.id) {
                                _pn = this;
                                return;
                            }
                        });
                        if (_pn) {
                            var _conn = _node.relationships.addAssociation(_pn, _pr);
                            _lines.push(_conn.line);
                            return;
                        }
                    });
                }
            }
            if ($s.isFunction(cb)) {
                cb.apply(this, [_lines]);
            }
        };

        //you can have a node with no parents or children, yet with relational lines attached
        //because another node has it as its child...returns an array of raphael connection objects
        //was using now not -- should this be kept?
        _node.allAttachedRelationships = function () {
            var relationships = [];
            $s.each(_node.slate.nodes.allNodes, function () {
                $s.each(this.relationships.children, function () {
                    if (this.parent.options.id === _node.options.id) {
                        relationships.push(this);
                    }
                    if (this.child.options.id === _node.options.id) {
                        relationships.push(this);
                    }
                });
                $s.each(this.relationships.parents, function () {
                    if (this.parent.options.id === _node.options.id) {
                        relationships.push(this);
                    }
                    if (this.child.options.id === _node.options.id) {
                        relationships.push(this);
                    }
                });
                $s.each(this.relationships.associations, function () {
                    if (this.parent.options.id === _node.options.id) {
                        relationships.push(this);
                    }
                    if (this.child.options.id === _node.options.id) {
                        relationships.push(this);
                    }
                });
            });
            return relationships;
        };

        _node.toFront = function () {
            $s.each(_node.relationships.children, function () {
                this.line.toFront();
                if (this.child.options.isPinnedExact) {
                    this.child.vect.toFront();
                    this.child.text.toFront();
                    this.child.link.toFront();
                }
            });
            _.invoke(_.pluck(_node.relationships.parents, "line"), "toFront");
            _.invoke(_.pluck(_node.relationships.associations, "line"), "toFront");

            _node.vect.toFront();
            _node.text.toFront();
            _node.link.toFront();
        };

        _node.toBack = function () {
            _node.link.toBack();
            _node.text.toBack();
            _node.vect.toBack();
            $s.each(_node.relationships.children, function () {
                this.line.toBack();
                if (this.child.options.isPinnedExact) {
                    this.child.link.toBack();
                    this.child.text.toBack();
                    this.child.vect.toBack();
                }
            });
            _.invoke(_.pluck(_node.relationships.parents, "line"), "toBack");
            _.invoke(_.pluck(_node.relationships.associations, "line"), "toBack");
        };

        _node.move = function (pkg) {
            //for text animation
            var lx = pkg.data.x - 5;
            var tx = pkg.data.x + (_node.options.width / 2);
            var ty = pkg.data.y + (_node.options.height / 2);

            if (_node.vect.type !== "rect") {
                tx = pkg.data.x;
                ty = pkg.data.y;
            }

            //always hide by default
            _node.link.hide();
            _node.menu.hide();

            var onAnimate = function () {
                var dx = _node.options.vectorPath === "ellipse" ? _node.vect.attr("cx") : _node.vect.attr("x");
                var dy = _node.options.vectorPath === "ellipse" ? _node.vect.attr("cy") : _node.vect.attr("y");

                _node.options.yPos = dy;
                _node.options.xPos = dx;

                _node.refresh();
                //_node.pinChildNodes();
                //_node.relationships.refresh();
            };

            eve.on("anim.frame.*", onAnimate);
            _node.text.animate({ x: tx, y: ty }, 500, ">");
            _node.link.animate({ x: lx, y: ty }, 500, ">");
            var att = _node.options.vectorPath === "ellipse" ? { cx: pkg.data.x, cy: pkg.data.y} : { x: pkg.data.x, y: pkg.data.y };
            _node.vect.animate(att, 500, ">", function () {
                _node.options.yPos = pkg.data.y;
                _node.options.xPos = pkg.data.x;
                _node.refresh();
                //_node.pinChildNodes();
                eve.unbind("anim.frame.*", onAnimate);

                //link
                var lc = _node.linkCoords();
                _node.link.transform(["t", lc.x, ",", lc.y, "s", ".8", ",", ".8", "r", "180"].join());
                if (_node.options.link.show) _node.link.show();

                _node.slate.birdseye && _node.slate.birdseye.refresh(true);
            });
        };

        _node.position = function (location, cb, easing, dur) {

            var _vpt = _node.vect.getBBox(), zr = _node.slate.options.viewPort.zoom.r;
            var d = $s.getDimensions(_node.slate.options.container);
            if (easing === undefined) easing = 'swingFromTo';
            if (dur === undefined) dur = 1000;

            var cw = d.width, ch = d.height, nw = _node.options.width * zr, nh = _node.options.height * zr;

            //get upper left coords
            var _x = (_vpt.x * zr);
            var _y = (_vpt.y * zr);

            switch (location) {
                case "lowerright":
                    _x = _x - (cw - nw) - pad;
                    _y = _y - (ch - nh) - pad;
                    break;
                case "lowerleft":
                    _x = _x - pad;
                    _y = _y - (ch - nh) - pad;
                    break;
                case "upperright":
                    _x = _x - (cw - nw) - pad;
                    _y = _y - pad;
                    break;
                case "upperleft":
                    _x = _x - pad;
                    _y = _y - pad;
                    break;
                default:
                    _x = _x - (cw / 2 - nw / 2);
                    _y = _y - (ch / 2 - nh / 2);
                    break;
            }

            if (_x === _node.slate.options.viewPort.left && _y === _node.slate.options.viewPort.top) {
                cb.apply();
            } else {
                _node.slate.canvas.move({ x: _x, y: _y, dur: dur, callbacks: { after: function () { _node.slate.birdseye && _node.slate.birdseye.refresh(true); cb.apply(); } }, isAbsolute: true, easing: easing });
            }
        };

        _node.mark = function () {

            var _vpt = _node.vect.getBBox()
                , _x = _vpt.x
                , _y = _vpt.y;

            if (!_marker) {
                //if (_node.options.vectorPath === "ellipse") {
                //    _x = _x - (_node.options.width / 2);
                //    _y = _y - (_node.options.height / 2);
                //}
                _marker = _node.slate.paper.rect(_x - 10, _y - 10, _node.options.width + 20, _node.options.height + 20, 10).attr({ "stroke-width": 2, "stroke": "red", fill: "#ccc", "fill-opacity": .8 }).toBack();
            }
            else
                _marker.attr({ x: (_x - 10), y: (_y - 10), width: (_node.options.width + 20), height: (_node.options.height + 20) });
        };

        _node.unmark = function () {
            _marker && _marker.remove();
            _marker = null;
        };

        //var _prevAllowDrag, _prevAllowMenu;
        _node.disable = function () {
            //_prevAllowDrag = _node.options.allowDrag;
            //_prevAllowMenu = _node.options.allowMenu;
            _node.options.allowMenu = false;
            _node.options.allowDrag = false;
            _node.relationships.unwireHoverEvents();
        };

        _node.enable = function () {
            _node.options.allowMenu = true; // _prevAllowMenu || true;
            _node.options.allowDrag = true; // _prevAllowDrag || true;
            _node.relationships.wireHoverEvents();
        };

        _node.offset = function () {
            var _x = _node.options.xPos - _node.slate.options.viewPort.left;
            var _y = _node.options.yPos - _node.slate.options.viewPort.top;
            if (_node.options.vectorPath === "ellipse") {
                _x = _x - (_node.options.width / 2);
                _y = _y - (_node.options.height / 2);
            }

            //var z = _node.slate.options.viewPort.zoom.r;
            //var _x = ((off.x - d.width) * z) / 2;
            //var _y = ((off.y - d.height) * z) / 2;

            return { x: _x, y: _y };
        };

        _node.pinChildNodes = function () {
            var pinRow = 3, totPinned = 0, mxHeight = 0, ypad = 20, xpad = 20;
            var _y = _node.options.yPos + (_node.options.vectorPath === "ellipse" ? _node.options.height / 2 : _node.options.height) + ypad;
            var widthx = [];
            var xys = [];
            var xyst = [];

            $s.each(_node.relationships.children, function () {
                if (this.child.options.isPinnedExact) {
                    totPinned++;

                    mxHeight = Math.max(mxHeight, this.child.options.height);
                    widthx.push(this.child.options.width + xpad);

                    var tot = 0;
                    $s.each(widthx, function () {
                        tot += this;
                    });
                    var edge = tot / 2;

                    //to ensure ellipses are centered...
                    var offs = this.child.options.vectorPath === "ellipse" ? 0 : _node.options.width / 2;

                    var baseX = _node.options.xPos + offs - edge + xpad / 2;
                    xyst = [];
                    for (wx = 0; wx < widthx.length; wx++) {
                        xyst.push({ y: _y, x: baseX });
                        baseX += widthx[wx];
                    }

                    if (totPinned % _node.options.pinnedRowCount === 0) {
                        _y += mxHeight + ypad;
                        $s.each(xyst, function () {
                            xys.push(this);
                        });
                        widthx = [];
                        xyst = [];
                    }
                } else if (this.child.options.isPinned) {
                    //var att = this.child.vect.type == "rect" ? { x: this.child.vect.ox + dx, y: this.child.vect.oy + dy} : { cx: this.child.vect.ox + dx, cy: this.child.vect.oy + dy };
                    //_self.moveNode(this.child.vect, att);
                }
            });

            //cleanup
            $s.each(xyst, function () {
                xys.push(this);
            });

            var pi = -1;
            $s.each(_node.relationships.children, function () {
                if (this.child.options.isPinnedExact) {
                    pi++;
                    var _x = this.child.options.vectorPath === "ellipse" ? xys[pi].x + (this.child.options.width / 2) : xys[pi].x;
                    var _y = this.child.options.vectorPath === "ellipse" ? xys[pi].y + (this.child.options.height / 2) : xys[pi].y;

                    this.child.options.xPos = _x;
                    this.child.options.yPos = _y;
                    var att = this.child.options.vectorPath === "ellipse" ? { cx: _x, cy: _y} : { x: _x, y: _y };
                    this.child.relationships.moveNode(att, true); //this.child.vect, 
                }
            });

            if (xys.length === 0)
                return { x: 0, y: 0 }

            return { x: xys[xys.length - 1].x, y: xys[xys.length - 1].y };
        };

        _node.textCoords = function () {
            var tx = _node.options.xPos + (_node.options.width / 2);
            var ty = _node.options.yPos + (_node.options.height / 2);

            if (_node.vect.type !== "rect") {
                tx = _node.options.xPos;
                ty = _node.options.yPos;
            }
            return { x: tx, y: ty };
        };

        _node.linkCoords = function () {
            var x = _node.options.xPos - 20;
            var y = _node.options.yPos + (_node.options.height / 2) - 22;

            if (_node.vect.type !== "rect") {
                y = _node.options.yPos - 22;
                x = (_node.options.xPos - _node.options.width / 2) - 20;
            }
            return { x: x, y: y };
        };

        _node.init = function () {
            if (_node.options.id > -1) {
                $s.ajax(url(_node.options.ajax.nodeCreated)
					, function (respText, resp) {
					    _node.options.holdData = eval('(' + respText + ')');
					    bindSlates(_node.options.holdData);
					}, JSON.stringify(_node.options));
            }
        };

        /*
        $.each($s.fn.node.fn, function () {
        if ($s.isFunction(this)) {
        if (arguments[0].substring(0, 1) === '_') {
        this.apply(_node);
        delete $s.fn.node.fn[arguments[0]];
        }
        }
        });
        */

        $s.each($s.fn.node.fn, function () {
            if (Slatebox.isFunction(this)) {
                if (arguments[0].substring(0, 1) === '_') {
                    var p = arguments[0].replace("_", "");
                    _node[p] = {};
                    _node[p] = this.apply(_node[p]);
                    _node[p]._ = _node;
                    //delete _node["_" + p];
                }
            }
        });
        return _node;
    };
    $s.fn.node.fn = $s.fn.node.prototype = {};
})(Slatebox);(function ($s) {
    $s.fn.search = function (_options) {
        if (!(this instanceof $s.fn.search))
            return new $s.fn.search(_options);

        var _self = this, _total = 0, loadUrl = '/Slates/Search?page={page}&rpp={rpp}&q={q}&username={username}&slateid={slateid}&apikey={apikey}'

        var options = {
            container: 'loadSlates'
            , width: 820
            , height: 220
            , size: 180
            , rowCount: 0
            , optional: { q: '', username: '', slateid: '' }
            , apiKey: ''
            , baseApiUrl: ''
            , loadUrl: null
            , outerFrameClass: 'outerFrameClass' //background-color:#f8f8f8;border:1px inset #ccc;margin:4px;
            , slateContainerClass: 'searchSlateContainerClass'
            , underSlateClass: 'underSlateClass'
            , onSlateBound: null
            , onSlateBindingComplete: null
            , onNoResults: null
            , pageNumber: 1
            , rpp: 4
        };

        $s.extend(options, _options);

        _self.setOption = function (key, val) {
            options[key] = val;
        };

        _self.getOption = function (key) {
            return options[key];
        };

        function setNav() {
            $s.el("goForward").style.visibility = 'hidden';
            $s.el("goBackward").style.visibility = 'hidden';

            if (options.pageNumber * options.rpp < _total)
                $s.el("goForward").style.visibility = 'visible';

            if (options.pageNumber > 1)
                $s.el("goBackward").style.visibility = 'visible';
        };

        function slide(isBack) {
            var _l = options.width;
            if (isBack) _l = (options.width * -1);
            emile($s.el("internalLoad"), "left:" + _l + "px", {
                duration: 400
                , after: function () {
                    _self.loadSlates();
                }
            });
        };

        _self.loadSlates = function (cb) {
            var _b = "<div class='l pad10' style='font-size:80pt;cursor:pointer;visibility:hidden;' id='goBackward'> < </div>";
            var _f = "<div class='l pad10' style='font-size:80pt;cursor:pointer;visibility:hidden;' id='goForward'> > </div>";
            $s.el(options.container).innerHTML = _b + "<div id='internalLoadSlider' class='l' style='overflow:hidden;width:" + options.width + "px;height:" + options.height + "px;'><div id='internalLoad' style='position:relative;left:0;top:0;' class='l " + options.outerFrameClass + "'></div></div>" + _f;

            var _base = options.loadUrl || options.baseApiUrl + loadUrl;
            var _get = _base.replace(/{page}/g, options.pageNumber)
                .replace(/{rpp}/g, options.rpp)
                .replace(/{apikey}/g, options.apiKey)
                + "&callback=?";

            if (options.optional.q && options.optional.q !== "")
                _get = _get.replace(/{q}/g, options.optional.q);
            else
                _get = _get.replace(/&q={q}/g, "");

            if (options.optional.slateid && options.optional.slateid !== "")
                _get = _get.replace(/{slateid}/g, options.optional.slateid);
            else
                _get = _get.replace(/&slateid={slateid}/g, "");

            if (options.optional.username && options.optional.username !== "")
                _get = _get.replace(/{username}/g, options.optional.username);
            else
                _get = _get.replace(/&username={username}/g, "");

            $s.getJSON(_get, function (respText) {
                var _as = "";
                var _tp = "<div class='l pad10 " + options.slateContainerClass + "' style='position:relative;left:0;top:0;'><div class='openSlate' id='slate_{id}' rel='{id}' style='height:" + options.size + "px;width:" + options.size + "px;" + options.slateStyle + "'></div><div class='" + options.underSlateClass + "' id='underSlate_{id}'></div></div>";
                var _pkg = respText;
                _total = _pkg.response.TotalCount;
                var _slates = _pkg.response.Return;

                var _s = -1;
                $s.each(_slates, function () {
                    _s++;
                    if (options.rowCount !== 0 && _s > 1 && options.rowCount % _s === 0) {
                        _as += "<div style='clear:both;'></div>";
                    }
                    _as += _tp.replace(/{id}/gi, this.options.id);
                });

                if (_slates.length === 0) {
                    if ($s.isFunction(options.onNoResults)) {
                        options.onNoResults.apply(this);
                    }
                }

                $s.el("internalLoad").innerHTML = _as;

                $s.el("goForward").onclick = function (e) {
                    options.pageNumber += 1;
                    slide(true);
                };

                $s.el("goBackward").onclick = function (e) {
                    options.pageNumber -= 1;
                    slide();
                };

                var _rslates = [];
                $s.each(_slates, function () {
                    var _sf = this, _maxHeight = 0;
                    var _slate = $s.instance.slate({ container: $s.el('slate_' + _sf.options.id), viewPort: { allowDrag: false }, name: _sf.options.name, description: _sf.options.description }).canvas.init();
                    var _json = JSON.stringify(_sf);
                    _slate.loadJSON(_json);
                    _rslates.push({ id: this.options.id, json: _json });
                    _slate.resize(options.size - 20, 0, 5);
                    _slate.disable();
                    if ($s.isFunction(options.onSlateBound)) {
                        options.onSlateBound.apply(this, [_sf, $s.el("underSlate_" + _sf.options.id)]);
                    }
                });

                setNav();

                if ($s.isFunction(options.onSlateBindingComplete)) {
                    options.onSlateBindingComplete.apply(this, [_rslates, _pkg.response.PageIndex]);
                }

                if ($s.isFunction(cb)) cb.apply(this);
            });
        };
        _self.loadSlates();
        return _self;
    }
})(Slatebox);; (function (sb) {
    var _searchView = function (_options) {
        var _self = this;

        if (!(_self instanceof _searchView))
            return new _searchView();

        var options = {
            userName: ''
            , baseApiUrl: ''
            , baseUrl: 'http://slatebox.com'
            , containers: {
                slate: { 
                    parent: ''
                    ,instance: ''
                }
                , search: {
                    parent: ''
                    ,instance: ''
                }
                , spin: ''
            }
            , apiKey: ''
            , imageFolder: 'http://slatebox.com/public/images/'
            , height: 600
            , width: 950
            , size: 240
            , canvasHeight: 600
            , transitionDuration: 1000
            , rpp: 2
            , rowCount: 3
            , events: {
                onSlateShown: null
                ,onSearchShown: null
                ,onSlateBound: null
                ,onCollaborationInitiated: null
                ,onNoResults: null
            }
            , collaboration: { name: '', ip: '' }
        };

        sb.extend(options, _options);
        
        //create spin while waiting for search to load
        createSpin();
       
        var _slate, _search = new sb().search({
            container: options.containers.search.instance
            , slateStyle: ''
            , apiKey: options.apiKey
            , optional: { username: options.userName }
            , baseApiUrl: options.baseApiUrl
            , height: options.height
            , width: options.width
            , size: options.size
            , rpp: options.rpp
            , onSlateBound: function (slate, underSlate) {
                if (sb.isFunction(options.events.onSlateBound))
                    options.events.onSlateBound.apply(this, [slate, underSlate]);
                
            }
            , onSlateBindingComplete: function (slates) {
                sb.each(sb.select("div.openSlate"), function () {
                    var _s = this;
                    _s.onclick = function (e) {
                        var _id = this.getAttribute("rel");
                        showSlate(_id);
                    };
                    _s.onmouseover = function (e) {
                        _s.style.backgroundColor = "#eee";
                        _s.style.cursor = "pointer";
                    };
                    _s.onmouseout = function (e) {
                        _s.style.backgroundColor = "#fff";
                    };
                });
                options.searchSpin();

                if (sb.isFunction(options.events.onSearchShown))
                    options.events.onSearchShown.apply(this);
            }
            , onNoResults: function() {
                if (sb.isFunction(options.events.onNoResults))
                    options.events.onNoResults.apply(this);
            }
        });

        function createSpin() {
            
            sb.each(sb.select("div.sb_searchSpin"), function() {
                sb.el(options.containers.spin).removeChild(this);
            });

            var _searchSpin = document.createElement("div");
            var _id =  "searchSpin_" + sb.guid();
            _searchSpin.setAttribute("id", _id);
            _searchSpin.setAttribute("class", "sb_searchSpin");
            _searchSpin.style.padding = "10px;"
            _searchSpin.style.marginRight = "auto";
            _searchSpin.style.marginLeft = "auto";
            _searchSpin.style.height = "30px"
            _searchSpin.style.width = "30px";
            sb.el(options.containers.spin).appendChild(_searchSpin);
            options.searchSpin = new spinner(_id, 25, 12, 16, 3, "#000");
        };

        function showSlate(slateId) {
            createSpin();
            emile(options.containers.search.parent, "top:" + options.canvasHeight + "px", {
                duration: options.transitionDuration
                , after: function () {
                    sb.el(options.containers.search.top).style.display = "none";
                    sb.el(options.containers.search.parent).style.display = "none";
                    sb.el(options.containers.slate.parent).style.display = "block";
                    sb.el(options.containers.slate.parent).style.top = options.canvasHeight + "px";

                    emile(sb.el(options.containers.slate.parent), "top:0px", {
                        duration: options.transitionDuration
                        , after: function () {
                            sb.getJSON(options.baseApiUrl + "/Slates/Search?slateid=" + slateId + "&apiKey=" + options.apiKey + "&callback=?", function (pkg) {
                                var _jsonToLoad = pkg.response.Return[0];

                                if (!_slate) {
                                    _slate = sb.instance.slate({
                                        container: sb.el(options.containers.slate.instance)
                                    }).canvas.init({
                                        imageFolder: options.imageFolder
                                    });
                                }

                                _slate.loadJSON(JSON.stringify(_jsonToLoad));

                                _slate.options.collaboration.showPanel = true;
                                _slate.options.collaboration.panelContainer = sb.el(options.containers.slate.instance);
                                _slate.options.collaboration.panelLocation = 'upperright';
                                _slate.options.showZoom = false;
                                _slate.options.showBirdsEye = true;

                                _slate.init(options.collaboration.name, "", options.collaboration.ip, 150);

                                /*
                                //no attaching for now...
                                _slate.websync.attach(function (subs) {
                                    if (subs.length > 1 && !_slate.options.collaboration.allowPublic) {
                                        _slate.options.viewPort.allowDrag = false;
                                        _slate.options.showZoom = false;
                                        _slate.zoomSlider.hide();
                                        if (sb.isFunction(options.events.onCollaborationInitiated))
                                            options.events.onCollaborationInitiated.apply(this);
                                    }
                                });
                                */

                                if (!_slate.options.collaboration.allowPublic) {
                                    _slate.disable();
                                    _slate.options.viewPort.allowDrag = true;
                                    _slate.options.showZoom = true;
                                     _slate.zoomSlider.show();
                                    _slate.zoomSlider.setValue(_slate.options.viewPort.width);
                                }                                    

                                if (sb.isFunction(options.events.onSlateShown))
                                    options.events.onSlateShown.apply(this, [_slate]);  
                                    
                                options.searchSpin();                              
                            });
                        }
                    });
                }
            });
        };

        _self.search = _search;

        _self.hideSlate = function() {
            _slate.websync && _slate.websync.disconnect(function () {
                emile(sb.el(options.containers.slate.parent), "top:" + options.canvasHeight + "px", {
                    duration: options.transitionDuration
                    , after: function () {

                        sb.el(options.containers.slate.parent).style.display = "none";
                        _slate.paper.clear();
                        sb.el(options.containers.search.parent).style.display = "block";
                        sb.el(options.containers.search.top).style.display = "block";
                        sb.el(options.containers.search.parent).style.top = options.canvasHeight + "px";

                        emile(sb.el(options.containers.search.parent), "top:0px", {
                            duration: options.transitionDuration
                            , after: function () {
                                if (sb.isFunction(options.events.onSearchShown))
                                    options.events.onSearchShown.apply(this);
                            }
                        });
                    }
                });
            });
        };
    };
    window.SearchView = _searchView;
})(Slatebox);// emile.js (c) 2009 Thomas Fuchs
// Licensed under the terms of the MIT license.

(function (emile, container) {
    var parseEl = document.createElement('div'),
    props = ('backgroundColor borderBottomColor borderBottomWidth borderLeftColor borderLeftWidth ' +
    'borderRightColor borderRightWidth borderSpacing borderTopColor borderTopWidth bottom color fontSize ' +
    'fontWeight height left letterSpacing lineHeight marginBottom marginLeft marginRight marginTop maxHeight ' +
    'maxWidth minHeight minWidth opacity outlineColor outlineOffset outlineWidth paddingBottom paddingLeft ' +
    'paddingRight paddingTop right textIndent top width wordSpacing zIndex').split(' ');

    function interpolate(source, target, pos) { return (source + (target - source) * pos).toFixed(3); }
    function s(str, p, c) { return str.substr(p, c || 1); }
    function color(source, target, pos) {
        var i = 2, j, c, tmp, v = [], r = [];
        while (j = 3, c = arguments[i - 1], i--)
            if (s(c, 0) == 'r') { c = c.match(/\d+/g); while (j--) v.push(~ ~c[j]); } else {
                if (c.length == 4) c = '#' + s(c, 1) + s(c, 1) + s(c, 2) + s(c, 2) + s(c, 3) + s(c, 3);
                while (j--) v.push(parseInt(s(c, 1 + j * 2, 2), 16));
            }
        while (j--) { tmp = ~ ~(v[j + 3] + (v[j] - v[j + 3]) * pos); r.push(tmp < 0 ? 0 : tmp > 255 ? 255 : tmp); }
        return 'rgb(' + r.join(',') + ')';
    }

    function parse(prop) {
        var p = parseFloat(prop), q = prop.replace(/^[\-\d\.]+/, '');
        return isNaN(p) ? { v: q, f: color, u: ''} : { v: p, f: interpolate, u: q };
    }

    function normalize(style) {
        var css, rules = {}, i = props.length, v;
        parseEl.innerHTML = '<div style="' + style + '"></div>';
        css = parseEl.childNodes[0].style;
        while (i--) if (v = css[props[i]]) rules[props[i]] = parse(v);
        return rules;
    }

    container[emile] = function (el, style, opts, after) {
        el = typeof el == 'string' ? document.getElementById(el) : el;
        opts = opts || {};
        var target = normalize(style), comp = el.currentStyle ? el.currentStyle : getComputedStyle(el, null),
                      prop, current = {}, start = +new Date, dur = opts.duration || 200, finish = start + dur, interval,
                      easing = opts.easing || function (pos) { return (-Math.cos(pos * Math.PI) / 2) + 0.5; };
        for (prop in target) current[prop] = parse(comp[prop]);
        interval = setInterval(function () {
            var time = +new Date, pos = time > finish ? 1 : (time - start) / dur;
            for (prop in target) {
                el.style[prop] = target[prop].f(current[prop].v, target[prop].v, easing(pos)) + target[prop].u;
            }
            if (time > finish) {
                clearInterval(interval);
                opts.after && opts.after();
                after && setTimeout(after, 1);
            } else {
                opts.during && opts.during();
            }
        }, 10);
    }
})('emile', this);(function ($s, $e) {
    var n = function () {
        var _self = this;

        if (!(_self instanceof Notify))
            return new Notify();

        var uid = $s.guid();
        var options = {
            msg: ''
            , hgt: 50
            , duration: 300
            , className: 'warningBar'
            , delayClose: 0
            , spinner: null //{innerDiameter: 16, outerDiameter: 8, ticks: 6, ticksWidth: 5, color: #fff}
            , hideClose: false
            , onOpen: null
            , msgBar: "messageBar" + uid
            , popFromBottom: false
        };

        _self.message = function (_options) {
            $s.extend(options, _options);

            //hide other bars if visible
            $s.each($s.select("div.notify"), function () {
                this.style.visibility = 'hidden';
            });

            if ($s.el(options.msgBar) && $s.el(options.msgBar).style.visibility === "visible") {
                var _height = $s.getDimensions($s.el(options.msgBar)).height;
                $e($s.el(options.msgBar), "top:" + _height * -1 + "px", {
                    duration: options.duration
                    , after: function () {
                        if ($s.el(options.msgBar) !== null) {
                            document.body.removeChild($s.el(options.msgBar));
                        }
                        buildBar();
                    }
                });
            } else {
                buildBar();
            }

            function buildBar() {
                var _inside = "<div style='min-width:950px;'><div id='msgSpinner_" + uid + "' style='padding:2px;float:left;width:30px;'></div><div style='text-align:left;padding: 10px;float:left;width:84%;' id='notifyBarMessage_" + uid + "'>" + options.msg + "</div><div style='float:right;margin-top:6px;padding-right:2px;width:4%;'><a href='javascript:' class='lnkCloseMessage' id='lnkCloseMessage_" + uid + "'>X</a></div></div>";
                var _notify = document.createElement("div");
                _notify.setAttribute("class", options.className + " notify");
                _notify.setAttribute("rel", options.popFromBottom); //for resizing window
                _notify.style.position = "absolute";
                _notify.style.height = options.hgt + "px";

                var _cssToAnimate = "top:0px";
                if (options.popFromBottom) {
                    var ws = $s.windowSize();
                    _notify.style.top = (ws.height + options.hgt) + "px";
                    _cssToAnimate = "top:" + (ws.height - options.hgt) + "px";
                } else {
                    _notify.style.top = (options.hgt * -1) + "px";
                }

                //_notify.style.display = "none";
                _notify.setAttribute("id", options.msgBar);
                _notify.innerHTML = _inside;
                document.body.appendChild(_notify);

                $e($s.el(options.msgBar), _cssToAnimate, {
                    duration: options.duration
                    , after: function () {
                        if (options.spinner) {
                            options.spinner = new spinner($s.el("msgSpinner_" + uid), options.spinner.innerDiameter, options.spinner.outerDiameter, options.spinner.ticks, options.spinner.ticksWidth, options.spinner.color);
                        }

                        if (!options.hideClose) {
                            $s.el('lnkCloseMessage_' + uid).onclick = function (e) {
                                e.preventDefault();
                                _self.closeMessage();
                            };
                        } else {
                            $s.el("lnkCloseMessage_" + uid).style.display = "none";
                        }

                        if (options.delayClose && options.delayClose > 0) {
                            setTimeout(function () {
                                if ($s.isFunction(options.spinner)) {
                                    options.spinner();
                                }
                                setTimeout(function () {
                                    _self.closeMessage();
                                }, options.duration);
                            }, options.delayClose);
                        }

                        if ($s.isFunction(options.onOpen)) {
                            options.onOpen.apply(this, [$s.el("notifyBarMessage_" + uid), _self]);
                        }
                    }
                });
            };
            return _self;
        }

        _self.changeMessage = function (msg) {
            $s.el("notifyBarMessage_" + uid).innerHTML = msg;
            return _self;
        };

        _self.visible = function () {
            return $s.el(options.msgBar) !== null;
        };

        _self.resize = function (h, d, cb) {
            if ($s.el(options.msgBar) !== null) {

                var _cssToAnimate = "top:" + (h * -1) + "px";
                if (options.popFromBottom)
                    _cssToAnimate = "top:" + ($s.windowSize().height - h) + "px";

                $e($s.el(options.msgBar), _cssToAnimate, {
                    duration: d
                    , after: function () {
                        if ($s.isFunction(cb)) {
                            cb.apply(this);
                        }
                    }
                });
            } else {
                if ($s.isFunction(cb)) {
                    cb.apply(this);
                }
            }
        };

        _self.closeMessage = function (cb) {
            if ($s.el(options.msgBar) !== null) {

                var _cssToAnimate = "top:" + (options.hgt * -1) + "px";
                if (options.popFromBottom)
                    _cssToAnimate = "top:" + ($s.windowSize().height + options.hgt) + "px";

                $e($s.el(options.msgBar), _cssToAnimate, {
                    duration: options.duration
                    , after: function () {
                        document.body.removeChild($s.el(options.msgBar));

                        //show other bars if hidden
                        $s.each($s.select("div.notify"), function () {
                            this.style.visibility = 'visible';
                        });

                        if ($s.isFunction(options.onClose)) {
                            options.onClose.apply(this);
                        }
                        if ($s.isFunction(cb)) {
                            cb.apply(this);
                        }
                    }
                });
            } else {
                if ($s.isFunction(cb)) {
                    cb.apply(this);
                }
            }
        };
    };
    $s.addEvent(window, "resize", function () {
        $s.each($s.select("div.notify"), function () {
            if (this.getAttribute("rel") === "true") {
                var ws = $s.windowSize();
                var d = $s.getDimensions(this);
                this.style.top = (ws.height - d.height) + "px";
            }
        });
    });
    window.Notify = n;
})(Slatebox, emile);spinner = (function () {
    var _sp = function spinner(holderid, R1, R2, count, stroke_width, colour) {
        var sectorsCount = count || 12,
            color = colour || "#fff",
            width = stroke_width || 15,
            r1 = Math.min(R1, R2) || 35,
            r2 = Math.max(R1, R2) || 60,
            cx = r2 + width,
            cy = r2 + width,
            r = Raphael(holderid, r2 * 2 + width * 2, r2 * 2 + width * 2),
            sectors = [],
            opacity = [],
            beta = 2 * Math.PI / sectorsCount,
            pathParams = { stroke: color, "stroke-width": width, "stroke-linecap": "round" };

        Raphael.getColor.reset();
        for (var i = 0; i < sectorsCount; i++) {
            var alpha = beta * i - Math.PI / 2,
                        cos = Math.cos(alpha),
                        sin = Math.sin(alpha);
            opacity[i] = 1 / sectorsCount * i;
            sectors[i] = r.path([["M", cx + r1 * cos, cy + r1 * sin], ["L", cx + r2 * cos, cy + r2 * sin]]).attr(pathParams);
            if (color == "rainbow") {
                sectors[i].attr("stroke", Raphael.getColor());
            }
        }
        var tick;
        (function ticker() {
            opacity.unshift(opacity.pop());
            for (var i = 0; i < sectorsCount; i++) {
                sectors[i].attr("opacity", opacity[i]);
            }
            r.safari();
            tick = setTimeout(ticker, 1000 / sectorsCount);
        })();
        return function () {
            clearTimeout(tick);
            r.clear();
        };
    };
    return _sp;
})();/*
    http://www.JSON.org/json2.js
    2011-01-18

    Public Domain.

    NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK.

    See http://www.JSON.org/js.html


    This code should be minified before deployment.
    See http://javascript.crockford.com/jsmin.html

    USE YOUR OWN COPY. IT IS EXTREMELY UNWISE TO LOAD CODE FROM SERVERS YOU DO
    NOT CONTROL.


    This file creates a global JSON object containing two methods: stringify
    and parse.

        JSON.stringify(value, replacer, space)
            value       any JavaScript value, usually an object or array.

            replacer    an optional parameter that determines how object
                        values are stringified for objects. It can be a
                        function or an array of strings.

            space       an optional parameter that specifies the indentation
                        of nested structures. If it is omitted, the text will
                        be packed without extra whitespace. If it is a number,
                        it will specify the number of spaces to indent at each
                        level. If it is a string (such as '\t' or '&nbsp;'),
                        it contains the characters used to indent at each level.

            This method produces a JSON text from a JavaScript value.

            When an object value is found, if the object contains a toJSON
            method, its toJSON method will be called and the result will be
            stringified. A toJSON method does not serialize: it returns the
            value represented by the name/value pair that should be serialized,
            or undefined if nothing should be serialized. The toJSON method
            will be passed the key associated with the value, and this will be
            bound to the value

            For example, this would serialize Dates as ISO strings.

                Date.prototype.toJSON = function (key) {
                    function f(n) {
                        // Format integers to have at least two digits.
                        return n < 10 ? '0' + n : n;
                    }

                    return this.getUTCFullYear()   + '-' +
                         f(this.getUTCMonth() + 1) + '-' +
                         f(this.getUTCDate())      + 'T' +
                         f(this.getUTCHours())     + ':' +
                         f(this.getUTCMinutes())   + ':' +
                         f(this.getUTCSeconds())   + 'Z';
                };

            You can provide an optional replacer method. It will be passed the
            key and value of each member, with this bound to the containing
            object. The value that is returned from your method will be
            serialized. If your method returns undefined, then the member will
            be excluded from the serialization.

            If the replacer parameter is an array of strings, then it will be
            used to select the members to be serialized. It filters the results
            such that only members with keys listed in the replacer array are
            stringified.

            Values that do not have JSON representations, such as undefined or
            functions, will not be serialized. Such values in objects will be
            dropped; in arrays they will be replaced with null. You can use
            a replacer function to replace those with JSON values.
            JSON.stringify(undefined) returns undefined.

            The optional space parameter produces a stringification of the
            value that is filled with line breaks and indentation to make it
            easier to read.

            If the space parameter is a non-empty string, then that string will
            be used for indentation. If the space parameter is a number, then
            the indentation will be that many spaces.

            Example:

            text = JSON.stringify(['e', {pluribus: 'unum'}]);
            // text is '["e",{"pluribus":"unum"}]'


            text = JSON.stringify(['e', {pluribus: 'unum'}], null, '\t');
            // text is '[\n\t"e",\n\t{\n\t\t"pluribus": "unum"\n\t}\n]'

            text = JSON.stringify([new Date()], function (key, value) {
                return this[key] instanceof Date ?
                    'Date(' + this[key] + ')' : value;
            });
            // text is '["Date(---current time---)"]'


        JSON.parse(text, reviver)
            This method parses a JSON text to produce an object or array.
            It can throw a SyntaxError exception.

            The optional reviver parameter is a function that can filter and
            transform the results. It receives each of the keys and values,
            and its return value is used instead of the original value.
            If it returns what it received, then the structure is not modified.
            If it returns undefined then the member is deleted.

            Example:

            // Parse the text. Values that look like ISO date strings will
            // be converted to Date objects.

            myData = JSON.parse(text, function (key, value) {
                var a;
                if (typeof value === 'string') {
                    a =
/^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2}(?:\.\d*)?)Z$/.exec(value);
                    if (a) {
                        return new Date(Date.UTC(+a[1], +a[2] - 1, +a[3], +a[4],
                            +a[5], +a[6]));
                    }
                }
                return value;
            });

            myData = JSON.parse('["Date(09/09/2001)"]', function (key, value) {
                var d;
                if (typeof value === 'string' &&
                        value.slice(0, 5) === 'Date(' &&
                        value.slice(-1) === ')') {
                    d = new Date(value.slice(5, -1));
                    if (d) {
                        return d;
                    }
                }
                return value;
            });


    This is a reference implementation. You are free to copy, modify, or
    redistribute.
*/

/*jslint evil: true, strict: false, regexp: false */

/*members "", "\b", "\t", "\n", "\f", "\r", "\"", JSON, "\\", apply,
    call, charCodeAt, getUTCDate, getUTCFullYear, getUTCHours,
    getUTCMinutes, getUTCMonth, getUTCSeconds, hasOwnProperty, join,
    lastIndex, length, parse, prototype, push, replace, slice, stringify,
    test, toJSON, toString, valueOf
*/


// Create a JSON object only if one does not already exist. We create the
// methods in a closure to avoid creating global variables.

var JSON;
if (!JSON) {
    JSON = {};
}

(function () {
    "use strict";

    function f(n) {
        // Format integers to have at least two digits.
        return n < 10 ? '0' + n : n;
    }

    if (typeof Date.prototype.toJSON !== 'function') {

        Date.prototype.toJSON = function (key) {

            return isFinite(this.valueOf()) ?
                this.getUTCFullYear()     + '-' +
                f(this.getUTCMonth() + 1) + '-' +
                f(this.getUTCDate())      + 'T' +
                f(this.getUTCHours())     + ':' +
                f(this.getUTCMinutes())   + ':' +
                f(this.getUTCSeconds())   + 'Z' : null;
        };

        String.prototype.toJSON      =
            Number.prototype.toJSON  =
            Boolean.prototype.toJSON = function (key) {
                return this.valueOf();
            };
    }

    var cx = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
        escapable = /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
        gap,
        indent,
        meta = {    // table of character substitutions
            '\b': '\\b',
            '\t': '\\t',
            '\n': '\\n',
            '\f': '\\f',
            '\r': '\\r',
            '"' : '\\"',
            '\\': '\\\\'
        },
        rep;


    function quote(string) {

// If the string contains no control characters, no quote characters, and no
// backslash characters, then we can safely slap some quotes around it.
// Otherwise we must also replace the offending characters with safe escape
// sequences.

        escapable.lastIndex = 0;
        return escapable.test(string) ? '"' + string.replace(escapable, function (a) {
            var c = meta[a];
            return typeof c === 'string' ? c :
                '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
        }) + '"' : '"' + string + '"';
    }


    function str(key, holder) {

// Produce a string from holder[key].

        var i,          // The loop counter.
            k,          // The member key.
            v,          // The member value.
            length,
            mind = gap,
            partial,
            value = holder[key];

// If the value has a toJSON method, call it to obtain a replacement value.

        if (value && typeof value === 'object' &&
                typeof value.toJSON === 'function') {
            value = value.toJSON(key);
        }

// If we were called with a replacer function, then call the replacer to
// obtain a replacement value.

        if (typeof rep === 'function') {
            value = rep.call(holder, key, value);
        }

// What happens next depends on the value's type.

        switch (typeof value) {
        case 'string':
            return quote(value);

        case 'number':

// JSON numbers must be finite. Encode non-finite numbers as null.

            return isFinite(value) ? String(value) : 'null';

        case 'boolean':
        case 'null':

// If the value is a boolean or null, convert it to a string. Note:
// typeof null does not produce 'null'. The case is included here in
// the remote chance that this gets fixed someday.

            return String(value);

// If the type is 'object', we might be dealing with an object or an array or
// null.

        case 'object':

// Due to a specification blunder in ECMAScript, typeof null is 'object',
// so watch out for that case.

            if (!value) {
                return 'null';
            }

// Make an array to hold the partial results of stringifying this object value.

            gap += indent;
            partial = [];

// Is the value an array?

            if (Object.prototype.toString.apply(value) === '[object Array]') {

// The value is an array. Stringify every element. Use null as a placeholder
// for non-JSON values.

                length = value.length;
                for (i = 0; i < length; i += 1) {
                    partial[i] = str(i, value) || 'null';
                }

// Join all of the elements together, separated with commas, and wrap them in
// brackets.

                v = partial.length === 0 ? '[]' : gap ?
                    '[\n' + gap + partial.join(',\n' + gap) + '\n' + mind + ']' :
                    '[' + partial.join(',') + ']';
                gap = mind;
                return v;
            }

// If the replacer is an array, use it to select the members to be stringified.

            if (rep && typeof rep === 'object') {
                length = rep.length;
                for (i = 0; i < length; i += 1) {
                    k = rep[i];
                    if (typeof k === 'string') {
                        v = str(k, value);
                        if (v) {
                            partial.push(quote(k) + (gap ? ': ' : ':') + v);
                        }
                    }
                }
            } else {

// Otherwise, iterate through all of the keys in the object.

                for (k in value) {
                    if (Object.hasOwnProperty.call(value, k)) {
                        v = str(k, value);
                        if (v) {
                            partial.push(quote(k) + (gap ? ': ' : ':') + v);
                        }
                    }
                }
            }

// Join all of the member texts together, separated with commas,
// and wrap them in braces.

            v = partial.length === 0 ? '{}' : gap ?
                '{\n' + gap + partial.join(',\n' + gap) + '\n' + mind + '}' :
                '{' + partial.join(',') + '}';
            gap = mind;
            return v;
        }
    }

// If the JSON object does not yet have a stringify method, give it one.

    if (typeof JSON.stringify !== 'function') {
        JSON.stringify = function (value, replacer, space) {

// The stringify method takes a value and an optional replacer, and an optional
// space parameter, and returns a JSON text. The replacer can be a function
// that can replace values, or an array of strings that will select the keys.
// A default replacer method can be provided. Use of the space parameter can
// produce text that is more easily readable.

            var i;
            gap = '';
            indent = '';

// If the space parameter is a number, make an indent string containing that
// many spaces.

            if (typeof space === 'number') {
                for (i = 0; i < space; i += 1) {
                    indent += ' ';
                }

// If the space parameter is a string, it will be used as the indent string.

            } else if (typeof space === 'string') {
                indent = space;
            }

// If there is a replacer, it must be a function or an array.
// Otherwise, throw an error.

            rep = replacer;
            if (replacer && typeof replacer !== 'function' &&
                    (typeof replacer !== 'object' ||
                    typeof replacer.length !== 'number')) {
                throw new Error('JSON.stringify');
            }

// Make a fake root object containing our value under the key of ''.
// Return the result of stringifying the value.

            return str('', {'': value});
        };
    }


// If the JSON object does not yet have a parse method, give it one.

    if (typeof JSON.parse !== 'function') {
        JSON.parse = function (text, reviver) {

// The parse method takes a text and an optional reviver function, and returns
// a JavaScript value if the text is a valid JSON text.

            var j;

            function walk(holder, key) {

// The walk method is used to recursively walk the resulting structure so
// that modifications can be made.

                var k, v, value = holder[key];
                if (value && typeof value === 'object') {
                    for (k in value) {
                        if (Object.hasOwnProperty.call(value, k)) {
                            v = walk(value, k);
                            if (v !== undefined) {
                                value[k] = v;
                            } else {
                                delete value[k];
                            }
                        }
                    }
                }
                return reviver.call(holder, key, value);
            }


// Parsing happens in four stages. In the first stage, we replace certain
// Unicode characters with escape sequences. JavaScript handles many characters
// incorrectly, either silently deleting them, or treating them as line endings.

            text = String(text);
            cx.lastIndex = 0;
            if (cx.test(text)) {
                text = text.replace(cx, function (a) {
                    return '\\u' +
                        ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
                });
            }

// In the second stage, we run the text against regular expressions that look
// for non-JSON patterns. We are especially concerned with '()' and 'new'
// because they can cause invocation, and '=' because it can cause mutation.
// But just to be safe, we want to reject all unexpected forms.

// We split the second stage into 4 regexp operations in order to work around
// crippling inefficiencies in IE's and Safari's regexp engines. First we
// replace the JSON backslash pairs with '@' (a non-JSON character). Second, we
// replace all simple value tokens with ']' characters. Third, we delete all
// open brackets that follow a colon or comma or that begin the text. Finally,
// we look to see that the remaining characters are only whitespace or ']' or
// ',' or ':' or '{' or '}'. If that is so, then the text is safe for eval.

            if (/^[\],:{}\s]*$/
                    .test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, '@')
                        .replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']')
                        .replace(/(?:^|:|,)(?:\s*\[)+/g, ''))) {

// In the third stage we use the eval function to compile the text into a
// JavaScript structure. The '{' operator is subject to a syntactic ambiguity
// in JavaScript: it can begin a block or an object literal. We wrap the text
// in parens to eliminate the ambiguity.

                j = eval('(' + text + ')');

// In the optional fourth stage, we recursively walk the new structure, passing
// each name/value pair to a reviver function for possible transformation.

                return typeof reviver === 'function' ?
                    walk({'': j}, '') : j;
            }

// If the text is not JSON parseable, then a SyntaxError is thrown.

            throw new SyntaxError('JSON.parse');
        };
    }
}());// Underscore.js 1.1.6
// (c) 2011 Jeremy Ashkenas, DocumentCloud Inc.
// Underscore is freely distributable under the MIT license.
// Portions of Underscore are inspired or borrowed from Prototype,
// Oliver Steele's Functional, and John Resig's Micro-Templating.
// For all details and documentation:
// http://documentcloud.github.com/underscore
(function () {
    var p = this, C = p._, m = {}, i = Array.prototype, n = Object.prototype, f = i.slice, D = i.unshift, E = n.toString, l = n.hasOwnProperty, s = i.forEach, t = i.map, u = i.reduce, v = i.reduceRight, w = i.filter, x = i.every, y = i.some, o = i.indexOf, z = i.lastIndexOf; n = Array.isArray; var F = Object.keys, q = Function.prototype.bind, b = function (a) { return new j(a) }; typeof module !== "undefined" && module.exports ? (module.exports = b, b._ = b) : p._ = b; b.VERSION = "1.1.6"; var h = b.each = b.forEach = function (a, c, d) {
        if (a != null) if (s && a.forEach === s) a.forEach(c, d); else if (b.isNumber(a.length)) for (var e =
0, k = a.length; e < k; e++) { if (c.call(d, a[e], e, a) === m) break } else for (e in a) if (l.call(a, e) && c.call(d, a[e], e, a) === m) break
    }; b.map = function (a, c, b) { var e = []; if (a == null) return e; if (t && a.map === t) return a.map(c, b); h(a, function (a, g, G) { e[e.length] = c.call(b, a, g, G) }); return e }; b.reduce = b.foldl = b.inject = function (a, c, d, e) {
        var k = d !== void 0; a == null && (a = []); if (u && a.reduce === u) return e && (c = b.bind(c, e)), k ? a.reduce(c, d) : a.reduce(c); h(a, function (a, b, f) { !k && b === 0 ? (d = a, k = !0) : d = c.call(e, d, a, b, f) }); if (!k) throw new TypeError("Reduce of empty array with no initial value");
        return d
    }; b.reduceRight = b.foldr = function (a, c, d, e) { a == null && (a = []); if (v && a.reduceRight === v) return e && (c = b.bind(c, e)), d !== void 0 ? a.reduceRight(c, d) : a.reduceRight(c); a = (b.isArray(a) ? a.slice() : b.toArray(a)).reverse(); return b.reduce(a, c, d, e) }; b.find = b.detect = function (a, c, b) { var e; A(a, function (a, g, f) { if (c.call(b, a, g, f)) return e = a, !0 }); return e }; b.filter = b.select = function (a, c, b) { var e = []; if (a == null) return e; if (w && a.filter === w) return a.filter(c, b); h(a, function (a, g, f) { c.call(b, a, g, f) && (e[e.length] = a) }); return e };
    b.reject = function (a, c, b) { var e = []; if (a == null) return e; h(a, function (a, g, f) { c.call(b, a, g, f) || (e[e.length] = a) }); return e }; b.every = b.all = function (a, c, b) { var e = !0; if (a == null) return e; if (x && a.every === x) return a.every(c, b); h(a, function (a, g, f) { if (!(e = e && c.call(b, a, g, f))) return m }); return e }; var A = b.some = b.any = function (a, c, d) { c || (c = b.identity); var e = !1; if (a == null) return e; if (y && a.some === y) return a.some(c, d); h(a, function (a, b, f) { if (e = c.call(d, a, b, f)) return m }); return e }; b.include = b.contains = function (a, c) {
        var b =
!1; if (a == null) return b; if (o && a.indexOf === o) return a.indexOf(c) != -1; A(a, function (a) { if (b = a === c) return !0 }); return b
    }; b.invoke = function (a, c) { var d = f.call(arguments, 2); return b.map(a, function (a) { return (c.call ? c || a : a[c]).apply(a, d) }) }; b.pluck = function (a, c) { return b.map(a, function (a) { return a[c] }) }; b.max = function (a, c, d) { if (!c && b.isArray(a)) return Math.max.apply(Math, a); var e = { computed: -Infinity }; h(a, function (a, b, f) { b = c ? c.call(d, a, b, f) : a; b >= e.computed && (e = { value: a, computed: b }) }); return e.value }; b.min = function (a,
c, d) { if (!c && b.isArray(a)) return Math.min.apply(Math, a); var e = { computed: Infinity }; h(a, function (a, b, f) { b = c ? c.call(d, a, b, f) : a; b < e.computed && (e = { value: a, computed: b }) }); return e.value }; b.sortBy = function (a, c, d) { return b.pluck(b.map(a, function (a, b, f) { return { value: a, criteria: c.call(d, a, b, f)} }).sort(function (a, b) { var c = a.criteria, d = b.criteria; return c < d ? -1 : c > d ? 1 : 0 }), "value") }; b.sortedIndex = function (a, c, d) { d || (d = b.identity); for (var e = 0, f = a.length; e < f; ) { var g = e + f >> 1; d(a[g]) < d(c) ? e = g + 1 : f = g } return e }; b.toArray =
function (a) { if (!a) return []; if (a.toArray) return a.toArray(); if (b.isArray(a)) return a; if (b.isArguments(a)) return f.call(a); return b.values(a) }; b.size = function (a) { return b.toArray(a).length }; b.first = b.head = function (a, b, d) { return b != null && !d ? f.call(a, 0, b) : a[0] }; b.rest = b.tail = function (a, b, d) { return f.call(a, b == null || d ? 1 : b) }; b.last = function (a) { return a[a.length - 1] }; b.compact = function (a) { return b.filter(a, function (a) { return !!a }) }; b.flatten = function (a) {
    return b.reduce(a, function (a, d) {
        if (b.isArray(d)) return a.concat(b.flatten(d));
        a[a.length] = d; return a
    }, [])
}; b.without = function (a) { var c = f.call(arguments, 1); return b.filter(a, function (a) { return !b.include(c, a) }) }; b.uniq = b.unique = function (a, c) { return b.reduce(a, function (a, e, f) { if (0 == f || (c === !0 ? b.last(a) != e : !b.include(a, e))) a[a.length] = e; return a }, []) }; b.intersect = function (a) { var c = f.call(arguments, 1); return b.filter(b.uniq(a), function (a) { return b.every(c, function (c) { return b.indexOf(c, a) >= 0 }) }) }; b.zip = function () {
    for (var a = f.call(arguments), c = b.max(b.pluck(a, "length")), d = Array(c),
e = 0; e < c; e++) d[e] = b.pluck(a, "" + e); return d
}; b.indexOf = function (a, c, d) { if (a == null) return -1; var e; if (d) return d = b.sortedIndex(a, c), a[d] === c ? d : -1; if (o && a.indexOf === o) return a.indexOf(c); d = 0; for (e = a.length; d < e; d++) if (a[d] === c) return d; return -1 }; b.lastIndexOf = function (a, b) { if (a == null) return -1; if (z && a.lastIndexOf === z) return a.lastIndexOf(b); for (var d = a.length; d--; ) if (a[d] === b) return d; return -1 }; b.range = function (a, b, d) {
    arguments.length <= 1 && (b = a || 0, a = 0); d = arguments[2] || 1; for (var e = Math.max(Math.ceil((b - a) /
d), 0), f = 0, g = Array(e); f < e; ) g[f++] = a, a += d; return g
}; b.bind = function (a, b) { if (a.bind === q && q) return q.apply(a, f.call(arguments, 1)); var d = f.call(arguments, 2); return function () { return a.apply(b, d.concat(f.call(arguments))) } }; b.bindAll = function (a) { var c = f.call(arguments, 1); c.length == 0 && (c = b.functions(a)); h(c, function (c) { a[c] = b.bind(a[c], a) }); return a }; b.memoize = function (a, c) { var d = {}; c || (c = b.identity); return function () { var b = c.apply(this, arguments); return l.call(d, b) ? d[b] : d[b] = a.apply(this, arguments) } }; b.delay =
function (a, b) { var d = f.call(arguments, 2); return setTimeout(function () { return a.apply(a, d) }, b) }; b.defer = function (a) { return b.delay.apply(b, [a, 1].concat(f.call(arguments, 1))) }; var B = function (a, b, d) { var e; return function () { var f = this, g = arguments, h = function () { e = null; a.apply(f, g) }; d && clearTimeout(e); if (d || !e) e = setTimeout(h, b) } }; b.throttle = function (a, b) { return B(a, b, !1) }; b.debounce = function (a, b) { return B(a, b, !0) }; b.once = function (a) { var b = !1, d; return function () { if (b) return d; b = !0; return d = a.apply(this, arguments) } };
    b.wrap = function (a, b) { return function () { var d = [a].concat(f.call(arguments)); return b.apply(this, d) } }; b.compose = function () { var a = f.call(arguments); return function () { for (var b = f.call(arguments), d = a.length - 1; d >= 0; d--) b = [a[d].apply(this, b)]; return b[0] } }; b.after = function (a, b) { return function () { if (--a < 1) return b.apply(this, arguments) } }; b.keys = F || function (a) { if (a !== Object(a)) throw new TypeError("Invalid object"); var b = [], d; for (d in a) l.call(a, d) && (b[b.length] = d); return b }; b.values = function (a) {
        return b.map(a,
b.identity)
    }; b.functions = b.methods = function (a) { return b.filter(b.keys(a), function (c) { return b.isFunction(a[c]) }).sort() }; b.extend = function (a) { h(f.call(arguments, 1), function (b) { for (var d in b) b[d] !== void 0 && (a[d] = b[d]) }); return a }; b.defaults = function (a) { h(f.call(arguments, 1), function (b) { for (var d in b) a[d] == null && (a[d] = b[d]) }); return a }; b.clone = function (a) { return b.isArray(a) ? a.slice() : b.extend({}, a) }; b.tap = function (a, b) { b(a); return a }; b.isEqual = function (a, c) {
        if (a === c) return !0; var d = typeof a; if (d !=
typeof c) return !1; if (a == c) return !0; if (!a && c || a && !c) return !1; if (a._chain) a = a._wrapped; if (c._chain) c = c._wrapped; if (a.isEqual) return a.isEqual(c); if (b.isDate(a) && b.isDate(c)) return a.getTime() === c.getTime(); if (b.isNaN(a) && b.isNaN(c)) return !1; if (b.isRegExp(a) && b.isRegExp(c)) return a.source === c.source && a.global === c.global && a.ignoreCase === c.ignoreCase && a.multiline === c.multiline; if (d !== "object") return !1; if (a.length && a.length !== c.length) return !1; d = b.keys(a); var e = b.keys(c); if (d.length != e.length) return !1;
        for (var f in a) if (!(f in c) || !b.isEqual(a[f], c[f])) return !1; return !0
    }; b.isEmpty = function (a) { if (b.isArray(a) || b.isString(a)) return a.length === 0; for (var c in a) if (l.call(a, c)) return !1; return !0 }; b.isElement = function (a) { return !!(a && a.nodeType == 1) }; b.isArray = n || function (a) { return E.call(a) === "[object Array]" }; b.isArguments = function (a) { return !(!a || !l.call(a, "callee")) }; b.isFunction = function (a) { return !(!a || !a.constructor || !a.call || !a.apply) }; b.isString = function (a) { return !!(a === "" || a && a.charCodeAt && a.substr) };
    b.isNumber = function (a) { return !!(a === 0 || a && a.toExponential && a.toFixed) }; b.isNaN = function (a) { return a !== a }; b.isBoolean = function (a) { return a === !0 || a === !1 }; b.isDate = function (a) { return !(!a || !a.getTimezoneOffset || !a.setUTCFullYear) }; b.isRegExp = function (a) { return !(!a || !a.test || !a.exec || !(a.ignoreCase || a.ignoreCase === !1)) }; b.isNull = function (a) { return a === null }; b.isUndefined = function (a) { return a === void 0 }; b.noConflict = function () { p._ = C; return this }; b.identity = function (a) { return a }; b.times = function (a, b, d) {
        for (var e =
0; e < a; e++) b.call(d, e)
    }; b.mixin = function (a) { h(b.functions(a), function (c) { H(c, b[c] = a[c]) }) }; var I = 0; b.uniqueId = function (a) { var b = I++; return a ? a + b : b }; b.templateSettings = { evaluate: /<%([\s\S]+?)%>/g, interpolate: /<%=([\s\S]+?)%>/g }; b.template = function (a, c) {
        var d = b.templateSettings; d = "var __p=[],print=function(){__p.push.apply(__p,arguments);};with(obj||{}){__p.push('" + a.replace(/\\/g, "\\\\").replace(/'/g, "\\'").replace(d.interpolate, function (a, b) { return "'," + b.replace(/\\'/g, "'") + ",'" }).replace(d.evaluate ||
null, function (a, b) { return "');" + b.replace(/\\'/g, "'").replace(/[\r\n\t]/g, " ") + "__p.push('" }).replace(/\r/g, "\\r").replace(/\n/g, "\\n").replace(/\t/g, "\\t") + "');}return __p.join('');"; d = new Function("obj", d); return c ? d(c) : d
    }; var j = function (a) { this._wrapped = a }; b.prototype = j.prototype; var r = function (a, c) { return c ? b(a).chain() : a }, H = function (a, c) { j.prototype[a] = function () { var a = f.call(arguments); D.call(a, this._wrapped); return r(c.apply(b, a), this._chain) } }; b.mixin(b); h(["pop", "push", "reverse", "shift", "sort",
"splice", "unshift"], function (a) { var b = i[a]; j.prototype[a] = function () { b.apply(this._wrapped, arguments); return r(this._wrapped, this._chain) } }); h(["concat", "join", "slice"], function (a) { var b = i[a]; j.prototype[a] = function () { return r(b.apply(this._wrapped, arguments), this._chain) } }); j.prototype.chain = function () { this._chain = !0; return this }; j.prototype.value = function () { return this._wrapped } 
})();(function ($s, $slate) {
    $slate.fn._nodes = function () {
        var _self = this
        _self.allNodes = [];

        function refreshBe() {
            _self._.birdseye && _self._.birdseye.refresh(false);
        };

        _self.copyNodePositions = function (source) {
            //var _zr = _corner.options.viewPort.zoom.r;
            $s.each(_self.allNodes, function () {
                var cn = this;
                $s.each(source, function () {
                    if (this.options.id === cn.options.id) {
                        var dx = this.options.xPos; // +((this.options.xPos / _zr) - this.options.xPos);
                        var dy = this.options.yPos; // +((this.options.yPos / _zr) - this.options.yPos);
                        var att = this.options.vectorPath === "ellipse" ? { cx: dx, cy: dy} : { x: dx, y: dy };
                        cn.relationships.moveNode(att);
                    }
                });
            });
        };

        _self.addRange = function (_nodes) {
            $s.each(_nodes, function () {
                _self.add(this);
            });
            return _self;
        };

        _self.removeRange = function (_nodes) {
            $s.each(_nodes, function () {
                _self.allNodes = removeNode(_self.allNodes, this);
            });
            return _self;
        };

        _self.add = function (_node, isTemp) {
            _node.slate = _self._; //parent
            _self.allNodes.push(_node);
            addToCanvas(_node);
            //if (isTemp === undefined) refreshBe();
        };

        _self.remove = function (_node) {
            _self.allNodes = remove(_self.allNodes, _node);
            _node.slate = null;
            removeFromCanvas(_node);
            //refreshBe();
        };

        function getParentChild(obj) {
            var _parent, _child;
            $s.each(_self.allNodes, function () {
                var _node = this;
                if (this.options.id === obj.parent) {
                    _parent = _node;
                } else if (this.options.id === obj.child) {
                    _child = _node;
                }
                if (_parent && _child) return;
            });

            return { p: _parent, c: _child };
        };

        _self.removeRelationship = function (rm) {
            var pc = getParentChild(rm);
            var _parent = pc.p, _child = pc.c;
            if (_parent && _child) {
                _parent.relationships.removeChild(_child);
                _child.relationships.removeParent(_parent);
                _parent.relationships.removeAssociation(_child);
                _child.relationships.removeAssociation(_parent);
            }
        };

        _self.addRelationship = function (add) {
            var pc = getParentChild(add);
            var _parent = pc.p, _child = pc.c;
            if (_parent && _child) {
                switch (add.type) {
                    case "association":
                        _parent.relationships.addAssociation(_child);
                        break;
                    case "parent":
                        _parent.relationships.addParent(_child);
                        break;
                }
            }
        };

        _self.closeAllMenus = function (exception) {
            $s.each(_self.allNodes, function () {
                if (this.options.id === exception) {
                } else {
                    this.menu && this.menu.hide();
                }
            });
        };

        _self.closeAllConnectors = function () {
            $s.each(_self.allNodes, function () {
                this.connectors && this.connectors.remove();
                this.resize && this.resize.hide();
            });
        };

        _self.one = function (id) {
            var cn = null;
            $s.each(_self.allNodes, function () {
                if (this.options.id === id) {
                    cn = this;
                    return;
                }
            });
            return cn;
        };

        function remove(a, obj) {
            var _na = new Array();
            $s.each(a, function () {
                if (this.options.id !== obj.options.id) {
                    _na.push(this);
                }
            });
            return _na;
        }

        function removeFromCanvas(_node) {
            _node.vect.remove();
            _node.text.remove();
            _node.link.remove();
        };

        function addToCanvas(_node) {
            var vect = null;
            var text = null;
            var link = null;
            var vectOpt = { stroke: '#000', "stroke-width": _node.options.borderWidth, fill: (_node.options.backgroundColor || "none") };
            var _x = _node.options.xPos;
            var _y = _node.options.yPos;
            var paperToUse = _self._.paper;
            var percent = 1;

            var _width = _node.options.width;
            var _height = _node.options.height;

            switch (_node.options.vectorPath) {
                case "ellipse":
                    vect = paperToUse.ellipse(_x * percent, _y * percent, (_width / 2) * percent, (_height / 2) * percent).attr(vectOpt);
                    break;
                case "rectangle":
                    vect = paperToUse.rect(_x * percent, _y * percent, _width * percent, _height * percent).attr(vectOpt);
                    break;
                case "roundedrectangle":
                    vect = paperToUse.rect(_x * percent, _y * percent, _width * percent, _height * percent, 10).attr(vectOpt);
                    break;
            }

            vect.transform("s" + _node.options.scaleX + "," + _node.options.scaleY);

            //need to set in case toback or tofront is called and the load order changes in the context plugin
            vect.node.setAttribute("rel", _node.options.id);
            vect.data({ id: _node.options.id });
            _node.vect = vect;

            /*
            var tx = _x + (_width / 2);
            var ty = _y + (_height / 2);

            if (_node.options.vectorPath === "ellipse") {
            tx = _x;
            ty = _y;
            }
            */

            tc = _node.textCoords();
            lc = _node.linkCoords();
            text = paperToUse.text(tc.x, tc.y, (_node.options.text || '')).attr({ "font-size": _node.options.fontSize + "pt", fill: _node.options.foregroundColor || "#000" });
            link = paperToUse.linkArrow().transform(["t", lc.x, ",", lc.y, "s", ".8", ",", ".8", "r", "180"].join());

            _node.text = text;
            _node.link = link;

            _node.relationships.wireHoverEvents();
            _node.relationships.wireDragEvents();
            _node.links && _node.links.wireEvents();

            if (_node.options.image && _node.options.image !== "") {
                _node.vect.attr({ "fill": "url(" + _node.options.image + ")", "stroke-width": 2, "stroke": "#000" });
            }

            if (!_node.options.link || !_node.options.link.show) {
                _node.link.hide();
            }

            return vect;
        };
        return _self;
    }
})(Slatebox, Slatebox.fn.slate);(function ($s, $slate) {
    $slate.fn._message = function () {
        var _self = this

        _self.show = function (msg, time) {
            var mb = _self._.messageBox;
            var d = $s.getDimensions(_self._.options.container);

            if (_self._.messageBox === undefined) {
                var r = _self._.paper;
                mb = _self._.messageBox = r.set();
                mb.push(r.rect(0, 0, d.width, 28, 7).attr({ fill: "#ffff99" }));
                mb.push(r.text(0, 0, "").standard());
            }

            mb.show();

            mb[1].attr({ text: msg });
            var _w = mb[1].getBBox().width;
            mb[0].attr({ width: _w + 12 });

            var _x = _self._.options.viewPort.left + ((d.width - mb[0].attr("width")) / 2);
            var _y = _self._.options.viewPort.top + 20;
            mb[0].attr({ x: _x, y: _y, "fill-opacity": 0 });
            mb[1].attr({ x: _x + (_w / 2) + 6, y: _y + 13, "fill-opacity": 0 });

            mb.animate({ "fill-opacity": 1 }, 500, function () {
                setTimeout(function () {
                    mb.animate({ "fill-opacity": 0 }, 500, function () {
                        mb.hide();
                    });
                }, time);
            });
        };

        return _self;
    }
})(Slatebox, Slatebox.fn.slate);(function ($s, $slate) {
    $slate.fn._canvas = function () {
        if (Raphael === undefined) {
            alert("You must load Raphael in order to use the Slatebox.slate.canvas.js plugin!");
        }

        var _self = this, _paper, _internal, _status, imageFolder, _dken = null;

        var cp = function (e) {
            var m = $s.mousePos(e);

            difX = Canvas.objInitPos.left + (m.x - Canvas.objInitialMousePos.x);
            difY = Canvas.objInitPos.top + (m.y - Canvas.objInitialMousePos.y);

            _width = _self._.options.containerStyle.width;
            _height = _self._.options.containerStyle.height;
            _vpWidth = _self._.options.viewPort.width;
            _vpHeight = _self._.options.viewPort.height;

            if (difX > 0) difX = 0; else if (Math.abs(difX) + _width > _vpWidth) difX = _width - vpWidth;
            if (difY > 0) difY = 0; else if (Math.abs(difY) + _height > _vpHeight) difY = _height - _vpHeight;

            return { x: difX, y: difY };
        };

        var Canvas = {
            objInitPos: {},
            objInitialMousePos: { x: 0, y: 0 },
            kinetic: null,
            isDragging: false,
            initDrag: function (e) {
                if (_self._.options.viewPort.allowDrag) {
                    _self._.multiselection && _self._.multiselection.end();
                    this.isDragging = true;
                    var m = $s.mousePos(e);
                    Canvas.objInitPos = $s.positionedOffset(_internal);
                    var offsets = $s.positionedOffset(_self._.options.container);
                    Canvas.objInitialMousePos = { x: m.x + offsets.left, y: m.y + offsets.top };

                    var xy = cp(e);

                    _status.innerHTML = Math.abs(xy.x) + ', ' + Math.abs(xy.y);

                    if (_self._.options.showStatus) {
                        _status.style.display = 'block';
                        _self._.multiselection && _self._.multiselection.hide();
                    }

                    _internal.style.cursor = 'url(' + imageFolder + 'closedhand.cur), default';

                    if (m.allTouches) {
                        _self._.options.lastTouches = m.allTouches;
                    }

                    if ($s.isFunction(_self._.removeContextMenus)) _self._.removeContextMenus();
                    $s.stopEvent(e);
                } else {
                    if ($s.isFunction(_self._.onSelectionStart)) {
                        _self._.onSelectionStart.apply(this, [e]);
                    } else {
                        $s.stopEvent(e);
                    }
                }
            },
            setCursor: function (containerInstance) {
                if (this.isDragging)
                    _internal.style.cursor = 'url(' + imageFolder + 'closedhand.cur), default';
                else
                    _internal.style.cursor = 'url(' + imageFolder + 'openhand.cur), default';
            },
            onDrag: function (e) {
                if (this.isDragging && _self._.options.viewPort.allowDrag) {
                    var xy = cp(e);
                    if (xy.allTouches && xy.allTouches.length > 1) {
                        _self._.options.lastTouches = xy.allTouches;
                    }

                    _status.innerHTML = Math.abs(xy.x) + ', ' + Math.abs(xy.y);
                    _internal.style.left = xy.x + "px";
                    _internal.style.top = xy.y + "px";
                }
            },
            endDrag: function (e) {
                if (this.isDragging && _self._.options.viewPort.allowDrag) {
                    this.isDragging = false;
                    //var m = $s.mousePos(e);

                    _internal.style.cursor = 'url(' + imageFolder + 'openhand.cur), default';
                    _status.style.display = 'none';
                    _self._.multiselection && _self._.multiselection.show();

                    var xy = cp(e);
                    _self._.options.viewPort.left = Math.abs(xy.x); // Math.abs((difX * _self._.options.viewPort.zoom) / _self._.options.viewPort.width);
                    _self._.options.viewPort.top = Math.abs(xy.y); // Math.abs((difY * _self._.options.viewPort.zoom) / _self._.options.viewPort.height);

                    _self._.birdseye && _self._.birdseye.refresh(true);

                    _self.broadcast();

                    /*
                    if (!isNaN(Canvas.objInitPos.left)) {
                    difX = Canvas.objInitPos.left + (Canvas.objInitialMousePos.x);
                    difY = Canvas.objInitPos.top + (Canvas.objInitialMousePos.y);

                    _width = _self._.options.containerStyle.width;
                    _height = _self._.options.containerStyle.height;

                    vpWidth = _self._.options.viewPort.width; //* _self._.options.viewPort.zoom) / _self._.options.viewPort.width;
                    vpHeight = _self._.options.viewPort.height // * _self._.options.viewPort.zoom) / _self._.options.viewPort.height;

                    if (difX >= 0) difX = 0; else if (Math.abs(difX) + _width > vpWidth) difX = _width - vpWidth;
                    if (difY >= 0) difY = 0; else if (Math.abs(difY) + _height > vpHeight) difY = _height - vpHeight;
                    Canvas.objInitPos = {};

                    _self._.options.viewPort.left = Math.abs(difX); // Math.abs((difX * _self._.options.viewPort.zoom) / _self._.options.viewPort.width);
                    _self._.options.viewPort.top = Math.abs(difY); // Math.abs((difY * _self._.options.viewPort.zoom) / _self._.options.viewPort.height);
                    //Canvas.saveAndBroadcast(difX, difY);
                    }
                    */
                }
            }
        };

        _self.broadcast = function () {
            _self._.websync && _self._.websync.send({ type: "onCanvasMove", data: { left: _self._.options.viewPort.left, top: _self._.options.viewPort.top} });
        };

        _self.move = function (_opts) {
            var opts = {
                x: 0
                , y: 0
                , dur: 500
                , callbacks: { after: null, during: null }
                , isAbsolute: true
                , easing: 'easeFromTo'
            };

            $s.extend(opts, _opts);

            _self._.nodes.closeAllConnectors();
            var x = opts.x;
            var y = opts.y;
            var dc = $s.isFunction(opts.callbacks.during);
            if (opts.isAbsolute === false) {
                x = _self._.options.viewPort.left + x;
                y = _self._.options.viewPort.top + y;
            }

            if (opts.dur > 0) {

                emile(_internal, "left:" + (x * -1) + "px;top:" + y * -1 + "px", {
                    duration: opts.dur
                    , before: function () {
                        _self._.options.allowDrag = false;
                    }
                    , after: function () {
                        _self._.options.allowDrag = true;
                        _self._.options.viewPort.left = Math.abs(parseInt(_internal.style.left.replace("px", "")));
                        _self._.options.viewPort.top = Math.abs(parseInt(_internal.style.top.replace("px", "")));
                        opts.callbacks.after && opts.callbacks.after.apply(_self);
                    }
                    , during: function () {
                        dc && opts.callbacks.during.apply(this);
                    }
                    , easing: _self.easing[opts.easing]
                });

            } else {
                //x = Math.abs(_self._.options.viewPort.left) + Math.abs(x) * -1;
                //y = Math.abs(_self._.options.viewPort.top) + Math.abs(y) * -1;
                _internal.style.left = (x * -1) + "px";
                _internal.style.top = (y * -1) + "px";
                _self._.options.viewPort.left = Math.abs(x);
                _self._.options.viewPort.top = Math.abs(y);
            }
        };

        _self.resize = function (val, x, y) {

            val = parseInt(val);

            var R = (_self._.options.viewPort.width / val);
            var dimen = $s.getDimensions(_self._.options.container);

            _internal.style.width = "50000px";
            _internal.style.height = "50000px";

            var _top = ((_self._.options.viewPort.top * -1) * R);
            var _left = ((_self._.options.viewPort.left * -1) * R);

            var _centerY = y || (((dimen.height / 2 * R) - (dimen.height / 2)) * -1);
            var _centerX = x || (((dimen.width / 2 * R) - (dimen.width / 2)) * -1);

            _top = (_top + _centerY);
            _left = (_left + _centerX);

            _internal.style.top = _top + "px";
            _internal.style.left = _left + "px";

            _self._.options.viewPort.zoom = { w: val, h: val, l: parseInt(_left * -1), t: parseInt(_top * -1), r: _self._.options.viewPort.originalWidth / val };
            //if (_self._.options.viewPort.lockZoom === false) z.r = R;
        };

        _self.clear = function () {
            _self._.options.container.innerHTML = "";
            return _self._;
        };

        _self.init = function (_options) {
            var options = { imageFolder: '/public/images/' };
            $s.extend(options, _options);
            var c = _self._.options.container;
            _self._.options.imageFolder = options.imageFolder;
            imageFolder = options.imageFolder;
            if (typeof (c) === "string") c = $s.el(c);
            if (c === undefined || c === null) {
                throw new Error("You must provide a container to initiate the canvas!");
            }

            /*
            var _cw = _self._.options.containerStyle.width + "px";
            var _ch = _self._.options.containerStyle.height + "px";
            if (_self._.options.containerStyle.width === 'auto') {
            _ws = $s.windowSize();
            _self._.options.containerStyle.width = _ws.width;
            _self._.options.containerStyle.height = _ws.height;
            _cw = _ws.width + "px";
            _ch = _ws.height + "px"
            }

            c.style.width = _cw;
            c.style.height = _ch;
            */

            //wipe it clean
            c.innerHTML = "";
            if (_paper) _paper.clear();

            if (_internal) c.removeChild(_internal);

            //internal
            _internal = document.createElement('div');
            _internal.setAttribute("class", "slateboxInternal");
            c.appendChild(_internal);

            //status
            var d = $s.getDimensions(c);
            _status = document.createElement("div");
            _status.style.position = "absolute";
            _status.style.height = " 20px";
            _status.style.left = '0px';
            _status.style.color = "#000";
            _status.style.fontSize = "10pt";
            _status.style.fontFamily = "trebuchet ms";
            _status.style.top = "0px";
            _status.style.display = "none";
            _status.style.padding = "5px";
            _status.style.filter = "alpha(opacity=80)";
            _status.style.opacity = '.80';
            _status.style.backgroundColor = "#ffff99"
            _status.style.fontWeight = "bold";
            c.appendChild(_status);

            //style container
            var cs = _self._.options.containerStyle;
            //c.style.border = cs.border;
            //c.style.backgroundImage = cs.backgroundImage;
            //c.style.backgroundRepeat = cs.backgroundImageIsTiled; bad for ie
            //c.style.backgroundColor = cs.backgroundColor;
            c.style.position = "relative";
            c.style.overflow = "hidden";

            //style internal
            var _w = _self._.options.viewPort.width;
            var _h = _self._.options.viewPort.height;
            var _l = _self._.options.viewPort.left;
            var _t = _self._.options.viewPort.top;
            _internal.style.width = _w + "px";
            _internal.style.height = _h + "px";
            _internal.style.left = (_l * -1) + "px";
            _internal.style.top = (_t * -1) + "px";
            _internal.style.position = 'absolute';
            _self.borderTop = _self.borderTop + 2 || 2;
            _internal.style.borderTop = _self.borderTop + "px";
            _internal.style.cursor = 'url(' + imageFolder + 'openhand.cur), default'

            if (_self._.options.viewPort.allowDrag) {

                $s.each(['onmousedown', 'ontouchstart'], function () { _internal[this] = Canvas.initDrag; }); //
                $s.each(['onmousemove', 'ontouchmove'], function () { _internal[this] = Canvas.onDrag; }); //
                $s.each(['onmouseup', 'ontouchend'], function () { _internal[this] = Canvas.endDrag; }); //
                $s.each(['onmouseout'], function () { _internal[this] = Canvas.endDrag; });

                /*
                _internal.onmousedown = Canvas.initDrag;
                _internal.onmousemove = Canvas.onDrag;
                _internal.onmouseup = Canvas.endDrag;
                _internal.onmouseout = Canvas.endDrag;
                */

                var origVal, zoomX, zoomY;
                _internal.ongesturestart = function (e) {
                    e.preventDefault();
                    _self._.options.viewPort.allowDrag = false;
                    if (_self._.options.lastTouches) {
                        var lt = _self._.options.lastTouches;
                        zoomX = lt[0].x;
                        zoomY = lt[0].y;
                        if (lt.length > 1) {
                            zoomX = (Math.max(lt[0].x, lt[1].x || 0) - Math.min(lt[0].x, lt[1].x || lt[0].x)) / 2; // + Math.min(lt[0].x, lt[1].x || lt[0].x);
                            zoomY = (Math.max(lt[0].y, lt[1].y || 0) - Math.min(lt[0].y, lt[1].y || lt[0].y)) / 2; // + Math.min(lt[0].y, lt[1].y || lt[0].y);
                        }
                        origVal = _self._.options.viewPort.zoom.w;
                        //_self._.paper.rect(xMiddle + _self._.options.viewPort.left, yMiddle + _self._.options.viewPort.top, 10, 10);
                    }
                }
                _internal.ongesturechange = function (e) {
                    var val = origVal / e.scale;

                    _self._.zoom(0, 0, val, val, false);
                    _self._.canvas.resize(val); //, zoomX, zoomY

                    $s.select('li.rmitem')[0].innerHTML = _self._.options.lastTouches[0].x + " , " + _self._.options.lastTouches[1].x
                }
                _internal.ongestureend = function (e) {
                    _self._.options.viewPort.allowDrag = true;
                    //try expanding the canvas -- i think this is consistently firing, but the _internal is out of scope after a bit...

                    //$s.select('li.rmitem')[0].innerHTML = z.w + " , " + z.h + " , " + z.l + " , " + z.t;
                    var z = _self._.options.viewPort.zoom;
                    var vp = _self._.options.viewPort;
                    vp.width = z.w;
                    vp.height = z.h;
                    vp.left = z.l;
                    vp.top = z.t;
                }
            }

            _paper = Raphael(_internal, _w, _h);

            _self._.options.viewPort.originalHeight = _h;
            _self._.options.viewPort.originalWidth = _w;

            _self._.paper = _paper;
            return _self._;
        };

        _self.rawSVG = function () {
            return _internal.innerHTML;
        };

        _self.darken = function (percent) {
            if (_dken === null) {
                _dken = document.createElement("div");
                var ws = $s.windowSize();
                _dken.style.backgroundColor = '#ccc';
                _dken.style.position = 'absolute';
                _dken.style.left = '0px';
                _dken.style.top = '0px';
                _dken.style.width = ws.width + "px";
                _dken.style.height = ws.height + "px";
                _dken.style.zIndex = 999;
                _dken.style.filter = "alpha(opacity=" + percent + ")";
                _dken.style.opacity = (percent / 100);
                document.body.appendChild(_dken);
            }
            return _dken;
        };

        $s.addEvent(window, "resize", function () {
            if (_dken !== null) {
                var ws = $s.windowSize();
                _dken.style.width = ws.width + "px";
                _dken.style.height = ws.height + "px";
            }
        });

        _self.lighten = function () {
            _dken && document.body.removeChild(_dken);
            _dken = null;
        };

        _self.get = function () {
            return _internal;
        };

        _self.draggable = function () { return _internal; }

        _self.easing = {
            elastic: function (pos) { return -1 * Math.pow(4, -8 * pos) * Math.sin((pos * 6 - 1) * (2 * Math.PI) / 2) + 1 },
            swingFromTo: function (pos) { var s = 1.70158; return ((pos /= 0.5) < 1) ? 0.5 * (pos * pos * (((s *= (1.525)) + 1) * pos - s)) : 0.5 * ((pos -= 2) * pos * (((s *= (1.525)) + 1) * pos + s) + 2) },
            swingFrom: function (pos) { var s = 1.70158; return pos * pos * ((s + 1) * pos - s) },
            swingTo: function (pos) { var s = 1.70158; return (pos -= 1) * pos * ((s + 1) * pos + s) + 1 },
            bounce: function (pos) { if (pos < (1 / 2.75)) { return (7.5625 * pos * pos) } else { if (pos < (2 / 2.75)) { return (7.5625 * (pos -= (1.5 / 2.75)) * pos + 0.75) } else { if (pos < (2.5 / 2.75)) { return (7.5625 * (pos -= (2.25 / 2.75)) * pos + 0.9375) } else { return (7.5625 * (pos -= (2.625 / 2.75)) * pos + 0.984375) } } } },
            bouncePast: function (pos) { if (pos < (1 / 2.75)) { return (7.5625 * pos * pos) } else { if (pos < (2 / 2.75)) { return 2 - (7.5625 * (pos -= (1.5 / 2.75)) * pos + 0.75) } else { if (pos < (2.5 / 2.75)) { return 2 - (7.5625 * (pos -= (2.25 / 2.75)) * pos + 0.9375) } else { return 2 - (7.5625 * (pos -= (2.625 / 2.75)) * pos + 0.984375) } } } },
            easeFromTo: function (pos) { if ((pos /= 0.5) < 1) { return 0.5 * Math.pow(pos, 4) } return -0.5 * ((pos -= 2) * Math.pow(pos, 3) - 2) },
            easeFrom: function (pos) { return Math.pow(pos, 4) },
            easeTo: function (pos) { return Math.pow(pos, 0.25) }
        };

        return _self;
    }
})(Slatebox, Slatebox.fn.slate);(function ($s, $slate) {
    $slate.fn._birdseye = function () {
        if (Raphael === undefined) {
            alert("You must load Raphael in order to use the Slatebox.slate.birdseye.js plugin!");
        }

        var _self = this, _be, _corner, _handle, orx, sp, options, _parentDimen, _parentOffset, _lastX, _lastY, _wpadding, _hpadding;

        _self.show = function (_options) {

            options = {
                size: 200
                , onHandleMove: null
            };

            $s.extend(options, _options);

            var c = _self._.options.container;
            _parentDimen = $s.getDimensions(c);
            _parentOffset = $s.positionedOffset(c);

            _be = document.createElement('div');
            _be.setAttribute("id", "slateBirdsEye_" + _self._.options.id);
            _be.style.position = "absolute";
            _be.style.height = options.size + "px";
            _be.style.width = options.size + "px";
            _be.style.border = "2px inset #333";
            _be.style.backgroundColor = "#fff";

            c.appendChild(_be);
            setBe();

            _corner = $s.instance.slate({
                container: $s.el("slateBirdsEye_" + _self._.options.id)
                , viewPort: { allowDrag: false }
                , collaboration: { allow: false }
                , showZoom: false
                , showBirdsEye: false
                , events: {
                    onNodeDragged: function () {
                        _self._.nodes.copyNodePositions(_corner.nodes.allNodes);
                    }
                }
            }).canvas.init();
            _self.refresh();

            $s.addEvent(window, "resize", function () {
                var c = _self._.options.container;
                _parentDimen = $s.getDimensions(c);
                _parentOffset = $s.positionedOffset(c);
                setBe();
            });

            if (!_self._.options.showBirdsEye) _self.disable();
        };

        _self.enabled = function () { return _corner !== undefined; };

        _self.enable = function () {
            if (!_corner) _self.show();
            $s.el("slateBirdsEye_" + _self._.options.id).style.display = "block";
        };

        _self.disable = function () {
            $s.el("slateBirdsEye_" + _self._.options.id).style.display = "none";
        };

        function setBe() {
            _be.style.left = (_parentDimen.width - options.size) + "px";
            _be.style.top = "-2px";
        };

        _self.relationshipsChanged = function (pkg) {
            if (_corner) {
                switch (pkg.type) {
                    case "removeRelationship":
                        _corner.nodes.removeRelationship(pkg.data);
                        //{ parent: c.parent.options.id, child: c.child.options.id };
                        break;
                    case "addRelationship":
                        //data: { id: _self._.options.id, relationships: rels} };
                        _corner.nodes.addRelationship(pkg.data);
                        break;
                }
            }
        };

        _self.nodeChanged = function (pkg) {
            if (_corner) {
                var _node;
                switch (pkg.type) {
                    case 'onNodeShapeChanged':
                        _node = _corner.nodes.one(pkg.data.id);
                        _node.shapes.set(pkg.data);
                        break;
                    case "onNodeTextChanged":
                        _node = _corner.nodes.one(pkg.data.id);
                        _node.editor.set(pkg.data.text, pkg.data.fontSize, pkg.data.fontColor);
                        break;
                    case "onNodeColorChanged":
                        _node = _corner.nodes.one(pkg.data.id);
                        _node.colorpicker.set(pkg.data);
                        break;
                    case "onNodeImageChanged":
                        _node = _corner.nodes.one(pkg.data.id);
                        _node.images.set(pkg.data.img, pkg.data.w, pkg.data.h);
                        break;
                    case "onNodeResized":
                        _node = _corner.nodes.one(pkg.data.id);
                        _node.resize.set(pkg.data.width, pkg.data.height);
                        break;
                    case "onNodeToFront":
                        _node = _corner.nodes.one(pkg.data.id);
                        _node.vect.toFront();
                        break;
                    case "onNodeToBack":
                        _node = _corner.nodes.one(pkg.data.id);
                        _node.vect.toBack();
                        break;
                    case "onNodeLocked":
                        _node = _corner.nodes.one(pkg.data.id);
                        _node.options.allowDrag = false;
                    case "onNodeUnlocked":
                        _node = _corner.nodes.one(pkg.data.id);
                        _node.options.allowDrag = true;
                        break;
                    case "onNodeAttached":
                        _node = _corner.nodes.one(pkg.data.id);
                        _node.relationships.attach();
                        break;
                }
            }
        };

        _self.nodeDeleted = function (pkg) {
            if (_corner) {
                var _node = _corner.nodes.one(pkg.data.id);
                _node.del();
            }
        };

        _self.nodeDetatched = function (pkg) {
            if (_corner) {
                var _node = _corner.nodes.one(pkg.data.id);
                _node.relationships.detatch();
            }
        };

        _self.reload = function (json) {
            if (_handle) _handle.remove();
            _corner.loadJSON(json);
            _self.refresh(true);
        };

        _self.refresh = function (blnNoAdditions) {
            if (_corner) {
                if (_handle) _handle.remove();

                if (blnNoAdditions === true) {
                    _corner.canvas.move({ x: _self._.options.viewPort.left, y: _self._.options.viewPort.top, dur: 0, isAbsolute: true });
                    _corner.nodes.copyNodePositions(_self._.nodes.allNodes); //repositionNodes();
                } else {
                    _corner.loadJSON(_self._.exportDifference(_corner), true);
                }

                orx = _self._.getOrientation();

                if (_self._.options.viewPort.left < orx.left)
                    _wpadding = ((_self._.options.viewPort.left) - (orx.left));
                else
                    _wpadding = (_self._.options.viewPort.left - orx.left) + (_parentDimen.width - orx.width); // (_self._.options.viewPort.left + _parentDimen.width) - (orx.left + orx.width);

                _hpadding = ((_self._.options.viewPort.top) - (orx.top));

                var _pw = Math.max(Math.abs(_wpadding), (orx.width < _parentDimen.width ? (_parentDimen.width - orx.width) : 0));
                var _ph = Math.max(Math.abs(_hpadding), (orx.height < _parentDimen.height ? (_parentDimen.height - orx.height) : 0));

                var wp = ((orx.width + _pw) / options.size) * _self._.options.viewPort.width;
                var hp = ((orx.height + _ph) / options.size) * _self._.options.viewPort.height;

                sp = Math.max(wp, hp);

                var _r = Math.max(_self._.options.viewPort.width, _self._.options.viewPort.height) / sp;
                var l = (orx.left + (_wpadding < 0 ? _wpadding : 0)) * _r - 5;
                var t = (orx.top + (_hpadding < 0 ? _hpadding : 0)) * _r - 5;

                _corner.zoom(0, 0, sp, sp, true);
                _corner.options.viewPort.zoom.r = _corner.options.viewPort.originalWidth / sp;
                _corner.canvas.move({ x: l, y: t, dur: 0, isAbsolute: true });
                _corner.disable();

                var _ix = _self._.options.viewPort.left / _self._.options.viewPort.zoom.r; // +_wpadding; // orx.left; // -(orx.left - _self._.options.viewPort.left); //+_self._.options.viewPort.left; // orx.left + orx.width / 2;
                var _iy = _self._.options.viewPort.top / _self._.options.viewPort.zoom.r; // +_hpadding; // orx.top; // -(orx.top - _self._.options.viewPort.top); //+_self._.options.viewPort.top; // orx.top + orx.height / 2;

                var _w = _parentDimen.width / _self._.options.viewPort.zoom.r, _h = _parentDimen.height / _self._.options.viewPort.zoom.r;
                _handle = _corner.paper.rect(_ix, _iy, _w, _h).attr({ stroke: 'red', "stroke-width": 1, fill: "#f8f8f8", "fill-opacity": ".6" });
                wireHandle();
            }
        };

        var init = function () {
            _handle.ox = this.attr("x");
            _handle.oy = this.attr("y");
        };

        var move = function (x, y) {

            var _zr = _corner.options.viewPort.originalWidth / sp;
            x = x + ((x / _zr) - x);
            y = y + ((y / _zr) - y);

            var _mx = _handle.ox + x;
            var _my = _handle.oy + y;

            _handle.attr({ x: _mx, y: _my });

            var bb = _handle.getBBox();
            var _cx = bb.x * _self._.options.viewPort.zoom.r; // -_parentDimen.width / 2;
            var _cy = bb.y * _self._.options.viewPort.zoom.r; // -_parentDimen.height / 2;

            if ($s.isFunction(options.onHandleMove)) {
                options.onHandleMove.apply(this, [_cx, _cy]);
            };

            _self._.canvas.move({ x: _cx, y: _cy, dur: 0, isAbsolute: true });

            _lastX = bb.x;
            _lastY = bb.y;
            _lastOrx = orx;

            //_handle.transform(["t", x, y].join());
        };

        var up = function (e) {
            _self.refresh();
            _self._.canvas.broadcast();
        };

        function wireHandle() {
            _handle.drag(move, init, up);
        };

        return _self;
    }
})(Slatebox, Slatebox.fn.slate);(function ($s, $slate, fm) {
    $slate.fn._websync = function () {
        if (!fm) { alert("You must load websync javascript dependencies before initializing collaboration!"); }
        var _self = this, client = fm.websync.client, util = fm.utilities, json = fm.json, _subscribers = []
            , onSubscribersChanged = [], _collabDiv, _parentDimen, _parentOffset, _userName, _userProfileImage, _userIP
            , _chatBubbles = [];

        _self.subscribers = _subscribers;

        _self.attach = function (onChange) {
            var _id = $s.guid();
            var _eve = { id: _id, eve: onChange };
            onSubscribersChanged.push(_eve);
            return _id;
        }

        _self.detatch = function (id) {
            onSubscribersChanged = _.filter(onSubscribersChanged, function (s) { return s.id !== id; });
        };

        function notify(subs) {
            $s.each(onSubscribersChanged, function () {
                this.eve.apply(_self, [subs]);
            });
        };

        _self.init = function (key, userName, userProfileImage, userIP) {
            _userName = userName;
            _userProfileImage = userProfileImage;
            _userIP = userIP;

            if (client.isConnected() === undefined) {
                client.initialize({
                    key: key
                });
            }

            if (!client.isConnected()) {
                client.connect({
                    onSuccess: function (args) {
                        //util.log('Connected!');
                    },
                    onFailure: function (args) {
                        util.log('Connect failed: ' + args.error);
                    }
                });
                client.subscribe({
                    channel: '/' + _self._.options.id,
                    onSuccess: function (args) {
                        var pkg = { type: "onRollCall", data: { id: client.getId(), name: userName, profileImage: userProfileImage, ip: userIP} };
                        send(pkg);

                        //either adds or updates the subscriber
                        updateSubscribers(pkg);
                    },
                    onFailure: function (args) {
                        util.log('Subscribe failed: ' + args.error);
                    },
                    onReceive: function (args) {
                        receive(args.data);
                    },
                    onSubscribersChange: function (args) {
                        //only handles unsubscribes...and those are likely due to timeouts
                        var change = args.change;
                        var _atLeastOneUnsubscribe = false;
                        for (var i = 0; i < change.clients.length; i++) {
                            var c = change.clients[i];

                            if (change.type == 'unsubscribe') {
                                _subscribers = _.reject(_subscribers, function (s) { return s.id === c.id; });
                                _atLeastOneUnsubscribe = true;
                            }
                        }

                        if (_atLeastOneUnsubscribe) {
                            refreshCollaboratorList();
                            notify(_subscribers);
                        }
                    }
                });
            }
        };

        function send(pkg) {
            _self._.setChanged(true);
            if ($s.isFunction(_self._.options.onSlateChanged)) {
                _self._.options.onSlateChanged.apply(this, [_subscribers.length]);
            }
            pkg.ip = _userIP;
            client.publish({
                channel: '/' + _self._.options.id,
                data: pkg,
                onSuccess: function (args) {
                    //util.log('Published!');
                },
                onFailure: function (args) {
                    util.log('Publish failed: ' + args.error);
                }
            });
        };

        function receive(pkg) {
            var dur = 500, cn;
            switch (pkg.type) {
                case "onCanvasMove":
                    var opts = {
                        x: pkg.data.left
                        , y: pkg.data.top
                        , dur: dur
                        , callback: {
                            after: function () {
                                _self._.birdseye && _self._.birdseye.refresh(true);
                            }
                        }
                        , isAbsolute: true
                    };
                    _self._.canvas.move(opts);
                    chat(pkg, 'That was me\n moving the canvas!');
                    break;
                case "onNodeMove":
                    cn = _self._.nodes.one(pkg.data.id);
                    cn.move(pkg);
                    chat(pkg, 'That was me\n moving the node!');
                    break;
                case "removeRelationship":
                    _self._.nodes.removeRelationship(pkg.data);
                    _self._.birdseye && _self._.birdseye.relationshipsChanged(pkg);
                    chat(pkg, 'That was me\n removing the relationship!');
                    break;
                case "addRelationship":
                    _self._.nodes.addRelationship(pkg.data);
                    _self._.birdseye && _self._.birdseye.relationshipsChanged(pkg);
                    chat(pkg, 'That was me\n adding the relationship!');
                    break;
                case "onNodeTextChanged":
                    cn = _self._.nodes.one(pkg.data.id);
                    cn.editor.set(pkg.data.text, pkg.data.fontSize, pkg.data.fontColor);
                    _self._.birdseye && _self._.birdseye.nodeChanged(pkg);
                    chat(pkg, 'That was me\n changing the text!');
                    break;
                case "onNodeColorChanged":
                    cn = _self._.nodes.one(pkg.data.id);
                    cn.colorpicker.set(pkg.data);
                    _self._.birdseye && _self._.birdseye.nodeChanged(pkg);
                    chat(pkg, 'That was me\n changing the color!');
                    break;
                case "onNodeResized":
                    cn = _self._.nodes.one(pkg.data.id);
                    cn.resize.set(pkg.data.width, pkg.data.height, 500);
                    _self._.birdseye && _self._.birdseye.nodeChanged(pkg);
                    chat(pkg, 'That was me\n changing the size!');
                    break;
                case "onNodeDeleted":
                    cn = _self._.nodes.one(pkg.data.id);
                    cn.del()
                    _self._.birdseye && _self._.birdseye.nodeDeleted(pkg);
                    chat(pkg, 'That was me\n deleting the node!');
                    break;
                case "onNodeDetatched":
                    var cn = _self._.nodes.one(pkg.data.id);
                    cn.relationships.detatch();
                    _self._.birdseye && _self._.birdseye.nodeDetatched(pkg);
                    chat(pkg, 'That was me\n detatching the node!');
                    break;
                case "onNodeAttached":
                    cn = _self._.nodes.one(pkg.data.id);
                    cn.relationships.attach();
                    _self._.birdseye && _self._.birdseye.nodeChanged(pkg);
                    chat(pkg, 'That was me\n re-attaching the node!');
                    break;
                case "onNodeImageChanged":
                    cn = _self._.nodes.one(pkg.data.id);
                    cn.images.set(pkg.data.img, pkg.data.w, pkg.data.w);
                    _self._.birdseye && _self._.birdseye.nodeChanged(pkg);
                    chat(pkg, 'That was me\n changing the image!');
                    break;
                case "onNodeAdded":
                    _self._.loadJSON(pkg.data, true);
                    _self._.birdseye && _self._.birdseye.refresh();
                    chat(pkg, 'That was me\n adding the node!');
                    break;
                case "onNodeShapeChanged":
                    cn = _self._.nodes.one(pkg.data.id);
                    cn.shapes.set(pkg.data);
                    _self._.birdseye && _self._.birdseye.nodeChanged(pkg);
                    chat(pkg, 'That was me\n changing the shape!');
                    break;
                case "onNodeToFront":
                    cn = _self._.nodes.one(pkg.data.id);
                    cn.vect.toFront();
                    _self._.birdseye && _self._.birdseye.nodeChanged(pkg);
                    chat(pkg, 'That was me\n bringing to front!');
                    break;
                case "onNodeToBack":
                    cn = _self._.nodes.one(pkg.data.id);
                    cn.vect.toBack();
                    _self._.birdseye && _self._.birdseye.nodeChanged(pkg);
                    chat(pkg, 'That was me\n send to back!');
                    break;
                case "onNodeLocked":
                    cn = _self._.nodes.one(pkg.data.id);
                    cn.options.allowDrag = false;
                    _self._.birdseye && _self._.birdseye.nodeChanged(pkg);
                    chat(pkg, 'That was me\n locking the node!');
                case "onNodeUnlocked":
                    cn = _self._.nodes.one(pkg.data.id);
                    cn.options.allowDrag = true;
                    _self._.birdseye && _self._.birdseye.nodeChanged(pkg);
                    chat(pkg, 'That was me\n unlocking the node!');
                    break;
                case "onNodeLinkAdded":
                    cn = _self._.nodes.one(pkg.data.id);
                    cn.links && cn.links.set(pkg.data.linkType, pkg.data.linkData);
                    chat(pkg, 'That was me\n adding the resource link!');
                    break;
                case "onNodeLinkRemoved":
                    cn = _self._.nodes.one(pkg.data.id);
                    cn.links && cn.links.unset();
                    chat(pkg, 'That was me\n removing the link!');
                    break;
                case "onZoom":
                    var val = pkg.data.zoomLevel;
                    _self._.zoom(0, 0, val, val, false);
                    _self._.canvas.resize(val);

                    var z = _self._.options.viewPort.zoom;

                    //var v = options.slate.options.viewPort;
                    _self._.options.viewPort.width = z.w;
                    _self._.options.viewPort.height = z.h;
                    _self._.options.viewPort.left = z.l;
                    _self._.options.viewPort.top = z.t;

                    _self._.zoomSlider.setValue(z.w);
                    _self._.birdseye && _self._.birdseye.refresh(true);
                    chat(pkg, 'That was me\n zooming the canvas!');
                    break;

                case "onRollCall":
                    updateSubscribers(pkg);
                    //broadcast credentials
                    var pkg = { type: "onRollCallResponse", data: { id: client.getId(), name: _userName, profileImage: _userProfileImage, ip: _userIP} };
                    send(pkg);
                    refreshCollaboratorList();
                    notify(_subscribers);
                    break;

                case "onRollCallResponse":
                    updateSubscribers(pkg);
                    refreshCollaboratorList();

                    //notify listening modules that a response was given
                    notify(_subscribers);
                    break;

                case "onChat":
                    chat(pkg);
                    break;
            }
        };

        function chat(pkg, msg) {
            var ix = getIndex(pkg);
            if (ix > -1) {
                var _id = _subscribers[ix].id;

                if (msg) pkg.msg = msg;

                if ($s.isFunction(_self._.options.collaboration.onCollaboration))
                    _self._.options.collaboration.onCollaboration.apply(this, [_subscribers[ix].name, msg]);

                var cbi = -1;
                $s.each(_chatBubbles, function () {
                    cbi++;
                    if (this.id === _id) {
                        _chatBubbles[cbi].showing = true;
                        _chatBubbles[cbi].messages.push(pkg.msg);
                        setChatBubble(_chatBubbles[cbi]);
                        return;
                    }
                });
            }
        };

        function getIndex(pkg) {
            //localhost counter helps only for debugging purposes on one box...
            var _localhost = 0;
            for (i = 0; i < _subscribers.length; i++) {
                if (_subscribers[i].ip === '127.0.0.1') _localhost++;
                if (_subscribers[i].ip === pkg.ip && _localhost < 2) {
                    return i;
                }
            }
            return -1;
        };

        function updateSubscribers(pkg) {
            var inx = getIndex(pkg);
            if (inx === -1) {
                _subscribers.push(pkg.data);
            } else {
                $s.extend(_subscribers[inx], { name: pkg.data.name, id: pkg.data.id, profileImage: pkg.data.profileImage });
            }
        }

        _self.updateName = function (o, n, p) {
            for (i = 0; i < _subscribers.length; i++) {
                if (_subscribers[i].name.split('<br/>')[0] === o || _subscribers[i].name === o) {
                    var s = _subscribers[i];
                    s.name = n;
                    s.profileImage = p || s.profileImage;
                    var pkg = { type: "onRollCall", data: { id: s.id, name: n, profileImage: p || s.profileImage, ip: s.ip} };
                    send(pkg);
                    refreshCollaboratorList();
                }
            }
        };

        _self.hide = function () {
            _collabDiv.style.display = 'none';
        };

        _self.show = function () {
            if (_self._.options.collaboration.showPanel) {
                _collabDiv.style.display = 'block';
            }
        };

        _self.disconnect = function (cb) {
            if (client.isConnected())
                client.disconnect({ onComplete: function () { cb.apply(this); } });
            else
                cb.apply(this);
        };

        function pc() {

        }

        function refreshCollaboratorList() {
            if (_self._.options.collaboration.showPanel) {
                if (_subscribers.length <= 1) {
                    if (_collabDiv) document.body.removeChild(_collabDiv);
                    _collabDiv = null;
                    return;
                }

                _parentDimen = $s.windowSize();

                if (!_collabDiv) {
                    _collabDiv = document.createElement('div');
                    _collabDiv.setAttribute("id", "slateCollaborators_" + _self._.options.id);
                    _collabDiv.style.position = "absolute";

                    _collabDiv.style.height = "222px";
                    _collabDiv.style.width = "180px";
                    _collabDiv.style.border = "1px solid #333";
                    _collabDiv.style.backgroundColor = "#fff";
                    _collabDiv.style.display = "block";

                    if (_self._.options.collaboration.panelContainer) {
                        var ele = $s.ensureEle(_self._.options.collaboration.panelContainer);
                        ele.appendChild(_collabDiv);
                    } else {
                        document.body.appendChild(_collabDiv);
                    }

                    setCollab();
                } else {
                    _collabDiv.innerHTML = "";
                }

                _chatBubbles = [];
                $s.each($s.select("div.chatBubble"), function () {
                    document.body.removeChild(this);
                });

                var _all = '';
                var _tmp = "<div id='chatBubble_{id}' style='border-bottom:1px solid #000;clear:both;height:80px;padding:3px;background-color:{color}'><div style='float:left;' class='slate_collaborator' rel='{id}'><img src='{image}' style='width:80px;'/></div><div style='float:right;'>{name}</div></div>";
                var _cur = 0, _max = 2, _waiting = 0, _waitingList = '';
                $s.each(_subscribers, function () {
                    _cur++;
                    if (_cur <= _max) {
                        var src = this.profileImage || _self._.options.imageFolder + "user.png";
                        var _clr = "#fff";
                        if (this.ip === _userIP) {
                            _clr = "#ffff99";
                            if (this.name.indexOf("YOU") == -1)
                                this.name = this.name + "<br/>(YOU)";
                        }
                        _all += _tmp.replace(/{id}/gi, this.id).replace(/{name}/gi, this.name).replace(/{image}/gi, src).replace(/{color}/g, _clr);

                        _chatBubbles.push({ id: this.id, owner: 'chatBubble_' + this.id, bubble: createBubble(), showing: false, messages: [], clr: _clr });
                    } else {
                        _waitingList += this.name + " ";
                        _waiting++;
                    }
                });

                _all += "<div style='clear:both;'>" + _waiting + " user(s) waiting</div>";
                _all += "<div style='clear:both;'>" +
                            "<input type='text' id='txtChat_" + _self._.options.id + "' maxlength='26' style='height:17px;color:#333;width:174px;font-size:10pt;' value='type to chat and hit enter'/>" +
                        "</div>";
                _collabDiv.innerHTML = _all;

                setTimeout(function () {
                    var _chatText = $s.el("txtChat_" + _self._.options.id);
                    _chatText.onblur = function (e) {
                        if (_chatText.value === "") {
                            _chatText.value = "type to chat and hit enter";
                            _chatText.style.color = "#333";
                        } else {
                            _chatText.style.color = "#000";
                        }
                    };

                    _chatText.onfocus = function (e) {
                        if (_chatText.value === "type to chat and hit enter") {
                            _chatText.value = "";
                            _chatText.style.color = "#000";
                        }
                    };

                    _chatText.onkeypress = function (e) {
                        var txt = _chatText.value;
                        if ($s.getKey(e) === 13 && txt !== "") {
                            e.preventDefault();
                            var pkg = { type: "onChat", msg: txt };
                            _chatText.value = "";
                            send(pkg); //sent to collaborators
                            chat(pkg); //displays locally for the user to see..
                        }
                    };
                }, 100);
            }
        };

        $s.addEvent(window, "resize", function (e) {
            if (_collabDiv) {
                _parentDimen = $s.windowSize();
                setCollab();
            }
        });

        _self.send = function (pkg) {
            send(pkg);
        };

        function setCollab() {
            if (_self._.options.collaboration.panelContainer) {
                var ele = $s.ensureEle(_self._.options.collaboration.panelContainer);
                var dimen = $s.getDimensions(ele);
                switch (_self._.options.collaboration.panelLocation) {
                    case "upperright":
                        _collabDiv.style.left = (dimen.width - 185) + "px";
                        _collabDiv.style.top = "0px";
                        break;
                    default:
                        _collabDiv.style.left = (dimen.width - 185) + "px";
                        _collabDiv.style.top = (dimen.height - 227) + "px";
                        break;
                }
            } else {
                _collabDiv.style.left = (_parentDimen.width - 185) + "px";
                _collabDiv.style.top = (_parentDimen.height - 227) + "px";
            }
        };

        function createBubble() {
            var _chatBubble = document.createElement('div');
            _chatBubble.setAttribute("class", "chatBubble");
            _chatBubble.style.position = "absolute";
            _chatBubble.style.display = "none";
            document.body.appendChild(_chatBubble);
            return _chatBubble;
        };

        function setChatBubble(bub) {
            if (bub.showing) {
                var off = $s.positionedOffset($s.el(bub.owner));

                bub.bubble.style.left = off.left - 150 + "px";
                bub.bubble.style.top = off.top - 40 + "px";
                bub.bubble.style.display = "block";
                if (!bub.r)
                    bub.r = new Raphael(bub.bubble, 180, 130);

                //bub.rsb && bub.rsb.remove();
                var clr = bub.clr || "#fff";
                bub.rsb = bubble(bub.r, 70, 30, bub.messages[bub.messages.length - 1], clr);

                if (bub.chatWire !== null)
                    window.clearTimeout(bub.chatWire);

                bub.chatWire = window.setTimeout(function () {
                    bub.bubble.style.display = "none";
                    bub.showing = false;
                    bub.rsb && bub.rsb.remove();
                    window.clearTimeout(bub.chatWire);
                    bub.chatWire = null;
                }, 5000);
            }
        };

        function setChatBubbles() {
            $s.each(_chatBubbles, function () {
                setChatBubble(this);
            });
        };

        function bubble(r, x, y, txt, clr) {
            var _sb = r.speechbubble(x, y, txt);
            if (clr !== undefined) {
                _sb[0].attr({ fill: clr });
            }
            return _sb;
        };

        return _self;
    }
})(Slatebox, Slatebox.fn.slate, fm);(function ($s, $slate) {
    $slate.fn._zoomSlider = function () {
        var _self = this, slider;

        _self.setValue = function (val) {
            slider.setValue(val);
        };

        _self.hide = function () {
            if ($s.el("slateSlider") !== null) {
                _self._.options.container.removeChild($s.el("slateSlider"));
            }
        };

        _self.show = function (_options) {

            _self.hide();

            var options = {
                height: 320
                , width: 70
                , offset: { left: 10, top: 50 }
                , slider: { height: 300, min: 6000, max: 200000, set: 5000 }
            };

            $s.extend(options, _options);

            var c = _self._.options.container;
            var scx = document.createElement('div');
            scx.setAttribute("id", "slateSlider");
            scx.style.position = "absolute";
            scx.style.height = options.height + "px";
            scx.style.width = options.width + "px";
            scx.style.left = options.offset.left + "px";
            scx.style.top = options.offset.top + "px";
            c.appendChild(scx);

            options.paper = Raphael("slateSlider", options.width, options.height);

            slider = options.paper.slider(options.slider.height, options.slider.min, options.slider.max, options.slider.set, function (val) { //length, start, end, initVal, onSlide, onDone

                if (Raphael.svg) {
                    _self._.zoom(0, 0, val, val, false);
                    _self._.canvas.resize(val);
                }

            }, function (val) {

                _self._.zoom(0, 0, val, val, false);
                _self._.canvas.resize(val);

                var z = _self._.options.viewPort.zoom;

                //var v = _self._.options.viewPort;
                _self._.options.viewPort.width = z.w;
                _self._.options.viewPort.height = z.h;
                _self._.options.viewPort.left = z.l;
                _self._.options.viewPort.top = z.t;

                _self._.birdseye && _self._.birdseye.refresh(true);
                _self._.websync && _self._.websync.send({ type: 'onZoom', data: { zoomLevel: val} });
            });
        };

        return _self;
    }
})(Slatebox, Slatebox.fn.slate);(function ($s, $slate) {
    $slate.fn._keyboard = function () {
        var _self = this, hoverNode = null;

        _self.start = function (_hoverNode) {
            hoverNode = _hoverNode;
            $s.addEvent(document, "keydown", _press);
        };

        var _press = function (e) {
            var _key = $s.getKey(e);
            if (hoverNode) {
                hoverNode.context && hoverNode.context.hide();
                switch (_key) {
                    case 39: //left
                        hoverNode.connectors.addUnpinnedNode(true);
                        break;
                    case 40: //down
                        hoverNode.connectors.addPinnedNode(true);
                        break;
                    case 46: //delete
                        hoverNode.toolbar.del();
                        break;
                }
                return $s.stopEvent(e);
            } else if (_self._.multiselection && _self._.multiselection.isSelecting()) {
                switch (_key) {
                    case 46: //delete
                        _self._.multiselection.del();
                        break;
                }
            }
        };

        _self.end = function () {
            hoverNode = null;
            if (!_self._.multiselection.isSelecting()) {
                $s.removeEvent(document, "keydown", _press);
            }
        };

        return _self;
    }
})(Slatebox, Slatebox.fn.slate);(function ($s, $slate) {
    $slate.fn._multiselection = function () {
        var _self = this, selRect = null, ox, oy, _init, marker = null, selectedNodes = [], origPos = null, resizer = null, minSize = 100;

        _self.init = function () {
            var c = _self._.options.container;
            _init = document.createElement("div");
            _init.style.position = "absolute";
            _init.style.height = "18px";
            _init.style.left = '1px';
            _init.style.color = "#081272";
            _init.style.fontSize = "11pt";
            _init.style.fontFamily = "trebuchet ms";
            _init.style.top = "1px";
            _init.style.display = "block";
            _init.style.padding = "5px";
            _init.style.backgroundColor = "#f8f8f8";
            _init.style.cursor = "pointer";
            _init.innerHTML = "[multi-select]";
            c.appendChild(_init);

            $s.addEvent(_init, "click", function (e) {
                switch (_init.innerHTML) {
                    case "[multi-select]":
                        _self.start();
                        break;
                    case "selecting [click to stop]...":
                        _self.end();
                        break;
                }
            });
        };

        _self.hide = function () {
            if (_init) _init.style.display = "none";
        };

        _self.show = function () {
            if (_init) _init.style.display = "block";
        };

        _self.start = function () {
            _self._.disable(); // options.viewPort.allowDrag = false;
            _init.innerHTML = "selecting [click to stop]...";
            _self._.onSelectionStart = function (e) {
                var p = xy(e);
                selRect = _self._.paper.rect(p.x, p.y, 10, 10).attr({ "stroke-dasharray": "-" });
                $s.addEvent(_self._.canvas.get(), "mousemove", _move);
                $s.addEvent(_self._.canvas.get(), "mouseup", _select);
                ox = p.x;
                oy = p.y;
            };
        };

        _self.isSelecting = function () {
            return (marker !== null);
        };

        _self.del = function () {
            if (confirm('Are you sure you want to remove the selected nodes?')) {
                $s.each(selectedNodes, function () {
                    this.toolbar.del();
                });
                _self.end();
            }
        };

        _self.end = function () {
            if (marker !== null) {
                resizer.unmouseover(_resizeHover);
                //marker.undrag(markerEvents.move, markerEvents.init, markerEvents.up);
                //resizer.undrag(resizeEvents.move, resizeEvents.init, resizeEvents.up);
                marker.remove();
                resizer.remove();
                marker = null;
                _self._.keyboard && _self._.keyboard.end();
            }
            if (_init) _init.innerHTML = "[multi-select]";
        };

        _self.endSelection = function () {
            selRect && selRect.remove();
            _self._.enable();
            _self._.onSelectionStart = null;
            $s.removeEvent(_self._.canvas.get(), "mousemove", _move);
            $s.removeEvent(_self._.canvas.get(), "mouseup", _select);
        };

        var xy = function (e) {
            var mp = $s.mousePos(e);
            var off = $s.positionedOffset(_self._.options.container);
            var _x = mp.x + _self._.options.viewPort.left - off.left;
            var _y = mp.y + _self._.options.viewPort.top - off.top;
            var z = _self._.options.viewPort.zoom.r;
            return { x: _x / z, y: _y / z };
        };

        var _move = function (e) {
            p = xy(e);
            var height = p.y - oy;
            var width = p.x - ox;

            if (height > 0) {
                selRect.attr({ height: height });
            } else {
                selRect.attr({ y: p.y, height: (oy - p.y) });
            }
            if (width > 0) {
                selRect.attr({ width: width });
            } else {
                selRect.attr({ x: p.x, width: (ox - p.x) });
            }
        };

        var _select = function (e) {
            var sr = selRect.getBBox();
            var l = _self._.options.viewPort.left;
            var t = _self._.options.viewPort.top;
            var z = _self._.options.viewPort.zoom.r;
            selectedNodes = _.filter(_self._.nodes.allNodes, function (n) {
                return ((n.options.xPos + n.options.width > sr.x && n.options.xPos < sr.x + sr.width) && (n.options.yPos + n.options.height > sr.y && n.options.yPos < sr.y + sr.height))
            });

            if (selectedNodes.length > 1) {
                var orient = _self._.getOrientation(selectedNodes);
                var w = orient.width / z;
                var h = orient.height / z;
                if (w < minSize) w = minSize;
                if (h < minSize) h = minSize;

                marker = _self._.paper.rect(orient.left / z, orient.top / z, w, h).attr({ "stroke-dasharray": "-", "fill": "#f8f8f8" });
                marker.toBack();
                origPos = marker.getBBox();

                _self.endSelection();

                //resizer
                var _nx = origPos.x + origPos.width;
                var _ny = origPos.y + origPos.height;

                resizer = _self._.paper.resize(_self._.options.imageFolder + "2_lines.png").transform(["t", _nx - 5, ",", _ny - 5].join()).attr({ fill: "#fff", "stroke": "#000" });
                resizer.mouseover(_resizeHover);
                marker.drag(markerEvents.move, markerEvents.init, markerEvents.up);
                resizer.drag(resizeEvents.move, resizeEvents.init, resizeEvents.up);

                //hiding resizer for now
                //resizer.hide();

                //unmark all and remove connectors
                _self._.unMarkAll();

                $s.each(selectedNodes, function () {
                    this.connectors.remove();
                    this.resize.hide();
                });

                //activate keyboard shortcuts for this group...
                _self._.keyboard && _self._.keyboard.start();

            } else if (selectedNodes.length === 1) {
                selectedNodes[0].menu.show();
                selectedNodes[0].mark();
                _self.endSelection();
                _self.end();
            } else {
                _self.endSelection();
                _self.end();
            }
        };

        var _resizeHover = function (e) { resizer.attr({ cursor: 'nw-resize' }); };

        var markerEvents = {
            init: function (x, y) {
                _self._.options.viewPort.allowDrag = false;
                marker.ox = marker.attr("x");
                marker.oy = marker.attr("y");
                $s.each(selectedNodes, function () {
                    this.vect.ox = this.vect.type == "rect" ? this.vect.attr("x") : this.vect.attr("cx");
                    this.vect.oy = this.vect.type == "rect" ? this.vect.attr("y") : this.vect.attr("cy");
                });
            }
            , move: function (dx, dy) {
                var _zr = _self._.options.viewPort.zoom.r;
                dx = dx + ((dx / _zr) - dx);
                dy = dy + ((dy / _zr) - dy);

                var att = { x: marker.ox + dx, y: marker.oy + dy };
                marker.attr(att);

                $s.each(selectedNodes, function () {
                    var attx = this.options.vectorPath === "ellipse" ? { cx: this.vect.ox + dx, cy: this.vect.oy + dy} : { x: this.vect.ox + dx, y: this.vect.oy + dy };
                    this.relationships.moveNode(attx);
                });

                var _nx = origPos.x + origPos.width + dx - 5;
                var _ny = origPos.y + origPos.height + dy - 5;
                resizer.transform(["t", _nx, ",", _ny].join(""));
            }
            , up: function (e) {
                _self._.options.viewPort.allowDrag = true;
                _self._.birdseye && _self._.birdseye.refresh(true);

                var _sids = _(selectedNodes).chain().pluck('options').pluck('id').value();

                $s.each(selectedNodes, function () {

                    if (this.options.isPinnedExact) {
                        //if the parent is NOT in the group of selected nodes, then detatch it...
                        var _allParents = _(this.relationships.parents).chain().pluck('parent').pluck('options').pluck('id').value();
                        if (_.intersect(_sids, _allParents).length === 0) {
                            this.relationships.detatch(true);
                            var detPkg = { type: 'onNodeDetatched', data: { id: this.options.id} };
                            _self._.websync && _self._.websync.send(detPkg);
                            _self._.birdseye && _self._.birdseye.nodeDetatched(detPkg);
                            this.refresh();
                        }
                    }

                    this.websync && _self._.websync.send({
                        type: "onNodeMove"
                        , data: {
                            id: this.options.id
                            , x: this.options.xPos
                            , y: this.options.yPos
                        }
                    });
                });

                origPos = marker.getBBox();
            }
        };

        function _resize() {
            var mbb = marker.getBBox();

            var xStatic = mbb.x + mbb.width / 2;
            var yStatic = mbb.y + mbb.height / 2;
            var yScale = mbb.height / origPos.height;
            var xScale = mbb.width / origPos.width;

            $s.each(selectedNodes, function () {
                var nx = xStatic + (xScale * (this.options.xPos - xStatic));
                var ny = yStatic + (yScale * (this.options.yPos - yStatic));

                var att = this.options.vectorPath === "ellipse" ? { cx: nx, cy: ny} : { x: nx, y: ny };
                this.relationships.moveNode(att);

                var nw = xScale * this.options.width; // ((mbb.width * this.options.width) / origPos.width);
                var nh = yScale * this.options.height; // ((mbb.height * this.options.height) / origPos.height);
                this.resize.set(nw, nh, 0);
            });
        };

        var resizeEvents = {
            init: function () {
                _self._.disable();
                $s.each(selectedNodes, function () {
                    this.vect.ox = this.vect.type == "rect" ? this.vect.attr("x") : this.vect.attr("cx");
                    this.vect.oy = this.vect.type == "rect" ? this.vect.attr("y") : this.vect.attr("cy");
                });
            }
            , move: function (dx, dy) {

                var _zr = _self._.options.viewPort.zoom.r;
                dx = dx + ((dx / _zr) - dx);
                dy = dy + ((dy / _zr) - dy);

                var _width = origPos.width + (dx * 2);
                var _height = origPos.height + (dy * 2);

                var _nx = origPos.x + origPos.width + dx - 5;
                var _ny = origPos.y + origPos.height + dy - 5;
                var rw = true, rh = true;
                if (_width < minSize) { _width = minSize; rw = false; }
                if (_height < minSize) { _height = minSize; rh = false; }

                resizer.transform(["t", _nx, ",", _ny].join(""));

                var att = { width: _width, height: _height };
                rw && $s.extend(att, { x: origPos.x - dx });
                rh && $s.extend(att, { y: origPos.y - dy });

                marker.attr(att);
            }
            , up: function () {
                _self._.enable();
                _resize();

                $s.each(selectedNodes, function () {
                    this.resize.send();
                });

                _self._.birdseye && _self._.birdseye.refresh(true);

                origPos = marker.getBBox();
                var _nx = origPos.x + origPos.width;
                var _ny = origPos.y + origPos.height;
                resizer.transform(["t", _nx - 5, ",", _ny - 5].join(""));
            }
        };

        return _self;
    }
})(Slatebox, Slatebox.fn.slate);(function ($s, $n) {
    $n.fn._colorpicker = function (_options) {
        var options = {
            colors: [
                { hex: "000000", to: "575757"} //black //six to a row
                , { hex: "FFFFFF", to: "d9d9d9"} //white
                , { hex: "FF0000", to: "a31616"} //red
                , { hex: "C3FF68", to: "afff68"} //green
                , { hex: "0B486B", to: "3B88B5"} //blue
                , { hex: "FBB829", to: "cd900e"} //orange
                , { hex: "BFF202", to: "D1F940"} //yellow
                , { hex: "FF0066", to: "aa1d55"} ///pink
                , { hex: "800F25", to: "3d0812"} //dark red
                , { hex: "A40802", to: "d70b03"} //red
                , { hex: "FF5EAA", to: "cf5d93"} //strong pink
                , { hex: "740062", to: "D962C6"} //purple
                , { hex: "FF4242", to: "A61515"} //red
                , { hex: "D15C57", to: "9D5C58"} //pinkish
                , { hex: "FCFBE3", to: "c9c56f"} //light yellow-white
                , { hex: "FF9900", to: "c98826"} //orange
                , { hex: "369001", to: "9CEE6C"} //green
                , { hex: "9E906E", to: "675324"} //brown
                , { hex: "F3D915", to: "F9EA7C"} //yellow 2
                , { hex: "031634", to: "2D579A"} // dark blue
                , { hex: "556270", to: "7b92ab"} //gray-blue
                , { hex: "1693A5", to: "23aad6"} //turquoise
                , { hex: "ADD8C7", to: "59a989"} //light turquoise
                , { hex: "261C21", to: "EB9605" }
            ]
            , useColorLovers: false
        };
        $s.extend(options, _options);
        if (options.colors !== null) {
            $s.availColors = options.colors;
        }
        var _self = this;

        function getColors(callback) {
            if ($s.availColors === undefined) {
                var apiUrl = "http://www.colourlovers.com/api/colors/top&jsonCallback=?"; //&context=" + this.imageContext;
                $s.getJSON(apiUrl,
                    function (data) {
                        $s.availColors = data;
                        if ($s.isFunction(callback))
                            callback.apply(this, [$s.availColors]);
                    }
                );
            } else {
                if ($s.isFunction(callback))
                    callback.apply(this, [$s.availColors]);
            }
        };

        _self.prepColors = function () {
            getColors();
        };

        _self.show = function (x, y, _m) {

            var wx = _m[4].attr("width");

            var _rowOff = wx - 125;

            var _x = x + _rowOff;
            var _y = y + 5;

            getColors(function (data) {
                var tot = -1, rowCount = 6, rp = _self._.slate.paper, w = 15, h = 15, p = 4;

                $s.each(data, function () {
                    tot++;
                    if (tot % rowCount === 0 && tot !== 0) {
                        _y += h + p;
                        _x = _self._.options.xPos + _rowOff;
                        if (_self._.options.vectorPath === 'ellipse') {
                            _x = _x - _self._.options.width / 2;
                        }
                    }
                    var _hex = this.hex;
                    var _to = this.to;
                    var _swatch = rp.rect(_x, _y, w, h, 3).attr({ stroke: '#000', fill: ["90-#", _hex, "-#", _to].join('') });

                    _swatch.mouseover(function (e) {
                        _self._.slate.glow(this);
                        //this.animate({ transform: "s1.5, 1.5" }, 200);
                    });

                    _swatch.mouseout(function (e) {
                        _self._.slate.unglow();
                        //this.animate({ transform: "s1,1" }, 200);
                    });

                    _swatch.mousedown(function (e) {
                        this.loop();
                        var _backColor = this.attr("fill");
                        var _testColor = "#" + _backColor.split(/90-#/g)[1].split(/-#/g)[0];
                        var _textColor = Raphael.rgb2hsb(_testColor).b < .4 ? "#fff" : "#000";

                        if (_self._.options.image !== "") _self._.options.image = "";

                        var _pkg = { type: "onNodeColorChanged", data: { id: _self._.options.id, color: _backColor} };
                        broadcast(_pkg);
                        _self._.slate.birdseye && _self._.slate.birdseye.nodeChanged(_pkg);
                    });

                    _m.push(_swatch);
                    _x += w + p;
                    if (tot > 19) return;
                });
            });
        };

        function broadcast(_pkg) {
            _self.set(_pkg.data);
            _self._.slate.websync && _self._.slate.websync.send(_pkg);
        };

        _self.set = function (cast) {
            _self._.options.backgroundColor = cast.color;
            _self._.vect.attr({ fill: cast.color });
            //$s.each(_self._.relationships.children, function () {
            //    this.lineColor = cast.color;
            //});
            _self._.relationships.refresh();
        };

        return _self;
    }
})(Slatebox, Slatebox.fn.node);(function ($s, $n) {
    $n.fn._connectors = function () {
        var _self = this;
        var buttons;
        var pinnedRowCount = 3;
        var _lastUnpinned = { options: { xPos: null, width: null, yPos: null} };

        _self.remove = function () {
            _.invoke(buttons, 'remove');
        };
        _self.removeSettingsButton = function () { buttons.setting.remove(); }
        _self.show = function (x, y, _m, onSettingsClicked) {
            var btnRadius = 15;
            var r = _self._.slate.paper;

            //menu offset, resetting back
            y = y + 80;

            px = x;
            py = y;

            var _nx = x + _self._.options.width;
            var _ny = y + (_self._.options.height / 2);

            var _cx = x + (_self._.options.width / 2);
            var _cy = y + _self._.options.height;

            var oo = 15, so = .75, btnAttr = { fill: "#fff", stroke: "#000" };

            buttons = {
                setting: r.setting().transform(["t", x + _self._.options.width / 2 - 17, ",", y - 22].join()).attr(btnAttr)
                , unPinned: r.arrow().transform(["t", _nx - 16, ",", _ny - 16, "s", so, so].join()).attr(btnAttr)
                , pinned: r.arrow().transform(["t", _cx - 13, ",", _cy - 9, "s", so, so, "r", "90"].join()).attr(btnAttr)
            };

            $s.each(['mousedown'], function () {
                buttons.setting[this](function (e) {
                    _self._.slate.unglow();
                    onSettingsClicked.apply(this);
                    this.remove();
                    _self._.slate.multiselection && _self._.slate.multiselection.end();
                    _self._.context && _self._.context.hide();
                    _self._.editor && _self._.editor.end();
                    _self._.images && _self._.images.end();
                    _self._.links && _self._.links.end();
                });

                buttons.unPinned[this](function (e) {
                    _self._.slate.unglow();
                    _self._.connectors.addUnpinnedNode();
                    this.loop();
                    _self._.context && _self._.context.hide();
                });

                buttons.pinned[this](function (e) {
                    _self._.slate.unglow();
                    _self._.connectors.addPinnedNode();
                    this.loop();
                    _self._.context && _self._.context.hide();
                });
            });

            $s.each(buttons, function () {
                _m.push(this);
                this.mouseover(function (e) {
                    _self._.slate.glow(this);
                });
                this.mouseout(function (e) {
                    _self._.slate.unglow();
                });
            });

            var rs = _self._.resize.show(_nx, _cy);
            _m.push(rs);

            return _self;
        };

        _self.reset = function () {
            _lastUnpinned = { options: { xPos: null, width: null, yPos: null} };
        };

        function broadcast(_snap) {
            var pkg = { type: 'onNodeAdded', data: _self._.slate.exportDifference(_snap) };
            _self._.slate.websync && _self._.slate.websync.send(pkg);
        };

        _self.addUnpinnedNode = function (skipCenter) {
            //add new node to the right of this one.
            var _snap = _self._.slate.snapshot();

            var _options = $s.clone(_self._.options);
            delete _options.id;
            delete _options.link;
            _options.xPos = (_lastUnpinned.xPos || _self._.options.xPos) + (_self._.options.width || _lastUnpinned.width) + 10;
            _options.text = "";
            _options.width = _self._.options.width;
            _options.height = _self._.options.height;
            var newNode = $s.instance.node(_options);
            _self._.slate.nodes.add(newNode);
            _lastUnpinned = newNode.options;

            broadcast(_snap);
            _self._.slate.birdseye && _self._.slate.birdseye.refresh(false);

            _self._.slate.unMarkAll();

            //fire the editor
            if (skipCenter === undefined) {
                newNode.position('center', function () {
                    newNode.editor && newNode.editor.start();
                });
            }

            return newNode;
        };

        _self.addPinnedNode = function (skipCenter) {
            var _snap = _self._.slate.snapshot();

            var _options = $s.clone(_self._.options);
            delete _options.id;
            delete _options.link;

            //_options.xPos = -99;
            //_options.yPos = -99;
            _options.text = "";
            if (_self._.options.image === "") {
                _options.width = 50;
                _options.height = 50;
            }
            _options.isPinnedExact = true;
            _options.showParentArrow = false;
            _options.showChildArrow = false;

            //add the new node
            var newNode = $s.instance.node(_options);
            _self._.slate.nodes.add(newNode);

            //add relationship
            _self._.relationships.addChild(newNode);
            _self._.pinChildNodes();

            //broadcast the entire difference (new node + new relationship)
            broadcast(_snap);
            _self._.slate.birdseye && _self._.slate.birdseye.refresh(false);

            _self._.slate.unMarkAll();

            if (skipCenter === undefined) {
                newNode.position('center', function () {
                    newNode.editor && newNode.editor.start();
                });
            }

            return newNode;
        };
        return _self;
    }
})(Slatebox, Slatebox.fn.node);(function ($s, $n) {
    $n.fn._editor = function () {
        var _self = this, _tempId = $s.guid().replace("-", ""), lineBreaks = [], _keypress, _submit, _cancel, cursor, ll, _ntfy;
        _self.editing = false;
        _self.start = function () {
            _self.editing = true;
            _self._.slate.keyboard && _self._.slate.keyboard.end();

            cursor = _self._.slate.paper.rect(-99, -99, 8, 1).attr({ fill: "#000" }).loop({
                pkg: [{ fill: "#fff" }, { fill: "#000"}]
                , duration: 500
                , repeat: true
            });
            //positionedoffset?
            _self._.slate.nodes.closeAllMenus();

            var origText = '', origSize;

            function sizes() {
                var _sel = "<select id='ddlTxtSize'>";
                for (ptx = 10; ptx < 201; ptx++) {
                    if (ptx % 2 === 0) {
                        _sel += "<option>" + ptx + "</option>";
                    }
                }
                _sel += "</select>";
                return _sel;
            };

            function buildColorPicker() {
                var ctmp = "<div style='float:left;padding:3px;margin-right:5px;background-color:#f8f8f8;border:1px solid #ccc;cursor:pointer;' class='changeTextColor' rel='{text}'><div style='width:30px;height:30px;background-color:{color};float:left;border:1px solid #333;'></div></div>";
                var _colors = ctmp.replace(/{color}/gi, '#000').replace(/{text}/gi, 'black');
                _colors += ctmp.replace(/{color}/gi, '#FFF').replace(/{text}/gi, 'white');
                return _colors;
            };

            function setColor(clr, txtBg) {
                _self.set(null, null, clr);
                var txt = $s.el("txtForNode")
                txt.style.backgroundColor = txtBg;
                txt.style.color = clr;
                txt.focus();
            };

            if ($s.select("div.embedBar").length > 0) {
                $s.each($s.select("div.embedBar"), function () {
                    document.body.removeChild(this);
                });
            };

            _ntfy = new Notify().message({
                hgt: 120
                , duration: 200
                , className: 'embedBar'
                , delayClose: 0
                , spinner: null
                , hideClose: false
                , popFromBottom: true
                , onClose: function () {
                    //_self.set(origText, origSize);
                    _self.end();
                }
                , onOpen: function (container, _ntfy) {
                    container.innerHTML = "<div style='width:900px;'>You are editing the node's text!<br/><div style='float:left;margin-right:8px;height:60px;'><textarea id='txtForNode' style='text-align:center;width:500px;height:70px;'></textarea></div><div style='float:left;margin-right:20px;'>Size " + sizes() + " pt&nbsp;&nbsp;<button id='btnSubmitText' style='width:80px;font-size:14pt;'>Update</button><div id='textColorPicker' style='margin-top:4px;'></div></div></div>";
                    origText = _self._.options.text;
                    origSize = _self._.options.fontSize;
                    $s.el("txtForNode").value = origText;
                    $s.el("txtForNode").select();

                    $s.el("btnSubmitText").onclick = function (e) {
                        _self.end();
                        submitChanges();
                    };
                    $s.el("ddlTxtSize").value = origSize;
                    $s.el("ddlTxtSize").onchange = function (e) {
                        var pt = this.options[this.selectedIndex].value;
                        _self.set(_self._.options.text, pt);
                    };

                    $s.el("textColorPicker").innerHTML = buildColorPicker();
                    $s.each($s.select("div.changeTextColor"), function () {
                        var btn = this;
                        btn.onclick = function (e) {
                            btn.style.border = "1px solid red";
                            switch (btn.getAttribute("rel")) {
                                case "black":
                                    setColor("#000", "#fff");
                                    break;
                                case "white":
                                    setColor("#fff", "#000");
                                    break;
                            }
                        };
                        btn.onmouseover = function (e) {
                            btn.style.border = "1px solid red";
                        };
                        btn.onmouseout = function (e) {
                            btn.style.border = "1px solid #ccc";
                        };
                    });

                    $s.el("txtForNode").onkeyup = function (e) {
                        var _v = this.value;
                        if (_v === "") _v = " ";
                        var _text = _self._.text;
                        _self._.options.text = _v;
                        var keyCode = $s.getKey(e || event);
                        _text.attr({ "text": (keyCode === 13 || keyCode === 32) ? _v + " " : _v });
                        var ts = _text.getBBox();
                        if (keyCode === 13 || keyCode === 32) { setTimeout(function () { _text.attr({ "text": _self._.options.text }); }, 0) };

                        //get the width of the last line of text
                        var _sp = _text.attr("text").split("\n");
                        ll = _self._.slate.paper.text(-99, -99, _sp[_sp.length - 1]).attr({ "font-size": _self._.options.fontSize + "pt" });
                        var _b = ll.getBBox();
                        ll.remove();

                        _self.resize();
                        _self.resize();
                        _self._.mark();

                        _self._.refresh();

                        var centerX = (_sp.length > 1 ? (ts.width / 2 + _b.width / 2) : ts.width);
                        cursor.attr({ x: ts.x + centerX, y: ts.y + ts.height, "font-size": _self._.options.fontSize + "pt" });
                    };
                }
            });

            _self._.mark();
        };

        _self.set = function (t, s, c) {
            t && (_self._.options.text = t);
            s && (_self._.options.fontSize = s);
            c && (_self._.options.foregroundColor = c);
            t && s && (_self._.text.attr({ "font-size": + s + "pt", "text": t }));
            c && (_self._.text.attr({ fill: c }));
            _self.resize();
            _self.resize();
        };

        function submitChanges() {
            //broadcast
            var textPkg = { type: "onNodeTextChanged", data: { id: _self._.options.id, text: _self._.options.text, fontSize: _self._.options.fontSize, fontColor: _self._.options.foregroundColor} };
            if (_self._.slate.websync) _self._.slate.websync.send(textPkg);
            if (_self._.slate.birdseye) _self._.slate.birdseye.nodeChanged(textPkg);
        };

        _self.resize = function () {
            var _text = _self._.text;

            var _xPad = 20;
            var _yPad = 20;

            if (_self._.options.vectorPath === "ellipse") {
                _xPad = 30;
                _yPad = 30;
            }

            var _shim = false;
            if (_text.attr("text") === " ") { _text.attr("text", "."); _shim = true; }

            var ts = _text.getBBox();

            if (_shim) _text.attr("text", " ");

            var _rsWidth = ts.width + _xPad
                , _rsHeight = ts.height + _yPad
                , _rsX = ts.x - _xPad / 2
                , _rsY = ts.y - _yPad / 2
                , _rscX = ts.x + (_self._.options.width / 2) - _xPad / 2
                , _rscY = ts.y + (_self._.options.height / 2) - _yPad / 2;

            if (_rsWidth < _self._.options.width) {
                _rsWidth = _self._.options.width;
                _rsX = _self._.options.xPos;
                _rscX = _self._.options.xPos;
            }

            if (_rsHeight < _self._.options.height) {
                _rsHeight = _self._.options.height;
                _rsY = _self._.options.yPos;
                _rscY = _self._.options.yPos;
            }

            var att = _self._.vect.type == "rect" ? { x: _rsX, y: _rsY} : { cx: _rscX, cy: _rscY };
            var wdt = _self._.vect.type == "rect" ? { width: _rsWidth, height: _rsHeight} : { rx: _rsWidth / 2, ry: _rsHeight / 2 };
            $s.extend(att, wdt);

            _self._.vect.attr(att);
            _self._.options.xPos = _rsX;
            _self._.options.width = _rsWidth;
            _self._.options.yPos = _rsY;
            _self._.options.height = _rsHeight;

            _self._.link.attr(_self._.linkCoords());
        };

        _self.end = function () {
            _self.editing = false;
            _self._.unmark();
            if (_ntfy && _ntfy.visible()) submitChanges();
            _ntfy && _ntfy.closeMessage();

            if (cursor) {
                cursor.remove();
                cursor = null;
            }
        };

        return _self;
    }
})(Slatebox, Slatebox.fn.node);(function ($s, $n) {
    $n.fn._links = function () {
        var _self = this, _ntfy;

        var options = {
            thumbUrl: '/Thumbnail'
            , existenceUrl: '/UrlExists'
        };

        _self.start = function (_options) {
            _self._.slate.nodes.closeAllMenus();

            $s.extend(options, (_options || {}));

            _ntfy = new Notify().message({
                hgt: 185
                , duration: 200
                , className: 'embedBar'
                , delayClose: 0
                , spinner: null
                , hideClose: false
                , popFromBottom: true
                , onOpen: function (container, _ntfy) {
                    _self._.slate.unglow();
                    container.innerHTML = "<div style='width:900px;'>" +
                                            "<div id='linkForm'><div id='provideUrlPlaceholder' style='height:75px;width:900px;'>" +
                                                "<span style='font-size:20pt;' id='spnEmbedAction'>Provide an external link (will always open in a new window)</span><br/>" +
                                                "<span style='font-size:20pt;color:#ccc;' id='embedUrlPrefix'>http://</span>" +
                                                "<input type='text' id='txtUrl' style='width:450px;font-size:20pt;'/>" +
                                                "&nbsp;<button id='btnApply' style='font-size:20pt;'>go</button>&nbsp;<button id='btnRemove' style='font-size:20pt;visibility:hidden;'>remove</button>" +
                                            "</div>" +
                                            "<div id='messagePlaceholder' style='display:none;height:75px;font-size:20pt;width:900px;'></div>" +
                                            "<div style='margin-top:10px;padding:3px;width:800px;height:63px;font-size:14pt;'>WHERE do you want to link to?" +
                                                "<div style='border-top:1px dashed #ccc;padding:5px;font-size:12pt;'>" +
                                                    "<label for='radioLinkUrl' style='float:left;cursor:pointer;'>" +
                                                        "<input type='radio' id='radioLinkUrl' name='linkType' checked/>" +
                                                        "an EXTERNAL website" +
                                                    "</label>" +
                                                    "<label for='radioLinkSlate' style='float:left;margin-left:15px;cursor:pointer;'>" +
                                                        "<input type='radio' id='radioLinkSlate' name='linkType'/>" +
                                                        "a node on THIS slate" +
                                                    "</label>" +
                                                    "<label for='radioLinkExternalSlate' style='margin-left:15px;float:left;cursor:pointer;display:none;'>" +
                                                        "<input type='radio' id='radioLinkExternalSlate' name='linkType' disabled/>" +
                                                        "a DIFFERENT slate<br/>(coming soon)" +
                                                    "</label>" +
                                                "</div>" +
                                            "</div></div><div id='processForm' style='font-size:20pt;'></div>" +
                                        "</div>";

                    _self._.link.hide();
                    $s.el("txtUrl").focus();
                    _self._.mark();

                    if (_self._.options.link && _self._.options.link.show) {
                        $s.el("btnRemove").style.visibility = "visible";
                    }

                    $s.addEvent($s.el("btnRemove"), "click", function (e) {
                        _self.unset();
                        var pkg = { type: 'onNodeLinkRemoved', data: { id: _self._.options.id} };
                        if (_self._.slate.websync) _self._.slate.websync.send(pkg);
                        return $s.stopEvent(e);
                    });

                    $s.addEvent($s.el("txtUrl"), "keypress", function (e) {
                        if ($s.getKey(e) === 13) {
                            bindURL();
                        }
                    });

                    $s.addEvent($s.el("txtUrl"), "focus", function (e) {
                        this.select();
                    });

                    $s.addEvent($s.el("btnApply"), "click", function (e) {
                        bindURL();
                        return $s.stopEvent(e);
                    });

                    $s.addEvent($s.el("radioLinkUrl"), "click", function (e) {
                        removeInternalLinking();
                        $s.el("provideUrlPlaceholder").style.display = "block";
                        $s.el("messagePlaceholder").style.display = "none";
                    });

                    $s.addEvent($s.el("radioLinkSlate"), "click", function (e) {
                        _self._.slate.options.linking = {
                            onNode: function (node) {
                                _self.set('currentSlate', node.options.id, true);
                                var pkg = { type: 'onNodeLinkAdded', data: { id: _self._.options.id, linkType: 'currentSlate', linkData: node.options.id} };
                                if (_self._.slate.websync) _self._.slate.websync.send(pkg);

                                _ntfy && _ntfy.resize(50, 300, function () {
                                    _ntfy.changeMessage("You've set the link! Returning you to your original node in a moment...");
                                    setTimeout(function () {
                                        _self._.position('center', function () {
                                            //back
                                            _ntfy.changeMessage("The connection is all set!");
                                            setTimeout(function () { _ntfy && _ntfy.closeMessage(); }, 1500);
                                        }, 'swingFromTo', 1500);
                                    }, 2000);
                                });
                                removeInternalLinking();
                            }
                        };
                        $s.el("provideUrlPlaceholder").style.display = "none";
                        $s.el("messagePlaceholder").style.display = "block";
                        $s.el("messagePlaceholder").innerHTML = "Scroll the slate to the node you'd like to link to and click it.";
                    });

                    $s.addEvent($s.el("radioLinkExternalSlate"), "click", function (e) {
                        _self._.slate.options.linking = {
                            onSlate: function (slate) {

                            }
                        };
                        $s.el("provideUrlPlaceholder").style.display = "none";
                        $s.el("messagePlaceholder").style.display = "block";
                        $s.el("messagePlaceholder").innerHTML = "Select the slate that you'd like to link to using the menu above.";
                    });
                }
                , onClose: function () {
                    if (_self._.options.link.show) {
                        _self._.link.show();
                    }
                }
            });
        };

        function removeInternalLinking() {
            _self._.slate.options.linking = null;
        };

        function checkUrlExistence(u, cb) {
            var pkg = JSON.stringify({ Url: u });

            $s.el("btnApply").setAttribute("disabled", "disabled");
            $s.el("btnApply").innerHTML = "Checking...";

            $s.ajax(options.existenceUrl, function (respText, resp) {
                var _getUrl = JSON.parse(respText);
                $s.el("btnApply").removeAttribute("disabled", "disabled");
                $s.el("btnApply").innerHTML = "go";
                cb.apply(this, [_getUrl.exists]);
            }, pkg, 'POST');
        }

        function bindURL() {
            var u = $s.el("txtUrl").value.replace(/http:\/\//gi, '');
            checkUrlExistence(u, function (exists) {
                if (exists !== true) {
                    alert("Sorry but " + u + " doesn't look to be a valid URL. Please check it!");
                } else {
                    _self.set('externalUrl', u, true);
                    var pkg = { type: 'onNodeLinkAdded', data: { id: _self._.options.id, linkType: 'externalUrl', linkData: u} };
                    if (_self._.slate.websync) _self._.slate.websync.send(pkg);
                }
            });
        };

        _self.end = function () {
            _self._.unmark();
            _self._.slate.unglow();
            _ntfy && _ntfy.closeMessage();
        };

        _self.set = function (type, data, prepare) {
            if (!_self._.options.link) _self._.options.link = {};
            if (!_self._.options.link.thumbnail) _self._.options.link.thumbnail = { width: 175, height: 175 };

            switch (type) {
                case 'externalUrl':
                    $s.extend(_self._.options.link, { type: type, data: data, show: true });
                    if (prepare === true) {
                        $s.el("linkForm").style.display = 'none';
                        $s.el("processForm").style.display = 'block';
                        $s.el("processForm").innerHTML = "<div style='margin-top:-3px;float:left;'><span id='spanFetchThumb'></span></div><div style='margin-left:10px;float:left;margin-top:0px;font-size:14pt;'>Fetching URL Thumbnail (it only takes this long the first time)...</div>";

                        _ntfy && _ntfy.resize(50, 300, function () {
                            if ($s.el("spanFetchThumb") !== null)
                                var _spinner = new spinner("spanFetchThumb", 8, 16, 15, 1, "#fff");
                        });

                        var thumbpkg = JSON.stringify({ Url: _self._.options.link.data, Width: _self._.options.link.thumbnail.width, Height: _self._.options.link.thumbnail.height });
                        $s.ajax(options.thumbUrl, function (respText, resp) {
                            var _getUrl = JSON.parse(respText);
                            $s.imageExists(_getUrl.url, function (loaded, w, h) {
                                _self._.link.show();
                                _self.end();
                            });
                        }, thumbpkg, "POST");
                    } else {
                        _self._.link.show();
                    }
                    break;
                case 'currentSlate':
                    $s.extend(_self._.options.link, { type: type, data: data, show: true });
                    break;
                case 'externalSlate':
                    break;

            }

        };

        _self.unset = function () {
            $s.extend(_self._.options.link, { type: '', data: '', show: false });
            _self._.link.hide();
            _self.end();
        };

        _self.processEvent = function () {
            switch (_self._.options.link.type) {
                case "externalUrl":
                    var surl = _self._.options.link.data.length > 20 ? _self._.options.link.data.substring(0, 20) + "..." : _self._.options.link.data;
                    var _msg = surl;
                    _self._.link.tooltip({ type: 'image', msg: _msg }, _self._.options.link.thumbnail.width, _self._.options.link.thumbnail.height);
                    var thumbpkg = JSON.stringify({ Url: _self._.options.link.data, Width: _self._.options.link.thumbnail.width, Height: _self._.options.link.thumbnail.height });
                    $s.ajax(options.thumbUrl, function (respText, resp) {
                        var _getUrl = JSON.parse(respText);
                        if (_getUrl.url !== "") {
                            $s.imageExists(_getUrl.url, function (loaded, w, h) {
                                _self._.link.tt[0].attr({ "fill": "url(" + _getUrl.url + ")" });
                            });
                        }
                    }, thumbpkg, 'POST');
                    break;
                case "externalSlate":
                    break;
                case "currentSlate":
                    _self._.slate.addtip(_self._.link.tooltip({ type: 'text', msg: "Jump to another node" }, 140, 23));
                    break;
            }
        };

        _self.wireEvents = function () {
            _self._.link.mouseover(function (e) {
                _self._.slate.glow(_self._.link);
                _self.processEvent();
                $s.stopEvent(e);
            });

            _self._.link.mouseout(function (e) {
                _self._.slate.unglow();
                switch (_self._.options.link.type) {
                    case 'externalUrl':
                        _self._.link.untooltip();
                        break;
                    case "currentSlate":
                        _self._.slate.untooltip();
                        break;
                }
                $s.stopEvent(e);
            });

            _self._.link.click(function (e) {
                switch (_self._.options.link.type) {
                    case "externalUrl":
                        window.open(["http://", _self._.options.link.data].join(""), 'sb_external');
                        break;
                    case "externalSlate":
                        break;
                    case "currentSlate":
                        var n = _self._.slate.nodes.one(_self._.options.link.data),
                            _vpt = n.vect.getBBox(), zr = _self._.slate.options.viewPort.zoom.r;

                        n.position('center', function () {
                            n.mark();
                        }, 'swingFromTo', 2000);

                        break;
                }
                $s.stopEvent(e);
            });
        };

        return _self;
    }
})(Slatebox, Slatebox.fn.node);(function ($s, $n) {
    $n.fn._menu = function () {
        var _self = this;
        var _m = null
        var _isOpen = false;

        _self.isOpen = function () { return _isOpen; };
        _self.show = function (ttl) {
            if (_self._.slate !== null) {
                var r = _self._.slate.paper;
                if (ttl === undefined) ttl = 3000;
                if (_m) { _.invoke(_m, 'remove'); _m = null; }

                var off = _self._.offset();
                var _x = off.x + _self._.slate.options.viewPort.left;
                var _y = off.y + _self._.slate.options.viewPort.top - 80;

                _m = r.set();

                //right, bottom, and settings connectors
                _self._.connectors.show(_x, _y, _m, function () {
                    loadParent(r, _x, _y);
                });

                if (_self.wasOpen) { _self.wasOpen = false; _self._.connectors.removeSettingsButton(); loadParent(r, _x, _y); }
            }
        };

        _self.hide = function () {
            if (_m) { _.invoke(_m, 'remove'); _m = null; }
            _isOpen = false;
        };

        function loadParent(r, _x, _y) {

            _isOpen = true;

            //menu parent
            var _menuParent = r.rect(_x, _y, 295, 80, 10).attr({ "fill": "90-#ccc-#eee" });
            _menuParent.node.style.cursor = "pointer";
            _m.push(_menuParent);

            //toolbar -- connector, editor, deleter
            _self._.toolbar.show(_x, _y, _m);

            //color picker
            _self._.colorpicker.show(_x, _y, _m);

            //shapes -- change the node shape to rect, rounded rect, ellipse
            _self._.shapes.show(_x, _y, _m);

            //lines on menu
            _m.push(r.path(["M", _x, _y + 36, "L", _x + 160, _y + 36].join(",")).attr({ stroke: "#000" }));
            _m.push(r.path(["M", _x + 160, _y, "L", _x + 160, _y + 80].join(",")).attr({ stroke: "#000" }));

            //menu
            var cls = r.deleter().attr({ fill: "#ddd", stroke: "#333" }).transform(["t", (_x + 275), ",", (_y - 13), "s", ",", ".75", ".75"].join());
            cls.mouseover(function () {
                _self._.slate.glow(cls);
            });
            cls.mouseout(function () {
                _self._.slate.unglow();
            });
            cls.mousedown(function () {
                _self._.slate.unglow();
                _self.hide();
            });
            _m.push(cls);

        };

        return _self;
    }
})(Slatebox, Slatebox.fn.node);(function ($s, $n) {
    $n.fn._relationships = function () {
        var _self = this;
        _self.parents = [];
        _self.children = [];
        _self.associations = [];

        function broadcast(pkg) {
            _self._.slate.websync && _self._.slate.websync.send(pkg);
        };

        function refreshBe() {
            //refresh the birds eye
            var _json = null;
            if (_self._.slate.birdseye)
                _json = _self._.slate.birdseye.refresh(false);
            return _json;
        };

        _self.addParent = function (_node, parentPkg, isTemp) {
            var cx = _self._.slate.paper;
            var _connection = cx.connection({
                id: $s.guid()
                , parent: _node
                , child: _self._
                , lineColor: _self._.options.lineColor
                , lineWidth: _self._.options.lineWidth
                , lineOpacity: _self._.options.lineOpacity
                , blnStraight: parentPkg.isStraightLine || false
                , showParentArrow: parentPkg.showParentArrow || false
                , showChildArrow: parentPkg.showChildArrow || false
            });
            _connection.line.toBack();
            _self.parents.push(_connection);

            _node.relationships.children.push(_connection);

            if (isTemp === undefined || isTemp === false)
                wireLineEvents(_connection);
            //refreshBe();
            return _connection;
        };

        _self.addChild = function (_node, childPkg) {
            var cx = _self._.slate.paper;
            childPkg = childPkg || {};
            var _connection = cx.connection({
                id: $s.guid()
                , parent: _self._
                , child: _node
                , lineColor: _self._.options.lineColor
                , lineWidth: _self._.options.lineWidth
                , lineOpacity: _self._.options.lineOpacity
                , blnStraight: childPkg.isStraightLine || false
                , showParentArrow: childPkg.showParentArrow || false
                , showChildArrow: childPkg.showChildArrow || false
            });
            _connection.line.toBack();
            _self.children.push(_connection);
            _node.relationships.parents.push(_connection);

            wireLineEvents(_connection);
            //refreshBe();

            return _connection;
        };

        _self.addAssociation = function (_node, assocPkg) {
            var cx = _self._.slate.paper;
            assocPkg = assocPkg || {};
            var _connection = cx.connection({
                id: $s.guid()
                , parent: _self._
                , child: _node
                , lineColor: _self._.options.lineColor
                , lineWidth: _self._.options.lineWidth
                , lineOpacity: _self._.options.lineOpacity
                , blnStraight: assocPkg.isStraightLine || false
                , showParentArrow: assocPkg.showParentArrow || false
                , showChildArrow: assocPkg.showChildArrow || false
            });
            _connection.line.toBack();
            _self.associations.push(_connection);
            _node.relationships.associations.push(_connection);

            wireLineEvents(_connection);
            //refreshBe();

            return _connection;
        };

        function wireLineEvents(c) {
            if (_self._.options.allowMenu) {
                var sp = 200, highlight = "#ff0";
                c.line.node.style.cursor = "pointer";
                c.line.mouseover(function () {
                    if (_self._.slate.options.enabled) {
                        _self._.slate.glow(this);
                        c.showChildArrow && (_self._.slate.glow(c.childArrow));
                        c.showParentArrow && (_self._.slate.glow(c.parentArrow));
                    }
                });
                c.line.mouseout(function () {
                    _self._.slate.unglow();
                });

                c.line.mousedown(function (e) {
                    if (_self._.slate.options.enabled) {
                        _self._.slate.unglow();

                        var pkg = { type: "removeRelationship", data: { parent: c.parent.options.id, child: c.child.options.id} };
                        _self._.slate.nodes.removeRelationship(pkg.data);
                        _self._.slate.birdseye && _self._.slate.birdseye.relationshipsChanged(pkg);
                        broadcast(pkg);

                        _self.initiateTempNode(e, c.parent);
                    }
                });
            }
        };

        _self.initiateTempNode = function (e, _parent, isNew) {
            var mp = $s.mousePos(e);
            var _slate = _parent.slate;

            var off = $s.positionedOffset(_slate.options.container);

            var _zr = _self._.slate.options.viewPort.zoom.r;
            var xp = (_slate.options.viewPort.left + mp.x - off.left);
            var yp = (_slate.options.viewPort.top + mp.y - off.top);

            var _tempNode = $s.instance.node({
                id: _self._.slate.tempNodeId
                , xPos: xp + ((xp / _zr) - xp)
                , yPos: yp + ((yp / _zr) - yp)
                , lineColor: "#990000"
                , backgroundColor: "#ffffff"
                , vectorPath: 'ellipse'
                , width: 30
                , height: 30
            });

            _slate.nodes.add(_tempNode, true);
            var _tempRelationship = _tempNode.relationships.addParent(_parent, {}, true);

            _tempRelationship.hoveredOver = null;
            _tempRelationship.lastHoveredOver = null;

            //initiates the drag
            _tempNode.vect.initDrag(e); //, off.x, off.y);
            _slate.options.viewPort.allowDrag = false;

            _tempNode.vect.mousemove(function (e) {
                _self._.slate.paper.connection(_tempRelationship);

                //is there a current hit?
                if (_tempRelationship.hoveredOver === null) { //(e.clientX + e.clientY) % 2 === 0 && 
                    _tempRelationship.hoveredOver = hitTest($s.mousePos(e));
                    if (_tempRelationship.hoveredOver !== null) {
                        //yes, currently over a node -- scale it
                        _tempRelationship.hoveredOver.vect.animate({ "stroke-width": 5 }, 500, function () {
                            _tempRelationship.hoveredOver.vect.animate({ "stroke-width": _self._.options.borderWidth }, 500, function () {
                                _tempRelationship.hoveredOver = null;
                            });
                        });

                        //_tempRelationship.hoveredOver.vect.animate({ scale: '1.25, 1.25' }, 200);
                        //remember this node
                        //_tempRelationship.lastHoveredOver = _tempRelationship.hoveredOver;
                    } else {
                        //no current hit...is there a previous hit to reset?
                        //if (_tempRelationship.lastHoveredOver !== null) {
                        //    _tempRelationship.lastHoveredOver.vect.attr({ fill: _self._.options.backgroundColor });
                        //_tempRelationship.lastHoveredOver.vect.animate({ scale: '1,1' }, 200);
                        //    _tempRelationship.lastHoveredOver = null;
                        //}
                    }
                }
            });
            _tempNode.vect.mouseup(function (e) {

                _tempNode.relationships.removeParent(_parent);
                _tempNode.slate.nodes.remove(_tempNode);

                var overNode = hitTest($s.mousePos(e));
                if (overNode !== null) {
                    //overNode.vect.transform("s1,1,");
                    _parent.relationships.addAssociation(overNode);
                    var _pkg = { type: "addRelationship", data: { type: 'association', parent: _parent.options.id, child: overNode.options.id} };
                    _self._.slate.birdseye && _self._.slate.birdseye.relationshipsChanged(_pkg);
                    broadcast(_pkg);
                }

                if (_self._.slate.options.enabled)
                    _parent.slate.options.viewPort.allowDrag = true;
            });
        };

        _self.removeAll = function () {
            $s.each(_self.children, function () {
                this.child.relationships.removeParent(_self._); //.parent);
                _self._.slate.paper.removeConnection(this);
            });
            $s.each(_self.parents, function () {
                this.parent.relationships.removeChild(_self._); //this.parent);
                _self._.slate.paper.removeConnection(this);
            });
            $s.each(_self.associations, function () {
                this.child.relationships.removeAssociation(_self._); //.parent);
                _self._.slate.paper.removeConnection(this);
            });
            _self.parents = [];
            _self.children = [];
            _self.associations = [];
        };

        _self.removeParent = function (_node) {
            _self.parents = remove(_self.parents, 'parent', _node);
            //_node.relationships.children = remove(_node.relationships.children, 'child', _node);
            return _self;
        };

        _self.removeChild = function (_node) {
            //_node.relationships.parents = remove(_node.relationships.parents, 'parent', _node); //was parent
            _self.children = remove(_self.children, 'child', _node);
            return _self;
        };

        _self.removeAssociation = function (_node) {
            //_node.relationships.associations = remove(_node.relationships.associations, 'parent', _node); //was parent
            _self.associations = remove(_self.associations, 'child', _node);
            _self.associations = remove(_self.associations, 'parent', _node);
            return _self;
        };

        function hitTest(mp) {
            var overNode = null;
            var off = $s.positionedOffset(_self._.slate.options.container);
            $s.each(_self._.slate.nodes.allNodes, function () {
                if (this.options.id !== _self._.slate.tempNodeId && this.options.id !== _self._.options.id) {
                    var _bb = this.vect.getBBox();

                    var _zr = _self._.slate.options.viewPort.zoom.r;
                    var xp = (_self._.slate.options.viewPort.left + mp.x - off.left);
                    var yp = (_self._.slate.options.viewPort.top + mp.y - off.top);

                    var c = {
                        x: xp + ((xp / _zr) - xp)
                        , y: yp + ((yp / _zr) - yp)
                    };

                    if (c.x > _bb.x && c.x < _bb.x + _bb.width && c.y > _bb.y && c.y < _bb.y + _bb.height) {
                        overNode = this;
                        return;
                    }
                }
            });
            return overNode;
        };

        function remove(a, type, obj) {
            var _na = new Array();
            $s.each(a, function () {
                if (this[type].options.id === obj.options.id) {
                    _self._.slate.paper.removeConnection(this);
                } else {
                    _na.push(this);
                }
            });
            return _na;
        };

        var dragger = function (x, y) {
            if (_self._.events && $s.isFunction(_self._.events.onClick)) {
                _self._.events.onClick.apply(this, [function () {
                    _initDrag(this);
                } ]);
            } else {
                _initDrag(this);
            }
        };

        function _initDrag(_vect) {
            _self._.slate.multiselection && _self._.slate.multiselection.end();
            if (_self._.slate.options.linking) {
                _self._.slate.options.linking.onNode.apply(_vect, [_self._]);
            } else {
                if (_self._.options.allowDrag) {
                    _vect.ox = _vect.type == "rect" ? _vect.attr("x") : _vect.attr("cx");
                    _vect.oy = _vect.type == "rect" ? _vect.attr("y") : _vect.attr("cy");

                    _self._.setStartDrag();
                }
            }
        };

        var move = function (dx, dy) {
            if (_self._.options.allowDrag) {
                if (_self._.options.isPinnedExact) {
                    _self.detatch();
                    var detPkg = { type: 'onNodeDetatched', data: { id: _self._.options.id} };
                    _self._.slate.websync && _self._.slate.websync.send(detPkg);
                    _self._.slate.birdseye && _self._.slate.birdseye.nodeDetatched(detPkg);
                }

                var _zr = _self._.slate.options.viewPort.zoom.r;
                dx = dx + ((dx / _zr) - dx);
                dy = dy + ((dy / _zr) - dy);

                var att = _self._.options.vectorPath === "ellipse" ? { cx: _self._.vect.ox + dx, cy: _self._.vect.oy + dy} : { x: _self._.vect.ox + dx, y: _self._.vect.oy + dy };
                _self.moveNode(att);
            }
        };

        var up = function (e) {
            //this.animate({ "fill-opacity": 1, "fill": _self._.options.backgroundColor }, 500);
            _self._.setEndDrag();

            _self._.slate.birdseye && _self._.slate.birdseye.refresh(true);

            if (_self._.slate.events && $s.isFunction(_self._.slate.events.onNodeDragged))
                _self._.slate.events.onNodeDragged.apply(this);

            if (_self._.context && !_self._.context.isVisible()) {
                _self._.slate.websync && _self._.slate.websync.send({
                    type: "onNodeMove"
                    , data: {
                        id: _self._.options.id
                        , x: _self._.options.xPos
                        , y: _self._.options.yPos
                    }
                });
            }
        };

        _self.reattachable = function () {
            return _(_self._.relationships.parents).chain().pluck('child').pluck('options').pluck('id')
                .any(function (id) { return id === _self._.options.id; }).value();
        };

        _self.attach = function () {
            _self._.options.isPinnedExact = true;
            $s.each(_self._.relationships.parents, function () {
                if (this.child.options.id === _self._.options.id) {
                    this.blnStraight = false;
                    this.showChildArrow = false;
                    _self._.options.reattachable = false;
                    _self._.connectors && _self._.connectors.remove();
                    _self._.resize && _self._.resize.hide();
                    this.parent.pinChildNodes();
                    return;
                }
            });
        };

        _self.detatch = function (blnSkipPin) {
            _self._.options.isPinnedExact = false;
            _self._.options.lineOpacity = 1;

            //find the id of the relationships guid and set this one to direct
            $s.each(_self._.relationships.parents, function () {
                if (this.child.options.id === _self._.options.id) {
                    this.blnStraight = true;
                    this.showChildArrow = true;
                    this.lineOpacity = 1;
                    _self._.options.reattachable = true;
                    if (blnSkipPin === undefined)
                        this.parent.pinChildNodes();
                    return;
                }
            });
        };

        _self.moveNode = function (att, blnKeepMenusOpen) {
            _self._.vect.attr(att);

            _self.refresh();

            _self._.options.xPos = att.x || att.cx;
            _self._.options.yPos = att.y || att.cy;

            var lc = _self._.linkCoords();
            _self._.text.attr(_self._.textCoords()); //transform("t" + tx + "," + ty); //
            _self._.link.transform(["t", lc.x, ",", lc.y, "s", ".8", ",", ".8", "r", "180"].join());
            //_self._.link.attr(_self._.linkCoords()); //transform("t" + tx + "," + ty); //

            //close all open menus
            if (blnKeepMenusOpen !== true)
                _self._.slate.nodes.closeAllMenus();

            _self._.pinChildNodes();
        };

        _self.refresh = function () {
            var slate = _self._.slate.paper;
            for (var i = _self.children.length; i--; ) {
                slate.connection(_self.children[i]);
            }
            for (var i = _self.parents.length; i--; ) {
                slate.connection(_self.parents[i]);
            }
            for (var i = _self.associations.length; i--; ) {
                slate.connection(_self.associations[i]);
            }
            slate.safari();
        };

        var _over = function (e) {
            _self._.slate.options.viewPort.allowDrag = false;

            _self._.slate.keyboard && _self._.slate.keyboard.start(_self._);

            //close all open menus
            _self._.slate.nodes.closeAllMenus(_self._.options.id);
            if (_self._.menu && $s.isFunction(_self._.menu.show) && _self._.options.allowMenu && !_self._.menu.isOpen()) {
                _self._.menu.show();
            }
            $s.stopEvent(e);
        };

        var _out = function (e) {
            if (_self._.slate.options.enabled)
                _self._.slate.options.viewPort.allowDrag = true;
            _self._.slate.unglow();
            _self._.slate.keyboard && _self._.slate.keyboard.end();
            $s.stopEvent(e);
        };

        var _dbl = function (e) {
            if (_self._.slate.options.enabled) {
                _self._.position('center', function () {
                    _self._.editor && _self._.editor.start();
                });
            }
            $s.stopEvent(e);
        };

        var v = [];
        _self.wireHoverEvents = function () {
            v = [];
            v.push({ o: _self._.vect, over: _over, out: _out, dbl: _dbl });
            v.push({ o: _self._.text, over: _over, out: _out, dbl: _dbl });
            if (_self._.options.id !== _self._.slate.tempNodeId) {
                $s.each(v, function () {
                    this.o.mouseover(this.over);
                    this.o.mouseout(this.out);
                    this.o.dblclick(this.dbl);
                });
            }
        };

        _self.unwireHoverEvents = function () {
            $s.each(v, function () {
                $s.each(v, function () {
                    this.o.events && this.o.unmouseover(this.over); //_.indexOf(_.pluck(this.o.events, 'name'), "mouseover") > -1
                    this.o.events && this.o.unmouseout(this.out);
                    this.o.events && this.o.undblclick(this.dbl);
                });
            });
        };

        _self.wireDragEvents = function () {
            _self._.vect.drag(move, dragger, up);
            _self._.text.mousedown(function (e) {
                _self._.vect.initDrag(e);
            });
        };

        return _self;
    };
})(Slatebox, Slatebox.fn.node);(function ($s, $n) {
    $n.fn._images = function () {
        var _self = this, _ntfy, options, _searchType = 'image';
        _self.imagesearching = false;
        _self.start = function (_options) {
            _self.imagesearching = true;
            _self._.slate.nodes.closeAllMenus();

            options = {
                searchPatterns: true
                , searchImages: true
                , bingAppKey: '12823C29973B5F456864BDC99D4E68C0C60298D1'
                , imageUrl: 'http://api.bing.net/json.aspx?AppId={appid}&Query={query}&Sources=Image&Version=2.0&Market=en-us&Adult={adult}&Image.Filters={size}&Image.Count={rpp}&Image.Offset={page}&JsonType=callback&JsonCallback=?'
                , patternUrl: 'http://www.colourlovers.com/api/patterns?keywords={query}&orderCol=score&sortBy=DESC&numResults={rpp}&resultOffset={page}&jsonCallback=?'
                , size: 'Small'
                , adultRestriction: 'Moderate' //could be strict
                , isColor: true
                , paging: { rpp: 4, page: 0, total: 0 }
            };

            $s.extend(options, _options || {});

            var origImage;
            _ntfy = new Notify().message({
                hgt: 185
                , duration: 200
                , className: 'embedBar'
                , delayClose: 0
                , spinner: null
                , hideClose: false
                , popFromBottom: true
                , onOpen: function (container, _ntfy) {

                    container.innerHTML = "<div style='width:900px;'>" +
                                            "<div id='embedDivAction' style='float:left;width:260px;'>" +
                                                "<span style='font-size:20pt;' id='spnEmbedAction'>Search</span> (to embed in node)<br/>" +
                                                "<span style='display:none;font-size:20pt;color:#ccc;' id='embedUrlPrefix'>http://</span><input type='text' id='txtSearch' style='width:170px;font-size:20pt;'/>" +
                                                "&nbsp;<button id='btnImageSearch' style='font-size:20pt;'>go</button>" +
                                                "<div id='imgShowSize' style='padding-top:10px'>" +
                                                    "<span id='lblImageSize' style='font-size:12pt;'>Size</span> " +
                                                    "<select id='ddlImageSize'>" +
                                                        "<option>Icon</option>" +
                                                        "<option selected>Small</option>" +
                                                        "<option>Medium</option>" +
                                                        "<option>Large</option>" +
                                                        "<option>All</option>" +
                                                    "</select>" +
                                                    "<label for='chkAsUrl' style='cursor:pointer'><input type='checkbox' id='chkAsUrl' />Provide URL</label>" +
                                                "</div>" +
                                                "<div style='padding-top:10px;font-size:12pt;'><label for='radioImage' style='cursor:pointer;'>" +
                                                    "<input type='radio' id='radioImage' name='imageSearchType' checked/>" +
                                                    "images" +
                                                "</label>" +
                                                "<label for='radioPattern' style='cursor:pointer;'>" +
                                                    "<input type='radio' id='radioPattern' name='imageSearchType'/>" +
                                                    "patterns" +
                                                "</label></div>" +
                                            "</div>" +
                                            "<div style='float:left;width:30px;visibility:hidden;margin-right:-10px;margin-left:-10px;font-size:40pt;cursor:pointer;' id='lnkSearchBack' class='imgChanger'> < </div>" +
                                            "<div style='float:left;width:470px;' id='imgResults'></div>" +
                                            "<div style='float:left;width:30px;visibility:hidden;margin-right:-10px;margin-left:-10px;font-size:40pt;cursor:pointer;' id='lnkSearchForward' class='imgChanger'> > </div>" +
                                        "</div>";

                    origImage = _self._.options.image;
                    $s.el("ddlImageSize").value = options.size;

                    $s.el("txtSearch").focus();
                    _self._.mark();

                    $s.addEvent($s.el("txtSearch"), "keypress", function (e) {
                        if ($s.getKey(e) === 13) {
                            if ($s.el("chkAsUrl").checked) {
                                bindURL();
                            } else {
                                bindResults();
                                options.paging.page = 0;
                                options.paging.total = 0;
                            }
                        }
                    });

                    $s.addEvent($s.el("txtSearch"), "focus", function (e) {
                        this.select();
                    });

                    $s.addEvent($s.el("btnImageSearch"), "click", function (e) {
                        if ($s.el("chkAsUrl").checked) {
                            bindURL();
                        } else {
                            bindResults();
                            options.paging.page = 0;
                            options.paging.total = 0;
                        }
                        return $s.stopEvent(e);
                    });

                    $s.addEvent($s.el("lnkSearchForward"), "click", function (e) {
                        options.paging.page++;
                        bindResults();
                        return $s.stopEvent(e);
                    });

                    $s.addEvent($s.el("lnkSearchBack"), "click", function (e) {
                        options.paging.page--;
                        bindResults();
                        return $s.stopEvent(e);
                    });

                    $s.addEvent($s.el("radioPattern"), "click", function (e) {
                        if ($s.el("chkAsUrl").checked) {
                            $s.el("chkAsUrl").checked = false;
                            shrinkBox(function () {
                                $s.el("imgShowSize").style.visibility = 'hidden';
                            });
                        } else {
                            $s.el("imgShowSize").style.visibility = 'hidden';
                        }
                    });

                    $s.addEvent($s.el("radioImage"), "click", function (e) {
                        $s.el("imgShowSize").style.visibility = 'visible';
                    });

                    $s.addEvent($s.el("chkAsUrl"), "click", function (e) {
                        if (this.checked) {
                            $s.el("ddlImageSize").style.visibility = 'hidden';
                            $s.el("lblImageSize").style.visibility = 'hidden';
                            $s.el("embedUrlPrefix").style.display = "inline";
                            $s.el("spnEmbedAction").innerHTML = "Provide URL";
                            $s.each($s.select("div.imgChanger"), function () {
                                this.style.display = 'none';
                            });
                            $s.el("imgResults").style.display = "none";
                            $s.el("embedDivAction").style.width = "850px";
                            emile($s.el("txtSearch"), "width:600px", {
                                duration: 500
                                , after: function () {
                                    $s.el("txtSearch").setAttribute("placeholder", "provide the url to your image");
                                }
                            });
                        } else {
                            shrinkBox();
                        }
                    });

                    $s.each($s.select("div.imgChanger"), function () {
                        $s.addEvent(this, "mouseover", function (e) {
                            this.style.color = '#fff';
                        });
                        $s.addEvent(this, "mouseout", function (e) {
                            this.style.color = '#000';
                        });
                    });
                }
            });
        };

        var isp = function () {
            return $s.el("radioPattern").checked;
        };

        function shrinkBox(cb) {
            emile($s.el("txtSearch"), "width:170px", {
                duration: 500
                , after: function () {
                    $s.el("ddlImageSize").style.visibility = 'visible';
                    $s.el("lblImageSize").style.visibility = 'visible';
                    $s.el("embedUrlPrefix").style.display = "none";
                    $s.el("spnEmbedAction").innerHTML = "Search";
                    $s.each($s.select("div.imgChanger"), function () {
                        this.style.display = 'block';
                    });
                    $s.el("imgResults").style.display = "block";
                    $s.el("embedDivAction").style.width = "260px";

                    $s.el("txtSearch").removeAttribute("placeholder");

                    if ($s.isFunction(cb)) cb.apply(this);
                }
            });
        };

        function bindURL() {
            var u = ["http://", $s.el("txtSearch").value.replace('http://', '')].join('');
            $s.imageExists(u, function (w, h) {
                _set(u, w, h);
            });

            setTimeout(function () {
                if (_self._.options.image !== u) {
                    alert("Sorry, that image could not be loaded.");
                }
            }, 2000);
        };

        function bindResults() {
            hideNav();
            var _size = "Size:" + $s.el("ddlImageSize").value;
            if ($s.el("ddlImageSize").value === "Icon") {
                _size = "Size:Width:64&Image.Filters=Style:Graphics&Image.Filters=Face:Other";
            } else if ($s.el("ddlImageSize").value === "All") {
                _size = "";
            }

            var _url = options.imageUrl
                        .replace(/{query}/gi, $s.el("txtSearch").value)
                        .replace(/{size}/gi, _size)
                        .replace(/{appid}/gi, options.bingAppKey)
                        .replace(/{rpp}/gi, options.paging.rpp)
                        .replace(/{page}/gi, (options.paging.page * options.paging.rpp))
                        .replace(/{adult}/gi, options.adultRestriction);

            if (isp()) {
                _url = options.patternUrl
                        .replace(/{query}/gi, $s.el("txtSearch").value)
                        .replace(/{rpp}/gi, options.paging.rpp)
                        .replace(/{page}/gi, options.paging.page);
            }

            var _template = "<div style='float:left;cursor:pointer;border:1px solid transparent;padding:5px;height:150px;overflow:hidden;' class='searchImage' rel='{url}|{width}|{height}'><div style='width:100px;height:125px;text-align:center;'><img src='{thumb}' title='{title}' alt='{title}' style='width:{imgSize}px;'/></div><div style='text-align:center;'>{width} x {height}</div></div>";
            var _results = '';
            $s.getJSON(_url, function (context, data) {
                var results = [];
                objs = isp() ? context : context.SearchResponse.Image.Results;
                if (!objs) objs = [];
                options.paging.total = isp() ? ((options.paging.page * options.paging.rpp) + context.length + 1) : context.SearchResponse.Image.Total;
                options.paging.page = isp() ? options.paging.page : context.SearchResponse.Image.Offset / options.paging.rpp;
                $s.each(objs, function () {
                    var _title = this.Title;
                    var _url = this.MediaUrl;
                    var _thumb = this.Thumbnail && this.Thumbnail.Url;
                    var _width = this.Width;
                    var _height = this.Height;
                    var _imgSize = 90;
                    if (isp()) {
                        _title = this.title;
                        _thumb = this.imageUrl;
                        _url = this.imageUrl;
                        _width = 75;
                        _height = 75;
                    }

                    if ($s.el("ddlImageSize").value === "Icon") _imgSize = 64;
                    _results += _template.replace(/{url}/gi, _url).replace(/{thumb}/gi, _thumb).replace(/{imgSize}/gi, _imgSize).replace(/{title}/gi, _title).replace(/{width}/gi, _width).replace(/{height}/gi, _height);
                });
                if (objs.length === 0) {
                    $s.el("imgResults").innerHTML = "<div style='font-size:20pt;color:#fff;margin-top:20px;'>There are no results!</div>";
                } else {
                    $s.el("imgResults").innerHTML = _results;
                    setNav();
                    setImageSelect();
                }
            });
        };

        function setImageSelect() {
            $s.each($s.select("div.searchImage"), function () {
                $s.addEvent(this, "click", function (e) {
                    var _sel = this.getAttribute("rel").split('|');
                    var img = _sel[0], w = parseInt(_sel[1]), h = parseInt(_sel[2]);
                    _set(img, w, h);
                });
                $s.addEvent(this, "mouseover", function (e) {
                    this.style.border = "1px solid #ccc";
                    this.style.backgroundColor = '#333';
                    this.style.color = '#fff'
                });
                $s.addEvent(this, "mouseout", function (e) {
                    this.style.border = "1px solid transparent";
                    this.style.backgroundColor = 'transparent';
                    this.style.color = '#000'
                });
            });
        };

        function _set(img, w, h) {
            _self.set(img, w, h);
            _self._.mark();

            var _pkg = { type: 'onNodeImageChanged', data: { id: _self._.options.id, img: _self._.options.image, w: _self._.options.width, h: _self._.options.height} };
            _self._.slate.birdseye && _self._.slate.birdseye.nodeChanged(_pkg);
            _self._.slate.websync && _self._.slate.websync.send(_pkg);
        };

        function hideNav() {
            $s.el("lnkSearchForward").style.visibility = 'hidden';
            $s.el("lnkSearchBack").style.visibility = 'hidden';
        };

        function setNav() {
            if (((options.paging.page + 1) * options.paging.rpp) < options.paging.total) {
                $s.el("lnkSearchForward").style.visibility = 'visible';
            }
            if (options.paging.page > 0) {
                $s.el("lnkSearchBack").style.visibility = 'visible';
            }
        };

        _self.end = function () {
            _self.imagesearching = false;
            _self._.unmark();
            _ntfy && _ntfy.closeMessage();
        };

        _self.set = function (img, w, h) {
            _self._.options.image = img;
            sz = { "fill": "url(" + _self._.options.image + ")", "stroke-width": 2, "stroke": "#000" };

            if (_self._.options.width < w || !_self._.options.text) {
                _self._.options.width = w;
                var asz = _self._.vect.type == "rect" ? { width: w} : { rx: w / 2 };
                $s.extend(sz, asz);
            }

            if (_self._.options.height < h || !_self._.options.text) {
                _self._.options.height = h;
                var asz = _self._.vect.type == "rect" ? { height: h} : { ry: h / 2 };
                $s.extend(sz, asz);
            }

            _self._.vect.attr(sz);
            _self._.refresh();
            _self._.connectors && _self._.connectors.remove();
            _self._.resize && _self._.resize.hide();
        };

        return _self;
    }
})(Slatebox, Slatebox.fn.node);; (function ($s, $n) {
    $n.fn._shapes = function () {
        var _self = this, hover = "", changer;

        _self.show = function (x, y, _m) {
            x = x + 10;
            var _r = _self._.slate.paper;
            var _s = { fill: "#fff", stroke: "#000", "stroke-width": 1 };
            var shapes = [
                _r.rect(x + 16, y + 43, 30, 30).attr(_s)
                , _r.rect(x + 56, y + 43, 30, 30, 5).attr(_s)
                , _r.ellipse(x + 110, y + 58, 16, 16).attr(_s)
            ];

            $s.each(shapes, function () {
                this.mouseover(function (e) {
                    _self._.slate.glow(this);
                });
                this.mouseout(function (e) {
                    _self._.slate.unglow();
                });
                this.mousedown(function (e) {
                    if (this.type !== _self._.options.vectorPath) {
                        var pkg = { type: 'onNodeShapeChanged', data: { id: _self._.options.id, shape: this.type, rx: this.attr("rx") } };
                        _self.set(pkg.data);
                        _self._.slate.websync && _self._.slate.websync.send(pkg);
                        _self._.slate.birdseye && _self._.slate.birdseye.nodeChanged(pkg);
                    }
                });
                _m.push(this);
            });
        };

        _self.set = function(pkg) {
            _self._.vect.remove();
            var _r = _self._.slate.paper;
            var vectOpt = { fill: _self._.options.backgroundColor, "stroke-width": 2, "stroke": "#000" };
            if (_self._.options.image && _self._.options.image !== "") vectOpt.fill = "url(" + _self._.options.image + ")";
            var _x = _self._.options.xPos, _y = _self._.options.yPos, 
                _width = _self._.options.width, _height = _self._.options.height;
            switch (pkg.shape) {
                case "ellipse":
                    if (_self._.options.vectorPath !== "ellipse") {
                        _self._.options.vectorPath = "ellipse";
                        _self._.vect = _r.ellipse(_x + _width / 2, _y + _height / 2, (_width / 2), (_height / 2)).attr(vectOpt);
                        _self._.options.xPos += _width / 2;
                        _self._.options.yPos += _height / 2;
                    }
                    break;
                case "rect":
                    if (_self._.options.vectorPath === "ellipse") {
                        _self._.options.xPos -= _width / 2;
                        _self._.options.yPos -= _height / 2;
                        _x = _self._.options.xPos;
                        _y = _self._.options.yPos;
                    }
                    _self._.options.vectorPath = pkg.rx > 0 ? "roundedrectangle" : "rectangle";
                    _self._.vect = _r.rect(_x, _y, _width, _height, pkg.rx > 0 ? 10 : 0).attr(vectOpt);
                    break;
            }
            _self._.text.toFront();
            _self._.link.toFront();
            _self._.relationships.wireHoverEvents();
            _self._.relationships.wireDragEvents();

            //needed for tofront and toback ops of the context menu
            _self._.vect.data({id: _self._.options.id});
            _self._.context.init();
        };

        return _self;
    }
})(Slatebox, Slatebox.fn.node);(function ($s, $n) {
    $n.fn._template = new function() {
        var _self = this;

        _self.hello = function () { alert(parent.options.name); }

        return _self;
    }
})(Slatebox, Slatebox.fn.node);(function ($s, $n) {
    $n.fn._toolbar = function () {
        var _self = this;
        _self.show = function (x, y, _m) {
            var y = y + 1;
            var _r = _self._.slate.paper;

            //build toolbar
            var cOptions = { fill: "#eee", stroke: "#333" }
            var cs = 14, cm = 16

            toolbar = [
                _r.handle().data({ msg: 'Connect To Node', width: 110 }).attr({ fill: "90-#999-#ccc", stroke: "#333", "stroke-width": 2 }).transform(["t", x, ",", y, "s", ".85", ".85"].join())
                , _r.deleter().data({ msg: 'Delete', width: 60 }).attr({ fill: "90-#999-#ccc", stroke: "#333", "stroke-width": 2 }).transform(["t", (x + 30), ",", y, "s", ".85", ".85"].join())
                , _r.editor().data({ msg: 'Edit Text', width: 70 }).attr({ fill: "90-#999-#ccc", stroke: "#333", "stroke-width": 2 }).transform(["t", (x + 60), ",", y, "s", ".85", ".85"].join())
                , _r.searcher().data({ msg: 'Embed Image', width: 90 }).attr({ fill: "90-#999-#ccc", stroke: "#333", "stroke-width": 2 }).transform(["t", (x + 90), ",", y, "s", ".85", ".85"].join())
                , _r.link().data({ msg: 'Add Link', width: 70 }).attr({ fill: "90-#999-#ccc", stroke: "#333", "stroke-width": 2 }).transform(["t", (x + 120), ",", y, "s", ".85", ".85"].join())
            ];

            $s.each(toolbar, function () {
                this.mouseover(function (e) {
                    _self._.slate.glow(this);
                    var _text = this.data("msg");
                    _self._.slate.addtip(this.tooltip({ type: 'text', msg: _text }, this.data("width"), this.data("height")));
                    $s.stopEvent(e);
                });
                this.mouseout(function (e) {
                    _self._.slate.unglow();
                    this.untooltip();
                    $s.stopEvent(e);
                });
            });

            for (t = 0; t < 5; t++)
                (function (t) {
                    $s.each(['mousedown'], function () {
                        toolbar[t][this](function (e) {
                            e.stopPropagation();
                            _self._.slate.unglow();
                            _self._.slate.untooltip();
                            if (_self._.events && $s.isFunction(_self._.events.onToolbarClick)) {
                                _self._.events.onToolbarClick.apply(this, [t]);
                            } else {
                                if (t === 0) { //connector
                                    _self._.relationships.initiateTempNode(e, _self._, true);
                                    _self._.menu.hide();
                                } else if (t === 1) {
                                    _self.del();
                                } else if (t === 2) {
                                    _self._.slate.stopEditing();
                                    //fire the editor
                                    _self._.position('center', function () {
                                        _self._.editor.start();
                                    });
                                } else if (t === 3) { //searcher
                                    //var mp = $s.mousePos(e);
                                    //mp.y = mp.y + 130; //adjust up

                                    _self._.slate.unMarkAll();
                                    _self._.slate.stopEditing();
                                    _self._.position('center', function () {
                                        _self._.images.start();
                                    });
                                } else if (t === 4) {
                                    //var mp = $s.mousePos(e);
                                    //mp.y = mp.y + 130; //adjust up

                                    _self._.slate.unMarkAll();
                                    _self._.slate.stopEditing();

                                    _self._.position('center', function () {
                                        _self._.links.start();
                                    });
                                }
                            }
                        });
                    });
                })(t);

            $s.each(toolbar, function () {
                _m.push(this);
            });
            return _self;
        };

        _self.del = function () {
            if (_self._.slate.nodes.allNodes.length <= 1) {
                alert("Sorry, this is the last node on the slate, you cannot delete this one!");
            } else {
                var s = _self._.slate;
                _self._.del();
                var delPkg = { type: 'onNodeDeleted', data: { id: _self._.options.id} };
                s.websync && s.websync.send(delPkg);
                s.birdseye && s.birdseye.nodeDeleted(delPkg);
            }
        };

        return _self;
    }
})(Slatebox, Slatebox.fn.node);(function ($s, $n, $si) {
    $n.fn._context = function () {
        var _self = this, _contextMenu, _priorAllowDrag = true, _height = 150;

        //wire up event
        var _wire = window.setInterval(function () {
            if (_self._) {
                _self.init();
                window.clearInterval(_wire);
            }
        }, 500);

        _self.init = function () {
            if (_self._.text.node) {
                _self._.text.node.oncontextmenu = _self._.vect.node.oncontextmenu = function (e) {
                    _priorAllowDrag = _self._.options.allowDrag;
                    _self._.options.allowDrag = false;
                    _self.hide();
                    buildContext(e);
                    setTimeout(function (e) {
                        _self._.options.allowDrag = _priorAllowDrag;
                    }, 2);
                    return $s.stopEvent(e);
                };
            }
        };

        function buildContext(e) {
            _contextMenu = document.createElement('div');
            _contextMenu.setAttribute("id", "contextMenu_" + _self._.options.id);
            _contextMenu.setAttribute("class", "sb_cm");
            document.body.appendChild(_contextMenu);
            setContext(e);
        };

        function menuItems() {
            var _tmp = "<div style='padding:5px;' class='sb_contextMenuItem' rel='{func}'>{text}</div>";
            var _inside = _tmp.replace(/{func}/g, "tofront").replace(/{text}/g, "to front");
            _inside += _tmp.replace(/{func}/g, "toback").replace(/{text}/g, "to back");
            if (_priorAllowDrag) {
                _inside += _tmp.replace(/{func}/g, "lock").replace(/{text}/g, "lock");
            } else {
                _inside += _tmp.replace(/{func}/g, "unlock").replace(/{text}/g, "unlock");
            }

            if (_self._.relationships.reattachable()) {
                _inside += _tmp.replace(/{func}/g, "pin").replace(/{text}/g, "pin to parent");
                _height = 183;
            }

            _inside += _tmp.replace(/{func}/g, "close").replace(/{text}/g, "close");
            return _inside;
        };

        function setContext(e) {
            _contextMenu.innerHTML = menuItems();

            $s.each($s.select("div.contextMenuItem"), function (e) {
                this.onclick = function (e) {
                    var act = this.getAttribute("rel"), _reorder = false;
                    var pkg = { type: '', data: { id: _self._.options.id} };
                    switch (act) {
                        case "tofront":
                            _self._.toFront();
                            _reorder = true;
                            pkg.type = 'onNodeToFront';
                            break;
                        case "toback":
                            _self._.toBack();
                            _reorder = true;
                            pkg.type = 'onNodeToBack';
                            break;
                        case "lock":
                            _self._.disable();
                            pkg.type = 'onNodeLocked';
                            break;
                        case "unlock":
                            _self._.enable();
                            pkg.type = 'onNodeUnlocked';
                            break;
                        case "pin":
                            _self._.relationships.attach();
                            pkg.type = "onNodeAttached";
                            break;
                        case "close":
                            break;
                    }
                    if (_reorder) {
                        var zIndex = 0;
                        for (var node = _self._.slate.paper.bottom; node != null; node = node.next) {
                            if (node.type === "ellipse" || node.type === "rect") {
                                zIndex++;
                                var _id = node.data("id");

                                //not all rects have an id (the menu box is a rect, but it has no options.id because it is not a node
                                //so you cannot always show this...
                                if (_id) {
                                    var reorderedNode = _.detect(_self._.slate.nodes.allNodes, function (n) { return n.options.id === _id; });
                                    reorderedNode.sortorder = zIndex;
                                }
                            }
                        }
                        _self._.slate.nodes.allNodes.sort(function (a, b) { return a.sortorder < b.sortorder ? -1 : 1 });
                    }
                    if (pkg.type !== "") broadcast(pkg);
                    _self.hide();
                };
            });

            var mp = $s.mousePos(e);

            var _x = mp.x; // _self._.options.xPos - _self._.slate.options.viewPort.left + _self._.options.width / 3;
            var _y = mp.y; // _self._.options.yPos - _self._.slate.options.viewPort.top;
            _contextMenu.style.left = _x + "px";
            _contextMenu.style.top = _y + "px";
            _contextMenu.style.height = _height + "px";
        };

        function broadcast(pkg) {
            //broadcast
            if (_self._.slate.websync) _self._.slate.websync.send(pkg);
            if (_self._.slate.birdseye) _self._.slate.birdseye.nodeChanged(pkg);
        };

        _self.hide = function () {
            _self._.slate.removeContextMenus();
            _contextMenu = null;
        }

        _self.isVisible = function () {
            return (_contextMenu !== null);
        };

        return _self;
    };
    $si.fn.removeContextMenus = function () {
        var _cm = $s.select("div.sb_cm")
        $s.each(_cm, function () {
            document.body.removeChild(this);
        });
    };

})(Slatebox, Slatebox.fn.node, Slatebox.fn.slate);(function ($s, $n) {
    $n.fn._resize = function () {
        var _self = this, resize;

        _self.show = function (x, y) {
            var r = _self._.slate.paper;
            resize = r.resize(_self._.slate.options.imageFolder + "2_lines.png").transform(["t", x - 5, ",", y - 5].join()).attr({ fill: "#fff", "stroke": "#000" });

            resize.mouseover(function (e) {
                resize.attr({ cursor: 'nw-resize' });
            });

            resize.drag(move, start, up);

            return resize;
        };

        _self.hide = function (r) {
            resize && resize.remove();
        };

        var _minWidth = 10, _minHeight = 10, _dragAllowed = false, _origWidth, _origHeight;
        var start = function () {
            _self._.slate.multiselection && _self._.slate.multiselection.end();
            this.ox = this.attr("x");
            this.oy = this.attr("y");

            _self._.setStartDrag();
            _self._.connectors.remove();

            _dragAllowed = _self._.slate.options.viewPort.allowDrag;
            _self._.slate.disable();

            if (_self._.options.text !== " ") {
                var mm = _self._.text.getBBox();
                _minWidth = mm.width + 10;
                _minHeight = mm.height + 10;
            }

            _origWidth = _self._.options.width;
            _origHeight = _self._.options.height;
        },
        move = function (dx, dy) {

            var _zr = _self._.slate.options.viewPort.zoom.r;
            dx = dx + ((dx / _zr) - dx);
            dy = dy + ((dy / _zr) - dy);

            var _transWidth = _origWidth + dx;
            var _transHeight = _origHeight + dy;

            if (_transWidth > _minWidth)
                this.attr({ x: this.ox + dx });

            if (_transHeight > _minHeight)
                this.attr({ y: this.oy + dy });

            if (_self._.events && $s.isFunction(_self._.events.onResizing)) {
                _self._.events.onResizing.apply(this, [_transWidth, _transHeight]);
            }

            _self.set(_transWidth, _transHeight);
        },
        up = function () {
            _self._.slate.enable();
            resize.remove();
            _self._.setEndDrag();
            //_self._.relationships.wireHoverEvents();

            if (_self._.events && $s.isFunction(_self._.events.onResized)) {
                _self._.events.onResized.apply(this, [_self.send]);
            } else {
                _self.send();
            }
        };

        _self.send = function () {
            //broadcast change to birdseye and collaborators
            var pkg = { type: 'onNodeResized', data: { id: _self._.options.id, height: _self._.options.height, width: _self._.options.width} };
            _self._.slate.birdseye && _self._.slate.birdseye.nodeChanged(pkg);
            _self._.slate.websync && _self._.slate.websync.send(pkg);
        };

        _self.set = function (width, height, dur) {
            var natt = {}, tatt = {}, latt = {};
            if (!dur) dur = 0;
            if (width > _minWidth) {
                var watt = _self._.vect.type == "rect" ? { width: width} : { rx: width / 2 };
                if (dur === 0)
                    _self._.vect.attr(watt);
                else
                    natt = watt;

                var tx = _self._.vect.attr("x") + (width / 2), lx = _self._.vect.attr("x") - 5;
                if (_self._.options.vectorPath === "ellipse") {
                    tx = _self._.vect.attr("cx");
                    lx = _self._.vect.attr("cx") + (width / 2);
                }

                if (dur === 0) {
                    _self._.text.attr({ x: tx });
                    //console.log("setting link x: " + lx);
                    //_self._.link.attr({ x: lx });
                } else {
                    tatt = { x: tx };
                    latt = { x: lx };
                }

                _self._.options.width = width;
            }

            if (height > _minHeight) {
                var hatt = _self._.vect.type == "rect" ? { height: height} : { ry: height / 2 };
                if (dur === 0)
                    _self._.vect.attr(hatt);
                else
                    natt = $s.extend(natt, hatt);

                var ty = _self._.vect.attr("y") + (height / 2);
                if (_self._.options.vectorPath === "ellipse") {
                    ty = _self._.vect.attr("cy");
                }

                if (dur === 0) {
                    _self._.text.attr({ y: ty });
                    //_self._.link.attr({ y: ty });
                    //console.log("setting link y: " + ty);
                } else {
                    $s.extend(tatt, { y: ty });
                    $s.extend(latt, { y: ty });
                }

                _self._.options.height = height;
            }

            if (dur > 0) {
                _self._.text.animate(tatt, dur);
                _self._.link.hide();

                var onAnimate = function () {
                    _self._.refresh();
                };

                eve.on("anim.frame.*", onAnimate);
                _self._.vect.animate(natt, dur, function () {
                    var lc = _self._.linkCoords();
                    _self._.link.transform(["t", lc.x, ",", lc.y, "s", ".8", ",", ".8", "r", "180"].join());
                    if (_self._.options.link.show) _self._.link.show();
                    _self._.refresh();
                    eve.unbind("anim.frame.*", onAnimate);
                });

            } else {
                var lc = _self._.linkCoords();
                _self._.link.transform(["t", lc.x, ",", lc.y, "s", ".8", ",", ".8", "r", "180"].join());
                _self._.refresh();
            }
        };

        return _self;
    }
})(Slatebox, Slatebox.fn.node);// ┌─────────────────────────────────────────────────────────────────────┐ \\
// │ Raphaël 2.0 - JavaScript Vector Library                             │ \\
// ├─────────────────────────────────────────────────────────────────────┤ \\
// │ Copyright (c) 2008-2011 Dmitry Baranovskiy (http://raphaeljs.com)   │ \\
// │ Copyright (c) 2008-2011 Sencha Labs (http://sencha.com)             │ \\
// │ Licensed under the MIT (http://raphaeljs.com/license.html) license. │ \\
// └─────────────────────────────────────────────────────────────────────┘ \\

// ┌──────────────────────────────────────────────────────────────────────────────────────┐ \\
// │ Eve 0.3.2 - JavaScript Events Library                                                │ \\
// ├──────────────────────────────────────────────────────────────────────────────────────┤ \\
// │ Copyright (c) 2008-2011 Dmitry Baranovskiy (http://dmitry.baranovskiy.com/)          │ \\
// │ Licensed under the MIT (http://www.opensource.org/licenses/mit-license.php) license. │ \\
// └──────────────────────────────────────────────────────────────────────────────────────┘ \\

(function (glob) {
    var version = "0.3.2",
        has = "hasOwnProperty",
        separator = /[\.\/]/,
        wildcard = "*",
        fun = function () { },
        numsort = function (a, b) {
            return a - b;
        },
        current_event,
        stop,
        events = { n: {} },

        eve = function (name, scope) {
            var e = events,
                oldstop = stop,
                args = Array.prototype.slice.call(arguments, 2),
                listeners = eve.listeners(name),
                z = 0,
                f = false,
                l,
                indexed = [],
                queue = {},
                out = [],
                errors = [];
            current_event = name;
            stop = 0;
            for (var i = 0, ii = listeners.length; i < ii; i++) if ("zIndex" in listeners[i]) {
                indexed.push(listeners[i].zIndex);
                if (listeners[i].zIndex < 0) {
                    queue[listeners[i].zIndex] = listeners[i];
                }
            }
            indexed.sort(numsort);
            while (indexed[z] < 0) {
                l = queue[indexed[z++]];
                out.push(l.apply(scope, args));
                if (stop) {
                    stop = oldstop;
                    return out;
                }
            }
            for (i = 0; i < ii; i++) {
                l = listeners[i];
                if ("zIndex" in l) {
                    if (l.zIndex == indexed[z]) {
                        out.push(l.apply(scope, args));
                        if (stop) {
                            stop = oldstop;
                            return out;
                        }
                        do {
                            z++;
                            l = queue[indexed[z]];
                            l && out.push(l.apply(scope, args));
                            if (stop) {
                                stop = oldstop;
                                return out;
                            }
                        } while (l)
                    } else {
                        queue[l.zIndex] = l;
                    }
                } else {
                    out.push(l.apply(scope, args));
                    if (stop) {
                        stop = oldstop;
                        return out;
                    }
                }
            }
            stop = oldstop;
            return out.length ? out : null;
        };

    eve.listeners = function (name) {
        var names = name.split(separator),
            e = events,
            item,
            items,
            k,
            i,
            ii,
            j,
            jj,
            nes,
            es = [e],
            out = [];
        for (i = 0, ii = names.length; i < ii; i++) {
            nes = [];
            for (j = 0, jj = es.length; j < jj; j++) {
                e = es[j].n;
                items = [e[names[i]], e[wildcard]];
                k = 2;
                while (k--) {
                    item = items[k];
                    if (item) {
                        nes.push(item);
                        out = out.concat(item.f || []);
                    }
                }
            }
            es = nes;
        }
        return out;
    };


    eve.on = function (name, f) {
        var names = name.split(separator),
            e = events;
        for (var i = 0, ii = names.length; i < ii; i++) {
            e = e.n;
            !e[names[i]] && (e[names[i]] = { n: {} });
            e = e[names[i]];
        }
        e.f = e.f || [];
        for (i = 0, ii = e.f.length; i < ii; i++) if (e.f[i] == f) {
            return fun;
        }
        e.f.push(f);
        return function (zIndex) {
            if (+zIndex == +zIndex) {
                f.zIndex = +zIndex;
            }
        };
    };

    eve.stop = function () {
        stop = 1;
    };

    eve.nt = function (subname) {
        if (subname) {
            return new RegExp("(?:\\.|\\/|^)" + subname + "(?:\\.|\\/|$)").test(current_event);
        }
        return current_event;
    };

    eve.unbind = function (name, f) {
        var names = name.split(separator),
            e,
            key,
            splice,
            cur = [events];
        for (var i = 0, ii = names.length; i < ii; i++) {
            for (var j = 0; j < cur.length; j += splice.length - 2) {
                splice = [j, 1];
                e = cur[j].n;
                if (names[i] != wildcard) {
                    if (e[names[i]]) {
                        splice.push(e[names[i]]);
                    }
                } else {
                    for (key in e) if (e[has](key)) {
                        splice.push(e[key]);
                    }
                }
                cur.splice.apply(cur, splice);
            }
        }
        for (i = 0, ii = cur.length; i < ii; i++) {
            e = cur[i];
            while (e.n) {
                if (f) {
                    if (e.f) {
                        for (j = 0, jj = e.f.length; j < jj; j++) if (e.f[j] == f) {
                            e.f.splice(j, 1);
                            break;
                        }
                        !e.f.length && delete e.f;
                    }
                    for (key in e.n) if (e.n[has](key) && e.n[key].f) {
                        var funcs = e.n[key].f;
                        for (j = 0, jj = funcs.length; j < jj; j++) if (funcs[j] == f) {
                            funcs.splice(j, 1);
                            break;
                        }
                        !funcs.length && delete e.n[key].f;
                    }
                } else {
                    delete e.f;
                    for (key in e.n) if (e.n[has](key) && e.n[key].f) {
                        delete e.n[key].f;
                    }
                }
                e = e.n;
            }
        }
    };

    eve.version = version;
    eve.toString = function () {
        return "You are running Eve " + version;
    };
    (typeof module != "undefined" && module.exports) ? (module.exports = eve) : (glob.eve = eve);
})(this);

// ┌─────────────────────────────────────────────────────────────────────┐ \\
// │ "Raphaël 2.0" - JavaScript Vector Library                           │ \\
// ├─────────────────────────────────────────────────────────────────────┤ \\
// │ Copyright (c) 2008-2011 Dmitry Baranovskiy (http://raphaeljs.com)   │ \\
// │ Copyright (c) 2008-2011 Sencha Labs (http://sencha.com)             │ \\
// │ Licensed under the MIT (http://raphaeljs.com/license.html) license. │ \\
// └─────────────────────────────────────────────────────────────────────┘ \\
(function () {

    function R(first) {
        if (R.is(first, "function")) {
            return loaded ? first() : eve.on("DOMload", first);
        } else if (R.is(first, array)) {
            var a = first,
                cnv = R._engine.create[apply](R, a.splice(0, 3 + R.is(a[0], nu))),
                res = cnv.set(),
                i = 0,
                ii = a.length,
                j;
            for (; i < ii; i++) {
                j = a[i] || {};
                elements[has](j.type) && res.push(cnv[j.type]().attr(j));
            }
            return res;
        } else {
            var args = Array.prototype.slice.call(arguments, 0);
            if (R.is(args[args.length - 1], "function")) {
                var f = args.pop();
                return loaded ? f.call(R._engine.create[apply](R, args)) : eve.on("DOMload", function () {
                    f.call(R._engine.create[apply](R, args));
                });
            } else {
                return R._engine.create[apply](R, arguments);
            }
        }
    }
    R.version = "2.0.0";
    R.eve = eve;
    var loaded,
        separator = /[, ]+/,
        elements = { circle: 1, rect: 1, path: 1, ellipse: 1, text: 1, image: 1 },
        formatrg = /\{(\d+)\}/g,
        proto = "prototype",
        has = "hasOwnProperty",
        g = {
            doc: document,
            win: window
        },
        oldRaphael = {
            was: Object.prototype[has].call(g.win, "Raphael"),
            is: g.win.Raphael
        },
        Paper = function () {


            this.ca = this.customAttributes = {};
        },
        paperproto,
        appendChild = "appendChild",
        apply = "apply",
        concat = "concat",
        supportsTouch = "createTouch" in g.doc,
        E = "",
        S = " ",
        Str = String,
        split = "split",
        events = "click dblclick mousedown mousemove mouseout mouseover mouseup touchstart touchmove touchend touchcancel"[split](S),
        touchMap = {
            mousedown: "touchstart",
            mousemove: "touchmove",
            mouseup: "touchend"
        },
        lowerCase = Str.prototype.toLowerCase,
        math = Math,
        mmax = math.max,
        mmin = math.min,
        abs = math.abs,
        pow = math.pow,
        PI = math.PI,
        nu = "number",
        string = "string",
        array = "array",
        toString = "toString",
        fillString = "fill",
        objectToString = Object.prototype.toString,
        paper = {},
        push = "push",
        ISURL = R._ISURL = /^url\(['"]?([^\)]+?)['"]?\)$/i,
        colourRegExp = /^\s*((#[a-f\d]{6})|(#[a-f\d]{3})|rgba?\(\s*([\d\.]+%?\s*,\s*[\d\.]+%?\s*,\s*[\d\.]+%?(?:\s*,\s*[\d\.]+%?)?)\s*\)|hsba?\(\s*([\d\.]+(?:deg|\xb0|%)?\s*,\s*[\d\.]+%?\s*,\s*[\d\.]+(?:%?\s*,\s*[\d\.]+)?)%?\s*\)|hsla?\(\s*([\d\.]+(?:deg|\xb0|%)?\s*,\s*[\d\.]+%?\s*,\s*[\d\.]+(?:%?\s*,\s*[\d\.]+)?)%?\s*\))\s*$/i,
        isnan = { "NaN": 1, "Infinity": 1, "-Infinity": 1 },
        bezierrg = /^(?:cubic-)?bezier\(([^,]+),([^,]+),([^,]+),([^\)]+)\)/,
        round = math.round,
        setAttribute = "setAttribute",
        toFloat = parseFloat,
        toInt = parseInt,
        upperCase = Str.prototype.toUpperCase,
        availableAttrs = R._availableAttrs = {
            "arrow-end": "none",
            "arrow-start": "none",
            blur: 0,
            "clip-rect": "0 0 1e9 1e9",
            cursor: "default",
            cx: 0,
            cy: 0,
            fill: "#fff",
            "fill-opacity": 1,
            font: '10px "Arial"',
            "font-family": '"Arial"',
            "font-size": "10",
            "font-style": "normal",
            "font-weight": 400,
            gradient: 0,
            height: 0,
            href: "http://raphaeljs.com/",
            opacity: 1,
            path: "M0,0",
            r: 0,
            rx: 0,
            ry: 0,
            src: "",
            stroke: "#000",
            "stroke-dasharray": "",
            "stroke-linecap": "butt",
            "stroke-linejoin": "butt",
            "stroke-miterlimit": 0,
            "stroke-opacity": 1,
            "stroke-width": 1,
            target: "_blank",
            "text-anchor": "middle",
            title: "Raphael",
            transform: "",
            width: 0,
            x: 0,
            y: 0
        },
        availableAnimAttrs = R._availableAnimAttrs = {
            blur: nu,
            "clip-rect": "csv",
            cx: nu,
            cy: nu,
            fill: "colour",
            "fill-opacity": nu,
            "font-size": nu,
            height: nu,
            opacity: nu,
            path: "path",
            r: nu,
            rx: nu,
            ry: nu,
            stroke: "colour",
            "stroke-opacity": nu,
            "stroke-width": nu,
            transform: "transform",
            width: nu,
            x: nu,
            y: nu
        },
        commaSpaces = /\s*,\s*/,
        hsrg = { hs: 1, rg: 1 },
        p2s = /,?([achlmqrstvxz]),?/gi,
        pathCommand = /([achlmrqstvz])[\s,]*((-?\d*\.?\d*(?:e[\-+]?\d+)?\s*,?\s*)+)/ig,
        tCommand = /([rstm])[\s,]*((-?\d*\.?\d*(?:e[\-+]?\d+)?\s*,?\s*)+)/ig,
        pathValues = /(-?\d*\.?\d*(?:e[\-+]?\d+)?)\s*,?\s*/ig,
        radial_gradient = R._radial_gradient = /^r(?:\(([^,]+?)\s*,\s*([^\)]+?)\))?/,
        eldata = {},
        sortByKey = function (a, b) {
            return a.key - b.key;
        },
        sortByNumber = function (a, b) {
            return toFloat(a) - toFloat(b);
        },
        fun = function () { },
        pipe = function (x) {
            return x;
        },
        rectPath = R._rectPath = function (x, y, w, h, r) {
            if (r) {
                return [["M", x + r, y], ["l", w - r * 2, 0], ["a", r, r, 0, 0, 1, r, r], ["l", 0, h - r * 2], ["a", r, r, 0, 0, 1, -r, r], ["l", r * 2 - w, 0], ["a", r, r, 0, 0, 1, -r, -r], ["l", 0, r * 2 - h], ["a", r, r, 0, 0, 1, r, -r], ["z"]];
            }
            return [["M", x, y], ["l", w, 0], ["l", 0, h], ["l", -w, 0], ["z"]];
        },
        ellipsePath = function (x, y, rx, ry) {
            if (ry == null) {
                ry = rx;
            }
            return [["M", x, y], ["m", 0, -ry], ["a", rx, ry, 0, 1, 1, 0, 2 * ry], ["a", rx, ry, 0, 1, 1, 0, -2 * ry], ["z"]];
        },
        getPath = R._getPath = {
            path: function (el) {
                return el.attr("path");
            },
            circle: function (el) {
                var a = el.attrs;
                return ellipsePath(a.cx, a.cy, a.r);
            },
            ellipse: function (el) {
                var a = el.attrs;
                return ellipsePath(a.cx, a.cy, a.rx, a.ry);
            },
            rect: function (el) {
                var a = el.attrs;
                return rectPath(a.x, a.y, a.width, a.height, a.r);
            },
            image: function (el) {
                var a = el.attrs;
                return rectPath(a.x, a.y, a.width, a.height);
            },
            text: function (el) {
                var bbox = el._getBBox();
                return rectPath(bbox.x, bbox.y, bbox.width, bbox.height);
            }
        },
        mapPath = R.mapPath = function (path, matrix) {
            if (!matrix) {
                return path;
            }
            var x, y, i, j, pathi;
            path = path2curve(path);
            for (i = 0, ii = path.length; i < ii; i++) {
                pathi = path[i];
                for (j = 1, jj = pathi.length; j < jj; j += 2) {
                    x = matrix.x(pathi[j], pathi[j + 1]);
                    y = matrix.y(pathi[j], pathi[j + 1]);
                    pathi[j] = x;
                    pathi[j + 1] = y;
                }
            }
            return path;
        };

    R._g = g;

    R.type = (g.win.SVGAngle || g.doc.implementation.hasFeature("http://www.w3.org/TR/SVG11/feature#BasicStructure", "1.1") ? "SVG" : "VML");
    if (R.type == "VML") {
        var d = g.doc.createElement("div"),
            b;
        d.innerHTML = '<v:shape adj="1"/>';
        b = d.firstChild;
        b.style.behavior = "url(#default#VML)";
        if (!(b && typeof b.adj == "object")) {
            return (R.type = E);
        }
        d = null;
    }


    R.svg = !(R.vml = R.type == "VML");
    R._Paper = Paper;

    R.fn = paperproto = Paper.prototype = R.prototype;
    R._id = 0;
    R._oid = 0;

    R.is = function (o, type) {
        type = lowerCase.call(type);
        if (type == "finite") {
            return !isnan[has](+o);
        }
        if (type == "array") {
            return o instanceof Array;
        }
        return (type == "null" && o === null) ||
                (type == typeof o && o !== null) ||
                (type == "object" && o === Object(o)) ||
                (type == "array" && Array.isArray && Array.isArray(o)) ||
                objectToString.call(o).slice(8, -1).toLowerCase() == type;
    };

    R.angle = function (x1, y1, x2, y2, x3, y3) {
        if (x3 == null) {
            var x = x1 - x2,
                y = y1 - y2;
            if (!x && !y) {
                return 0;
            }
            return (180 + math.atan2(-y, -x) * 180 / PI + 360) % 360;
        } else {
            return R.angle(x1, y1, x3, y3) - R.angle(x2, y2, x3, y3);
        }
    };

    R.rad = function (deg) {
        return deg % 360 * PI / 180;
    };

    R.deg = function (rad) {
        return rad * 180 / PI % 360;
    };

    R.snapTo = function (values, value, tolerance) {
        tolerance = R.is(tolerance, "finite") ? tolerance : 10;
        if (R.is(values, array)) {
            var i = values.length;
            while (i--) if (abs(values[i] - value) <= tolerance) {
                return values[i];
            }
        } else {
            values = +values;
            var rem = value % values;
            if (rem < tolerance) {
                return value - rem;
            }
            if (rem > values - tolerance) {
                return value - rem + values;
            }
        }
        return value;
    };


    var createUUID = R.createUUID = (function (uuidRegEx, uuidReplacer) {
        return function () {
            return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(uuidRegEx, uuidReplacer).toUpperCase();
        };
    })(/[xy]/g, function (c) {
        var r = math.random() * 16 | 0,
            v = c == "x" ? r : (r & 3 | 8);
        return v.toString(16);
    });


    R.setWindow = function (newwin) {
        eve("setWindow", R, g.win, newwin);
        g.win = newwin;
        g.doc = g.win.document;
        if (initWin) {
            initWin(g.win);
        }
    };
    var toHex = function (color) {
        if (R.vml) {
            // http://dean.edwards.name/weblog/2009/10/convert-any-colour-value-to-hex-in-msie/
            var trim = /^\s+|\s+$/g;
            var bod;
            try {
                var docum = new ActiveXObject("htmlfile");
                docum.write("<body>");
                docum.close();
                bod = docum.body;
            } catch (e) {
                bod = createPopup().document.body;
            }
            var range = bod.createTextRange();
            toHex = cacher(function (color) {
                try {
                    bod.style.color = Str(color).replace(trim, E);
                    var value = range.queryCommandValue("ForeColor");
                    value = ((value & 255) << 16) | (value & 65280) | ((value & 16711680) >>> 16);
                    return "#" + ("000000" + value.toString(16)).slice(-6);
                } catch (e) {
                    return "none";
                }
            });
        } else {
            var i = g.doc.createElement("i");
            i.title = "Rapha\xebl Colour Picker";
            i.style.display = "none";
            g.doc.body.appendChild(i);
            toHex = cacher(function (color) {
                i.style.color = color;
                return g.doc.defaultView.getComputedStyle(i, E).getPropertyValue("color");
            });
        }
        return toHex(color);
    },
    hsbtoString = function () {
        return "hsb(" + [this.h, this.s, this.b] + ")";
    },
    hsltoString = function () {
        return "hsl(" + [this.h, this.s, this.l] + ")";
    },
    rgbtoString = function () {
        return this.hex;
    },
    prepareRGB = function (r, g, b) {
        if (g == null && R.is(r, "object") && "r" in r && "g" in r && "b" in r) {
            b = r.b;
            g = r.g;
            r = r.r;
        }
        if (g == null && R.is(r, string)) {
            var clr = R.getRGB(r);
            r = clr.r;
            g = clr.g;
            b = clr.b;
        }
        if (r > 1 || g > 1 || b > 1) {
            r /= 255;
            g /= 255;
            b /= 255;
        }

        return [r, g, b];
    },
    packageRGB = function (r, g, b, o) {
        r *= 255;
        g *= 255;
        b *= 255;
        var rgb = {
            r: r,
            g: g,
            b: b,
            hex: R.rgb(r, g, b),
            toString: rgbtoString
        };
        R.is(o, "finite") && (rgb.opacity = o);
        return rgb;
    };


    R.color = function (clr) {
        var rgb;
        if (R.is(clr, "object") && "h" in clr && "s" in clr && "b" in clr) {
            rgb = R.hsb2rgb(clr);
            clr.r = rgb.r;
            clr.g = rgb.g;
            clr.b = rgb.b;
            clr.hex = rgb.hex;
        } else if (R.is(clr, "object") && "h" in clr && "s" in clr && "l" in clr) {
            rgb = R.hsl2rgb(clr);
            clr.r = rgb.r;
            clr.g = rgb.g;
            clr.b = rgb.b;
            clr.hex = rgb.hex;
        } else {
            if (R.is(clr, "string")) {
                clr = R.getRGB(clr);
            }
            if (R.is(clr, "object") && "r" in clr && "g" in clr && "b" in clr) {
                rgb = R.rgb2hsl(clr);
                clr.h = rgb.h;
                clr.s = rgb.s;
                clr.l = rgb.l;
                rgb = R.rgb2hsb(clr);
                clr.v = rgb.b;
            } else {
                clr = { hex: "none" };
                crl.r = clr.g = clr.b = clr.h = clr.s = clr.v = clr.l = -1;
            }
        }
        clr.toString = rgbtoString;
        return clr;
    };

    R.hsb2rgb = function (h, s, v, o) {
        if (this.is(h, "object") && "h" in h && "s" in h && "b" in h) {
            v = h.b;
            s = h.s;
            h = h.h;
            o = h.o;
        }
        h *= 360;
        var R, G, B, X, C;
        h = (h % 360) / 60;
        C = v * s;
        X = C * (1 - abs(h % 2 - 1));
        R = G = B = v - C;

        h = ~ ~h;
        R += [C, X, 0, 0, X, C][h];
        G += [X, C, C, X, 0, 0][h];
        B += [0, 0, X, C, C, X][h];
        return packageRGB(R, G, B, o);
    };

    R.hsl2rgb = function (h, s, l, o) {
        if (this.is(h, "object") && "h" in h && "s" in h && "l" in h) {
            l = h.l;
            s = h.s;
            h = h.h;
        }
        if (h > 1 || s > 1 || l > 1) {
            h /= 360;
            s /= 100;
            l /= 100;
        }
        h *= 360;
        var R, G, B, X, C;
        h = (h % 360) / 60;
        C = 2 * s * (l < .5 ? l : 1 - l);
        X = C * (1 - abs(h % 2 - 1));
        R = G = B = l - C / 2;

        h = ~ ~h;
        R += [C, X, 0, 0, X, C][h];
        G += [X, C, C, X, 0, 0][h];
        B += [0, 0, X, C, C, X][h];
        return packageRGB(R, G, B, o);
    };

    R.rgb2hsb = function (r, g, b) {
        b = prepareRGB(r, g, b);
        r = b[0];
        g = b[1];
        b = b[2];

        var H, S, V, C;
        V = mmax(r, g, b);
        C = V - mmin(r, g, b);
        H = (C == 0 ? null :
             V == r ? (g - b) / C :
             V == g ? (b - r) / C + 2 :
                      (r - g) / C + 4
            );
        H = ((H + 360) % 6) * 60 / 360;
        S = C == 0 ? 0 : C / V;
        return { h: H, s: S, b: V, toString: hsbtoString };
    };

    R.rgb2hsl = function (r, g, b) {
        b = prepareRGB(r, g, b);
        r = b[0];
        g = b[1];
        b = b[2];

        var H, S, L, M, m, C;
        M = mmax(r, g, b);
        m = mmin(r, g, b);
        C = M - m;
        H = (C == 0 ? null :
             M == r ? (g - b) / C :
             M == g ? (b - r) / C + 2 :
                      (r - g) / C + 4);
        H = ((H + 360) % 6) * 60 / 360;
        L = (M + m) / 2;
        S = (C == 0 ? 0 :
             L < .5 ? C / (2 * L) :
                      C / (2 - 2 * L));
        return { h: H, s: S, l: L, toString: hsltoString };
    };
    R._path2string = function () {
        return this.join(",").replace(p2s, "$1");
    };
    function repush(array, item) {
        for (var i = 0, ii = array.length; i < ii; i++) if (array[i] === item) {
            return array.push(array.splice(i, 1)[0]);
        }
    }
    function cacher(f, scope, postprocessor) {
        function newf() {
            var arg = Array.prototype.slice.call(arguments, 0),
                args = arg.join("\u2400"),
                cache = newf.cache = newf.cache || {},
                count = newf.count = newf.count || [];
            if (cache[has](args)) {
                repush(count, args);
                return postprocessor ? postprocessor(cache[args]) : cache[args];
            }
            count.length >= 1e3 && delete cache[count.shift()];
            count.push(args);
            cache[args] = f[apply](scope, arg);
            return postprocessor ? postprocessor(cache[args]) : cache[args];
        }
        return newf;
    }

    var preload = R._preload = function (src, f) {
        var img = g.doc.createElement("img");
        img.style.cssText = "position:absolute;left:-9999em;top-9999em";
        img.onload = function () {
            f.call(this);
            this.onload = null;
            g.doc.body.removeChild(this);
        };
        img.onerror = function () {
            g.doc.body.removeChild(this);
        };
        g.doc.body.appendChild(img);
        img.src = src;
    };

    function clrToString() {
        return this.hex;
    }


    R.getRGB = cacher(function (colour) {
        if (!colour || !!((colour = Str(colour)).indexOf("-") + 1)) {
            return { r: -1, g: -1, b: -1, hex: "none", error: 1, toString: clrToString };
        }
        if (colour == "none") {
            return { r: -1, g: -1, b: -1, hex: "none", toString: clrToString };
        }
        !(hsrg[has](colour.toLowerCase().substring(0, 2)) || colour.charAt() == "#") && (colour = toHex(colour));
        var res,
            red,
            green,
            blue,
            opacity,
            t,
            values,
            rgb = colour.match(colourRegExp);
        if (rgb) {
            if (rgb[2]) {
                blue = toInt(rgb[2].substring(5), 16);
                green = toInt(rgb[2].substring(3, 5), 16);
                red = toInt(rgb[2].substring(1, 3), 16);
            }
            if (rgb[3]) {
                blue = toInt((t = rgb[3].charAt(3)) + t, 16);
                green = toInt((t = rgb[3].charAt(2)) + t, 16);
                red = toInt((t = rgb[3].charAt(1)) + t, 16);
            }
            if (rgb[4]) {
                values = rgb[4][split](commaSpaces);
                red = toFloat(values[0]);
                values[0].slice(-1) == "%" && (red *= 2.55);
                green = toFloat(values[1]);
                values[1].slice(-1) == "%" && (green *= 2.55);
                blue = toFloat(values[2]);
                values[2].slice(-1) == "%" && (blue *= 2.55);
                rgb[1].toLowerCase().slice(0, 4) == "rgba" && (opacity = toFloat(values[3]));
                values[3] && values[3].slice(-1) == "%" && (opacity /= 100);
            }
            if (rgb[5]) {
                values = rgb[5][split](commaSpaces);
                red = toFloat(values[0]);
                values[0].slice(-1) == "%" && (red *= 2.55);
                green = toFloat(values[1]);
                values[1].slice(-1) == "%" && (green *= 2.55);
                blue = toFloat(values[2]);
                values[2].slice(-1) == "%" && (blue *= 2.55);
                (values[0].slice(-3) == "deg" || values[0].slice(-1) == "\xb0") && (red /= 360);
                rgb[1].toLowerCase().slice(0, 4) == "hsba" && (opacity = toFloat(values[3]));
                values[3] && values[3].slice(-1) == "%" && (opacity /= 100);
                return R.hsb2rgb(red, green, blue, opacity);
            }
            if (rgb[6]) {
                values = rgb[6][split](commaSpaces);
                red = toFloat(values[0]);
                values[0].slice(-1) == "%" && (red *= 2.55);
                green = toFloat(values[1]);
                values[1].slice(-1) == "%" && (green *= 2.55);
                blue = toFloat(values[2]);
                values[2].slice(-1) == "%" && (blue *= 2.55);
                (values[0].slice(-3) == "deg" || values[0].slice(-1) == "\xb0") && (red /= 360);
                rgb[1].toLowerCase().slice(0, 4) == "hsla" && (opacity = toFloat(values[3]));
                values[3] && values[3].slice(-1) == "%" && (opacity /= 100);
                return R.hsl2rgb(red, green, blue, opacity);
            }
            rgb = { r: red, g: green, b: blue, toString: clrToString };
            rgb.hex = "#" + (16777216 | blue | (green << 8) | (red << 16)).toString(16).slice(1);
            R.is(opacity, "finite") && (rgb.opacity = opacity);
            return rgb;
        }
        return { r: -1, g: -1, b: -1, hex: "none", error: 1, toString: clrToString };
    }, R);

    R.hsb = cacher(function (h, s, b) {
        return R.hsb2rgb(h, s, b).hex;
    });

    R.hsl = cacher(function (h, s, l) {
        return R.hsl2rgb(h, s, l).hex;
    });

    R.rgb = cacher(function (r, g, b) {
        return "#" + (16777216 | b | (g << 8) | (r << 16)).toString(16).slice(1);
    });

    R.getColor = function (value) {
        var start = this.getColor.start = this.getColor.start || { h: 0, s: 1, b: value || .75 },
            rgb = this.hsb2rgb(start.h, start.s, start.b);
        start.h += .075;
        if (start.h > 1) {
            start.h = 0;
            start.s -= .2;
            start.s <= 0 && (this.getColor.start = { h: 0, s: 1, b: start.b });
        }
        return rgb.hex;
    };

    R.getColor.reset = function () {
        delete this.start;
    };

    // http://schepers.cc/getting-to-the-point
    function catmullRom2bezier(crp) {
        var d = [];
        for (var i = 0, iLen = crp.length; iLen - 2 > i; i += 2) {
            var p = [{ x: +crp[i], y: +crp[i + 1] },
                     { x: +crp[i], y: +crp[i + 1] },
                     { x: +crp[i + 2], y: +crp[i + 3] },
                     { x: +crp[i + 4], y: +crp[i + 5]}];
            if (iLen - 4 == i) {
                p[0] = { x: +crp[i - 2], y: +crp[i - 1] };
                p[3] = p[2];
            } else if (i) {
                p[0] = { x: +crp[i - 2], y: +crp[i - 1] };
            }
            d.push(["C",
                (-p[0].x + 6 * p[1].x + p[2].x) / 6,
                (-p[0].y + 6 * p[1].y + p[2].y) / 6,
                (p[1].x + 6 * p[2].x - p[3].x) / 6,
                (p[1].y + 6 * p[2].y - p[3].y) / 6,
                p[2].x,
                p[2].y
            ]);
        }

        return d;
    }

    R.parsePathString = cacher(function (pathString) {
        if (!pathString) {
            return null;
        }
        var paramCounts = { a: 7, c: 6, h: 1, l: 2, m: 2, r: 4, q: 4, s: 4, t: 2, v: 1, z: 0 },
            data = [];
        if (R.is(pathString, array) && R.is(pathString[0], array)) { // rough assumption
            data = pathClone(pathString);
        }
        if (!data.length) {
            Str(pathString).replace(pathCommand, function (a, b, c) {
                var params = [],
                    name = b.toLowerCase();
                c.replace(pathValues, function (a, b) {
                    b && params.push(+b);
                });
                if (name == "m" && params.length > 2) {
                    data.push([b][concat](params.splice(0, 2)));
                    name = "l";
                    b = b == "m" ? "l" : "L";
                }
                if (name == "r") {
                    data.push([b][concat](params));
                } else while (params.length >= paramCounts[name]) {
                    data.push([b][concat](params.splice(0, paramCounts[name])));
                    if (!paramCounts[name]) {
                        break;
                    }
                }
            });
        }
        data.toString = R._path2string;
        return data;
    });

    R.parseTransformString = cacher(function (TString) {
        if (!TString) {
            return null;
        }
        var paramCounts = { r: 3, s: 4, t: 2, m: 6 },
            data = [];
        if (R.is(TString, array) && R.is(TString[0], array)) { // rough assumption
            data = pathClone(TString);
        }
        if (!data.length) {
            Str(TString).replace(tCommand, function (a, b, c) {
                var params = [],
                    name = lowerCase.call(b);
                c.replace(pathValues, function (a, b) {
                    b && params.push(+b);
                });
                data.push([b][concat](params));
            });
        }
        data.toString = R._path2string;
        return data;
    });

    R.findDotsAtSegment = function (p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y, t) {
        var t1 = 1 - t,
            t13 = pow(t1, 3),
            t12 = pow(t1, 2),
            t2 = t * t,
            t3 = t2 * t,
            x = t13 * p1x + t12 * 3 * t * c1x + t1 * 3 * t * t * c2x + t3 * p2x,
            y = t13 * p1y + t12 * 3 * t * c1y + t1 * 3 * t * t * c2y + t3 * p2y,
            mx = p1x + 2 * t * (c1x - p1x) + t2 * (c2x - 2 * c1x + p1x),
            my = p1y + 2 * t * (c1y - p1y) + t2 * (c2y - 2 * c1y + p1y),
            nx = c1x + 2 * t * (c2x - c1x) + t2 * (p2x - 2 * c2x + c1x),
            ny = c1y + 2 * t * (c2y - c1y) + t2 * (p2y - 2 * c2y + c1y),
            ax = t1 * p1x + t * c1x,
            ay = t1 * p1y + t * c1y,
            cx = t1 * c2x + t * p2x,
            cy = t1 * c2y + t * p2y,
            alpha = (90 - math.atan2(mx - nx, my - ny) * 180 / PI);
        (mx > nx || my < ny) && (alpha += 180);
        return {
            x: x,
            y: y,
            m: { x: mx, y: my },
            n: { x: nx, y: ny },
            start: { x: ax, y: ay },
            end: { x: cx, y: cy },
            alpha: alpha
        };
    };
    var pathDimensions = cacher(function (path) {
        if (!path) {
            return { x: 0, y: 0, width: 0, height: 0 };
        }
        path = path2curve(path);
        var x = 0,
            y = 0,
            X = [],
            Y = [],
            p;
        for (var i = 0, ii = path.length; i < ii; i++) {
            p = path[i];
            if (p[0] == "M") {
                x = p[1];
                y = p[2];
                X.push(x);
                Y.push(y);
            } else {
                var dim = curveDim(x, y, p[1], p[2], p[3], p[4], p[5], p[6]);
                X = X[concat](dim.min.x, dim.max.x);
                Y = Y[concat](dim.min.y, dim.max.y);
                x = p[5];
                y = p[6];
            }
        }
        var xmin = mmin[apply](0, X),
            ymin = mmin[apply](0, Y);
        return {
            x: xmin,
            y: ymin,
            width: mmax[apply](0, X) - xmin,
            height: mmax[apply](0, Y) - ymin
        };
    }, null, function (o) {
        return {
            x: o.x,
            y: o.y,
            width: o.width,
            height: o.height
        };
    }),
        pathClone = function (pathArray) {
            var res = [];
            if (!R.is(pathArray, array) || !R.is(pathArray && pathArray[0], array)) { // rough assumption
                pathArray = R.parsePathString(pathArray);
            }
            for (var i = 0, ii = pathArray.length; i < ii; i++) {
                res[i] = [];
                for (var j = 0, jj = pathArray[i].length; j < jj; j++) {
                    res[i][j] = pathArray[i][j];
                }
            }
            res.toString = R._path2string;
            return res;
        },
        pathToRelative = R._pathToRelative = cacher(function (pathArray) {
            if (!R.is(pathArray, array) || !R.is(pathArray && pathArray[0], array)) { // rough assumption
                pathArray = R.parsePathString(pathArray);
            }
            var res = [],
                x = 0,
                y = 0,
                mx = 0,
                my = 0,
                start = 0;
            if (pathArray[0][0] == "M") {
                x = pathArray[0][1];
                y = pathArray[0][2];
                mx = x;
                my = y;
                start++;
                res.push(["M", x, y]);
            }
            for (var i = start, ii = pathArray.length; i < ii; i++) {
                var r = res[i] = [],
                    pa = pathArray[i];
                if (pa[0] != lowerCase.call(pa[0])) {
                    r[0] = lowerCase.call(pa[0]);
                    switch (r[0]) {
                        case "a":
                            r[1] = pa[1];
                            r[2] = pa[2];
                            r[3] = pa[3];
                            r[4] = pa[4];
                            r[5] = pa[5];
                            r[6] = +(pa[6] - x).toFixed(3);
                            r[7] = +(pa[7] - y).toFixed(3);
                            break;
                        case "v":
                            r[1] = +(pa[1] - y).toFixed(3);
                            break;
                        case "m":
                            mx = pa[1];
                            my = pa[2];
                        default:
                            for (var j = 1, jj = pa.length; j < jj; j++) {
                                r[j] = +(pa[j] - ((j % 2) ? x : y)).toFixed(3);
                            }
                    }
                } else {
                    r = res[i] = [];
                    if (pa[0] == "m") {
                        mx = pa[1] + x;
                        my = pa[2] + y;
                    }
                    for (var k = 0, kk = pa.length; k < kk; k++) {
                        res[i][k] = pa[k];
                    }
                }
                var len = res[i].length;
                switch (res[i][0]) {
                    case "z":
                        x = mx;
                        y = my;
                        break;
                    case "h":
                        x += +res[i][len - 1];
                        break;
                    case "v":
                        y += +res[i][len - 1];
                        break;
                    default:
                        x += +res[i][len - 2];
                        y += +res[i][len - 1];
                }
            }
            res.toString = R._path2string;
            return res;
        }, 0, pathClone),
        pathToAbsolute = R._pathToAbsolute = cacher(function (pathArray) {
            if (!R.is(pathArray, array) || !R.is(pathArray && pathArray[0], array)) { // rough assumption
                pathArray = R.parsePathString(pathArray);
            }
            if (!pathArray || !pathArray.length) {
                return [["M", 0, 0]];
            }
            var res = [],
                x = 0,
                y = 0,
                mx = 0,
                my = 0,
                start = 0;
            if (pathArray[0][0] == "M") {
                x = +pathArray[0][1];
                y = +pathArray[0][2];
                mx = x;
                my = y;
                start++;
                res[0] = ["M", x, y];
            }
            for (var r, pa, i = start, ii = pathArray.length; i < ii; i++) {
                res.push(r = []);
                pa = pathArray[i];
                if (pa[0] != upperCase.call(pa[0])) {
                    r[0] = upperCase.call(pa[0]);
                    switch (r[0]) {
                        case "A":
                            r[1] = pa[1];
                            r[2] = pa[2];
                            r[3] = pa[3];
                            r[4] = pa[4];
                            r[5] = pa[5];
                            r[6] = +(pa[6] + x);
                            r[7] = +(pa[7] + y);
                            break;
                        case "V":
                            r[1] = +pa[1] + y;
                            break;
                        case "H":
                            r[1] = +pa[1] + x;
                            break;
                        case "R":
                            var dots = [x, y][concat](pa.slice(1));
                            for (var j = 2, jj = dots.length; j < jj; j++) {
                                dots[j] = +dots[j] + x;
                                dots[++j] = +dots[j] + y;
                            }
                            res.pop();
                            res = res[concat](catmullRom2bezier(dots));
                            break;
                        case "M":
                            mx = +pa[1] + x;
                            my = +pa[2] + y;
                        default:
                            for (j = 1, jj = pa.length; j < jj; j++) {
                                r[j] = +pa[j] + ((j % 2) ? x : y);
                            }
                    }
                } else if (pa[0] == "R") {
                    dots = [x, y][concat](pa.slice(1));
                    res.pop();
                    res = res[concat](catmullRom2bezier(dots));
                    r = ["R"][concat](pa.slice(-2));
                } else {
                    for (var k = 0, kk = pa.length; k < kk; k++) {
                        r[k] = pa[k];
                    }
                }
                switch (r[0]) {
                    case "Z":
                        x = mx;
                        y = my;
                        break;
                    case "H":
                        x = r[1];
                        break;
                    case "V":
                        y = r[1];
                        break;
                    case "M":
                        mx = r[r.length - 2];
                        my = r[r.length - 1];
                    default:
                        x = r[r.length - 2];
                        y = r[r.length - 1];
                }
            }
            res.toString = R._path2string;
            return res;
        }, null, pathClone),
        l2c = function (x1, y1, x2, y2) {
            return [x1, y1, x2, y2, x2, y2];
        },
        q2c = function (x1, y1, ax, ay, x2, y2) {
            var _13 = 1 / 3,
                _23 = 2 / 3;
            return [
                    _13 * x1 + _23 * ax,
                    _13 * y1 + _23 * ay,
                    _13 * x2 + _23 * ax,
                    _13 * y2 + _23 * ay,
                    x2,
                    y2
                ];
        },
        a2c = function (x1, y1, rx, ry, angle, large_arc_flag, sweep_flag, x2, y2, recursive) {
            // for more information of where this math came from visit:
            // http://www.w3.org/TR/SVG11/implnote.html#ArcImplementationNotes
            var _120 = PI * 120 / 180,
                rad = PI / 180 * (+angle || 0),
                res = [],
                xy,
                rotate = cacher(function (x, y, rad) {
                    var X = x * math.cos(rad) - y * math.sin(rad),
                        Y = x * math.sin(rad) + y * math.cos(rad);
                    return { x: X, y: Y };
                });
            if (!recursive) {
                xy = rotate(x1, y1, -rad);
                x1 = xy.x;
                y1 = xy.y;
                xy = rotate(x2, y2, -rad);
                x2 = xy.x;
                y2 = xy.y;
                var cos = math.cos(PI / 180 * angle),
                    sin = math.sin(PI / 180 * angle),
                    x = (x1 - x2) / 2,
                    y = (y1 - y2) / 2;
                var h = (x * x) / (rx * rx) + (y * y) / (ry * ry);
                if (h > 1) {
                    h = math.sqrt(h);
                    rx = h * rx;
                    ry = h * ry;
                }
                var rx2 = rx * rx,
                    ry2 = ry * ry,
                    k = (large_arc_flag == sweep_flag ? -1 : 1) *
                        math.sqrt(abs((rx2 * ry2 - rx2 * y * y - ry2 * x * x) / (rx2 * y * y + ry2 * x * x))),
                    cx = k * rx * y / ry + (x1 + x2) / 2,
                    cy = k * -ry * x / rx + (y1 + y2) / 2,
                    f1 = math.asin(((y1 - cy) / ry).toFixed(9)),
                    f2 = math.asin(((y2 - cy) / ry).toFixed(9));

                f1 = x1 < cx ? PI - f1 : f1;
                f2 = x2 < cx ? PI - f2 : f2;
                f1 < 0 && (f1 = PI * 2 + f1);
                f2 < 0 && (f2 = PI * 2 + f2);
                if (sweep_flag && f1 > f2) {
                    f1 = f1 - PI * 2;
                }
                if (!sweep_flag && f2 > f1) {
                    f2 = f2 - PI * 2;
                }
            } else {
                f1 = recursive[0];
                f2 = recursive[1];
                cx = recursive[2];
                cy = recursive[3];
            }
            var df = f2 - f1;
            if (abs(df) > _120) {
                var f2old = f2,
                    x2old = x2,
                    y2old = y2;
                f2 = f1 + _120 * (sweep_flag && f2 > f1 ? 1 : -1);
                x2 = cx + rx * math.cos(f2);
                y2 = cy + ry * math.sin(f2);
                res = a2c(x2, y2, rx, ry, angle, 0, sweep_flag, x2old, y2old, [f2, f2old, cx, cy]);
            }
            df = f2 - f1;
            var c1 = math.cos(f1),
                s1 = math.sin(f1),
                c2 = math.cos(f2),
                s2 = math.sin(f2),
                t = math.tan(df / 4),
                hx = 4 / 3 * rx * t,
                hy = 4 / 3 * ry * t,
                m1 = [x1, y1],
                m2 = [x1 + hx * s1, y1 - hy * c1],
                m3 = [x2 + hx * s2, y2 - hy * c2],
                m4 = [x2, y2];
            m2[0] = 2 * m1[0] - m2[0];
            m2[1] = 2 * m1[1] - m2[1];
            if (recursive) {
                return [m2, m3, m4][concat](res);
            } else {
                res = [m2, m3, m4][concat](res).join()[split](",");
                var newres = [];
                for (var i = 0, ii = res.length; i < ii; i++) {
                    newres[i] = i % 2 ? rotate(res[i - 1], res[i], rad).y : rotate(res[i], res[i + 1], rad).x;
                }
                return newres;
            }
        },
        findDotAtSegment = function (p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y, t) {
            var t1 = 1 - t;
            return {
                x: pow(t1, 3) * p1x + pow(t1, 2) * 3 * t * c1x + t1 * 3 * t * t * c2x + pow(t, 3) * p2x,
                y: pow(t1, 3) * p1y + pow(t1, 2) * 3 * t * c1y + t1 * 3 * t * t * c2y + pow(t, 3) * p2y
            };
        },
        curveDim = cacher(function (p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y) {
            var a = (c2x - 2 * c1x + p1x) - (p2x - 2 * c2x + c1x),
                b = 2 * (c1x - p1x) - 2 * (c2x - c1x),
                c = p1x - c1x,
                t1 = (-b + math.sqrt(b * b - 4 * a * c)) / 2 / a,
                t2 = (-b - math.sqrt(b * b - 4 * a * c)) / 2 / a,
                y = [p1y, p2y],
                x = [p1x, p2x],
                dot;
            abs(t1) > "1e12" && (t1 = .5);
            abs(t2) > "1e12" && (t2 = .5);
            if (t1 > 0 && t1 < 1) {
                dot = findDotAtSegment(p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y, t1);
                x.push(dot.x);
                y.push(dot.y);
            }
            if (t2 > 0 && t2 < 1) {
                dot = findDotAtSegment(p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y, t2);
                x.push(dot.x);
                y.push(dot.y);
            }
            a = (c2y - 2 * c1y + p1y) - (p2y - 2 * c2y + c1y);
            b = 2 * (c1y - p1y) - 2 * (c2y - c1y);
            c = p1y - c1y;
            t1 = (-b + math.sqrt(b * b - 4 * a * c)) / 2 / a;
            t2 = (-b - math.sqrt(b * b - 4 * a * c)) / 2 / a;
            abs(t1) > "1e12" && (t1 = .5);
            abs(t2) > "1e12" && (t2 = .5);
            if (t1 > 0 && t1 < 1) {
                dot = findDotAtSegment(p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y, t1);
                x.push(dot.x);
                y.push(dot.y);
            }
            if (t2 > 0 && t2 < 1) {
                dot = findDotAtSegment(p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y, t2);
                x.push(dot.x);
                y.push(dot.y);
            }
            return {
                min: { x: mmin[apply](0, x), y: mmin[apply](0, y) },
                max: { x: mmax[apply](0, x), y: mmax[apply](0, y) }
            };
        }),
        path2curve = R._path2curve = cacher(function (path, path2) {
            var p = pathToAbsolute(path),
                p2 = path2 && pathToAbsolute(path2),
                attrs = { x: 0, y: 0, bx: 0, by: 0, X: 0, Y: 0, qx: null, qy: null },
                attrs2 = { x: 0, y: 0, bx: 0, by: 0, X: 0, Y: 0, qx: null, qy: null },
                processPath = function (path, d) {
                    var nx, ny;
                    if (!path) {
                        return ["C", d.x, d.y, d.x, d.y, d.x, d.y];
                    }
                    !(path[0] in { T: 1, Q: 1 }) && (d.qx = d.qy = null);
                    switch (path[0]) {
                        case "M":
                            d.X = path[1];
                            d.Y = path[2];
                            break;
                        case "A":
                            path = ["C"][concat](a2c[apply](0, [d.x, d.y][concat](path.slice(1))));
                            break;
                        case "S":
                            nx = d.x + (d.x - (d.bx || d.x));
                            ny = d.y + (d.y - (d.by || d.y));
                            path = ["C", nx, ny][concat](path.slice(1));
                            break;
                        case "T":
                            d.qx = d.x + (d.x - (d.qx || d.x));
                            d.qy = d.y + (d.y - (d.qy || d.y));
                            path = ["C"][concat](q2c(d.x, d.y, d.qx, d.qy, path[1], path[2]));
                            break;
                        case "Q":
                            d.qx = path[1];
                            d.qy = path[2];
                            path = ["C"][concat](q2c(d.x, d.y, path[1], path[2], path[3], path[4]));
                            break;
                        case "L":
                            path = ["C"][concat](l2c(d.x, d.y, path[1], path[2]));
                            break;
                        case "H":
                            path = ["C"][concat](l2c(d.x, d.y, path[1], d.y));
                            break;
                        case "V":
                            path = ["C"][concat](l2c(d.x, d.y, d.x, path[1]));
                            break;
                        case "Z":
                            path = ["C"][concat](l2c(d.x, d.y, d.X, d.Y));
                            break;
                    }
                    return path;
                },
                fixArc = function (pp, i) {
                    if (pp[i].length > 7) {
                        pp[i].shift();
                        var pi = pp[i];
                        while (pi.length) {
                            pp.splice(i++, 0, ["C"][concat](pi.splice(0, 6)));
                        }
                        pp.splice(i, 1);
                        ii = mmax(p.length, p2 && p2.length || 0);
                    }
                },
                fixM = function (path1, path2, a1, a2, i) {
                    if (path1 && path2 && path1[i][0] == "M" && path2[i][0] != "M") {
                        path2.splice(i, 0, ["M", a2.x, a2.y]);
                        a1.bx = 0;
                        a1.by = 0;
                        a1.x = path1[i][1];
                        a1.y = path1[i][2];
                        ii = mmax(p.length, p2 && p2.length || 0);
                    }
                };
            for (var i = 0, ii = mmax(p.length, p2 && p2.length || 0); i < ii; i++) {
                p[i] = processPath(p[i], attrs);
                fixArc(p, i);
                p2 && (p2[i] = processPath(p2[i], attrs2));
                p2 && fixArc(p2, i);
                fixM(p, p2, attrs, attrs2, i);
                fixM(p2, p, attrs2, attrs, i);
                var seg = p[i],
                    seg2 = p2 && p2[i],
                    seglen = seg.length,
                    seg2len = p2 && seg2.length;
                attrs.x = seg[seglen - 2];
                attrs.y = seg[seglen - 1];
                attrs.bx = toFloat(seg[seglen - 4]) || attrs.x;
                attrs.by = toFloat(seg[seglen - 3]) || attrs.y;
                attrs2.bx = p2 && (toFloat(seg2[seg2len - 4]) || attrs2.x);
                attrs2.by = p2 && (toFloat(seg2[seg2len - 3]) || attrs2.y);
                attrs2.x = p2 && seg2[seg2len - 2];
                attrs2.y = p2 && seg2[seg2len - 1];
            }
            return p2 ? [p, p2] : p;
        }, null, pathClone),
        parseDots = R._parseDots = cacher(function (gradient) {
            var dots = [];
            for (var i = 0, ii = gradient.length; i < ii; i++) {
                var dot = {},
                    par = gradient[i].match(/^([^:]*):?([\d\.]*)/);
                dot.color = R.getRGB(par[1]);
                if (dot.color.error) {
                    return null;
                }
                dot.color = dot.color.hex;
                par[2] && (dot.offset = par[2] + "%");
                dots.push(dot);
            }
            for (i = 1, ii = dots.length - 1; i < ii; i++) {
                if (!dots[i].offset) {
                    var start = toFloat(dots[i - 1].offset || 0),
                        end = 0;
                    for (var j = i + 1; j < ii; j++) {
                        if (dots[j].offset) {
                            end = dots[j].offset;
                            break;
                        }
                    }
                    if (!end) {
                        end = 100;
                        j = ii;
                    }
                    end = toFloat(end);
                    var d = (end - start) / (j - i + 1);
                    for (; i < j; i++) {
                        start += d;
                        dots[i].offset = start + "%";
                    }
                }
            }
            return dots;
        }),
        tear = R._tear = function (el, paper) {
            el == paper.top && (paper.top = el.prev);
            el == paper.bottom && (paper.bottom = el.next);
            el.next && (el.next.prev = el.prev);
            el.prev && (el.prev.next = el.next);
        },
        tofront = R._tofront = function (el, paper) {
            if (paper.top === el) {
                return;
            }
            tear(el, paper);
            el.next = null;
            el.prev = paper.top;
            paper.top.next = el;
            paper.top = el;
        },
        toback = R._toback = function (el, paper) {
            if (paper.bottom === el) {
                return;
            }
            tear(el, paper);
            el.next = paper.bottom;
            el.prev = null;
            paper.bottom.prev = el;
            paper.bottom = el;
        },
        insertafter = R._insertafter = function (el, el2, paper) {
            tear(el, paper);
            el2 == paper.top && (paper.top = el);
            el2.next && (el2.next.prev = el);
            el.next = el2.next;
            el.prev = el2;
            el2.next = el;
        },
        insertbefore = R._insertbefore = function (el, el2, paper) {
            tear(el, paper);
            el2 == paper.bottom && (paper.bottom = el);
            el2.prev && (el2.prev.next = el);
            el.prev = el2.prev;
            el2.prev = el;
            el.next = el2;
        },
        removed = function (methodname) {
            return function () {
                throw new Error("Rapha\xebl: you are calling to method \u201c" + methodname + "\u201d of removed object");
            };
        },
        extractTransform = R._extractTransform = function (el, tstr) {
            if (tstr == null) {
                return el._.transform;
            }
            tstr = Str(tstr).replace(/\.{3}|\u2026/g, el._.transform || E);
            var tdata = R.parseTransformString(tstr),
                deg = 0,
                dx = 0,
                dy = 0,
                sx = 1,
                sy = 1,
                _ = el._,
                m = new Matrix;
            _.transform = tdata || [];
            if (tdata) {
                for (var i = 0, ii = tdata.length; i < ii; i++) {
                    var t = tdata[i],
                        tlen = t.length,
                        command = Str(t[0]).toLowerCase(),
                        absolute = t[0] != command,
                        inver = absolute ? m.invert() : 0,
                        x1,
                        y1,
                        x2,
                        y2,
                        bb;
                    if (command == "t" && tlen == 3) {
                        if (absolute) {
                            x1 = inver.x(0, 0);
                            y1 = inver.y(0, 0);
                            x2 = inver.x(t[1], t[2]);
                            y2 = inver.y(t[1], t[2]);
                            m.translate(x2 - x1, y2 - y1);
                        } else {
                            m.translate(t[1], t[2]);
                        }
                    } else if (command == "r") {
                        if (tlen == 2) {
                            bb = bb || el.getBBox(1);
                            m.rotate(t[1], bb.x + bb.width / 2, bb.y + bb.height / 2);
                            deg += t[1];
                        } else if (tlen == 4) {
                            if (absolute) {
                                x2 = inver.x(t[2], t[3]);
                                y2 = inver.y(t[2], t[3]);
                                m.rotate(t[1], x2, y2);
                            } else {
                                m.rotate(t[1], t[2], t[3]);
                            }
                            deg += t[1];
                        }
                    } else if (command == "s") {
                        if (tlen == 2 || tlen == 3) {
                            bb = bb || el.getBBox(1);
                            m.scale(t[1], t[tlen - 1], bb.x + bb.width / 2, bb.y + bb.height / 2);
                            sx *= t[1];
                            sy *= t[tlen - 1];
                        } else if (tlen == 5) {
                            if (absolute) {
                                x2 = inver.x(t[3], t[4]);
                                y2 = inver.y(t[3], t[4]);
                                m.scale(t[1], t[2], x2, y2);
                            } else {
                                m.scale(t[1], t[2], t[3], t[4]);
                            }
                            sx *= t[1];
                            sy *= t[2];
                        }
                    } else if (command == "m" && tlen == 7) {
                        m.add(t[1], t[2], t[3], t[4], t[5], t[6]);
                    }
                    _.dirtyT = 1;
                    el.matrix = m;
                }
            }

            el.matrix = m;

            _.sx = sx;
            _.sy = sy;
            _.deg = deg;
            _.dx = dx = m.e;
            _.dy = dy = m.f;

            if (sx == 1 && sy == 1 && !deg && _.bbox) {
                _.bbox.x += +dx;
                _.bbox.y += +dy;
            } else {
                _.dirtyT = 1;
            }
        },
        getEmpty = function (item) {
            var l = item[0];
            switch (l.toLowerCase()) {
                case "t": return [l, 0, 0];
                case "m": return [l, 1, 0, 0, 1, 0, 0];
                case "r": if (item.length == 4) {
                        return [l, 0, item[2], item[3]];
                    } else {
                        return [l, 0];
                    }
                case "s": if (item.length == 5) {
                        return [l, 1, 1, item[3], item[4]];
                    } else if (item.length == 3) {
                        return [l, 1, 1];
                    } else {
                        return [l, 1];
                    }
            }
        },
        equaliseTransform = R._equaliseTransform = function (t1, t2) {
            t2 = Str(t2).replace(/\.{3}|\u2026/g, t1);
            t1 = R.parseTransformString(t1) || [];
            t2 = R.parseTransformString(t2) || [];
            var maxlength = mmax(t1.length, t2.length),
                from = [],
                to = [],
                i = 0, j, jj,
                tt1, tt2;
            for (; i < maxlength; i++) {
                tt1 = t1[i] || getEmpty(t2[i]);
                tt2 = t2[i] || getEmpty(tt1);
                if ((tt1[0] != tt2[0]) ||
                    (tt1[0].toLowerCase() == "r" && (tt1[2] != tt2[2] || tt1[3] != tt2[3])) ||
                    (tt1[0].toLowerCase() == "s" && (tt1[3] != tt2[3] || tt1[4] != tt2[4]))
                    ) {
                    return;
                }
                from[i] = [];
                to[i] = [];
                for (j = 0, jj = mmax(tt1.length, tt2.length); j < jj; j++) {
                    j in tt1 && (from[i][j] = tt1[j]);
                    j in tt2 && (to[i][j] = tt2[j]);
                }
            }
            return {
                from: from,
                to: to
            };
        };
    R._getContainer = function (x, y, w, h) {
        var container;
        container = h == null && !R.is(x, "object") ? g.doc.getElementById(x) : x;
        if (container == null) {
            return;
        }
        if (container.tagName) {
            if (y == null) {
                return {
                    container: container,
                    width: container.style.pixelWidth || container.offsetWidth,
                    height: container.style.pixelHeight || container.offsetHeight
                };
            } else {
                return {
                    container: container,
                    width: y,
                    height: w
                };
            }
        }
        return {
            container: 1,
            x: x,
            y: y,
            width: w,
            height: h
        };
    };

    R.pathToRelative = pathToRelative;
    R._engine = {};

    R.path2curve = path2curve;

    R.matrix = function (a, b, c, d, e, f) {
        return new Matrix(a, b, c, d, e, f);
    };
    function Matrix(a, b, c, d, e, f) {
        if (a != null) {
            this.a = +a;
            this.b = +b;
            this.c = +c;
            this.d = +d;
            this.e = +e;
            this.f = +f;
        } else {
            this.a = 1;
            this.b = 0;
            this.c = 0;
            this.d = 1;
            this.e = 0;
            this.f = 0;
        }
    }
    (function (matrixproto) {

        matrixproto.add = function (a, b, c, d, e, f) {
            var out = [[], [], []],
                m = [[this.a, this.c, this.e], [this.b, this.d, this.f], [0, 0, 1]],
                matrix = [[a, c, e], [b, d, f], [0, 0, 1]],
                x, y, z, res;

            if (a && a instanceof Matrix) {
                matrix = [[a.a, a.c, a.e], [a.b, a.d, a.f], [0, 0, 1]];
            }

            for (x = 0; x < 3; x++) {
                for (y = 0; y < 3; y++) {
                    res = 0;
                    for (z = 0; z < 3; z++) {
                        res += m[x][z] * matrix[z][y];
                    }
                    out[x][y] = res;
                }
            }
            this.a = out[0][0];
            this.b = out[1][0];
            this.c = out[0][1];
            this.d = out[1][1];
            this.e = out[0][2];
            this.f = out[1][2];
        };

        matrixproto.invert = function () {
            var me = this,
                x = me.a * me.d - me.b * me.c;
            return new Matrix(me.d / x, -me.b / x, -me.c / x, me.a / x, (me.c * me.f - me.d * me.e) / x, (me.b * me.e - me.a * me.f) / x);
        };

        matrixproto.clone = function () {
            return new Matrix(this.a, this.b, this.c, this.d, this.e, this.f);
        };

        matrixproto.translate = function (x, y) {
            this.add(1, 0, 0, 1, x, y);
        };

        matrixproto.scale = function (x, y, cx, cy) {
            y == null && (y = x);
            (cx || cy) && this.add(1, 0, 0, 1, cx, cy);
            this.add(x, 0, 0, y, 0, 0);
            (cx || cy) && this.add(1, 0, 0, 1, -cx, -cy);
        };

        matrixproto.rotate = function (a, x, y) {
            a = R.rad(a);
            x = x || 0;
            y = y || 0;
            var cos = +math.cos(a).toFixed(9),
                sin = +math.sin(a).toFixed(9);
            this.add(cos, sin, -sin, cos, x, y);
            this.add(1, 0, 0, 1, -x, -y);
        };

        matrixproto.x = function (x, y) {
            return x * this.a + y * this.c + this.e;
        };

        matrixproto.y = function (x, y) {
            return x * this.b + y * this.d + this.f;
        };
        matrixproto.get = function (i) {
            return +this[Str.fromCharCode(97 + i)].toFixed(4);
        };
        matrixproto.toString = function () {
            return R.svg ?
                "matrix(" + [this.get(0), this.get(1), this.get(2), this.get(3), this.get(4), this.get(5)].join() + ")" :
                [this.get(0), this.get(2), this.get(1), this.get(3), 0, 0].join();
        };
        matrixproto.toFilter = function () {
            return "progid:DXImageTransform.Microsoft.Matrix(M11=" + this.get(0) +
                ", M12=" + this.get(2) + ", M21=" + this.get(1) + ", M22=" + this.get(3) +
                ", Dx=" + this.get(4) + ", Dy=" + this.get(5) + ", sizingmethod='auto expand')";
        };
        matrixproto.offset = function () {
            return [this.e.toFixed(4), this.f.toFixed(4)];
        };
        function norm(a) {
            return a[0] * a[0] + a[1] * a[1];
        }
        function normalize(a) {
            var mag = math.sqrt(norm(a));
            a[0] && (a[0] /= mag);
            a[1] && (a[1] /= mag);
        }

        matrixproto.split = function () {
            var out = {};
            // translation
            out.dx = this.e;
            out.dy = this.f;

            // scale and shear
            var row = [[this.a, this.c], [this.b, this.d]];
            out.scalex = math.sqrt(norm(row[0]));
            normalize(row[0]);

            out.shear = row[0][0] * row[1][0] + row[0][1] * row[1][1];
            row[1] = [row[1][0] - row[0][0] * out.shear, row[1][1] - row[0][1] * out.shear];

            out.scaley = math.sqrt(norm(row[1]));
            normalize(row[1]);
            out.shear /= out.scaley;

            // rotation
            var sin = -row[0][1],
                cos = row[1][1];
            if (cos < 0) {
                out.rotate = R.deg(math.acos(cos));
                if (sin < 0) {
                    out.rotate = 360 - out.rotate;
                }
            } else {
                out.rotate = R.deg(math.asin(sin));
            }

            out.isSimple = ! +out.shear.toFixed(9) && (out.scalex.toFixed(9) == out.scaley.toFixed(9) || !out.rotate);
            out.isSuperSimple = ! +out.shear.toFixed(9) && out.scalex.toFixed(9) == out.scaley.toFixed(9) && !out.rotate;
            out.noRotation = ! +out.shear.toFixed(9) && !out.rotate;
            return out;
        };

        matrixproto.toTransformString = function (shorter) {
            var s = shorter || this[split]();
            if (s.isSimple) {
                return "t" + [s.dx, s.dy] + "s" + [s.scalex, s.scaley, 0, 0] + "r" + [s.rotate, 0, 0];
            } else {
                return "m" + [this.get(0), this.get(1), this.get(2), this.get(3), this.get(4), this.get(5)];
            }
        };
    })(Matrix.prototype);

    // WebKit rendering bug workaround method
    var version = navigator.userAgent.match(/Version\/(.*?)\s/) || navigator.userAgent.match(/Chrome\/(\d+)/);
    if ((navigator.vendor == "Apple Computer, Inc.") && (version && version[1] < 4 || navigator.platform.slice(0, 2) == "iP") ||
        (navigator.vendor == "Google Inc." && version && version[1] < 8)) {

        paperproto.safari = function () {
            var rect = this.rect(-99, -99, this.width + 99, this.height + 99).attr({ stroke: "none" });
            setTimeout(function () { rect.remove(); });
        };
    } else {
        paperproto.safari = fun;
    }

    var preventDefault = function () {
        this.returnValue = false;
    },
    preventTouch = function () {
        return this.originalEvent.preventDefault();
    },
    stopPropagation = function () {
        this.cancelBubble = true;
    },
    stopTouch = function () {
        return this.originalEvent.stopPropagation();
    },
    addEvent = (function () {
        if (g.doc.addEventListener) {
            return function (obj, type, fn, element) {
                var realName = supportsTouch && touchMap[type] ? touchMap[type] : type,
                    f = function (e) {
                        var scrollY = g.doc.documentElement.scrollTop || g.doc.body.scrollTop,
                            scrollX = g.doc.documentElement.scrollLeft || g.doc.body.scrollLeft,
                            x = e.clientX + scrollX,
                            y = e.clientY + scrollY;
                        if (supportsTouch && touchMap[has](type)) {
                            for (var i = 0, ii = e.targetTouches && e.targetTouches.length; i < ii; i++) {
                                if (e.targetTouches[i].target == obj) {
                                    var olde = e;
                                    e = e.targetTouches[i];
                                    e.originalEvent = olde;
                                    e.preventDefault = preventTouch;
                                    e.stopPropagation = stopTouch;
                                    break;
                                }
                            }
                        }
                        return fn.call(element, e, x, y);
                    };
                obj.addEventListener(realName, f, false);
                return function () {
                    obj.removeEventListener(realName, f, false);
                    return true;
                };
            };
        } else if (g.doc.attachEvent) {
            return function (obj, type, fn, element) {
                var f = function (e) {
                    e = e || g.win.event;
                    var scrollY = g.doc.documentElement.scrollTop || g.doc.body.scrollTop,
                        scrollX = g.doc.documentElement.scrollLeft || g.doc.body.scrollLeft,
                        x = e.clientX + scrollX,
                        y = e.clientY + scrollY;
                    e.preventDefault = e.preventDefault || preventDefault;
                    e.stopPropagation = e.stopPropagation || stopPropagation;
                    return fn.call(element, e, x, y);
                };
                obj.attachEvent("on" + type, f);
                var detacher = function () {
                    obj.detachEvent("on" + type, f);
                    return true;
                };
                return detacher;
            };
        }
    })(),
    drag = [],
    dragMove = function (e) {
        var x = e.clientX,
            y = e.clientY,
            scrollY = g.doc.documentElement.scrollTop || g.doc.body.scrollTop,
            scrollX = g.doc.documentElement.scrollLeft || g.doc.body.scrollLeft,
            dragi,
            j = drag.length;
        while (j--) {
            dragi = drag[j];
            if (supportsTouch) {
                var i = e.touches.length,
                    touch;
                while (i--) {
                    touch = e.touches[i];
                    if (touch.identifier == dragi.el._drag.id) {
                        x = touch.clientX;
                        y = touch.clientY;
                        (e.originalEvent ? e.originalEvent : e).preventDefault();
                        break;
                    }
                }
            } else {
                e.preventDefault();
            }
            var node = dragi.el.node,
                o,
                next = node.nextSibling,
                parent = node.parentNode,
                display = node.style.display;
            g.win.opera && parent.removeChild(node);
            node.style.display = "none";
            o = dragi.el.paper.getElementByPoint(x, y);
            node.style.display = display;
            g.win.opera && (next ? parent.insertBefore(node, next) : parent.appendChild(node));
            o && eve("drag.over." + dragi.el.id, dragi.el, o);
            x += scrollX;
            y += scrollY;
            eve("drag.move." + dragi.el.id, dragi.move_scope || dragi.el, x - dragi.el._drag.x, y - dragi.el._drag.y, x, y, e);
        }
    },
    dragUp = function (e) {
        R.unmousemove(dragMove).unmouseup(dragUp);
        var i = drag.length,
            dragi;
        while (i--) {
            dragi = drag[i];
            dragi.el._drag = {};
            eve("drag.end." + dragi.el.id, dragi.end_scope || dragi.start_scope || dragi.move_scope || dragi.el, e);
        }
        drag = [];
    },

    elproto = R.el = {};
































    for (var i = events.length; i--; ) {
        (function (eventName) {
            R[eventName] = elproto[eventName] = function (fn, scope) {
                if (R.is(fn, "function")) {
                    this.events = this.events || [];
                    this.events.push({ name: eventName, f: fn, unbind: addEvent(this.shape || this.node || g.doc, eventName, fn, scope || this) });
                }
                return this;
            };
            R["un" + eventName] = elproto["un" + eventName] = function (fn) {
                var events = this.events,
                    l = events.length;
                while (l--) if (events[l].name == eventName && events[l].f == fn) {
                    events[l].unbind();
                    events.splice(l, 1);
                    !events.length && delete this.events;
                    return this;
                }
                return this;
            };
        })(events[i]);
    }


    elproto.data = function (key, value) {
        var data = eldata[this.id] = eldata[this.id] || {};
        if (arguments.length == 1) {
            if (R.is(key, "object")) {
                for (var i in key) if (key[has](i)) {
                    this.data(i, key[i]);
                }
                return this;
            }
            eve("data.get." + this.id, this, data[key], key);
            return data[key];
        }
        data[key] = value;
        eve("data.set." + this.id, this, value, key);
        return this;
    };

    elproto.removeData = function (key) {
        if (key == null) {
            eldata[this.id] = {};
        } else {
            eldata[this.id] && delete eldata[this.id][key];
        }
        return this;
    };

    elproto.hover = function (f_in, f_out, scope_in, scope_out) {
        return this.mouseover(f_in, scope_in).mouseout(f_out, scope_out || scope_in);
    };

    elproto.unhover = function (f_in, f_out) {
        return this.unmouseover(f_in).unmouseout(f_out);
    };

    elproto.drag = function (onmove, onstart, onend, move_scope, start_scope, end_scope) {
        this.initDrag = function (e) {
            (e.originalEvent || e).preventDefault();
            var scrollY = g.doc.documentElement.scrollTop || g.doc.body.scrollTop,
                    scrollX = g.doc.documentElement.scrollLeft || g.doc.body.scrollLeft;
            this._drag.x = e.clientX + scrollX;
            this._drag.y = e.clientY + scrollY;
            this._drag.id = e.identifier;
            !drag.length && R.mousemove(dragMove).mouseup(dragUp);
            drag.push({ el: this, move_scope: move_scope, start_scope: start_scope, end_scope: end_scope });
            onstart && eve.on("drag.start." + this.id, onstart);
            onmove && eve.on("drag.move." + this.id, onmove);
            onend && eve.on("drag.end." + this.id, onend);
            eve("drag.start." + this.id, start_scope || move_scope || this, e.clientX + scrollX, e.clientY + scrollY, e);
        }
        this._drag = {};
        this.mousedown(this.initDrag);
        return this;
    };

    elproto.onDragOver = function (f) {
        f ? eve.on("drag.over." + this.id, f) : eve.unbind("drag.over." + this.id);
    };

    elproto.undrag = function () {
        var i = drag.length;
        while (i--) if (drag[i].el == this) {
            R.unmousedown(drag[i].start);
            drag.splice(i++, 1);
            eve.unbind("drag.*." + this.id);
        }
        !drag.length && R.unmousemove(dragMove).unmouseup(dragUp);
    };

    paperproto.circle = function (x, y, r) {
        var out = R._engine.circle(this, x || 0, y || 0, r || 0);
        this.__set__ && this.__set__.push(out);
        return out;
    };

    paperproto.rect = function (x, y, w, h, r) {
        var out = R._engine.rect(this, x || 0, y || 0, w || 0, h || 0, r || 0);
        this.__set__ && this.__set__.push(out);
        return out;
    };

    paperproto.ellipse = function (x, y, rx, ry) {
        var out = R._engine.ellipse(this, x || 0, y || 0, rx || 0, ry || 0);
        this.__set__ && this.__set__.push(out);
        return out;
    };

    paperproto.path = function (pathString) {
        pathString && !R.is(pathString, string) && !R.is(pathString[0], array) && (pathString += E);
        var out = R._engine.path(R.format[apply](R, arguments), this);
        this.__set__ && this.__set__.push(out);
        return out;
    };

    paperproto.image = function (src, x, y, w, h) {
        var out = R._engine.image(this, src || "about:blank", x || 0, y || 0, w || 0, h || 0);
        this.__set__ && this.__set__.push(out);
        return out;
    };

    paperproto.text = function (x, y, text) {
        var out = R._engine.text(this, x || 0, y || 0, Str(text));
        this.__set__ && this.__set__.push(out);
        return out;
    };

    paperproto.set = function (itemsArray) {
        !R.is(itemsArray, "array") && (itemsArray = Array.prototype.splice.call(arguments, 0, arguments.length));
        var out = new Set(itemsArray);
        this.__set__ && this.__set__.push(out);
        return out;
    };

    paperproto.setStart = function (set) {
        this.__set__ = set || this.set();
    };

    paperproto.setFinish = function (set) {
        var out = this.__set__;
        delete this.__set__;
        return out;
    };

    paperproto.setSize = function (width, height) {
        return R._engine.setSize.call(this, width, height);
    };

    paperproto.setViewBox = function (x, y, w, h, fit) {
        return R._engine.setViewBox.call(this, x, y, w, h, fit);
    };


    paperproto.top = paperproto.bottom = null;

    paperproto.raphael = R;
    var getOffset = function (elem) {
        var box = elem.getBoundingClientRect(),
            doc = elem.ownerDocument,
            body = doc.body,
            docElem = doc.documentElement,
            clientTop = docElem.clientTop || body.clientTop || 0, clientLeft = docElem.clientLeft || body.clientLeft || 0,
            top = box.top + (g.win.pageYOffset || docElem.scrollTop || body.scrollTop) - clientTop,
            left = box.left + (g.win.pageXOffset || docElem.scrollLeft || body.scrollLeft) - clientLeft;
        return {
            y: top,
            x: left
        };
    };

    paperproto.getElementByPoint = function (x, y) {
        var paper = this,
            svg = paper.canvas,
            target = g.doc.elementFromPoint(x, y);
        if (g.win.opera && target.tagName == "svg") {
            var so = getOffset(svg),
                sr = svg.createSVGRect();
            sr.x = x - so.x;
            sr.y = y - so.y;
            sr.width = sr.height = 1;
            var hits = svg.getIntersectionList(sr, null);
            if (hits.length) {
                target = hits[hits.length - 1];
            }
        }
        if (!target) {
            return null;
        }
        while (target.parentNode && target != svg.parentNode && !target.raphael) {
            target = target.parentNode;
        }
        target == paper.canvas.parentNode && (target = svg);
        target = target && target.raphael ? paper.getById(target.raphaelid) : null;
        return target;
    };

    paperproto.getById = function (id) {
        var bot = this.bottom;
        while (bot) {
            if (bot.id == id) {
                return bot;
            }
            bot = bot.next;
        }
        return null;
    };

    paperproto.forEach = function (callback, thisArg) {
        var bot = this.bottom;
        while (bot) {
            if (callback.call(thisArg, bot) === false) {
                return this;
            }
            bot = bot.next;
        }
        return this;
    };
    function x_y() {
        return this.x + S + this.y;
    }
    function x_y_w_h() {
        return this.x + S + this.y + S + this.width + " \xd7 " + this.height;
    }

    elproto.getBBox = function (isWithoutTransform) {
        if (this.removed) {
            return {};
        }
        var _ = this._;
        if (isWithoutTransform) {
            if (_.dirty || !_.bboxwt) {
                this.realPath = getPath[this.type](this);
                _.bboxwt = pathDimensions(this.realPath);
                _.bboxwt.toString = x_y_w_h;
                _.dirty = 0;
            }
            return _.bboxwt;
        }
        if (_.dirty || _.dirtyT || !_.bbox) {
            if (_.dirty || !this.realPath) {
                _.bboxwt = 0;
                this.realPath = getPath[this.type](this);
            }
            _.bbox = pathDimensions(mapPath(this.realPath, this.matrix));
            _.bbox.toString = x_y_w_h;
            _.dirty = _.dirtyT = 0;
        }
        return _.bbox;
    };

    elproto.clone = function () {
        if (this.removed) {
            return null;
        }
        var out = this.paper[this.type]().attr(this.attr());
        this.__set__ && this.__set__.push(out);
        return out;
    };

    elproto.glow = function (glow) {
        if (this.type == "text") {
            return null;
        }
        glow = glow || {};
        var s = {
            width: (glow.width || 10) + (+this.attr("stroke-width") || 1),
            fill: glow.fill || false,
            opacity: glow.opacity || .5,
            offsetx: glow.offsetx || 0,
            offsety: glow.offsety || 0,
            color: glow.color || "#000"
        },
            c = s.width / 2,
            r = this.paper,
            out = r.set(),
            path = this.realPath || getPath[this.type](this);
        path = this.matrix ? mapPath(path, this.matrix) : path;
        for (var i = 1; i < c + 1; i++) {
            out.push(r.path(path).attr({
                stroke: s.color,
                fill: s.fill ? s.color : "none",
                "stroke-linejoin": "round",
                "stroke-linecap": "round",
                "stroke-width": +(s.width / c * i).toFixed(3),
                opacity: +(s.opacity / c).toFixed(3)
            }));
        }
        return out.insertBefore(this).translate(s.offsetx, s.offsety);
    };
    var curveslengths = {},
    getPointAtSegmentLength = function (p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y, length) {
        var len = 0,
            precision = 100,
            name = [p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y].join(),
            cache = curveslengths[name],
            old, dot;
        !cache && (curveslengths[name] = cache = { data: [] });
        cache.timer && clearTimeout(cache.timer);
        cache.timer = setTimeout(function () { delete curveslengths[name]; }, 2e3);
        if (length != null && !cache.precision) {
            var total = getPointAtSegmentLength(p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y);
            cache.precision = ~ ~total * 10;
            cache.data = [];
        }
        precision = cache.precision || precision;
        for (var i = 0; i < precision + 1; i++) {
            if (cache.data[i * precision]) {
                dot = cache.data[i * precision];
            } else {
                dot = R.findDotsAtSegment(p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y, i / precision);
                cache.data[i * precision] = dot;
            }
            i && (len += pow(pow(old.x - dot.x, 2) + pow(old.y - dot.y, 2), .5));
            if (length != null && len >= length) {
                return dot;
            }
            old = dot;
        }
        if (length == null) {
            return len;
        }
    },
    getLengthFactory = function (istotal, subpath) {
        return function (path, length, onlystart) {
            path = path2curve(path);
            var x, y, p, l, sp = "", subpaths = {}, point,
                len = 0;
            for (var i = 0, ii = path.length; i < ii; i++) {
                p = path[i];
                if (p[0] == "M") {
                    x = +p[1];
                    y = +p[2];
                } else {
                    l = getPointAtSegmentLength(x, y, p[1], p[2], p[3], p[4], p[5], p[6]);
                    if (len + l > length) {
                        if (subpath && !subpaths.start) {
                            point = getPointAtSegmentLength(x, y, p[1], p[2], p[3], p[4], p[5], p[6], length - len);
                            sp += ["C" + point.start.x, point.start.y, point.m.x, point.m.y, point.x, point.y];
                            if (onlystart) { return sp; }
                            subpaths.start = sp;
                            sp = ["M" + point.x, point.y + "C" + point.n.x, point.n.y, point.end.x, point.end.y, p[5], p[6]].join();
                            len += l;
                            x = +p[5];
                            y = +p[6];
                            continue;
                        }
                        if (!istotal && !subpath) {
                            point = getPointAtSegmentLength(x, y, p[1], p[2], p[3], p[4], p[5], p[6], length - len);
                            return { x: point.x, y: point.y, alpha: point.alpha };
                        }
                    }
                    len += l;
                    x = +p[5];
                    y = +p[6];
                }
                sp += p.shift() + p;
            }
            subpaths.end = sp;
            point = istotal ? len : subpath ? subpaths : R.findDotsAtSegment(x, y, p[0], p[1], p[2], p[3], p[4], p[5], 1);
            point.alpha && (point = { x: point.x, y: point.y, alpha: point.alpha });
            return point;
        };
    };
    var getTotalLength = getLengthFactory(1),
        getPointAtLength = getLengthFactory(),
        getSubpathsAtLength = getLengthFactory(0, 1);

    R.getTotalLength = getTotalLength;

    R.getPointAtLength = getPointAtLength;

    R.getSubpath = function (path, from, to) {
        if (this.getTotalLength(path) - to < 1e-6) {
            return getSubpathsAtLength(path, from).end;
        }
        var a = getSubpathsAtLength(path, to, 1);
        return from ? getSubpathsAtLength(a, from).end : a;
    };

    elproto.getTotalLength = function () {
        if (this.type != "path") { return; }
        if (this.node.getTotalLength) {
            return this.node.getTotalLength();
        }
        return getTotalLength(this.attrs.path);
    };

    elproto.getPointAtLength = function (length) {
        if (this.type != "path") { return; }
        return getPointAtLength(this.attrs.path, length);
    };

    elproto.getSubpath = function (from, to) {
        if (this.type != "path") { return; }
        return R.getSubpath(this.attrs.path, from, to);
    };

    var ef = R.easing_formulas = {
        linear: function (n) {
            return n;
        },
        "<": function (n) {
            return pow(n, 1.7);
        },
        ">": function (n) {
            return pow(n, .48);
        },
        "<>": function (n) {
            var q = .48 - n / 1.04,
                Q = math.sqrt(.1734 + q * q),
                x = Q - q,
                X = pow(abs(x), 1 / 3) * (x < 0 ? -1 : 1),
                y = -Q - q,
                Y = pow(abs(y), 1 / 3) * (y < 0 ? -1 : 1),
                t = X + Y + .5;
            return (1 - t) * 3 * t * t + t * t * t;
        },
        backIn: function (n) {
            var s = 1.70158;
            return n * n * ((s + 1) * n - s);
        },
        backOut: function (n) {
            n = n - 1;
            var s = 1.70158;
            return n * n * ((s + 1) * n + s) + 1;
        },
        elastic: function (n) {
            if (n == !!n) {
                return n;
            }
            return pow(2, -10 * n) * math.sin((n - .075) * (2 * PI) / .3) + 1;
        },
        bounce: function (n) {
            var s = 7.5625,
                p = 2.75,
                l;
            if (n < (1 / p)) {
                l = s * n * n;
            } else {
                if (n < (2 / p)) {
                    n -= (1.5 / p);
                    l = s * n * n + .75;
                } else {
                    if (n < (2.5 / p)) {
                        n -= (2.25 / p);
                        l = s * n * n + .9375;
                    } else {
                        n -= (2.625 / p);
                        l = s * n * n + .984375;
                    }
                }
            }
            return l;
        }
    };
    ef.easeIn = ef["ease-in"] = ef["<"];
    ef.easeOut = ef["ease-out"] = ef[">"];
    ef.easeInOut = ef["ease-in-out"] = ef["<>"];
    ef["back-in"] = ef.backIn;
    ef["back-out"] = ef.backOut;

    var animationElements = [],
        requestAnimFrame = window.requestAnimationFrame ||
                           window.webkitRequestAnimationFrame ||
                           window.mozRequestAnimationFrame ||
                           window.oRequestAnimationFrame ||
                           window.msRequestAnimationFrame ||
                           function (callback) {
                               setTimeout(callback, 16);
                           },
        animation = function () {
            var Now = +new Date,
                l = 0;
            for (; l < animationElements.length; l++) {
                var e = animationElements[l];
                if (e.el.removed || e.paused) {
                    continue;
                }
                var time = Now - e.start,
                    ms = e.ms,
                    easing = e.easing,
                    from = e.from,
                    diff = e.diff,
                    to = e.to,
                    t = e.t,
                    that = e.el,
                    set = {},
                    now,
                    init = {},
                    key;
                if (e.initstatus) {
                    time = (e.initstatus * e.anim.top - e.prev) / (e.percent - e.prev) * ms;
                    e.status = e.initstatus;
                    delete e.initstatus;
                    e.stop && animationElements.splice(l--, 1);
                } else {
                    e.status = (e.prev + (e.percent - e.prev) * (time / ms)) / e.anim.top;
                }
                if (time < 0) {
                    continue;
                }
                if (time < ms) {
                    var pos = easing(time / ms);
                    for (var attr in from) if (from[has](attr)) {
                        switch (availableAnimAttrs[attr]) {
                            case nu:
                                now = +from[attr] + pos * ms * diff[attr];
                                break;
                            case "colour":
                                now = "rgb(" + [
                                    upto255(round(from[attr].r + pos * ms * diff[attr].r)),
                                    upto255(round(from[attr].g + pos * ms * diff[attr].g)),
                                    upto255(round(from[attr].b + pos * ms * diff[attr].b))
                                ].join(",") + ")";
                                break;
                            case "path":
                                now = [];
                                for (var i = 0, ii = from[attr].length; i < ii; i++) {
                                    now[i] = [from[attr][i][0]];
                                    for (var j = 1, jj = from[attr][i].length; j < jj; j++) {
                                        now[i][j] = +from[attr][i][j] + pos * ms * diff[attr][i][j];
                                    }
                                    now[i] = now[i].join(S);
                                }
                                now = now.join(S);
                                break;
                            case "transform":
                                if (diff[attr].real) {
                                    now = [];
                                    for (i = 0, ii = from[attr].length; i < ii; i++) {
                                        now[i] = [from[attr][i][0]];
                                        for (j = 1, jj = from[attr][i].length; j < jj; j++) {
                                            now[i][j] = from[attr][i][j] + pos * ms * diff[attr][i][j];
                                        }
                                    }
                                } else {
                                    var get = function (i) {
                                        return +from[attr][i] + pos * ms * diff[attr][i];
                                    };
                                    // now = [["r", get(2), 0, 0], ["t", get(3), get(4)], ["s", get(0), get(1), 0, 0]];
                                    now = [["m", get(0), get(1), get(2), get(3), get(4), get(5)]];
                                }
                                break;
                            case "csv":
                                if (attr == "clip-rect") {
                                    now = [];
                                    i = 4;
                                    while (i--) {
                                        now[i] = +from[attr][i] + pos * ms * diff[attr][i];
                                    }
                                }
                                break;
                            default:
                                var from2 = [][concat](from[attr]);
                                now = [];
                                i = that.paper.customAttributes[attr].length;
                                while (i--) {
                                    now[i] = +from2[i] + pos * ms * diff[attr][i];
                                }
                                break;
                        }
                        set[attr] = now;
                    }
                    that.attr(set);
                    (function (id, that, anim) {
                        setTimeout(function () {
                            eve("anim.frame." + id, that, anim);
                        });
                    })(that.id, that, e.anim);
                } else {
                    (function (f, el, a) {
                        setTimeout(function () {
                            eve("anim.frame." + el.id, el, a);
                            eve("anim.finish." + el.id, el, a);
                            R.is(f, "function") && f.call(el);
                        });
                    })(e.callback, that, e.anim);
                    that.attr(to);
                    animationElements.splice(l--, 1);
                    if (e.repeat > 1 && !e.next) {
                        for (key in to) if (to[has](key)) {
                            init[key] = e.totalOrigin[key];
                        }
                        e.el.attr(init);
                        runAnimation(e.anim, e.el, e.anim.percents[0], null, e.totalOrigin, e.repeat - 1);
                    }
                    if (e.next && !e.stop) {
                        runAnimation(e.anim, e.el, e.next, null, e.totalOrigin, e.repeat);
                    }
                }
            }
            R.svg && that && that.paper && that.paper.safari();
            animationElements.length && requestAnimFrame(animation);
        },
        upto255 = function (color) {
            return color > 255 ? 255 : color < 0 ? 0 : color;
        };

    elproto.animateWith = function (element, anim, params, ms, easing, callback) {
        var a = params ? R.animation(params, ms, easing, callback) : anim;
        status = element.status(anim);
        return this.animate(a).status(a, status * anim.ms / a.ms);
    };
    function CubicBezierAtTime(t, p1x, p1y, p2x, p2y, duration) {
        var cx = 3 * p1x,
            bx = 3 * (p2x - p1x) - cx,
            ax = 1 - cx - bx,
            cy = 3 * p1y,
            by = 3 * (p2y - p1y) - cy,
            ay = 1 - cy - by;
        function sampleCurveX(t) {
            return ((ax * t + bx) * t + cx) * t;
        }
        function solve(x, epsilon) {
            var t = solveCurveX(x, epsilon);
            return ((ay * t + by) * t + cy) * t;
        }
        function solveCurveX(x, epsilon) {
            var t0, t1, t2, x2, d2, i;
            for (t2 = x, i = 0; i < 8; i++) {
                x2 = sampleCurveX(t2) - x;
                if (abs(x2) < epsilon) {
                    return t2;
                }
                d2 = (3 * ax * t2 + 2 * bx) * t2 + cx;
                if (abs(d2) < 1e-6) {
                    break;
                }
                t2 = t2 - x2 / d2;
            }
            t0 = 0;
            t1 = 1;
            t2 = x;
            if (t2 < t0) {
                return t0;
            }
            if (t2 > t1) {
                return t1;
            }
            while (t0 < t1) {
                x2 = sampleCurveX(t2);
                if (abs(x2 - x) < epsilon) {
                    return t2;
                }
                if (x > x2) {
                    t0 = t2;
                } else {
                    t1 = t2;
                }
                t2 = (t1 - t0) / 2 + t0;
            }
            return t2;
        }
        return solve(t, 1 / (200 * duration));
    }
    elproto.onAnimation = function (f) {
        f ? eve.on("anim.frame." + this.id, f) : eve.unbind("anim.frame." + this.id);
        return this;
    };
    function Animation(anim, ms) {
        var percents = [],
            newAnim = {};
        this.ms = ms;
        this.times = 1;
        if (anim) {
            for (var attr in anim) if (anim[has](attr)) {
                newAnim[toFloat(attr)] = anim[attr];
                percents.push(toFloat(attr));
            }
            percents.sort(sortByNumber);
        }
        this.anim = newAnim;
        this.top = percents[percents.length - 1];
        this.percents = percents;
    }

    Animation.prototype.delay = function (delay) {
        var a = new Animation(this.anim, this.ms);
        a.times = this.times;
        a.del = +delay || 0;
        return a;
    };

    Animation.prototype.repeat = function (times) {
        var a = new Animation(this.anim, this.ms);
        a.del = this.del;
        a.times = math.floor(mmax(times, 0)) || 1;
        return a;
    };
    function runAnimation(anim, element, percent, status, totalOrigin, times) {
        percent = toFloat(percent);
        var params,
            isInAnim,
            isInAnimSet,
            percents = [],
            next,
            prev,
            timestamp,
            ms = anim.ms,
            from = {},
            to = {},
            diff = {};
        if (status) {
            for (i = 0, ii = animationElements.length; i < ii; i++) {
                var e = animationElements[i];
                if (e.el.id == element.id && e.anim == anim) {
                    if (e.percent != percent) {
                        animationElements.splice(i, 1);
                        isInAnimSet = 1;
                    } else {
                        isInAnim = e;
                    }
                    element.attr(e.totalOrigin);
                    break;
                }
            }
        } else {
            status = +to; // NaN
        }
        for (var i = 0, ii = anim.percents.length; i < ii; i++) {
            if (anim.percents[i] == percent || anim.percents[i] > status * anim.top) {
                percent = anim.percents[i];
                prev = anim.percents[i - 1] || 0;
                ms = ms / anim.top * (percent - prev);
                next = anim.percents[i + 1];
                params = anim.anim[percent];
                break;
            } else if (status) {
                element.attr(anim.anim[anim.percents[i]]);
            }
        }
        if (!params) {
            return;
        }
        if (!isInAnim) {
            for (attr in params) if (params[has](attr)) {
                if (availableAnimAttrs[has](attr) || element.paper.customAttributes[has](attr)) {
                    from[attr] = element.attr(attr);
                    (from[attr] == null) && (from[attr] = availableAttrs[attr]);
                    to[attr] = params[attr];
                    switch (availableAnimAttrs[attr]) {
                        case nu:
                            diff[attr] = (to[attr] - from[attr]) / ms;
                            break;
                        case "colour":
                            from[attr] = R.getRGB(from[attr]);
                            var toColour = R.getRGB(to[attr]);
                            diff[attr] = {
                                r: (toColour.r - from[attr].r) / ms,
                                g: (toColour.g - from[attr].g) / ms,
                                b: (toColour.b - from[attr].b) / ms
                            };
                            break;
                        case "path":
                            var pathes = path2curve(from[attr], to[attr]),
                                toPath = pathes[1];
                            from[attr] = pathes[0];
                            diff[attr] = [];
                            for (i = 0, ii = from[attr].length; i < ii; i++) {
                                diff[attr][i] = [0];
                                for (var j = 1, jj = from[attr][i].length; j < jj; j++) {
                                    diff[attr][i][j] = (toPath[i][j] - from[attr][i][j]) / ms;
                                }
                            }
                            break;
                        case "transform":
                            var _ = element._,
                                eq = equaliseTransform(_[attr], to[attr]);
                            if (eq) {
                                from[attr] = eq.from;
                                to[attr] = eq.to;
                                diff[attr] = [];
                                diff[attr].real = true;
                                for (i = 0, ii = from[attr].length; i < ii; i++) {
                                    diff[attr][i] = [from[attr][i][0]];
                                    for (j = 1, jj = from[attr][i].length; j < jj; j++) {
                                        diff[attr][i][j] = (to[attr][i][j] - from[attr][i][j]) / ms;
                                    }
                                }
                            } else {
                                var m = (element.matrix || new Matrix),
                                    to2 = {
                                        _: { transform: _.transform },
                                        getBBox: function () {
                                            return element.getBBox(1);
                                        }
                                    };
                                from[attr] = [
                                    m.a,
                                    m.b,
                                    m.c,
                                    m.d,
                                    m.e,
                                    m.f
                                ];
                                extractTransform(to2, to[attr]);
                                to[attr] = to2._.transform;
                                diff[attr] = [
                                    (to2.matrix.a - m.a) / ms,
                                    (to2.matrix.b - m.b) / ms,
                                    (to2.matrix.c - m.c) / ms,
                                    (to2.matrix.d - m.d) / ms,
                                    (to2.matrix.e - m.e) / ms,
                                    (to2.matrix.e - m.f) / ms
                                ];
                                // from[attr] = [_.sx, _.sy, _.deg, _.dx, _.dy];
                                // var to2 = {_:{}, getBBox: function () { return element.getBBox(); }};
                                // extractTransform(to2, to[attr]);
                                // diff[attr] = [
                                //     (to2._.sx - _.sx) / ms,
                                //     (to2._.sy - _.sy) / ms,
                                //     (to2._.deg - _.deg) / ms,
                                //     (to2._.dx - _.dx) / ms,
                                //     (to2._.dy - _.dy) / ms
                                // ];
                            }
                            break;
                        case "csv":
                            var values = Str(params[attr])[split](separator),
                                from2 = Str(from[attr])[split](separator);
                            if (attr == "clip-rect") {
                                from[attr] = from2;
                                diff[attr] = [];
                                i = from2.length;
                                while (i--) {
                                    diff[attr][i] = (values[i] - from[attr][i]) / ms;
                                }
                            }
                            to[attr] = values;
                            break;
                        default:
                            values = [][concat](params[attr]);
                            from2 = [][concat](from[attr]);
                            diff[attr] = [];
                            i = element.paper.customAttributes[attr].length;
                            while (i--) {
                                diff[attr][i] = ((values[i] || 0) - (from2[i] || 0)) / ms;
                            }
                            break;
                    }
                }
            }
            var easing = params.easing,
                easyeasy = R.easing_formulas[easing];
            if (!easyeasy) {
                easyeasy = Str(easing).match(bezierrg);
                if (easyeasy && easyeasy.length == 5) {
                    var curve = easyeasy;
                    easyeasy = function (t) {
                        return CubicBezierAtTime(t, +curve[1], +curve[2], +curve[3], +curve[4], ms);
                    };
                } else {
                    easyeasy = pipe;
                }
            }
            timestamp = params.start || anim.start || +new Date;
            e = {
                anim: anim,
                percent: percent,
                timestamp: timestamp,
                start: timestamp + (anim.del || 0),
                status: 0,
                initstatus: status || 0,
                stop: false,
                ms: ms,
                easing: easyeasy,
                from: from,
                diff: diff,
                to: to,
                el: element,
                callback: params.callback,
                prev: prev,
                next: next,
                repeat: times || anim.times,
                origin: element.attr(),
                totalOrigin: totalOrigin
            };
            animationElements.push(e);
            if (status && !isInAnim && !isInAnimSet) {
                e.stop = true;
                e.start = new Date - ms * status;
                if (animationElements.length == 1) {
                    return animation();
                }
            }
            if (isInAnimSet) {
                e.start = new Date - e.ms * status;
            }
            animationElements.length == 1 && requestAnimFrame(animation);
        } else {
            isInAnim.initstatus = status;
            isInAnim.start = new Date - isInAnim.ms * status;
        }
        eve("anim.start." + element.id, element, anim);
    }

    R.animation = function (params, ms, easing, callback) {
        if (params instanceof Animation) {
            return params;
        }
        if (R.is(easing, "function") || !easing) {
            callback = callback || easing || null;
            easing = null;
        }
        params = Object(params);
        ms = +ms || 0;
        var p = {},
            json,
            attr;
        for (attr in params) if (params[has](attr) && toFloat(attr) != attr && toFloat(attr) + "%" != attr) {
            json = true;
            p[attr] = params[attr];
        }
        if (!json) {
            return new Animation(params, ms);
        } else {
            easing && (p.easing = easing);
            callback && (p.callback = callback);
            return new Animation({ 100: p }, ms);
        }
    };

    elproto.animate = function (params, ms, easing, callback) {
        var element = this;
        if (element.removed) {
            callback && callback.call(element);
            return element;
        }
        var anim = params instanceof Animation ? params : R.animation(params, ms, easing, callback);
        runAnimation(anim, element, anim.percents[0], null, element.attr());
        return element;
    };

    elproto.setTime = function (anim, value) {
        if (anim && value != null) {
            this.status(anim, mmin(value, anim.ms) / anim.ms);
        }
        return this;
    };

    elproto.status = function (anim, value) {
        var out = [],
            i = 0,
            len,
            e;
        if (value != null) {
            runAnimation(anim, this, -1, mmin(value, 1));
            return this;
        } else {
            len = animationElements.length;
            for (; i < len; i++) {
                e = animationElements[i];
                if (e.el.id == this.id && (!anim || e.anim == anim)) {
                    if (anim) {
                        return e.status;
                    }
                    out.push({
                        anim: e.anim,
                        status: e.status
                    });
                }
            }
            if (anim) {
                return 0;
            }
            return out;
        }
    };

    elproto.pause = function (anim) {
        for (var i = 0; i < animationElements.length; i++) if (animationElements[i].el.id == this.id && (!anim || animationElements[i].anim == anim)) {
            if (eve("anim.pause." + this.id, this, animationElements[i].anim) !== false) {
                animationElements[i].paused = true;
            }
        }
        return this;
    };

    elproto.resume = function (anim) {
        for (var i = 0; i < animationElements.length; i++) if (animationElements[i].el.id == this.id && (!anim || animationElements[i].anim == anim)) {
            var e = animationElements[i];
            if (eve("anim.resume." + this.id, this, e.anim) !== false) {
                delete e.paused;
                this.status(e.anim, e.status);
            }
        }
        return this;
    };

    elproto.stop = function (anim) {
        for (var i = 0; i < animationElements.length; i++) if (animationElements[i].el.id == this.id && (!anim || animationElements[i].anim == anim)) {
            if (eve("anim.stop." + this.id, this, animationElements[i].anim) !== false) {
                animationElements.splice(i--, 1);
            }
        }
        return this;
    };
    elproto.toString = function () {
        return "Rapha\xebl\u2019s object";
    };

    // Set
    var Set = function (items) {
        this.items = [];
        this.length = 0;
        this.type = "set";
        if (items) {
            for (var i = 0, ii = items.length; i < ii; i++) {
                if (items[i] && (items[i].constructor == elproto.constructor || items[i].constructor == Set)) {
                    this[this.items.length] = this.items[this.items.length] = items[i];
                    this.length++;
                }
            }
        }
    },
    setproto = Set.prototype;

    setproto.push = function () {
        var item,
            len;
        for (var i = 0, ii = arguments.length; i < ii; i++) {
            item = arguments[i];
            if (item && (item.constructor == elproto.constructor || item.constructor == Set)) {
                len = this.items.length;
                this[len] = this.items[len] = item;
                this.length++;
            }
        }
        return this;
    };

    setproto.pop = function () {
        this.length && delete this[this.length--];
        return this.items.pop();
    };

    setproto.forEach = function (callback, thisArg) {
        for (var i = 0, ii = this.items.length; i < ii; i++) {
            if (callback.call(thisArg, this.items[i], i) === false) {
                return this;
            }
        }
        return this;
    };
    for (var method in elproto) if (elproto[has](method)) {
        setproto[method] = (function (methodname) {
            return function () {
                var arg = arguments;
                return this.forEach(function (el) {
                    el[methodname][apply](el, arg);
                });
            };
        })(method);
    }
    setproto.attr = function (name, value) {
        if (name && R.is(name, array) && R.is(name[0], "object")) {
            for (var j = 0, jj = name.length; j < jj; j++) {
                this.items[j].attr(name[j]);
            }
        } else {
            for (var i = 0, ii = this.items.length; i < ii; i++) {
                this.items[i].attr(name, value);
            }
        }
        return this;
    };

    setproto.clear = function () {
        while (this.length) {
            this.pop();
        }
    };

    setproto.splice = function (index, count, insertion) {
        index = index < 0 ? mmax(this.length + index, 0) : index;
        count = mmax(0, mmin(this.length - index, count));
        var tail = [],
            todel = [],
            args = [],
            i;
        for (i = 2; i < arguments.length; i++) {
            args.push(arguments[i]);
        }
        for (i = 0; i < count; i++) {
            todel.push(this[index + i]);
        }
        for (; i < this.length - index; i++) {
            tail.push(this[index + i]);
        }
        var arglen = args.length;
        for (i = 0; i < arglen + tail.length; i++) {
            this.items[index + i] = this[index + i] = i < arglen ? args[i] : tail[i - arglen];
        }
        i = this.items.length = this.length -= count - arglen;
        while (this[i]) {
            delete this[i++];
        }
        return new Set(todel);
    };

    setproto.exclude = function (el) {
        for (var i = 0, ii = this.length; i < ii; i++) if (this[i] == el) {
            this.splice(i, 1);
            return true;
        }
    };
    setproto.animate = function (params, ms, easing, callback) {
        (R.is(easing, "function") || !easing) && (callback = easing || null);
        var len = this.items.length,
            i = len,
            item,
            set = this,
            collector;
        if (!len) {
            return this;
        }
        callback && (collector = function () {
            ! --len && callback.call(set);
        });
        easing = R.is(easing, string) ? easing : collector;
        var anim = R.animation(params, ms, easing, collector);
        item = this.items[--i].animate(anim);
        while (i--) {
            this.items[i] && !this.items[i].removed && this.items[i].animateWith(item, anim);
        }
        return this;
    };
    setproto.insertAfter = function (el) {
        var i = this.items.length;
        while (i--) {
            this.items[i].insertAfter(el);
        }
        return this;
    };
    setproto.getBBox = function () {
        var x = [],
            y = [],
            w = [],
            h = [];
        for (var i = this.items.length; i--; ) if (!this.items[i].removed) {
            var box = this.items[i].getBBox();
            x.push(box.x);
            y.push(box.y);
            w.push(box.x + box.width);
            h.push(box.y + box.height);
        }
        x = mmin[apply](0, x);
        y = mmin[apply](0, y);
        return {
            x: x,
            y: y,
            width: mmax[apply](0, w) - x,
            height: mmax[apply](0, h) - y
        };
    };
    setproto.clone = function (s) {
        s = new Set;
        for (var i = 0, ii = this.items.length; i < ii; i++) {
            s.push(this.items[i].clone());
        }
        return s;
    };
    setproto.toString = function () {
        return "Rapha\xebl\u2018s set";
    };


    R.registerFont = function (font) {
        if (!font.face) {
            return font;
        }
        this.fonts = this.fonts || {};
        var fontcopy = {
            w: font.w,
            face: {},
            glyphs: {}
        },
            family = font.face["font-family"];
        for (var prop in font.face) if (font.face[has](prop)) {
            fontcopy.face[prop] = font.face[prop];
        }
        if (this.fonts[family]) {
            this.fonts[family].push(fontcopy);
        } else {
            this.fonts[family] = [fontcopy];
        }
        if (!font.svg) {
            fontcopy.face["units-per-em"] = toInt(font.face["units-per-em"], 10);
            for (var glyph in font.glyphs) if (font.glyphs[has](glyph)) {
                var path = font.glyphs[glyph];
                fontcopy.glyphs[glyph] = {
                    w: path.w,
                    k: {},
                    d: path.d && "M" + path.d.replace(/[mlcxtrv]/g, function (command) {
                        return { l: "L", c: "C", x: "z", t: "m", r: "l", v: "c"}[command] || "M";
                    }) + "z"
                };
                if (path.k) {
                    for (var k in path.k) if (path[has](k)) {
                        fontcopy.glyphs[glyph].k[k] = path.k[k];
                    }
                }
            }
        }
        return font;
    };

    paperproto.getFont = function (family, weight, style, stretch) {
        stretch = stretch || "normal";
        style = style || "normal";
        weight = +weight || { normal: 400, bold: 700, lighter: 300, bolder: 800}[weight] || 400;
        if (!R.fonts) {
            return;
        }
        var font = R.fonts[family];
        if (!font) {
            var name = new RegExp("(^|\\s)" + family.replace(/[^\w\d\s+!~.:_-]/g, E) + "(\\s|$)", "i");
            for (var fontName in R.fonts) if (R.fonts[has](fontName)) {
                if (name.test(fontName)) {
                    font = R.fonts[fontName];
                    break;
                }
            }
        }
        var thefont;
        if (font) {
            for (var i = 0, ii = font.length; i < ii; i++) {
                thefont = font[i];
                if (thefont.face["font-weight"] == weight && (thefont.face["font-style"] == style || !thefont.face["font-style"]) && thefont.face["font-stretch"] == stretch) {
                    break;
                }
            }
        }
        return thefont;
    };

    paperproto.print = function (x, y, string, font, size, origin, letter_spacing) {
        origin = origin || "middle"; // baseline|middle
        letter_spacing = mmax(mmin(letter_spacing || 0, 1), -1);
        var out = this.set(),
            letters = Str(string)[split](E),
            shift = 0,
            path = E,
            scale;
        R.is(font, string) && (font = this.getFont(font));
        if (font) {
            scale = (size || 16) / font.face["units-per-em"];
            var bb = font.face.bbox[split](separator),
                top = +bb[0],
                height = +bb[1] + (origin == "baseline" ? bb[3] - bb[1] + (+font.face.descent) : (bb[3] - bb[1]) / 2);
            for (var i = 0, ii = letters.length; i < ii; i++) {
                var prev = i && font.glyphs[letters[i - 1]] || {},
                    curr = font.glyphs[letters[i]];
                shift += i ? (prev.w || font.w) + (prev.k && prev.k[letters[i]] || 0) + (font.w * letter_spacing) : 0;
                curr && curr.d && out.push(this.path(curr.d).attr({
                    fill: "#000",
                    stroke: "none",
                    transform: [["t", shift * scale, 0]]
                }));
            }
            out.transform(["...s", scale, scale, top, height, "t", (x - top) / scale, (y - height) / scale]);
        }
        return out;
    };


    R.format = function (token, params) {
        var args = R.is(params, array) ? [0][concat](params) : arguments;
        token && R.is(token, string) && args.length - 1 && (token = token.replace(formatrg, function (str, i) {
            return args[++i] == null ? E : args[i];
        }));
        return token || E;
    };

    R.fullfill = (function () {
        var tokenRegex = /\{([^\}]+)\}/g,
            objNotationRegex = /(?:(?:^|\.)(.+?)(?=\[|\.|$|\()|\[('|")(.+?)\2\])(\(\))?/g, // matches .xxxxx or ["xxxxx"] to run over object properties
            replacer = function (all, key, obj) {
                var res = obj;
                key.replace(objNotationRegex, function (all, name, quote, quotedName, isFunc) {
                    name = name || quotedName;
                    if (res) {
                        if (name in res) {
                            res = res[name];
                        }
                        typeof res == "function" && isFunc && (res = res());
                    }
                });
                res = (res == null || res == obj ? all : res) + "";
                return res;
            };
        return function (str, obj) {
            return String(str).replace(tokenRegex, function (all, key) {
                return replacer(all, key, obj);
            });
        };
    })();

    R.ninja = function () {
        oldRaphael.was ? (g.win.Raphael = oldRaphael.is) : delete Raphael;
        return R;
    };

    R.st = setproto;
    // Firefox <3.6 fix: http://webreflection.blogspot.com/2009/11/195-chars-to-help-lazy-loading.html
    (function (doc, loaded, f) {
        if (doc.readyState == null && doc.addEventListener) {
            doc.addEventListener(loaded, f = function () {
                doc.removeEventListener(loaded, f, false);
                doc.readyState = "complete";
            }, false);
            doc.readyState = "loading";
        }
        function isLoaded() {
            (/in/).test(doc.readyState) ? setTimeout(isLoaded, 9) : R.eve("DOMload");
        }
        isLoaded();
    })(document, "DOMContentLoaded");

    oldRaphael.was ? (g.win.Raphael = R) : (Raphael = R);

    eve.on("DOMload", function () {
        loaded = true;
    });
})();

// ┌─────────────────────────────────────────────────────────────────────┐ \\
// │ Raphaël 2 - JavaScript Vector Library                               │ \\
// ├─────────────────────────────────────────────────────────────────────┤ \\
// │ SVG Module                                                          │ \\
// ├─────────────────────────────────────────────────────────────────────┤ \\
// │ Copyright (c) 2008-2011 Dmitry Baranovskiy (http://raphaeljs.com)   │ \\
// │ Copyright (c) 2008-2011 Sencha Labs (http://sencha.com)             │ \\
// │ Licensed under the MIT (http://raphaeljs.com/license.html) license. │ \\
// └─────────────────────────────────────────────────────────────────────┘ \\
window.Raphael.svg && function (R) {
    var has = "hasOwnProperty",
        Str = String,
        toFloat = parseFloat,
        toInt = parseInt,
        math = Math,
        mmax = math.max,
        abs = math.abs,
        pow = math.pow,
        separator = /[, ]+/,
        eve = R.eve,
        E = "",
        S = " ";
    var xlink = "http://www.w3.org/1999/xlink",
        markers = {
            block: "M5,0 0,2.5 5,5z",
            classic: "M5,0 0,2.5 5,5 3.5,3 3.5,2z",
            diamond: "M2.5,0 5,2.5 2.5,5 0,2.5z",
            open: "M6,1 1,3.5 6,6",
            oval: "M2.5,0A2.5,2.5,0,0,1,2.5,5 2.5,2.5,0,0,1,2.5,0z"
        },
        markerCounter = {};
    R.toString = function () {
        return "Your browser supports SVG.\nYou are running Rapha\xebl " + this.version;
    };
    var $ = function (el, attr) {
        if (attr) {
            if (typeof el == "string") {
                el = $(el);
            }
            for (var key in attr) if (attr[has](key)) {
                if (key.substring(0, 6) == "xlink:") {
                    el.setAttributeNS(xlink, key.substring(6), Str(attr[key]));
                } else {
                    el.setAttribute(key, Str(attr[key]));
                }
            }
        } else {
            el = R._g.doc.createElementNS("http://www.w3.org/2000/svg", el);
            el.style && (el.style.webkitTapHighlightColor = "rgba(0,0,0,0)");
        }
        return el;
    },
    gradients = {},
    rgGrad = /^url\(#(.*)\)$/,
    removeGradientFill = function (node, paper) {
        var oid = node.getAttribute("fill");
        oid = oid && oid.match(rgGrad);
        if (oid && ! --gradients[oid[1]]) {
            delete gradients[oid[1]];
            paper.defs.removeChild(R._g.doc.getElementById(oid[1]));
        }
    },
    addGradientFill = function (element, gradient) {
        var type = "linear",
            id = element.id + gradient,
            fx = .5, fy = .5,
            o = element.node,
            SVG = element.paper,
            s = o.style,
            el = R._g.doc.getElementById(id);
        if (!el) {
            gradient = Str(gradient).replace(R._radial_gradient, function (all, _fx, _fy) {
                type = "radial";
                if (_fx && _fy) {
                    fx = toFloat(_fx);
                    fy = toFloat(_fy);
                    var dir = ((fy > .5) * 2 - 1);
                    pow(fx - .5, 2) + pow(fy - .5, 2) > .25 &&
                        (fy = math.sqrt(.25 - pow(fx - .5, 2)) * dir + .5) &&
                        fy != .5 &&
                        (fy = fy.toFixed(5) - 1e-5 * dir);
                }
                return E;
            });
            gradient = gradient.split(/\s*\-\s*/);
            if (type == "linear") {
                var angle = gradient.shift();
                angle = -toFloat(angle);
                if (isNaN(angle)) {
                    return null;
                }
                var vector = [0, 0, math.cos(R.rad(angle)), math.sin(R.rad(angle))],
                    max = 1 / (mmax(abs(vector[2]), abs(vector[3])) || 1);
                vector[2] *= max;
                vector[3] *= max;
                if (vector[2] < 0) {
                    vector[0] = -vector[2];
                    vector[2] = 0;
                }
                if (vector[3] < 0) {
                    vector[1] = -vector[3];
                    vector[3] = 0;
                }
            }
            var dots = R._parseDots(gradient);
            if (!dots) {
                return null;
            }
            if (element.gradient) {
                SVG.defs.removeChild(element.gradient);
                delete element.gradient;
            }

            id = id.replace(/[\(\)\s,\xb0#]/g, "-");
            el = $(type + "Gradient", { id: id });
            element.gradient = el;
            $(el, type == "radial" ? {
                fx: fx,
                fy: fy
            } : {
                x1: vector[0],
                y1: vector[1],
                x2: vector[2],
                y2: vector[3],
                gradientTransform: element.matrix.invert()
            });
            SVG.defs.appendChild(el);
            for (var i = 0, ii = dots.length; i < ii; i++) {
                el.appendChild($("stop", {
                    offset: dots[i].offset ? dots[i].offset : i ? "100%" : "0%",
                    "stop-color": dots[i].color || "#fff"
                }));
            }
        }
        $(o, {
            fill: "url(#" + id + ")",
            opacity: 1,
            "fill-opacity": 1
        });
        s.fill = E;
        s.opacity = 1;
        s.fillOpacity = 1;
        return 1;
    },
    updatePosition = function (o) {
        var bbox = o.getBBox(1);
        $(o.pattern, { patternTransform: o.matrix.invert() + " translate(" + bbox.x + "," + bbox.y + ")" });
    },
    addArrow = function (o, value, isEnd) {
        if (o.type == "path") {
            var values = Str(value).toLowerCase().split("-"),
                p = o.paper,
                se = isEnd ? "end" : "start",
                node = o.node,
                attrs = o.attrs,
                stroke = attrs["stroke-width"],
                i = values.length,
                type = "classic",
                from,
                to,
                dx,
                refX,
                attr,
                w = 3,
                h = 3,
                t = 5;
            while (i--) {
                switch (values[i]) {
                    case "block":
                    case "classic":
                    case "oval":
                    case "diamond":
                    case "open":
                    case "none":
                        type = values[i];
                        break;
                    case "wide": h = 5; break;
                    case "narrow": h = 2; break;
                    case "long": w = 5; break;
                    case "short": w = 2; break;
                }
            }
            if (type == "open") {
                w += 2;
                h += 2;
                t += 2;
                dx = 1;
                refX = isEnd ? 4 : 1;
                attr = {
                    fill: "none",
                    stroke: attrs.stroke
                };
            } else {
                refX = dx = w / 2;
                attr = {
                    fill: attrs.stroke,
                    stroke: "none"
                };
            }
            if (o._.arrows) {
                if (isEnd) {
                    o._.arrows.endPath && markerCounter[o._.arrows.endPath]--;
                    o._.arrows.endMarker && markerCounter[o._.arrows.endMarker]--;
                } else {
                    o._.arrows.startPath && markerCounter[o._.arrows.startPath]--;
                    o._.arrows.startMarker && markerCounter[o._.arrows.startMarker]--;
                }
            } else {
                o._.arrows = {};
            }
            if (type != "none") {
                var pathId = "raphael-marker-" + type,
                    markerId = "raphael-marker-" + se + type + w + h;
                if (!R._g.doc.getElementById(pathId)) {
                    p.defs.appendChild($($("path"), {
                        "stroke-linecap": "round",
                        d: markers[type],
                        id: pathId
                    }));
                    markerCounter[pathId] = 1;
                } else {
                    markerCounter[pathId]++;
                }
                var marker = R._g.doc.getElementById(markerId),
                    use;
                if (!marker) {
                    marker = $($("marker"), {
                        id: markerId,
                        markerHeight: h,
                        markerWidth: w,
                        orient: "auto",
                        refX: refX,
                        refY: h / 2
                    });
                    use = $($("use"), {
                        "xlink:href": "#" + pathId,
                        transform: (isEnd ? " rotate(180 " + w / 2 + " " + h / 2 + ") " : S) + "scale(" + w / t + "," + h / t + ")",
                        "stroke-width": 1 / ((w / t + h / t) / 2)
                    });
                    marker.appendChild(use);
                    p.defs.appendChild(marker);
                    markerCounter[markerId] = 1;
                } else {
                    markerCounter[markerId]++;
                    use = marker.getElementsByTagName("use")[0];
                }
                $(use, attr);
                var delta = dx * (type != "diamond" && type != "oval");
                if (isEnd) {
                    from = o._.arrows.startdx * stroke || 0;
                    to = R.getTotalLength(attrs.path) - delta * stroke;
                } else {
                    from = delta * stroke;
                    to = R.getTotalLength(attrs.path) - (o._.arrows.enddx * stroke || 0);
                }
                attr = {};
                attr["marker-" + se] = "url(#" + markerId + ")";
                if (to || from) {
                    attr.d = Raphael.getSubpath(attrs.path, from, to);
                }
                $(node, attr);
                o._.arrows[se + "Path"] = pathId;
                o._.arrows[se + "Marker"] = markerId;
                o._.arrows[se + "dx"] = delta;
                o._.arrows[se + "Type"] = type;
                o._.arrows[se + "String"] = value;
            } else {
                if (isEnd) {
                    from = o._.arrows.startdx * stroke || 0;
                    to = R.getTotalLength(attrs.path) - from;
                } else {
                    from = 0;
                    to = R.getTotalLength(attrs.path) - (o._.arrows.enddx * stroke || 0);
                }
                o._.arrows[se + "Path"] && $(node, { d: Raphael.getSubpath(attrs.path, from, to) });
                delete o._.arrows[se + "Path"];
                delete o._.arrows[se + "Marker"];
                delete o._.arrows[se + "dx"];
                delete o._.arrows[se + "Type"];
                delete o._.arrows[se + "String"];
            }
            for (attr in markerCounter) if (markerCounter[has](attr) && !markerCounter[attr]) {
                var item = R._g.doc.getElementById(attr);
                item && item.parentNode.removeChild(item);
            }
        }
    },
    dasharray = {
        "": [0],
        "none": [0],
        "-": [3, 1],
        ".": [1, 1],
        "-.": [3, 1, 1, 1],
        "-..": [3, 1, 1, 1, 1, 1],
        ". ": [1, 3],
        "- ": [4, 3],
        "--": [8, 3],
        "- .": [4, 3, 1, 3],
        "--.": [8, 3, 1, 3],
        "--..": [8, 3, 1, 3, 1, 3]
    },
    addDashes = function (o, value, params) {
        value = dasharray[Str(value).toLowerCase()];
        if (value) {
            var width = o.attrs["stroke-width"] || "1",
                butt = { round: width, square: width, butt: 0}[o.attrs["stroke-linecap"] || params["stroke-linecap"]] || 0,
                dashes = [],
                i = value.length;
            while (i--) {
                dashes[i] = value[i] * width + ((i % 2) ? 1 : -1) * butt;
            }
            $(o.node, { "stroke-dasharray": dashes.join(",") });
        }
    },
    setFillAndStroke = function (o, params) {
        var node = o.node,
            attrs = o.attrs,
            vis = node.style.visibility;
        node.style.visibility = "hidden";
        for (var att in params) {
            if (params[has](att)) {
                if (!R._availableAttrs[has](att)) {
                    continue;
                }
                var value = params[att];
                attrs[att] = value;
                switch (att) {
                    case "blur":
                        o.blur(value);
                        break;
                    case "href":
                    case "title":
                    case "target":
                        var pn = node.parentNode;
                        if (pn.tagName.toLowerCase() != "a") {
                            var hl = $("a");
                            pn.insertBefore(hl, node);
                            hl.appendChild(node);
                            pn = hl;
                        }
                        if (att == "target" && value == "blank") {
                            pn.setAttributeNS(xlink, "show", "new");
                        } else {
                            pn.setAttributeNS(xlink, att, value);
                        }
                        break;
                    case "cursor":
                        node.style.cursor = value;
                        break;
                    case "transform":
                        o.transform(value);
                        break;
                    case "arrow-start":
                        addArrow(o, value);
                        break;
                    case "arrow-end":
                        addArrow(o, value, 1);
                        break;
                    case "clip-rect":
                        var rect = Str(value).split(separator);
                        if (rect.length == 4) {
                            o.clip && o.clip.parentNode.parentNode.removeChild(o.clip.parentNode);
                            var el = $("clipPath"),
                                rc = $("rect");
                            el.id = R.createUUID();
                            $(rc, {
                                x: rect[0],
                                y: rect[1],
                                width: rect[2],
                                height: rect[3]
                            });
                            el.appendChild(rc);
                            o.paper.defs.appendChild(el);
                            $(node, { "clip-path": "url(#" + el.id + ")" });
                            o.clip = rc;
                        }
                        if (!value) {
                            var clip = R._g.doc.getElementById(node.getAttribute("clip-path").replace(/(^url\(#|\)$)/g, E));
                            clip && clip.parentNode.removeChild(clip);
                            $(node, { "clip-path": E });
                            delete o.clip;
                        }
                        break;
                    case "path":
                        if (o.type == "path") {
                            $(node, { d: value ? attrs.path = R._pathToAbsolute(value) : "M0,0" });
                            o._.dirty = 1;
                            if (o._.arrows) {
                                "startString" in o._.arrows && addArrow(o, o._.arrows.startString);
                                "endString" in o._.arrows && addArrow(o, o._.arrows.endString, 1);
                            }
                        }
                        break;
                    case "width":
                        node.setAttribute(att, value);
                        o._.dirty = 1;
                        if (attrs.fx) {
                            att = "x";
                            value = attrs.x;
                        } else {
                            break;
                        }
                    case "x":
                        if (attrs.fx) {
                            value = -attrs.x - (attrs.width || 0);
                        }
                    case "rx":
                        if (att == "rx" && o.type == "rect") {
                            break;
                        }
                    case "cx":
                        node.setAttribute(att, value);
                        o.pattern && updatePosition(o);
                        o._.dirty = 1;
                        break;
                    case "height":
                        node.setAttribute(att, value);
                        o._.dirty = 1;
                        if (attrs.fy) {
                            att = "y";
                            value = attrs.y;
                        } else {
                            break;
                        }
                    case "y":
                        if (attrs.fy) {
                            value = -attrs.y - (attrs.height || 0);
                        }
                    case "ry":
                        if (att == "ry" && o.type == "rect") {
                            break;
                        }
                    case "cy":
                        node.setAttribute(att, value);
                        o.pattern && updatePosition(o);
                        o._.dirty = 1;
                        break;
                    case "r":
                        if (o.type == "rect") {
                            $(node, { rx: value, ry: value });
                        } else {
                            node.setAttribute(att, value);
                        }
                        o._.dirty = 1;
                        break;
                    case "src":
                        if (o.type == "image") {
                            node.setAttributeNS(xlink, "href", value);
                        }
                        break;
                    case "stroke-width":
                        if (o._.sx != 1 || o._.sy != 1) {
                            value /= mmax(abs(o._.sx), abs(o._.sy)) || 1;
                        }
                        if (o.paper._vbSize) {
                            value *= o.paper._vbSize;
                        }
                        node.setAttribute(att, value);
                        if (attrs["stroke-dasharray"]) {
                            addDashes(o, attrs["stroke-dasharray"], params);
                        }
                        if (o._.arrows) {
                            "startString" in o._.arrows && addArrow(o, o._.arrows.startString);
                            "endString" in o._.arrows && addArrow(o, o._.arrows.endString, 1);
                        }
                        break;
                    case "stroke-dasharray":
                        addDashes(o, value, params);
                        break;
                    case "fill":
                        var isURL = Str(value).match(R._ISURL);
                        if (isURL) {
                            el = $("pattern");
                            var ig = $("image");
                            el.id = R.createUUID();
                            $(el, { x: 0, y: 0, patternUnits: "userSpaceOnUse", height: 1, width: 1 });
                            $(ig, { x: 0, y: 0, "xlink:href": isURL[1] });
                            el.appendChild(ig);

                            (function (el) {
                                R._preload(isURL[1], function () {
                                    var w = this.offsetWidth,
                                        h = this.offsetHeight;
                                    $(el, { width: w, height: h });
                                    $(ig, { width: w, height: h });
                                    o.paper.safari();
                                });
                            })(el);
                            o.paper.defs.appendChild(el);
                            node.style.fill = "url(#" + el.id + ")";
                            $(node, { fill: "url(#" + el.id + ")" });
                            o.pattern = el;
                            o.pattern && updatePosition(o);
                            break;
                        }
                        var clr = R.getRGB(value);
                        if (!clr.error) {
                            delete params.gradient;
                            delete attrs.gradient;
                            !R.is(attrs.opacity, "undefined") &&
                                R.is(params.opacity, "undefined") &&
                                $(node, { opacity: attrs.opacity });
                            !R.is(attrs["fill-opacity"], "undefined") &&
                                R.is(params["fill-opacity"], "undefined") &&
                                $(node, { "fill-opacity": attrs["fill-opacity"] });
                        } else if ((o.type == "circle" || o.type == "ellipse" || Str(value).charAt() != "r") && addGradientFill(o, value)) {
                            if ("opacity" in attrs || "fill-opacity" in attrs) {
                                var gradient = R._g.doc.getElementById(node.getAttribute("fill").replace(/^url\(#|\)$/g, E));
                                if (gradient) {
                                    var stops = gradient.getElementsByTagName("stop");
                                    $(stops[stops.length - 1], { "stop-opacity": ("opacity" in attrs ? attrs.opacity : 1) * ("fill-opacity" in attrs ? attrs["fill-opacity"] : 1) });
                                }
                            }
                            attrs.gradient = value;
                            attrs.fill = "none";
                            break;
                        }
                        clr[has]("opacity") && $(node, { "fill-opacity": clr.opacity > 1 ? clr.opacity / 100 : clr.opacity });
                    case "stroke":
                        clr = R.getRGB(value);
                        node.setAttribute(att, clr.hex);
                        att == "stroke" && clr[has]("opacity") && $(node, { "stroke-opacity": clr.opacity > 1 ? clr.opacity / 100 : clr.opacity });
                        if (att == "stroke" && o._.arrows) {
                            "startString" in o._.arrows && addArrow(o, o._.arrows.startString);
                            "endString" in o._.arrows && addArrow(o, o._.arrows.endString, 1);
                        }
                        break;
                    case "gradient":
                        (o.type == "circle" || o.type == "ellipse" || Str(value).charAt() != "r") && addGradientFill(o, value);
                        break;
                    case "opacity":
                        if (attrs.gradient && !attrs[has]("stroke-opacity")) {
                            $(node, { "stroke-opacity": value > 1 ? value / 100 : value });
                        }
                        // fall
                    case "fill-opacity":
                        if (attrs.gradient) {
                            gradient = R._g.doc.getElementById(node.getAttribute("fill").replace(/^url\(#|\)$/g, E));
                            if (gradient) {
                                stops = gradient.getElementsByTagName("stop");
                                $(stops[stops.length - 1], { "stop-opacity": value });
                            }
                            break;
                        }
                    default:
                        att == "font-size" && (value = toInt(value, 10) + "px");
                        var cssrule = att.replace(/(\-.)/g, function (w) {
                            return w.substring(1).toUpperCase();
                        });
                        node.style[cssrule] = value;
                        o._.dirty = 1;
                        node.setAttribute(att, value);
                        break;
                }
            }
        }

        tuneText(o, params);
        node.style.visibility = vis;
    },
    leading = 1.2,
    tuneText = function (el, params) {
        if (el.type != "text" || !(params[has]("text") || params[has]("font") || params[has]("font-size") || params[has]("x") || params[has]("y"))) {
            return;
        }
        var a = el.attrs,
            node = el.node,
            fontSize = node.firstChild ? toInt(R._g.doc.defaultView.getComputedStyle(node.firstChild, E).getPropertyValue("font-size"), 10) : 10;

        if (params[has]("text")) {
            a.text = params.text;
            while (node.firstChild) {
                node.removeChild(node.firstChild);
            }
            var texts = Str(params.text).split("\n"),
                tspans = [],
                tspan;
            for (var i = 0, ii = texts.length; i < ii; i++) {
                tspan = $("tspan");
                i && $(tspan, { dy: fontSize * leading, x: a.x });
                tspan.appendChild(R._g.doc.createTextNode(texts[i]));
                node.appendChild(tspan);
                tspans[i] = tspan;
            }
        } else {
            tspans = node.getElementsByTagName("tspan");
            for (i = 0, ii = tspans.length; i < ii; i++) if (i) {
                $(tspans[i], { dy: fontSize * leading, x: a.x });
            } else {
                $(tspans[0], { dy: 0 });
            }
        }
        $(node, { x: a.x, y: a.y });
        el._.dirty = 1;
        var bb = el._getBBox(),
            dif = a.y - (bb.y + bb.height / 2);
        dif && R.is(dif, "finite") && $(tspans[0], { dy: dif });
    },
    Element = function (node, svg) {
        var X = 0,
            Y = 0;

        this[0] = this.node = node;

        node.raphael = true;

        this.id = R._oid++;
        node.raphaelid = this.id;
        this.matrix = R.matrix();
        this.realPath = null;

        this.paper = svg;
        this.attrs = this.attrs || {};
        this._ = {
            transform: [],
            sx: 1,
            sy: 1,
            deg: 0,
            dx: 0,
            dy: 0,
            dirty: 1
        };
        !svg.bottom && (svg.bottom = this);

        this.prev = svg.top;
        svg.top && (svg.top.next = this);
        svg.top = this;

        this.next = null;
    },
    elproto = R.el;

    Element.prototype = elproto;
    elproto.constructor = Element;

    R._engine.path = function (pathString, SVG) {
        var el = $("path");
        SVG.canvas && SVG.canvas.appendChild(el);
        var p = new Element(el, SVG);
        p.type = "path";
        setFillAndStroke(p, {
            fill: "none",
            stroke: "#000",
            path: pathString
        });
        return p;
    };

    elproto.rotate = function (deg, cx, cy) {
        if (this.removed) {
            return this;
        }
        deg = Str(deg).split(separator);
        if (deg.length - 1) {
            cx = toFloat(deg[1]);
            cy = toFloat(deg[2]);
        }
        deg = toFloat(deg[0]);
        (cy == null) && (cx = cy);
        if (cx == null || cy == null) {
            var bbox = this.getBBox(1);
            cx = bbox.x + bbox.width / 2;
            cy = bbox.y + bbox.height / 2;
        }
        this.transform(this._.transform.concat([["r", deg, cx, cy]]));
        return this;
    };

    elproto.scale = function (sx, sy, cx, cy) {
        if (this.removed) {
            return this;
        }
        sx = Str(sx).split(separator);
        if (sx.length - 1) {
            sy = toFloat(sx[1]);
            cx = toFloat(sx[2]);
            cy = toFloat(sx[3]);
        }
        sx = toFloat(sx[0]);
        (sy == null) && (sy = sx);
        (cy == null) && (cx = cy);
        if (cx == null || cy == null) {
            var bbox = this.getBBox(1);
        }
        cx = cx == null ? bbox.x + bbox.width / 2 : cx;
        cy = cy == null ? bbox.y + bbox.height / 2 : cy;
        this.transform(this._.transform.concat([["s", sx, sy, cx, cy]]));
        return this;
    };

    elproto.translate = function (dx, dy) {
        if (this.removed) {
            return this;
        }
        dx = Str(dx).split(separator);
        if (dx.length - 1) {
            dy = toFloat(dx[1]);
        }
        dx = toFloat(dx[0]) || 0;
        dy = +dy || 0;
        this.transform(this._.transform.concat([["t", dx, dy]]));
        return this;
    };

    elproto.transform = function (tstr) {
        var _ = this._;
        if (tstr == null) {
            return _.transform;
        }
        R._extractTransform(this, tstr);

        this.clip && $(this.clip, { transform: this.matrix.invert() });
        this.pattern && updatePosition(this);
        this.node && $(this.node, { transform: this.matrix });

        if (_.sx != 1 || _.sy != 1) {
            var sw = this.attrs[has]("stroke-width") ? this.attrs["stroke-width"] : 1;
            this.attr({ "stroke-width": sw });
        }

        return this;
    };

    elproto.hide = function () {
        !this.removed && this.paper.safari(this.node.style.display = "none");
        return this;
    };

    elproto.show = function () {
        !this.removed && this.paper.safari(this.node.style.display = "");
        return this;
    };

    elproto.remove = function () {
        if (this.removed) {
            return;
        }
        this.paper.__set__ && this.paper.__set__.exclude(this);
        eve.unbind("*.*." + this.id);
        R._tear(this, this.paper);
        this.node.parentNode.removeChild(this.node);
        for (var i in this) {
            delete this[i];
        }
        this.removed = true;
    };
    elproto._getBBox = function () {
        if (this.node.style.display == "none") {
            this.show();
            var hide = true;
        }
        var bbox = {};
        try {
            bbox = this.node.getBBox();
        } catch (e) {
            // Firefox 3.0.x plays badly here
        } finally {
            bbox = bbox || {};
        }
        hide && this.hide();
        return bbox;
    };

    elproto.attr = function (name, value) {
        if (this.removed) {
            return this;
        }
        if (name == null) {
            var res = {};
            for (var a in this.attrs) if (this.attrs[has](a)) {
                res[a] = this.attrs[a];
            }
            res.gradient && res.fill == "none" && (res.fill = res.gradient) && delete res.gradient;
            res.transform = this._.transform;
            return res;
        }
        if (value == null && R.is(name, "string")) {
            if (name == "fill" && this.attrs.fill == "none" && this.attrs.gradient) {
                return this.attrs.gradient;
            }
            if (name == "transform") {
                return this._.transform;
            }
            var names = name.split(separator),
                out = {};
            for (var i = 0, ii = names.length; i < ii; i++) {
                name = names[i];
                if (name in this.attrs) {
                    out[name] = this.attrs[name];
                } else if (R.is(this.paper.customAttributes[name], "function")) {
                    out[name] = this.paper.customAttributes[name].def;
                } else {
                    out[name] = R._availableAttrs[name];
                }
            }
            return ii - 1 ? out : out[names[0]];
        }
        if (value == null && R.is(name, "array")) {
            out = {};
            for (i = 0, ii = name.length; i < ii; i++) {
                out[name[i]] = this.attr(name[i]);
            }
            return out;
        }
        if (value != null) {
            var params = {};
            params[name] = value;
        } else if (name != null && R.is(name, "object")) {
            params = name;
        }
        for (var key in params) {
            eve("attr." + key + "." + this.id, this, params[key]);
        }
        for (key in this.paper.customAttributes) if (this.paper.customAttributes[has](key) && params[has](key) && R.is(this.paper.customAttributes[key], "function")) {
            var par = this.paper.customAttributes[key].apply(this, [].concat(params[key]));
            this.attrs[key] = params[key];
            for (var subkey in par) if (par[has](subkey)) {
                params[subkey] = par[subkey];
            }
        }
        setFillAndStroke(this, params);
        return this;
    };

    elproto.toFront = function () {
        if (this.removed) {
            return this;
        }
        this.node.parentNode.appendChild(this.node);
        var svg = this.paper;
        svg.top != this && R._tofront(this, svg);
        return this;
    };

    elproto.toBack = function () {
        if (this.removed) {
            return this;
        }
        if (this.node.parentNode.firstChild != this.node) {
            this.node.parentNode.insertBefore(this.node, this.node.parentNode.firstChild);
            R._toback(this, this.paper);
            var svg = this.paper;
        }
        return this;
    };

    elproto.insertAfter = function (element) {
        if (this.removed) {
            return this;
        }
        var node = element.node || element[element.length - 1].node;
        if (node.nextSibling) {
            node.parentNode.insertBefore(this.node, node.nextSibling);
        } else {
            node.parentNode.appendChild(this.node);
        }
        R._insertafter(this, element, this.paper);
        return this;
    };

    elproto.insertBefore = function (element) {
        if (this.removed) {
            return this;
        }
        var node = element.node || element[0].node;
        node.parentNode.insertBefore(this.node, node);
        R._insertbefore(this, element, this.paper);
        return this;
    };
    elproto.blur = function (size) {
        // Experimental. No Safari support. Use it on your own risk.
        var t = this;
        if (+size !== 0) {
            var fltr = $("filter"),
                blur = $("feGaussianBlur");
            t.attrs.blur = size;
            fltr.id = R.createUUID();
            $(blur, { stdDeviation: +size || 1.5 });
            fltr.appendChild(blur);
            t.paper.defs.appendChild(fltr);
            t._blur = fltr;
            $(t.node, { filter: "url(#" + fltr.id + ")" });
        } else {
            if (t._blur) {
                t._blur.parentNode.removeChild(t._blur);
                delete t._blur;
                delete t.attrs.blur;
            }
            t.node.removeAttribute("filter");
        }
    };
    R._engine.circle = function (svg, x, y, r) {
        var el = $("circle");
        svg.canvas && svg.canvas.appendChild(el);
        var res = new Element(el, svg);
        res.attrs = { cx: x, cy: y, r: r, fill: "none", stroke: "#000" };
        res.type = "circle";
        $(el, res.attrs);
        return res;
    };
    R._engine.rect = function (svg, x, y, w, h, r) {
        var el = $("rect");
        svg.canvas && svg.canvas.appendChild(el);
        var res = new Element(el, svg);
        res.attrs = { x: x, y: y, width: w, height: h, r: r || 0, rx: r || 0, ry: r || 0, fill: "none", stroke: "#000" };
        res.type = "rect";
        $(el, res.attrs);
        return res;
    };
    R._engine.ellipse = function (svg, x, y, rx, ry) {
        var el = $("ellipse");
        svg.canvas && svg.canvas.appendChild(el);
        var res = new Element(el, svg);
        res.attrs = { cx: x, cy: y, rx: rx, ry: ry, fill: "none", stroke: "#000" };
        res.type = "ellipse";
        $(el, res.attrs);
        return res;
    };
    R._engine.image = function (svg, src, x, y, w, h) {
        var el = $("image");
        $(el, { x: x, y: y, width: w, height: h, preserveAspectRatio: "none" });
        el.setAttributeNS(xlink, "href", src);
        svg.canvas && svg.canvas.appendChild(el);
        var res = new Element(el, svg);
        res.attrs = { x: x, y: y, width: w, height: h, src: src };
        res.type = "image";
        return res;
    };
    R._engine.text = function (svg, x, y, text) {
        var el = $("text");
        // $(el, {x: x, y: y, "text-anchor": "middle"});
        svg.canvas && svg.canvas.appendChild(el);
        var res = new Element(el, svg);
        res.attrs = {
            x: x,
            y: y,
            "text-anchor": "middle",
            text: text,
            font: R._availableAttrs.font,
            stroke: "none",
            fill: "#000"
        };
        res.type = "text";
        setFillAndStroke(res, res.attrs);
        return res;
    };
    R._engine.setSize = function (width, height) {
        this.width = width || this.width;
        this.height = height || this.height;
        this.canvas.setAttribute("width", this.width);
        this.canvas.setAttribute("height", this.height);
        if (this._viewBox) {
            this.setViewBox.apply(this, this._viewBox);
        }
        return this;
    };
    R._engine.create = function () {
        var con = R._getContainer.apply(0, arguments),
            container = con && con.container,
            x = con.x,
            y = con.y,
            width = con.width,
            height = con.height;
        if (!container) {
            throw new Error("SVG container not found.");
        }
        var cnvs = $("svg"),
            css = "overflow:hidden;",
            isFloating;
        x = x || 0;
        y = y || 0;
        width = width || 512;
        height = height || 342;
        $(cnvs, {
            height: height,
            version: 1.1,
            width: width,
            xmlns: "http://www.w3.org/2000/svg"
        });
        if (container == 1) {
            cnvs.style.cssText = css + "position:absolute;left:" + x + "px;top:" + y + "px";
            R._g.doc.body.appendChild(cnvs);
            isFloating = 1;
        } else {
            cnvs.style.cssText = css + "position:relative";
            if (container.firstChild) {
                container.insertBefore(cnvs, container.firstChild);
            } else {
                container.appendChild(cnvs);
            }
        }
        container = new R._Paper;
        container.width = width;
        container.height = height;
        container.canvas = cnvs;
        // plugins.call(container, container, R.fn);
        container.clear();
        container._left = container._top = 0;
        isFloating && (container.renderfix = function () { });
        container.renderfix();
        return container;
    };
    R._engine.setViewBox = function (x, y, w, h, fit) {
        eve("setViewBox", this, this._viewBox, [x, y, w, h, fit]);
        var size = mmax(w / this.width, h / this.height),
            top = this.top,
            aspectRatio = fit ? "meet" : "xMinYMin",
            vb,
            sw;
        if (x == null) {
            if (this._vbSize) {
                size = 1;
            }
            delete this._vbSize;
            vb = "0 0 " + this.width + S + this.height;
        } else {
            this._vbSize = size;
            vb = x + S + y + S + w + S + h;
        }
        $(this.canvas, {
            viewBox: vb,
            preserveAspectRatio: aspectRatio
        });
        while (size && top) {
            sw = "stroke-width" in top.attrs ? top.attrs["stroke-width"] : 1;
            top.attr({ "stroke-width": sw });
            top._.dirty = 1;
            top._.dirtyT = 1;
            top = top.prev;
        }
        this._viewBox = [x, y, w, h, !!fit];
        return this;
    };

    R.prototype.renderfix = function () {
        var cnvs = this.canvas,
            s = cnvs.style,
            pos = cnvs.getScreenCTM() || cnvs.createSVGMatrix(),
            left = -pos.e % 1,
            top = -pos.f % 1;
        if (left || top) {
            if (left) {
                this._left = (this._left + left) % 1;
                s.left = this._left + "px";
            }
            if (top) {
                this._top = (this._top + top) % 1;
                s.top = this._top + "px";
            }
        }
    };

    R.prototype.clear = function () {
        R.eve("clear", this);
        var c = this.canvas;
        while (c.firstChild) {
            c.removeChild(c.firstChild);
        }
        this.bottom = this.top = null;
        (this.desc = $("desc")).appendChild(R._g.doc.createTextNode("Created with Rapha\xebl " + R.version));
        c.appendChild(this.desc);
        c.appendChild(this.defs = $("defs"));
    };

    R.prototype.remove = function () {
        eve("remove", this);
        this.canvas.parentNode && this.canvas.parentNode.removeChild(this.canvas);
        for (var i in this) {
            this[i] = removed(i);
        }
    };
    var setproto = R.st;
    for (var method in elproto) if (elproto[has](method) && !setproto[has](method)) {
        setproto[method] = (function (methodname) {
            return function () {
                var arg = arguments;
                return this.forEach(function (el) {
                    el[methodname].apply(el, arg);
                });
            };
        })(method);
    }
} (window.Raphael);

// ┌─────────────────────────────────────────────────────────────────────┐ \\
// │ Raphaël 2 - JavaScript Vector Library                               │ \\
// ├─────────────────────────────────────────────────────────────────────┤ \\
// │ VML Module                                                          │ \\
// ├─────────────────────────────────────────────────────────────────────┤ \\
// │ Copyright (c) 2008-2011 Dmitry Baranovskiy (http://raphaeljs.com)   │ \\
// │ Copyright (c) 2008-2011 Sencha Labs (http://sencha.com)             │ \\
// │ Licensed under the MIT (http://raphaeljs.com/license.html) license. │ \\
// └─────────────────────────────────────────────────────────────────────┘ \\
window.Raphael.vml && function (R) {
    var has = "hasOwnProperty",
        Str = String,
        toFloat = parseFloat,
        math = Math,
        round = math.round,
        mmax = math.max,
        mmin = math.min,
        abs = math.abs,
        fillString = "fill",
        separator = /[, ]+/,
        eve = R.eve,
        ms = " progid:DXImageTransform.Microsoft",
        S = " ",
        E = "",
        map = { M: "m", L: "l", C: "c", Z: "x", m: "t", l: "r", c: "v", z: "x" },
        bites = /([clmz]),?([^clmz]*)/gi,
        blurregexp = / progid:\S+Blur\([^\)]+\)/g,
        val = /-?[^,\s-]+/g,
        cssDot = "position:absolute;left:0;top:0;width:1px;height:1px",
        zoom = 21600,
        pathTypes = { path: 1, rect: 1, image: 1 },
        ovalTypes = { circle: 1, ellipse: 1 },
        path2vml = function (path) {
            var total = /[ahqstv]/ig,
                command = R._pathToAbsolute;
            Str(path).match(total) && (command = R._path2curve);
            total = /[clmz]/g;
            if (command == R._pathToAbsolute && !Str(path).match(total)) {
                var res = Str(path).replace(bites, function (all, command, args) {
                    var vals = [],
                        isMove = command.toLowerCase() == "m",
                        res = map[command];
                    args.replace(val, function (value) {
                        if (isMove && vals.length == 2) {
                            res += vals + map[command == "m" ? "l" : "L"];
                            vals = [];
                        }
                        vals.push(round(value * zoom));
                    });
                    return res + vals;
                });
                return res;
            }
            var pa = command(path), p, r;
            res = [];
            for (var i = 0, ii = pa.length; i < ii; i++) {
                p = pa[i];
                r = pa[i][0].toLowerCase();
                r == "z" && (r = "x");
                for (var j = 1, jj = p.length; j < jj; j++) {
                    r += round(p[j] * zoom) + (j != jj - 1 ? "," : E);
                }
                res.push(r);
            }
            return res.join(S);
        },
        compensation = function (deg, dx, dy) {
            var m = R.matrix();
            m.rotate(-deg, .5, .5);
            return {
                dx: m.x(dx, dy),
                dy: m.y(dx, dy)
            };
        },
        setCoords = function (p, sx, sy, dx, dy, deg) {
            var _ = p._,
                m = p.matrix,
                fillpos = _.fillpos,
                o = p.node,
                s = o.style,
                y = 1,
                flip = "",
                dxdy,
                kx = zoom / sx,
                ky = zoom / sy;
            s.visibility = "hidden";
            if (!sx || !sy) {
                return;
            }
            o.coordsize = abs(kx) + S + abs(ky);
            s.rotation = deg * (sx * sy < 0 ? -1 : 1);
            if (deg) {
                var c = compensation(deg, dx, dy);
                dx = c.dx;
                dy = c.dy;
            }
            sx < 0 && (flip += "x");
            sy < 0 && (flip += " y") && (y = -1);
            s.flip = flip;
            o.coordorigin = (dx * -kx) + S + (dy * -ky);
            if (fillpos || _.fillsize) {
                var fill = o.getElementsByTagName(fillString);
                fill = fill && fill[0];
                o.removeChild(fill);
                if (fillpos) {
                    c = compensation(deg, m.x(fillpos[0], fillpos[1]), m.y(fillpos[0], fillpos[1]));
                    fill.position = c.dx * y + S + c.dy * y;
                }
                if (_.fillsize) {
                    fill.size = _.fillsize[0] * abs(sx) + S + _.fillsize[1] * abs(sy);
                }
                o.appendChild(fill);
            }
            s.visibility = "visible";
        };
    R.toString = function () {
        return "Your browser doesn\u2019t support SVG. Falling down to VML.\nYou are running Rapha\xebl " + this.version;
    };
    addArrow = function (o, value, isEnd) {
        var values = Str(value).toLowerCase().split("-"),
            se = isEnd ? "end" : "start",
            i = values.length,
            type = "classic",
            w = "medium",
            h = "medium";
        while (i--) {
            switch (values[i]) {
                case "block":
                case "classic":
                case "oval":
                case "diamond":
                case "open":
                case "none":
                    type = values[i];
                    break;
                case "wide":
                case "narrow": h = values[i]; break;
                case "long":
                case "short": w = values[i]; break;
            }
        }
        var stroke = o.node.getElementsByTagName("stroke")[0];
        stroke[se + "arrow"] = type;
        stroke[se + "arrowlength"] = w;
        stroke[se + "arrowwidth"] = h;
    };
    setFillAndStroke = function (o, params) {
        // o.paper.canvas.style.display = "none";
        o.attrs = o.attrs || {};
        var node = o.node,
            a = o.attrs,
            s = node.style,
            xy,
            newpath = pathTypes[o.type] && (params.x != a.x || params.y != a.y || params.width != a.width || params.height != a.height || params.cx != a.cx || params.cy != a.cy || params.rx != a.rx || params.ry != a.ry || params.r != a.r),
            isOval = ovalTypes[o.type] && (a.cx != params.cx || a.cy != params.cy || a.r != params.r || a.rx != params.rx || a.ry != params.ry),
            res = o;


        for (var par in params) if (params[has](par)) {
            a[par] = params[par];
        }
        if (newpath) {
            a.path = R._getPath[o.type](o);
            o._.dirty = 1;
        }
        params.href && (node.href = params.href);
        params.title && (node.title = params.title);
        params.target && (node.target = params.target);
        params.cursor && (s.cursor = params.cursor);
        "blur" in params && o.blur(params.blur);
        if (params.path && o.type == "path" || newpath) {
            node.path = path2vml(~Str(a.path).toLowerCase().indexOf("r") ? R._pathToAbsolute(a.path) : a.path);
            if (o.type == "image") {
                o._.fillpos = [a.x, a.y];
                o._.fillsize = [a.width, a.height];
                setCoords(o, 1, 1, 0, 0, 0);
            }
        }
        "transform" in params && o.transform(params.transform);
        if (isOval) {
            var cx = +a.cx,
                cy = +a.cy,
                rx = +a.rx || +a.r || 0,
                ry = +a.ry || +a.r || 0;
            node.path = R.format("ar{0},{1},{2},{3},{4},{1},{4},{1}x", round((cx - rx) * zoom), round((cy - ry) * zoom), round((cx + rx) * zoom), round((cy + ry) * zoom), round(cx * zoom));
        }
        if ("clip-rect" in params) {
            var rect = Str(params["clip-rect"]).split(separator);
            if (rect.length == 4) {
                rect[2] = +rect[2] + (+rect[0]);
                rect[3] = +rect[3] + (+rect[1]);
                var div = node.clipRect || R._g.doc.createElement("div"),
                    dstyle = div.style;
                dstyle.clip = R.format("rect({1}px {2}px {3}px {0}px)", rect);
                if (!node.clipRect) {
                    dstyle.position = "absolute";
                    dstyle.top = 0;
                    dstyle.left = 0;
                    dstyle.width = o.paper.width + "px";
                    dstyle.height = o.paper.height + "px";
                    node.parentNode.insertBefore(div, node);
                    div.appendChild(node);
                    node.clipRect = div;
                }
            }
            if (!params["clip-rect"]) {
                node.clipRect && (node.clipRect.style.clip = E);
            }
        }
        if (o.textpath) {
            var textpathStyle = o.textpath.style;
            params.font && (textpathStyle.font = params.font);
            params["font-family"] && (textpathStyle.fontFamily = '"' + params["font-family"].split(",")[0].replace(/^['"]+|['"]+$/g, E) + '"');
            params["font-size"] && (textpathStyle.fontSize = params["font-size"]);
            params["font-weight"] && (textpathStyle.fontWeight = params["font-weight"]);
            params["font-style"] && (textpathStyle.fontStyle = params["font-style"]);
        }
        if ("arrow-start" in params) {
            addArrow(res, params["arrow-start"]);
        }
        if ("arrow-end" in params) {
            addArrow(res, params["arrow-end"], 1);
        }
        if (params.opacity != null ||
            params["stroke-width"] != null ||
            params.fill != null ||
            params.src != null ||
            params.stroke != null ||
            params["stroke-width"] != null ||
            params["stroke-opacity"] != null ||
            params["fill-opacity"] != null ||
            params["stroke-dasharray"] != null ||
            params["stroke-miterlimit"] != null ||
            params["stroke-linejoin"] != null ||
            params["stroke-linecap"] != null) {
            var fill = node.getElementsByTagName(fillString),
                newfill = false;
            fill = fill && fill[0];
            !fill && (newfill = fill = createNode(fillString));
            if (o.type == "image" && params.src) {
                fill.src = params.src;
            }
            params.fill && (fill.on = true);
            if (fill.on == null || params.fill == "none" || params.fill === null) {
                fill.on = false;
            }
            if (fill.on && params.fill) {
                var isURL = Str(params.fill).match(R._ISURL);
                if (isURL) {
                    fill.parentNode == node && node.removeChild(fill);
                    fill.rotate = true;
                    fill.src = isURL[1];
                    fill.type = "tile";
                    var bbox = o.getBBox(1);
                    fill.position = bbox.x + S + bbox.y;
                    o._.fillpos = [bbox.x, bbox.y];

                    R._preload(isURL[1], function () {
                        o._.fillsize = [this.offsetWidth, this.offsetHeight];
                    });
                } else {
                    fill.color = R.getRGB(params.fill).hex;
                    fill.src = E;
                    fill.type = "solid";
                    if (R.getRGB(params.fill).error && (res.type in { circle: 1, ellipse: 1} || Str(params.fill).charAt() != "r") && addGradientFill(res, params.fill, fill)) {
                        a.fill = "none";
                        a.gradient = params.fill;
                        fill.rotate = false;
                    }
                }
            }
            if ("fill-opacity" in params || "opacity" in params) {
                var opacity = ((+a["fill-opacity"] + 1 || 2) - 1) * ((+a.opacity + 1 || 2) - 1) * ((+R.getRGB(params.fill).o + 1 || 2) - 1);
                opacity = mmin(mmax(opacity, 0), 1);
                fill.opacity = opacity;
                if (fill.src) {
                    fill.color = "none";
                }
            }
            node.appendChild(fill);
            var stroke = (node.getElementsByTagName("stroke") && node.getElementsByTagName("stroke")[0]),
            newstroke = false;
            !stroke && (newstroke = stroke = createNode("stroke"));
            if ((params.stroke && params.stroke != "none") ||
                params["stroke-width"] ||
                params["stroke-opacity"] != null ||
                params["stroke-dasharray"] ||
                params["stroke-miterlimit"] ||
                params["stroke-linejoin"] ||
                params["stroke-linecap"]) {
                stroke.on = true;
            }
            (params.stroke == "none" || params.stroke === null || stroke.on == null || params.stroke == 0 || params["stroke-width"] == 0) && (stroke.on = false);
            var strokeColor = R.getRGB(params.stroke);
            stroke.on && params.stroke && (stroke.color = strokeColor.hex);
            opacity = ((+a["stroke-opacity"] + 1 || 2) - 1) * ((+a.opacity + 1 || 2) - 1) * ((+strokeColor.o + 1 || 2) - 1);
            var width = (toFloat(params["stroke-width"]) || 1) * .75;
            opacity = mmin(mmax(opacity, 0), 1);
            params["stroke-width"] == null && (width = a["stroke-width"]);
            params["stroke-width"] && (stroke.weight = width);
            width && width < 1 && (opacity *= width) && (stroke.weight = 1);
            stroke.opacity = opacity;

            params["stroke-linejoin"] && (stroke.joinstyle = params["stroke-linejoin"] || "miter");
            stroke.miterlimit = params["stroke-miterlimit"] || 8;
            params["stroke-linecap"] && (stroke.endcap = params["stroke-linecap"] == "butt" ? "flat" : params["stroke-linecap"] == "square" ? "square" : "round");
            if (params["stroke-dasharray"]) {
                var dasharray = {
                    "-": "shortdash",
                    ".": "shortdot",
                    "-.": "shortdashdot",
                    "-..": "shortdashdotdot",
                    ". ": "dot",
                    "- ": "dash",
                    "--": "longdash",
                    "- .": "dashdot",
                    "--.": "longdashdot",
                    "--..": "longdashdotdot"
                };
                stroke.dashstyle = dasharray[has](params["stroke-dasharray"]) ? dasharray[params["stroke-dasharray"]] : E;
            }
            newstroke && node.appendChild(stroke);
        }
        if (res.type == "text") {
            res.paper.canvas.style.display = E;
            var span = res.paper.span,
                m = 100,
                fontSize = a.font && a.font.match(/\d+(?:\.\d*)?(?=px)/);
            s = span.style;
            a.font && (s.font = a.font);
            a["font-family"] && (s.fontFamily = a["font-family"]);
            a["font-weight"] && (s.fontWeight = a["font-weight"]);
            a["font-style"] && (s.fontStyle = a["font-style"]);
            fontSize = toFloat(fontSize ? fontSize[0] : a["font-size"]);
            s.fontSize = fontSize * m + "px";
            res.textpath.string && (span.innerHTML = Str(res.textpath.string).replace(/</g, "&#60;").replace(/&/g, "&#38;").replace(/\n/g, "<br>"));
            var brect = span.getBoundingClientRect();
            res.W = a.w = (brect.right - brect.left) / m;
            res.H = a.h = (brect.bottom - brect.top) / m;
            // res.paper.canvas.style.display = "none";
            res.X = a.x;
            res.Y = a.y + res.H / 2;

            ("x" in params || "y" in params) && (res.path.v = R.format("m{0},{1}l{2},{1}", round(a.x * zoom), round(a.y * zoom), round(a.x * zoom) + 1));
            var dirtyattrs = ["x", "y", "text", "font", "font-family", "font-weight", "font-style", "font-size"];
            for (var d = 0, dd = dirtyattrs.length; d < dd; d++) if (dirtyattrs[d] in params) {
                res._.dirty = 1;
                break;
            }

            // text-anchor emulation
            switch (a["text-anchor"]) {
                case "start":
                    res.textpath.style["v-text-align"] = "left";
                    res.bbx = res.W / 2;
                    break;
                case "end":
                    res.textpath.style["v-text-align"] = "right";
                    res.bbx = -res.W / 2;
                    break;
                default:
                    res.textpath.style["v-text-align"] = "center";
                    res.bbx = 0;
                    break;
            }
            res.textpath.style["v-text-kern"] = true;
        }
        // res.paper.canvas.style.display = E;
    };
    addGradientFill = function (o, gradient, fill) {
        o.attrs = o.attrs || {};
        var attrs = o.attrs,
            pow = Math.pow,
            opacity,
            oindex,
            type = "linear",
            fxfy = ".5 .5";
        o.attrs.gradient = gradient;
        gradient = Str(gradient).replace(R._radial_gradient, function (all, fx, fy) {
            type = "radial";
            if (fx && fy) {
                fx = toFloat(fx);
                fy = toFloat(fy);
                pow(fx - .5, 2) + pow(fy - .5, 2) > .25 && (fy = math.sqrt(.25 - pow(fx - .5, 2)) * ((fy > .5) * 2 - 1) + .5);
                fxfy = fx + S + fy;
            }
            return E;
        });
        gradient = gradient.split(/\s*\-\s*/);
        if (type == "linear") {
            var angle = gradient.shift();
            angle = -toFloat(angle);
            if (isNaN(angle)) {
                return null;
            }
        }
        var dots = R._parseDots(gradient);
        if (!dots) {
            return null;
        }
        o = o.shape || o.node;
        if (dots.length) {
            o.removeChild(fill);
            fill.on = true;
            fill.method = "none";
            fill.color = dots[0].color;
            fill.color2 = dots[dots.length - 1].color;
            var clrs = [];
            for (var i = 0, ii = dots.length; i < ii; i++) {
                dots[i].offset && clrs.push(dots[i].offset + S + dots[i].color);
            }
            fill.colors = clrs.length ? clrs.join() : "0% " + fill.color;
            if (type == "radial") {
                fill.type = "gradientTitle";
                fill.focus = "100%";
                fill.focussize = "0 0";
                fill.focusposition = fxfy;
                fill.angle = 0;
            } else {
                // fill.rotate= true;
                fill.type = "gradient";
                fill.angle = (270 - angle) % 360;
            }
            o.appendChild(fill);
        }
        return 1;
    };
    Element = function (node, vml) {
        this[0] = this.node = node;
        node.raphael = true;
        this.id = R._oid++;
        node.raphaelid = this.id;
        this.X = 0;
        this.Y = 0;
        this.attrs = {};
        this.paper = vml;
        this.matrix = R.matrix();
        this._ = {
            transform: [],
            sx: 1,
            sy: 1,
            dx: 0,
            dy: 0,
            deg: 0,
            dirty: 1,
            dirtyT: 1
        };
        !vml.bottom && (vml.bottom = this);
        this.prev = vml.top;
        vml.top && (vml.top.next = this);
        vml.top = this;
        this.next = null;
    };
    var elproto = R.el;

    Element.prototype = elproto;
    elproto.constructor = Element;
    elproto.transform = function (tstr) {
        if (tstr == null) {
            return this._.transform;
        }
        var vbs = this.paper._viewBoxShift,
            vbt = vbs ? "s" + [vbs.scale, vbs.scale] + "-1-1t" + [vbs.dx, vbs.dy] : E,
            oldt;
        if (vbs) {
            oldt = tstr = Str(tstr).replace(/\.{3}|\u2026/g, this._.transform || E);
        }
        R._extractTransform(this, vbt + tstr);
        var matrix = this.matrix.clone(),
            skew = this.skew,
            o = this.node,
            split,
            isGrad = ~Str(this.attrs.fill).indexOf("-"),
            isPatt = !Str(this.attrs.fill).indexOf("url(");
        matrix.translate(-.5, -.5);
        if (isPatt || isGrad || this.type == "image") {
            skew.matrix = "1 0 0 1";
            skew.offset = "0 0";
            split = matrix.split();
            if ((isGrad && split.noRotation) || !split.isSimple) {
                o.style.filter = matrix.toFilter();
                var bb = this.getBBox(),
                    bbt = this.getBBox(1),
                    dx = bb.x - bbt.x,
                    dy = bb.y - bbt.y;
                o.coordorigin = (dx * -zoom) + S + (dy * -zoom);
                setCoords(this, 1, 1, dx, dy, 0);
            } else {
                o.style.filter = E;
                setCoords(this, split.scalex, split.scaley, split.dx, split.dy, split.rotate);
            }
        } else {
            o.style.filter = E;
            skew.matrix = Str(matrix);
            skew.offset = matrix.offset();
        }
        oldt && (this._.transform = oldt);
        return this;
    };
    elproto.rotate = function (deg, cx, cy) {
        if (this.removed) {
            return this;
        }
        if (deg == null) {
            return;
        }
        deg = Str(deg).split(separator);
        if (deg.length - 1) {
            cx = toFloat(deg[1]);
            cy = toFloat(deg[2]);
        }
        deg = toFloat(deg[0]);
        (cy == null) && (cx = cy);
        if (cx == null || cy == null) {
            var bbox = this.getBBox(1);
            cx = bbox.x + bbox.width / 2;
            cy = bbox.y + bbox.height / 2;
        }
        this._.dirtyT = 1;
        this.transform(this._.transform.concat([["r", deg, cx, cy]]));
        return this;
    };
    elproto.translate = function (dx, dy) {
        if (this.removed) {
            return this;
        }
        dx = Str(dx).split(separator);
        if (dx.length - 1) {
            dy = toFloat(dx[1]);
        }
        dx = toFloat(dx[0]) || 0;
        dy = +dy || 0;
        if (this._.bbox) {
            this._.bbox.x += dx;
            this._.bbox.y += dy;
        }
        this.transform(this._.transform.concat([["t", dx, dy]]));
        return this;
    };
    elproto.scale = function (sx, sy, cx, cy) {
        if (this.removed) {
            return this;
        }
        sx = Str(sx).split(separator);
        if (sx.length - 1) {
            sy = toFloat(sx[1]);
            cx = toFloat(sx[2]);
            cy = toFloat(sx[3]);
            isNaN(cx) && (cx = null);
            isNaN(cy) && (cy = null);
        }
        sx = toFloat(sx[0]);
        (sy == null) && (sy = sx);
        (cy == null) && (cx = cy);
        if (cx == null || cy == null) {
            var bbox = this.getBBox(1);
        }
        cx = cx == null ? bbox.x + bbox.width / 2 : cx;
        cy = cy == null ? bbox.y + bbox.height / 2 : cy;

        this.transform(this._.transform.concat([["s", sx, sy, cx, cy]]));
        this._.dirtyT = 1;
        return this;
    };
    elproto.hide = function () {
        !this.removed && (this.node.style.display = "none");
        return this;
    };
    elproto.show = function () {
        !this.removed && (this.node.style.display = E);
        return this;
    };
    elproto._getBBox = function () {
        if (this.removed) {
            return {};
        }
        if (this.type == "text") {
            return {
                x: this.X + (this.bbx || 0) - this.W / 2,
                y: this.Y - this.H,
                width: this.W,
                height: this.H
            };
        } else {
            return pathDimensions(this.attrs.path);
        }
    };
    elproto.remove = function () {
        if (this.removed) {
            return;
        }
        this.paper.__set__ && this.paper.__set__.exclude(this);
        R.eve.unbind("*.*." + this.id);
        R._tear(this, this.paper);
        this.node.parentNode.removeChild(this.node);
        this.shape && this.shape.parentNode.removeChild(this.shape);
        for (var i in this) {
            delete this[i];
        }
        this.removed = true;
    };
    elproto.attr = function (name, value) {
        if (this.removed) {
            return this;
        }
        if (name == null) {
            var res = {};
            for (var a in this.attrs) if (this.attrs[has](a)) {
                res[a] = this.attrs[a];
            }
            res.gradient && res.fill == "none" && (res.fill = res.gradient) && delete res.gradient;
            res.transform = this._.transform;
            return res;
        }
        if (value == null && R.is(name, "string")) {
            if (name == fillString && this.attrs.fill == "none" && this.attrs.gradient) {
                return this.attrs.gradient;
            }
            var names = name.split(separator),
                out = {};
            for (var i = 0, ii = names.length; i < ii; i++) {
                name = names[i];
                if (name in this.attrs) {
                    out[name] = this.attrs[name];
                } else if (R.is(this.paper.customAttributes[name], "function")) {
                    out[name] = this.paper.customAttributes[name].def;
                } else {
                    out[name] = R._availableAttrs[name];
                }
            }
            return ii - 1 ? out : out[names[0]];
        }
        if (this.attrs && value == null && R.is(name, "array")) {
            out = {};
            for (i = 0, ii = name.length; i < ii; i++) {
                out[name[i]] = this.attr(name[i]);
            }
            return out;
        }
        var params;
        if (value != null) {
            params = {};
            params[name] = value;
        }
        value == null && R.is(name, "object") && (params = name);
        for (var key in params) {
            eve("attr." + key + "." + this.id, this, params[key]);
        }
        if (params) {
            for (key in this.paper.customAttributes) if (this.paper.customAttributes[has](key) && params[has](key) && R.is(this.paper.customAttributes[key], "function")) {
                var par = this.paper.customAttributes[key].apply(this, [].concat(params[key]));
                this.attrs[key] = params[key];
                for (var subkey in par) if (par[has](subkey)) {
                    params[subkey] = par[subkey];
                }
            }
            // this.paper.canvas.style.display = "none";
            if (params.text && this.type == "text") {
                this.textpath.string = params.text;
            }
            setFillAndStroke(this, params);
            // this.paper.canvas.style.display = E;
        }
        return this;
    };
    elproto.toFront = function () {
        !this.removed && this.node.parentNode.appendChild(this.node);
        this.paper && this.paper.top != this && R._tofront(this, this.paper);
        return this;
    };
    elproto.toBack = function () {
        if (this.removed) {
            return this;
        }
        if (this.node.parentNode.firstChild != this.node) {
            this.node.parentNode.insertBefore(this.node, this.node.parentNode.firstChild);
            R._toback(this, this.paper);
        }
        return this;
    };
    elproto.insertAfter = function (element) {
        if (this.removed) {
            return this;
        }
        if (element.constructor == R.st.constructor) {
            element = element[element.length - 1];
        }
        if (element.node.nextSibling) {
            element.node.parentNode.insertBefore(this.node, element.node.nextSibling);
        } else {
            element.node.parentNode.appendChild(this.node);
        }
        R._insertafter(this, element, this.paper);
        return this;
    };
    elproto.insertBefore = function (element) {
        if (this.removed) {
            return this;
        }
        if (element.constructor == R.st.constructor) {
            element = element[0];
        }
        element.node.parentNode.insertBefore(this.node, element.node);
        R._insertbefore(this, element, this.paper);
        return this;
    };
    elproto.blur = function (size) {
        var s = this.node.runtimeStyle,
            f = s.filter;
        f = f.replace(blurregexp, E);
        if (+size !== 0) {
            this.attrs.blur = size;
            s.filter = f + S + ms + ".Blur(pixelradius=" + (+size || 1.5) + ")";
            s.margin = R.format("-{0}px 0 0 -{0}px", round(+size || 1.5));
        } else {
            s.filter = f;
            s.margin = 0;
            delete this.attrs.blur;
        }
    };

    R._engine.path = function (pathString, vml) {
        var el = createNode("shape");
        el.style.cssText = cssDot;
        el.coordsize = zoom + S + zoom;
        el.coordorigin = vml.coordorigin;
        var p = new Element(el, vml),
            attr = { fill: "none", stroke: "#000" };
        pathString && (attr.path = pathString);
        p.type = "path";
        p.path = [];
        p.Path = E;
        setFillAndStroke(p, attr);
        vml.canvas.appendChild(el);
        var skew = createNode("skew");
        skew.on = true;
        el.appendChild(skew);
        p.skew = skew;
        p.transform(E);
        return p;
    };
    R._engine.rect = function (vml, x, y, w, h, r) {
        var path = R._rectPath(x, y, w, h, r),
            res = vml.path(path),
            a = res.attrs;
        res.X = a.x = x;
        res.Y = a.y = y;
        res.W = a.width = w;
        res.H = a.height = h;
        a.r = r;
        a.path = path;
        res.type = "rect";
        return res;
    };
    R._engine.ellipse = function (vml, x, y, rx, ry) {
        var res = vml.path(),
            a = res.attrs;
        res.X = x - rx;
        res.Y = y - ry;
        res.W = rx * 2;
        res.H = ry * 2;
        res.type = "ellipse";
        setFillAndStroke(res, {
            cx: x,
            cy: y,
            rx: rx,
            ry: ry
        });
        return res;
    };
    R._engine.circle = function (vml, x, y, r) {
        var res = vml.path(),
            a = res.attrs;
        res.X = x - r;
        res.Y = y - r;
        res.W = res.H = r * 2;
        res.type = "circle";
        setFillAndStroke(res, {
            cx: x,
            cy: y,
            r: r
        });
        return res;
    };
    R._engine.image = function (vml, src, x, y, w, h) {
        var path = R._rectPath(x, y, w, h),
            res = vml.path(path).attr({ stroke: "none" }),
            a = res.attrs,
            node = res.node,
            fill = node.getElementsByTagName(fillString)[0];
        a.src = src;
        res.X = a.x = x;
        res.Y = a.y = y;
        res.W = a.width = w;
        res.H = a.height = h;
        a.path = path;
        res.type = "image";
        fill.parentNode == node && node.removeChild(fill);
        fill.rotate = true;
        fill.src = src;
        fill.type = "tile";
        res._.fillpos = [x, y];
        res._.fillsize = [w, h];
        node.appendChild(fill);
        setCoords(res, 1, 1, 0, 0, 0);
        return res;
    };
    R._engine.text = function (vml, x, y, text) {
        var el = createNode("shape"),
            path = createNode("path"),
            o = createNode("textpath");
        x = x || 0;
        y = y || 0;
        text = text || "";
        path.v = R.format("m{0},{1}l{2},{1}", round(x * zoom), round(y * zoom), round(x * zoom) + 1);
        path.textpathok = true;
        o.string = Str(text);
        o.on = true;
        el.style.cssText = cssDot;
        el.coordsize = zoom + S + zoom;
        el.coordorigin = "0 0";
        var p = new Element(el, vml),
            attr = {
                fill: "#000",
                stroke: "none",
                font: R._availableAttrs.font,
                text: text
            };
        p.shape = el;
        p.path = path;
        p.textpath = o;
        p.type = "text";
        p.attrs.text = Str(text);
        p.attrs.x = x;
        p.attrs.y = y;
        p.attrs.w = 1;
        p.attrs.h = 1;
        setFillAndStroke(p, attr);
        el.appendChild(o);
        el.appendChild(path);
        vml.canvas.appendChild(el);
        var skew = createNode("skew");
        skew.on = true;
        el.appendChild(skew);
        p.skew = skew;
        p.transform(E);
        return p;
    };
    R._engine.setSize = function (width, height) {
        var cs = this.canvas.style;
        this.width = width;
        this.height = height;
        width == +width && (width += "px");
        height == +height && (height += "px");
        cs.width = width;
        cs.height = height;
        cs.clip = "rect(0 " + width + " " + height + " 0)";
        if (this._viewBox) {
            setViewBox.apply(this, this._viewBox);
        }
        return this;
    };
    R._engine.setViewBox = function (x, y, w, h, fit) {
        R.eve("setViewBox", this, this._viewBox, [x, y, w, h, fit]);
        var width = this.width,
            height = this.height,
            size = 1 / mmax(w / width, h / height),
            H, W;
        if (fit) {
            H = height / h;
            W = width / w;
            if (w * H < width) {
                x -= (width - w * H) / 2 / H;
            }
            if (h * W < height) {
                y -= (height - h * W) / 2 / W;
            }
        }
        this._viewBox = [x, y, w, h, !!fit];
        this._viewBoxShift = {
            dx: -x,
            dy: -y,
            scale: size
        };
        this.forEach(function (el) {
            el.transform("...");
        });
        return this;
    };
    var createNode,
        initWin = function (win) {
            var doc = win.document;
            doc.createStyleSheet().addRule(".rvml", "behavior:url(#default#VML)");
            try {
                !doc.namespaces.rvml && doc.namespaces.add("rvml", "urn:schemas-microsoft-com:vml");
                createNode = function (tagName) {
                    return doc.createElement('<rvml:' + tagName + ' class="rvml">');
                };
            } catch (e) {
                createNode = function (tagName) {
                    return doc.createElement('<' + tagName + ' xmlns="urn:schemas-microsoft.com:vml" class="rvml">');
                };
            }
        };
    initWin(R._g.win);
    R._engine.create = function () {
        var con = R._getContainer.apply(0, arguments),
            container = con.container,
            height = con.height,
            s,
            width = con.width,
            x = con.x,
            y = con.y;
        if (!container) {
            throw new Error("VML container not found.");
        }
        var res = new R._Paper,
            c = res.canvas = R._g.doc.createElement("div"),
            cs = c.style;
        x = x || 0;
        y = y || 0;
        width = width || 512;
        height = height || 342;
        res.width = width;
        res.height = height;
        width == +width && (width += "px");
        height == +height && (height += "px");
        res.coordsize = zoom * 1e3 + S + zoom * 1e3;
        res.coordorigin = "0 0";
        res.span = R._g.doc.createElement("span");
        res.span.style.cssText = "position:absolute;left:-9999em;top:-9999em;padding:0;margin:0;line-height:1;";
        c.appendChild(res.span);
        cs.cssText = R.format("top:0;left:0;width:{0};height:{1};display:inline-block;position:relative;clip:rect(0 {0} {1} 0);overflow:hidden", width, height);
        if (container == 1) {
            R._g.doc.body.appendChild(c);
            cs.left = x + "px";
            cs.top = y + "px";
            cs.position = "absolute";
        } else {
            if (container.firstChild) {
                container.insertBefore(c, container.firstChild);
            } else {
                container.appendChild(c);
            }
        }
        // plugins.call(res, res, R.fn);
        res.renderfix = function () { };
        return res;
    };
    R.prototype.clear = function () {
        R.eve("clear", this);
        this.canvas.innerHTML = E;
        this.span = R._g.doc.createElement("span");
        this.span.style.cssText = "position:absolute;left:-9999em;top:-9999em;padding:0;margin:0;line-height:1;display:inline;";
        this.canvas.appendChild(this.span);
        this.bottom = this.top = null;
    };
    R.prototype.remove = function () {
        R.eve("remove", this);
        this.canvas.parentNode.removeChild(this.canvas);
        for (var i in this) {
            this[i] = removed(i);
        }
        return true;
    };

    var setproto = R.st;
    for (var method in elproto) if (elproto[has](method) && !setproto[has](method)) {
        setproto[method] = (function (methodname) {
            return function () {
                var arg = arguments;
                return this.forEach(function (el) {
                    el[methodname].apply(el, arg);
                });
            };
        })(method);
    }
} (window.Raphael);;(function($s) {
    Raphael.el.loop = function (_options) {
        var options = {
            pkg: [{"stroke-width": 3}, {"stroke-width": 1}]
            , duration: 200
            , repeat: false
        };
        $s.extend(options, _options);

        var _self = this;
        function loop() {
            _self.animate(options.pkg[0], options.duration, function() {
                _self.animate(options.pkg[1], options.duration, function() {
                    if (options.repeat) {
                        loop();
                    }
                });
            });
        };

        loop();

        return this;
    }
})(Slatebox);Raphael.el.button = function () {
    return this.attr({ "fill": "90-#000-#eee" });
};

Raphael.el.redbutton = function () {
    return this.attr({ "fill": "90-#990000-#eee" });
};

Raphael.el.buttonText = function () {
    return this.standard().attr({ "fill": "#fff" });
};

Raphael.el.activeColor = "#fffc51";

Raphael.el.active = function (anim, cb) {
    var pkg = { "stroke-width": 2, "stroke": this.activeColor };
    if (anim) {
        if (cb === undefined) return this.animate(pkg, 200);
        else return this.animate(pkg, 100, cb);
    } else {
        return this.attr(pkg);
    }
};

Raphael.el.isDisabled = function () {
    if (this.type === "text") {
        return this.attr("fill") === "#eee";
    } else {
        return this.attr("fill") === "#ccc";
    }
};

Raphael.el.disabled = function (anim, cb) {
    var pkg = { "fill": "#ccc", "stroke": "eee" };
    var tpkg = { "fill": "#eee" };
    if (this.type === "text") {
        return this.attr(tpkg);
    } else {
        return this.attr(pkg);
    }
};

Raphael.el.enabled = function (anim, cb) {
    if (this.type === "text") {
        return this.buttonText();
    } else {
        return this.button();
    }
};

Raphael.el.inactive = function (anim, cb) {
    var pkg = { "stroke-width": 1, "stroke": "#000" };
    if (anim) {
        if (cb === undefined) return this.animate(pkg, 200);
        else return this.animate(pkg, 100, cb);
    } else {
        return this.attr(pkg);
    }
};

Raphael.el.standard = function () {
    return this.attr({ "font-family": "Trebuchet MS", "font-size": "13pt" });
};
Raphael.el.tooltip = function (obj, w, h) {
    if (w === undefined) w = 80;
    if (h === undefined) h = 20;
    var _tt = this.paper.set();
    var pos = this.getBBox();

    if (obj.type === 'text') {
        //text tooltip
        _tt.push(this.paper.rect(pos.x, pos.y + (h * -1) - 10, w, h, 5).attr({ "fill": "#fff" }));
        _tt.push(this.paper.text(pos.x + 5, pos.y - 20, "").attr({ "stroke-width": 1, "text-anchor": "start", "stroke": "#fff", "font-size": 13, "fill": "#fff" }));
    } else {
        //image tooltip
        var xpad = (w * -1) - 5;
        _tt.push(this.paper.rect(pos.x + xpad, pos.y + (h / 2 * -1), w, h, 15).attr({ "stroke-width": 2, "stroke": "#fff" }));
        _tt.push(this.paper.rect(pos.x + xpad, pos.y + (h / 2 - 45), w, 47, 15)).attr({ "stroke-width": 2, fill: "90-#333-#000" });
        _tt.push(this.paper.text(pos.x + xpad + (w / 2), pos.y + (h / 2 - 20), "").attr({ "text-anchor": "middle", "stroke": "#fff", "font-weight": "normal", "font-family": "Verdana", "font-size": 11 }));
    }

    var s = this;
    if (!s.removed) {
        s.tt = _tt;
        if (obj.type === "text") {
            s.tt[0].animate({ "stroke": "#000", "fill": "#333" }, 200, function () {
                s.tt[1].attr({ text: obj.msg });
            });
        } else {
            s.tt[0].animate({ "stroke": "#000", "fill": "#333" }, 200, function () {
                //s.tt[1].attr({  });
                s.tt[2].attr({ text: obj.msg });
            });
        }
    }
    
    return s.tt;
};

Raphael.el.untooltip = function () {
    this.tt.remove();
    return this;
};Raphael.fn.connection = function (_options) {
    var options = {
        sb: Slatebox
        , parent: null
        , child: null
        , lineColor: "#fff"
        , lineOpacity: 1
        , lineWidth: 10
        , blnStraight: false
        , showParentArrow: false
        , showChildArrow: false
    };
    options.sb.extend(options, _options);

    function calcPath() {
        var bb1 = options.parent.vect.getBBox();
        var bb2 = options.child.vect.getBBox();

        var _px = (!isNaN(parseFloat(bb1.x)) && isFinite(bb1.x)) && bb1.x;
        var _pcx = (!isNaN(parseFloat(bb1.cx)) && isFinite(bb1.cx)) && bb1.cx;
        var _py = (!isNaN(parseFloat(bb1.y)) && isFinite(bb1.y)) && bb1.y;
        var _pcy = (!isNaN(parseFloat(bb1.cy)) && isFinite(bb1.cy)) && bb1.cy;

        var _cx = (!isNaN(parseFloat(bb2.x)) && isFinite(bb2.x)) && bb2.x;
        var _ccx = (!isNaN(parseFloat(bb2.cx)) && isFinite(bb2.cx)) && bb2.cx;
        var _cy = (!isNaN(parseFloat(bb2.y)) && isFinite(bb2.y)) && bb2.y;
        var _ccy = (!isNaN(parseFloat(bb2.cy)) && isFinite(bb2.cy)) && bb2.cy;

        var p = [{ x: (_px || _pcx) + bb1.width / 2, y: (_py || _pcy) - 1 },
        { x: _px + bb1.width / 2, y: _py + bb1.height + 1 },
        { x: _px - 1, y: _py + bb1.height / 2 },
        { x: _px + bb1.width + 1, y: _py + bb1.height / 2 },
        { x: _cx + bb2.width / 2, y: _cy - 1 },
        { x: _cx + bb2.width / 2, y: _cy + bb2.height + 1 },
        { x: _cx - 1, y: _cy + bb2.height / 2 },
        { x: _cx + bb2.width + 1, y: _cy + bb2.height / 2}],
        d = {}, dis = [];
        for (var i = 0; i < 4; i++) {
            for (var j = 4; j < 8; j++) {
                var dx = Math.abs(p[i].x - p[j].x),
                dy = Math.abs(p[i].y - p[j].y);
                if ((i == j - 4) || (((i != 3 && j != 6) || p[i].x < p[j].x) && ((i != 2 && j != 7) || p[i].x > p[j].x) && ((i != 0 && j != 5) || p[i].y > p[j].y) && ((i != 1 && j != 4) || p[i].y < p[j].y))) {
                    dis.push(dx + dy);
                    d[dis[dis.length - 1]] = [i, j];
                }
            }
        }
        if (dis.length == 0) {
            var res = [0, 4];
        } else {
            res = d[Math.min.apply(Math, dis)];
        }
        var x1 = p[res[0]].x,
        y1 = p[res[0]].y,
        x4 = p[res[1]].x,
        y4 = p[res[1]].y;
        dx = Math.max(Math.abs(x1 - x4) / 2, 10);
        dy = Math.max(Math.abs(y1 - y4) / 2, 10);
        var x2 = [x1, x1, x1 - dx, x1 + dx][res[0]].toFixed(3),
        y2 = [y1 - dy, y1 + dy, y1, y1][res[0]].toFixed(3),
        x3 = [0, 0, 0, 0, x4, x4, x4 - dx, x4 + dx][res[1]].toFixed(3),
        y3 = [0, 0, 0, 0, y1 + dy, y1 - dy, y4, y4][res[1]].toFixed(3);

        var size = 15;
        if (options.lineWidth > 10)
            size = 20;
        var shr = 2.4;
        var x1m = x1.toFixed(3), x4m = x4.toFixed(3), y1m = y1.toFixed(3), y4m = y4.toFixed(3);

        var path = ["M", x1m, y1.toFixed(3), "C", x2, y2, x3, y3, x4m, y4m].join(",");
        if (options.blnStraight) {
            x4m = (_cx + bb2.width / 2);
            y4m = (_cy + bb2.height / 2);
            path = ["M", x1m, y1.toFixed(3), "L", x4m, y4m].join(",");
        }

        if (options.showParentArrow) {
            var arrowAngle1 = Math.atan2(x3 - x4m, y4m - y3);
            if (options.blnStraight) {
                arrowAngle1 = Math.atan2(x1m - x4m, y4m - y1m);
            }
            arrowAngle1 = (arrowAngle1 / (2 * Math.PI)) * 360;
        }

        if (options.showChildArrow) {
            var arrowAngle2 = Math.atan2(x4m - x3, y3 - y4m);
            if (options.blnStraight) {
                arrowAngle2 = Math.atan2(x4m - x1m, y1m - y4m);
            }
            arrowAngle2 = (arrowAngle2 / (2 * Math.PI)) * 360;
        }

        var arrowPath1 = "M" + x4 + " " + y4 + " L" + (x4 - size) + " " + (y4 - size / shr) + " L" + (x4 - size) + " " + (y4 + size / shr) + " L" + x4 + " " + y4;
        var arrowPath2 = "M" + x1 + " " + y1 + " L" + (x1 - size) + " " + (y1 - size / shr) + " L" + (x1 - size) + " " + (y1 + size / shr) + " L" + x1 + " " + y1;

        return {
            path: path
            , parent: { arrowPath: arrowPath1, arrowAngle: arrowAngle1, centerX: x4, centerY: y4 }
            , child: { arrowPath: arrowPath2, arrowAngle: arrowAngle2, centerX: x1, centerY: y1 }
        };
    }

    this.removeConnection = function (options) {
        options.line.remove();
        if (options.showParentArrow) {
            options.parentArrow.remove();
        }
        if (options.showChildArrow) {
            options.childArrow.remove();
        }
    };

    var details = calcPath();
    if (options.line === undefined) {
        options.sb.extend(options, {
            line: this.path(details.path).attr({ stroke: options.lineColor, fill: "none", "stroke-width": options.lineWidth, "fill-opacity": options.lineOpacity, opacity: options.lineOpacity }) //.toBack() //, "arrow-end": "classic"
            , parentArrow: this.path(details.parent.arrowPath).attr({ fill: options.lineColor, "fill-opacity": options.lineOpacity, stroke: "none", opacity: options.lineOpacity }).transform("r" + (90 + details.parent.arrowAngle) + "," + details.parent.centerX + "," + details.parent.centerY) //.toBack()
            , childArrow: this.path(details.child.arrowPath).attr({ fill: options.lineColor, "fill-opacity": options.lineOpacity, stroke: "none", opacity: options.lineOpacity }).transform("r" + (90 + details.child.arrowAngle) + "," + details.child.centerX + "," + details.child.centerY) //.toBack()
        });

        if (!options.showParentArrow) options.parentArrow.hide();
        if (!options.showChildArrow) options.childArrow.hide();

    } else {
        options.line.attr({ path: details.path, stroke: options.lineColor, "stroke-width": options.lineWidth, "fill-opacity": options.lineOpacity, opacity: options.lineOpacity }); //.toBack(); //, "arrow-end": "classic"
        if (options.showParentArrow) {
            options.parentArrow.show();
            options.parentArrow.attr({ path: details.parent.arrowPath, fill: options.lineColor, "stroke-width": options.lineWidth, "fill-opacity": options.lineOpacity, opacity: options.lineOpacity }).transform("r" + (90 + details.parent.arrowAngle) + "," + details.parent.centerX + "," + details.parent.centerY); //.toBack();
        } else if (options.parentArrow) {
            options.parentArrow.hide();
        }

        if (options.showChildArrow) {
            options.childArrow.show();
            options.childArrow.attr({ path: details.child.arrowPath, fill: options.lineColor, "stroke-width": options.lineWidth, "fill-opacity": options.lineOpacity, opacity: options.lineOpacity }).transform("r" + (90 + details.child.arrowAngle) + "," + details.child.centerX + "," + details.child.centerY); //.toBack();
        } else if (options.childArrow) {
            options.childArrow.hide();
        }
    }
    return options;
};;(function (R) {
    R.fn.handle = function (x, y) {
        return this.path(icons.handle + c);
    };

    R.fn.editor = function (x, y) {
        return this.path(icons.editor + c);
    };

    R.fn.deleter = function (x, y) {
        return this.path(icons.deleter + c);
    };

    R.fn.searcher = function (x, y) {
        return this.path(icons.searcher + c);
    };

    R.fn.link = function (x, y) {
        return this.path(icons.link + c);
    };

    R.fn.up = function (x, y) {
        return this.path(icons.up);
    };

    R.fn.down = function (x, y) {
        return this.path(icons.up).transform("r180");
    };

    R.fn.setting = function (x, y) {
        return this.path(icons.settings + c).transform("s,.9,.9");
    };

    R.fn.arrow = function() {
        return this.path(icons.arrow + c);
    };

    R.fn.linkArrow = function() {
        return this.path(icons.arrow + c).attr({ fill: '#648CB2' });
    };

    R.fn.speechbubble = function(x, y, txt) {
        var _bubble = this.set();
        _bubble.push(this.path(icons.speechbubble).transform(["t", x, ",", y].join()).scale(6,4).scale(-1,1)).attr({fill: "#fff", stroke: "#000", "stroke-width": 3});
        _bubble.push(this.text(x + 10, y + 10, txt).attr({"font-size": 12}));
        return _bubble;
    };

    R.fn.resize = function(img) {
        //return this.rect(0,0,10,10);
        //return this.path("M8.818,9.464l9.712,10.792L8.818,9.464zM 11.783,20.823 17.326,18.918 19.804,13.604 24.348,26.72 zM 15.565,8.896 10.022,10.802 7.544,16.115 3,3 z");
        //var _resize = this.set();
        //_resize.push(this.path("M 56.6875 0.125 L 29.875 26.625 L -0.3125 56.53125 L 25.46875 56.53125 L 56.6875 25.375 L 56.6875 0.125 z").attr({stroke: "#fff", fill: "#fff"}).transform("s.5") );
        //_resize.push(this.path("M 0,56.829931 56.539823,0.29010776 zM 14.289023,56.569787 56.693882,14.164916 zM 25.229778,56.521813 57.03342,24.71816 z").attr({stroke: "#000"}).transform("s.5") );
        //_resize.push(this.path(icons.resizeMarker2));
        //return _resize;
        return this.image(img, 0, 0, 22, 23);
    };

    R.fn.slider = function(length, start, end, initVal, onSlide, onDone) {
        
        var _slider = this.set();
        _slider.push(this.rect(10, 10, 10, length, 5).attr({fill: "#ccc", stroke: "#333", "stroke-width": 2}));
        _slider.push(this.path(icons.sliderHandle).attr({fill: "#eee", stroke: "#ccc"}).transform("r270"));

        _slider.setValue = function(val) {
            var _setCurrent = ((val * length) / end);
            _slider[1].transform(["t", _slider[1].attr("x"), _setCurrent, "r270"].join());
            _lockX = _slider[1].attr("x"), _initY = _slider[1].attr("y"), _lyp = _setCurrent, _lastDy = 0;
        };

        //globals
        var _lockX, _initY, _lyp, _lastDy = 0;

        //set current value
        _slider.setValue(initVal);

        var init = function (x, y) {
        };

        var move = function (dx, dy) {
            dy = _lyp + dy;
            if (dy < 0) dy = 0;
            if (dy > length - 15) dy = length - 15;
            _lastDy = dy;

            _slider[1].transform(["t", _lockX, dy, "r270"].join());

            var currentValue = (((dy - _initY) * end) / length) + start;
            if (Slatebox.isFunction(onSlide)) {
                onSlide.apply(this, [currentValue]);
            };
        };

        var up = function () {
            _lyp = _lastDy - _initY;
            var currentValue = ((_lyp * end) / length) + start;
            if (Slatebox.isFunction(onSlide)) {
                onSlide.apply(this, [currentValue]);
            };
            if (Slatebox.isFunction(onDone)) {
                onDone.apply(this, [currentValue]);
            };
        };

        _slider[1].drag(move, init, up);

        return _slider;
    };

    var c = "M16,1.466C7.973,1.466,1.466,7.973,1.466,16c0,8.027,6.507,14.534,14.534,14.534c8.027,0,14.534-6.507,14.534-14.534C30.534,7.973,24.027,1.466,16,1.466z";

    var icons = {
        handle: "M26.33,15.836l-3.893-1.545l3.136-7.9c0.28-0.705-0.064-1.505-0.771-1.785c-0.707-0.28-1.506,0.065-1.785,0.771l-3.136,7.9l-4.88-1.937l3.135-7.9c0.281-0.706-0.064-1.506-0.77-1.786c-0.706-0.279-1.506,0.065-1.785,0.771l-3.136,7.9L8.554,8.781l-1.614,4.066l2.15,0.854l-2.537,6.391c-0.61,1.54,0.143,3.283,1.683,3.895l1.626,0.646L8.985,26.84c-0.407,1.025,0.095,2.188,1.122,2.596l0.93,0.369c1.026,0.408,2.188-0.095,2.596-1.121l0.877-2.207l1.858,0.737c1.54,0.611,3.284-0.142,3.896-1.682l2.535-6.391l1.918,0.761L26.33,15.836z"
        , editor: "M25.31,2.872l-3.384-2.127c-0.854-0.536-1.979-0.278-2.517,0.576l-1.334,2.123l6.474,4.066l1.335-2.122C26.42,4.533,26.164,3.407,25.31,2.872zM6.555,21.786l6.474,4.066L23.581,9.054l-6.477-4.067L6.555,21.786zM5.566,26.952l-0.143,3.819l3.379-1.787l3.14-1.658l-6.246-3.925L5.566,26.952z"
        , deleter: "M24.778,21.419 19.276,15.917 24.777,10.415 21.949,7.585 16.447,13.087 10.945,7.585 8.117,10.415 13.618,15.917 8.116,21.419 10.946,24.248 16.447,18.746 21.948,24.248z"
        , searcher: "M29.772,26.433l-7.126-7.126c0.96-1.583,1.523-3.435,1.524-5.421C24.169,8.093,19.478,3.401,13.688,3.399C7.897,3.401,3.204,8.093,3.204,13.885c0,5.789,4.693,10.481,10.484,10.481c1.987,0,3.839-0.563,5.422-1.523l7.128,7.127L29.772,26.433zM7.203,13.885c0.006-3.582,2.903-6.478,6.484-6.486c3.579,0.008,6.478,2.904,6.484,6.486c-0.007,3.58-2.905,6.476-6.484,6.484C10.106,20.361,7.209,17.465,7.203,13.885z"
        , up: "M1.67892,15.48059l23.55337,0l-11.37616,-13.92457l-12.17721,13.92457z"
        , arrow: "M16,1.466C7.973,1.466,1.466,7.973,1.466,16c0,8.027,6.507,14.534,14.534,14.534c8.027,0,14.534-6.507,14.534-14.534C30.534,7.973,24.027,1.466,16,1.466zM13.665,25.725l-3.536-3.539l6.187-6.187l-6.187-6.187l3.536-3.536l9.724,9.723L13.665,25.725z"
        , settings: "M16.015,12.03c-2.156,0-3.903,1.747-3.903,3.903c0,2.155,1.747,3.903,3.903,3.903c0.494,0,0.962-0.102,1.397-0.27l0.836,1.285l1.359-0.885l-0.831-1.276c0.705-0.706,1.142-1.681,1.142-2.757C19.918,13.777,18.171,12.03,16.015,12.03zM16,1.466C7.973,1.466,1.466,7.973,1.466,16c0,8.027,6.507,14.534,14.534,14.534c8.027,0,14.534-6.507,14.534-14.534C30.534,7.973,24.027,1.466,16,1.466zM26.174,20.809c-0.241,0.504-0.513,0.99-0.826,1.45L22.19,21.58c-0.481,0.526-1.029,0.994-1.634,1.385l0.119,3.202c-0.507,0.23-1.028,0.421-1.569,0.57l-1.955-2.514c-0.372,0.051-0.75,0.086-1.136,0.086c-0.356,0-0.706-0.029-1.051-0.074l-1.945,2.5c-0.541-0.151-1.065-0.342-1.57-0.569l0.117-3.146c-0.634-0.398-1.208-0.88-1.712-1.427L6.78,22.251c-0.313-0.456-0.583-0.944-0.826-1.448l2.088-2.309c-0.226-0.703-0.354-1.451-0.385-2.223l-2.768-1.464c0.055-0.563,0.165-1.107,0.301-1.643l3.084-0.427c0.29-0.702,0.675-1.352,1.135-1.942L8.227,7.894c0.399-0.389,0.83-0.744,1.283-1.07l2.663,1.672c0.65-0.337,1.349-0.593,2.085-0.75l0.968-3.001c0.278-0.021,0.555-0.042,0.837-0.042c0.282,0,0.56,0.022,0.837,0.042l0.976,3.028c0.72,0.163,1.401,0.416,2.036,0.75l2.704-1.697c0.455,0.326,0.887,0.681,1.285,1.07l-1.216,2.986c0.428,0.564,0.793,1.181,1.068,1.845l3.185,0.441c0.135,0.535,0.247,1.081,0.302,1.643l-2.867,1.516c-0.034,0.726-0.15,1.43-0.355,2.1L26.174,20.809z"
        , sliderHandle: "M16,3.5c-4.142,0-7.5,3.358-7.5,7.5c0,4.143,7.5,18.121,7.5,18.121S23.5,15.143,23.5,11C23.5,6.858,20.143,3.5,16,3.5z M16,14.584z"
        , speechbubble: "M16,5.333c-7.732,0-14,4.701-14,10.5c0,1.982,0.741,3.833,2.016,5.414L2,25.667l5.613-1.441c2.339,1.317,5.237,2.107,8.387,2.107c7.732,0,14-4.701,14-10.5C30,10.034,23.732,5.333,16,5.333z"
        , resizeMarker: "M -0.124,19.563999 19.440001,0M 3.891,20.542999 20.047001,4.3850002M 8.8249998,20.936001 20.936001,8.8249998"
        , link: "M15.667,4.601c-1.684,1.685-2.34,3.985-2.025,6.173l3.122-3.122c0.004-0.005,0.014-0.008,0.016-0.012c0.21-0.403,0.464-0.789,0.802-1.126c1.774-1.776,4.651-1.775,6.428,0c1.775,1.773,1.777,4.652,0.002,6.429c-0.34,0.34-0.727,0.593-1.131,0.804c-0.004,0.002-0.006,0.006-0.01,0.01l-3.123,3.123c2.188,0.316,4.492-0.34,6.176-2.023c2.832-2.832,2.83-7.423,0-10.255C23.09,1.77,18.499,1.77,15.667,4.601zM14.557,22.067c-0.209,0.405-0.462,0.791-0.801,1.131c-1.775,1.774-4.656,1.774-6.431,0c-1.775-1.774-1.775-4.653,0-6.43c0.339-0.338,0.725-0.591,1.128-0.8c0.004-0.006,0.005-0.012,0.011-0.016l3.121-3.123c-2.187-0.316-4.489,0.342-6.172,2.024c-2.831,2.831-2.83,7.423,0,10.255c2.833,2.831,7.424,2.831,10.257,0c1.684-1.684,2.342-3.986,2.023-6.175l-3.125,3.123C14.565,22.063,14.561,22.065,14.557,22.067zM9.441,18.885l2.197,2.197c0.537,0.537,1.417,0.537,1.953,0l8.302-8.302c0.539-0.536,0.539-1.417,0.002-1.952l-2.199-2.197c-0.536-0.539-1.416-0.539-1.952-0.002l-8.302,8.303C8.904,17.469,8.904,18.349,9.441,18.885z"
        , resizeMarker2: "M22.5,8.5v3.168l3.832,3.832L22.5,19.332V22.5l7-7L22.5,8.5zM8.5,22.5v-3.168L4.667,15.5L8.5,11.668V8.5l-7,7L8.5,22.5zM15.5,14.101c-0.928,0-1.68,0.751-1.68,1.68c0,0.927,0.752,1.681,1.68,1.681c0.927,0,1.68-0.754,1.68-1.681C17.18,14.852,16.427,14.101,15.5,14.101zM10.46,14.101c-0.928,0-1.68,0.751-1.68,1.68c0,0.927,0.752,1.681,1.68,1.681s1.68-0.754,1.68-1.681C12.14,14.852,11.388,14.101,10.46,14.101zM20.541,14.101c-0.928,0-1.682,0.751-1.682,1.68c0,0.927,0.754,1.681,1.682,1.681s1.68-0.754,1.68-1.681C22.221,14.852,21.469,14.101,20.541,14.101z"
    };
})(Raphael);
