if (typeof Prototype == 'undefined' || !Prototype.Version.match("1.6")) throw ("Prototype-ScraplrUI library require Prototype library >= 1.6.0"); (function(p) {
    var b = p.Browser,
    n = navigator;
    if (b.WebKit) {
        b.WebKitVersion = parseFloat(n.userAgent.match(/AppleWebKit\/([\d\.\+]*)/)[1]);
        b.Safari2 = (b.WebKitVersion < 420);
    }
    if (b.IE) {
        b.IEVersion = parseFloat(n.appVersion.split(';')[1].strip().split(' ')[1]);
        b.IE6 = b.IEVersion == 6;
        b.IE7 = b.IEVersion == 7;
    }
    p.falseFunction = function() {
        return false;
    };
    p.trueFunction = function() {
        return true;
    };
})(Prototype);
var ScraplrUI = {
    Abstract: {},
    Ajax: {}
};
Object.extend(Class.Methods, {
    extend: Object.extend.methodize(),
    addMethods: Class.Methods.addMethods.wrap(function(proceed, source) {
        if (!source) return this;
        if (!source.hasOwnProperty('methodsAdded')) return proceed(source);
        var callback = source.methodsAdded;
        delete source.methodsAdded;
        proceed(source);
        callback.call(source, this);
        source.methodsAdded = callback;
        return this;
    }),
    addMethod: function(name, lambda) {
        var methods = {};
        methods[name] = lambda;
        return this.addMethods(methods);
    },
    method: function(name) {
        return this.prototype[name].valueOf();
    },
    classMethod: function() {
        $A(arguments).flatten().each(function(method) {
            this[method] = (function() {
                return this[method].apply(this, arguments);
            }).bind(this.prototype);
        },
        this);
        return this;
    },
    undefMethod: function(name) {
        this.prototype[name] = undefined;
        return this;
    },
    removeMethod: function(name) {
        delete this.prototype[name];
        return this;
    },
    aliasMethod: function(newName, name) {
        this.prototype[newName] = this.prototype[name];
        return this;
    },
    aliasMethodChain: function(target, feature) {
        feature = feature.camelcase();
        this.aliasMethod(target + "Without" + feature, target);
        this.aliasMethod(target, target + "With" + feature);
        return this;
    }
});
Object.extend(Number.prototype, {
    snap: function(round) {
        return parseInt(round == 1 ? this: (this / round).floor() * round,10);
    }
});
Object.extend(String.prototype, {
    camelcase: function() {
        var string = this.dasherize().camelize();
        return string.charAt(0).toUpperCase() + string.slice(1);
    },
    makeElement: function() {
        var wrapper = new Element('div');
        wrapper.innerHTML = this;
        return wrapper.down();
    }
});
Object.extend(Array.prototype, {
    isEmpty: function() {
        return ! this.length;
    },
    at: function(index) {
        return this[index < 0 ? this.length + index: index];
    },
    removeAt: function(index) {
        if ( - index > this.length) return;
        return this.splice(index, 1)[0];
    },
    removeIf: function(iterator, context) {
        for (var i = this.length - 1, objects = []; i >= 0; i--)
        if (iterator.call(context, this[i], i)) objects.push(this.removeAt(i));
        return objects.reverse();
    },
    remove: function(object) {
        return this.removeIf(function(member) {
            return member === object;
        }).length;
    },
    insert: function(index) {
        if (index > this.length) this.length = index;
        else if (index < 0) index = this.length + index + 1;
        this.splice.apply(this, [index, 0].concat($A(arguments).slice(1)));
        return this;
    }
});
Array.prototype.empty = Array.prototype.isEmpty;
Element.addMethods({
    getScrollDimensions: function(element) {
        element = $(element);
        return {
            width: element.scrollWidth,
            height: element.scrollHeight
        };
    },
    getScrollOffset: function(element) {
        element = $(element);
        return Element._returnOffset(element.scrollLeft, element.scrollTop);
    },
    setScrollOffset: function(element, offset) {
        element = $(element);
        if (arguments.length == 3) offset = {
            left: offset,
            top: arguments[2]
        };
        element.scrollLeft = offset.left;
        element.scrollTop = offset.top;
        return element;
    },
    getNumStyle: function(element, style) {
        var value = parseFloat($(element).getStyle(style));
        return isNaN(value) ? null: value;
    },
    appendText: function(element, text) {
        element = $(element);
        element.appendChild(document.createTextNode(String.interpret(text)));
        return element;
    }
});
document.whenReady = (function() {
    var queue = [];
    document.observe('dom:loaded',
    function() {
        queue.invoke('call', document);
        queue.clear();
        document.whenReady = function(callback) {
            callback.bind(document).defer();
        };
    });
    return function(callback) {
        queue.push(callback);
    };
})();
Object.extend(document.viewport, {
    getScrollOffset: document.viewport.getScrollOffsets,
    setScrollOffset: function(offset) {
        Element.setScrollOffset(Prototype.Browser.WebKit ? document.body: document.documentElement, offset);
    },
    getScrollDimensions: function() {
        return Element.getScrollDimensions(Prototype.Browser.WebKit ? document.body: document.documentElement);
    }
});
document.whenReady(function() {
    window.$head = $(document.getElementsByTagName('head')[0]);
    window.$body = $(document.body);
}); (function() {
    ScraplrUI.Options = {
        methodsAdded: function(klass) {
            klass.classMethod($w(' setOptions allOptions optionsGetter optionsSetter optionsAccessor '));
        },
        setOptions: function(options) {
            if (!this.hasOwnProperty('options')) this.options = this.allOptions();
            this.options = Object.extend(this.options, options || {});
        },
        allOptions: function() {
            var superclass = this.constructor.superclass,
            ancestor = superclass && superclass.prototype;
            return (ancestor && ancestor.allOptions) ? Object.extend(ancestor.allOptions(), this.options) : Object.clone(this.options);
        },
        optionsGetter: function() {
            addOptionsAccessors(this, arguments, false);
        },
        optionsSetter: function() {
            addOptionsAccessors(this, arguments, true);
        },
        optionsAccessor: function() {
            this.optionsGetter.apply(this, arguments);
            this.optionsSetter.apply(this, arguments);
        }
    };
    function addOptionsAccessors(receiver, names, areSetters) {
        names = $A(names).flatten();
        if (names.empty()) names = Object.keys(receiver.allOptions());
        names.each(function(name) {
            var accessorName = (areSetters ? 'set': 'get') + name.camelcase();
            receiver[accessorName] = receiver[accessorName] || (areSetters ?
            function(value) {
                return this.options[name] = value;
            }: function() {
                return this.options[name];
            });
        });
    }
})();
ScraplrUI.IframeShim = Class.create(ScraplrUI.Options, {
    options: {
        parent: document.body
    },
    initialize: function(options) {
        this.setOptions(options);
        this.element = new Element('iframe', {
            style: 'position:absolute;filter:progid:DXImageTransform.Microsoft.Alpha(opacity=1);',
            src: 'javascript:false;',
            frameborder: 0
        });
        $(this.options.parent || document.body).insert(this.element);
    },
    hide: function() {
        this.element.hide();
        return this;
    },
    show: function() {
        this.element.show();
        return this;
    },
    positionUnder: function(element) {
        var element = $(element),
        offset = element.cumulativeOffset(),
        dimensions = element.getDimensions(),
        style = {
            left: offset[0] + 'px',
            top: offset[1] + 'px',
            width: dimensions.width + 'px',
            height: dimensions.height + 'px',
            zIndex: element.getStyle('zIndex') - 1
        };
        this.element.setStyle(style).show();
        return this;
    },
    setBounds: function(bounds) {
        for (prop in bounds) {
            bounds[prop] = parseInt(bounds[prop],10) + 'px';
        }
        this.element.setStyle(bounds);
        return this;
    },
    setSize: function(width, height) {
        this.element.style.width = parseInt(width,10) + "px";
        this.element.style.height = parseInt(height,10) + "px";
        return this;
    },
    setPosition: function(top, left) {
        this.element.style.top = parseInt(top,10) + "px";
        this.element.style.left = parseInt(left,10) + "px";
        return this;
    },
    destroy: function() {
        if (this.element) this.element.remove();
        return this;
    }
});
Element.addMethods({
    enableDrag: function(element) {
        return $(element).writeAttribute('draggable');
    },
    disableDrag: function(element) {
        return $(element).writeAttribute('draggable', null);
    },
    isDraggable: function(element) {
        return $(element).hasAttribute('draggable');
    }
}); (function() {
    var initPointer,
    draggedElement;
    document.observe('mousedown',
    function(event) {
        if (draggedElement = findDraggable(event.element())) {
            event.preventDefault();
            initPointer = event.pointer();
            document.observe('mousemove', startDrag);
            document.observe('mouseup', cancelDrag);
        }
    });
    function findDraggable(element) {
        while (element && element !== document) {
            if (element.hasAttribute('draggable')) return element;
            element = $(element.parentNode);
        }
    };
    function startDrag(event) {
        document.stopObserving('mousemove', startDrag).stopObserving('mouseup', cancelDrag).observe('mousemove', drag).observe('mouseup', endDrag);
        fire('drag:started', event);
    };
    function cancelDrag(event) {
        document.stopObserving('mousemove', startDrag).stopObserving('mouseup', cancelDrag);
    };
    function drag(event) {
        fire('drag:updated', event);
    };
    function endDrag(event) {
        document.stopObserving('mousemove', drag).stopObserving('mouseup', endDrag);
        fire('drag:ended', event);
    };
    function fire(eventName, event) {
        var pointer = event.pointer();
        draggedElement.fire(eventName, {
            dx: pointer.x - initPointer.x,
            dy: pointer.y - initPointer.y,
            mouseEvent: event
        });
    };
})();
var CSS = (function() {
    function fixPNG() {
        parseStylesheet.apply(this, $A(arguments).concat(fixRule));
    };
    function parseStylesheet() {
        var patterns = $A(arguments);
        var method = patterns.pop();
        var styleSheets = $A(document.styleSheets);
        if (patterns.length > 0) {
            styleSheets = styleSheets.select(function(css) {
                return patterns.any(function(pattern) {
                    return css.href && css.href.match(pattern);
                });
            });
        }
        styleSheets.each(function(styleSheet) {
            fixStylesheet.call(this, styleSheet, method);
        });
    };
    function fixStylesheet(stylesheet, method) {
        if (stylesheet.imports) $A(stylesheet.imports).each(fixStylesheet);
        var href = stylesheet.href || document.location.href;
        var docPath = href.substr(0, href.lastIndexOf('/'));
        $A(stylesheet.rules || stylesheet.cssRules).each(function(rule) {
            method.call(this, rule, docPath);
        });
    };
    var filterPattern = 'progid:DXImageTransform.Microsoft.AlphaImageLoader(src="#{src}",sizingMethod="#{method}")';
    function fixRule(rule, docPath) {
        var bgImg = rule.style.backgroundImage;
        if (bgImg && bgImg != 'none' && bgImg.match(/^url[("']+(.*\.png)[)"']+$/i)) {
            var src = RegExp.$1;
            var bgRepeat = rule.style.backgroundRepeat;
            if (src[0] != '/') src = docPath + "/" + src;
            rule.style.filter = filterPattern.interpolate({
                src: src,
                method: bgRepeat == "no-repeat" ? "crop": "scale"
            });
            rule.style.backgroundImage = "none";
        }
    };
    var preloadedImages = new Hash();
    function preloadRule(rule, docPath) {
        var bgImg = rule.style.backgroundImage;
        if (bgImg && bgImg != 'none' && bgImg != 'initial') {
            if (!preloadedImages.get(bgImg)) {
                bgImg.match(/^url[("']+(.*)[)"']+$/i);
                var src = RegExp.$1;
                if (! (src[0] == '/' || src.match(/^file:/) || src.match(/^https?:/))) src = docPath + "/" + src;
                preloadedImages.set(bgImg, true);
                var image = new Image();
                image.src = src;
            }
        }
    }
    return {
        fixPNG: (Prototype.Browser.IE && Prototype.Browser.IEVersion < 7) ? fixPNG: Prototype.emptyFunction,
        addRule: function(css) {
            var style = new Element('style', {
                type: 'text/css',
                media: 'screen'
            });
            $head.insert(style);
            if (style.styleSheet) style.styleSheet.cssText = css;
            else style.appendText(css);
            return style;
        },
        preloadImages: function() {
            if (navigator.userAgent.match(/Firefox\/3/)) return;
            parseStylesheet.apply(this, $A(arguments).concat(preloadRule));
        }
    };
})();
Object.extend(Date.prototype, {
    addDays: function(days) {
        return new Date(this.getFullYear(), this.getMonth(), this.getDate() + days, this.getHours(), this.getMinutes(), this.getSeconds(), this.getMilliseconds());
    },
    succ: function() {
        return this.addDays(1);
    },
    firstOfMonth: function() {
        return new Date(this.getFullYear(), this.getMonth(), 1);
    },
    endOfMonth: function() {
        return new Date(this.getFullYear(), this.getMonth() + 1, 0);
    },
    getDayOfYear: function() {
        return Math.ceil((this - new Date(this.getFullYear(), 0, 1)) / 86400000);
    },
    strftime: function(grammar) {
        var parts = {},
        i18n = Date.default_i18n;
        var lambda = function(date, part) {
            switch (part) {
            case 'a':
                return i18n.WEEKDAYS_MEDIUM[date.getDay()];
            case 'A':
                return i18n.WEEKDAYS[date.getDay()];
            case 'b':
            case 'h':
                return i18n.MONTHS_SHORT[date.getMonth()];
            case 'B':
                return i18n.MONTHS[date.getMonth()];
            case 'C':
                return Math.floor(date.getFullYear() / 100);
            case 'd':
                return date.getDate().toPaddedString(2);
            case 'e':
                return date.getDate();
            case 'j':
                return date.getDayOfYear();
            case 'm':
                return (date.getMonth() + 1).toPaddedString(2);
            case 'u':
                return date.getDay() || 7;
            case 'w':
                return date.getDay();
            case 'y':
                return date.getFullYear().toString().substring(2);
            case 'Y':
                return date.getFullYear();
            case 'H':
                return date.getHours().toPaddedString(2);
            case 'I':
                return (date.getHours() % 12).toPaddedString(2);
            case 'M':
                return date.getMinutes().toPaddedString(2);
            case 'p':
                return date.getHours() < 12 ? 'am': 'pm';
            case 'S':
                return date.getSeconds().toPaddedString(2);
            case 'n':
                return '\n';
            case 't':
                return '\t';
            case 'D':
                return date.strftime('%m/%d/%y');
            case 'r':
                return date.strftime('%I:%M:%S %p');
            case 'R':
                return date.strftime('%H:%M:%S');
            case 'T':
                return date.strftime('%H:%M:%S');
            case 'c':
                return date.strftime(i18n.FORMAT_DATETIME);
            case 'x':
                return date.strftime(i18n.FORMAT_DATE);
            case 'X':
                return date.strftime(i18n.FORMAT_TIME);
            }
        };
        grammar.scan(/\w+/,
        function(e) {
            var part = e.first();
            parts[part] = lambda(this, part);
        }.bind(this));
        return grammar.interpolate(parts, Date.STRFT_GRAMMER);
    },
    equalsDate: function(other) {
        return (this.getMonth() == other.getMonth() && this.getDate() == other.getDate() && this.getFullYear() == other.getFullYear());
    }
});
Object.extend(Date, {
    STRFT_GRAMMER: /(^|.|\r|\n)(\%(\w+))/,
    default_i18n: {
        MONTHS_SHORT: $w('Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec'),
        MONTHS: $w('January February March April May June July August September October November December'),
        WEEKDAYS_MEDIUM: $w('Sun Mon Tue Wed Thu Fri Sat'),
        WEEKDAYS: $w('Sunday Monday Tuesday Wednesday Thursday Friday Saturday'),
        FORMAT_DATE: '%m/%d/%Y',
        FORMAT_TIME: '%H:%M:%S',
        FORMAT_DATETIME: '%x %X'
    },
    parseString: function(dateString, format) {
        var date = new Date(),
        i18n = Date.default_i18n;
        format = format.replace('%D', '%m/%d/%y');
        format = format.replace('%T', '%H:%M:%S').replace('%r', '%I:%M:%S %p').replace('%R', '%H:%M:%S');
        format = format.replace('%c', i18n.FORMAT_DATETIME).replace('%x', i18n.FORMAT_DATE).replace('%X', i18n.FORMAT_TIME);
        var tokens = format.match(/%./g);
        dateString.split(/[^A-Za-z0-9\u00A1-\uFFFF]+/).each(function(e, i) {
            switch (tokens[i]) {
            case '%a':
            case '%A':
            case '%u':
            case '%w':
                break;
            case '%b':
            case '%h':
                date.setMonth(i18n.MONTHS_SHORT.indexOf(e));
                break;
            case '%B':
                date.setMonth(i18n.MONTHS.indexOf(e));
                break;
            case '%C':
                break;
            case '%d':
            case '%e':
                date.setDate(parseInt(e, 10));
                break;
            case '%j':
                break;
            case '%m':
                date.setMonth(parseInt(e, 10) - 1);
                break;
            case '%w':
                date.setDay(parseInt(e, 10));
                break;
            case '%y':
                var year = parseInt(e, 10);
                if (year < 50) year += 2000;
                if (year < 100) year += 1900;
                date.setYear(year);
                break;
            case '%Y':
                date.setFullYear(parseInt(e, 10));
                break;
            case '%H':
                date.setHours(parseInt(e, 10));
                break;
            case '%I':
                date.setHours(parseInt(e, 10));
                break;
            case '%M':
                date.setMinutes(parseInt(e, 10));
                break;
            case '%p':
                if (e == 'pm') date.setHours(date.getHours() + 12);
                break;
            case '%S':
                date.setSeconds(parseInt(e, 10));
                break;
            }
        });
        return date;
    }
});
ScraplrUI.Benchmark = {
    benchmark: function(lambda, iterations) {
        var date = new Date(); (iterations || 1).times(lambda);
        return (new Date() - date) / 1000;
    }
};
Element.addMethods({
    enableDrag: function(element) {
        return $(element).writeAttribute('draggable');
    },
    disableDrag: function(element) {
        return $(element).writeAttribute('draggable', null);
    },
    isDraggable: function(element) {
        return $(element).hasAttribute('draggable');
    }
}); (function() {
    var initPointer,
    draggedElement;
    document.observe('mousedown',
    function(event) {
        if (draggedElement = findDraggable(event.element())) {
            event.preventDefault();
            initPointer = event.pointer();
            document.observe('mousemove', startDrag);
            document.observe('mouseup', cancelDrag);
        }
    });
    function findDraggable(element) {
        while (element && element !== document) {
            if (element.hasAttribute('draggable')) return element;
            element = $(element.parentNode);
        }
    };
    function startDrag(event) {
        document.stopObserving('mousemove', startDrag).stopObserving('mouseup', cancelDrag).observe('mousemove', drag).observe('mouseup', endDrag);
        fire('drag:started', event);
    };
    function cancelDrag(event) {
        document.stopObserving('mousemove', startDrag).stopObserving('mouseup', cancelDrag);
    };
    function drag(event) {
        fire('drag:updated', event);
    };
    function endDrag(event) {
        document.stopObserving('mousemove', drag).stopObserving('mouseup', endDrag);
        fire('drag:ended', event);
    };
    function fire(eventName, event) {
        var pointer = event.pointer();
        draggedElement.fire(eventName, {
            dx: pointer.x - initPointer.x,
            dy: pointer.y - initPointer.y,
            mouseEvent: event
        });
    };
})(); (function(browser) {
    var watch,
    change,
    changed,
    fragment;
    function getFragment() {
        return top.location.hash.substr(1);
    }
    function setFragment(fragment) {
        top.location.hash = fragment || '';
    }
    function fragmentChanged(newFragment) {
        document.fire('state:changed', {
            value: newFragment,
            previousValue: fragment
        });
        fragment = newFragment;
    }
    document.whenReady(function() {
        var initialFragment = getFragment();
        if (initialFragment) fragmentChanged(initialFragment);
        if (browser.IE) {
            var iframe = new Element('iframe', {
                style: 'display: none'
            });
            $body.appendChild(iframe);
            var contentWindow = iframe.contentWindow;
            function writeFragmentToIFrame(fragment) {
                var doc = contentWindow.document;
                doc.open();
                doc.write(fragment || '');
                doc.close();
            }
            function readFragmentFromIFrame() {
                return contentWindow.document.body.innerText;
            }
            writeFragmentToIFrame(fragment);
            watch = function() {
                var newFragment = readFragmentFromIFrame();
                if (newFragment != fragment) {
                    setFragment(newFragment);
                    fragmentChanged(newFragment);
                }
            };
            change = function(newFragment) {
                if (newFragment != fragment) writeFragmentToIFrame(newFragment);
            };
        } else {
            var counter = history.length,
            fragments = [];
            watch = function() {
                var newFragment = getFragment(),
                newCounter = history.length;
                if (newFragment != fragment) {
                    fragmentChanged(newFragment);
                } else if (newCounter != counter) {
                    fragmentChanged(fragments[newCounter - 1]);
                    fragment = newFragment;
                }
                counter = newCounter;
            };
            change = function(newFragment) {
                setFragment(newFragment);
                if (browser.WebKit) {
                    fragments[history.length] = newFragment;
                }
            };
        }
    });
    ScraplrUI.State = {
        change: function(value) {
            change(value);
            changed = true;
        },
        isSupported: Prototype.trueFunction,
        _watch: function() {
            watch();
        }
    };
    if (browser.Opera && parseFloat(navigator.appVersion) < 9.5) {
        document.write('<img src="javascript:location.href=\'javascript:ScraplrUI.State._watch();\';" style="position:absolute; top:-1000px" />');
    }
    setInterval(function() {
        if (changed) watch();
    },
    50);
})(Prototype.Browser);
ScraplrUI.IframeShim = Class.create(ScraplrUI.Options, {
    options: {
        parent: document.body
    },
    initialize: function(options) {
        this.setOptions(options);
        this.element = new Element('iframe', {
            style: 'position:absolute;filter:progid:DXImageTransform.Microsoft.Alpha(opacity=1);',
            src: 'javascript:false;',
            frameborder: 0
        });
        $(this.options.parent || document.body).insert(this.element);
    },
    hide: function() {
        this.element.hide();
        return this;
    },
    show: function() {
        this.element.show();
        return this;
    },
    positionUnder: function(element) {
        var element = $(element),
        offset = element.cumulativeOffset(),
        dimensions = element.getDimensions(),
        style = {
            left: offset[0] + 'px',
            top: offset[1] + 'px',
            width: dimensions.width + 'px',
            height: dimensions.height + 'px',
            zIndex: element.getStyle('zIndex') - 1
        };
        this.element.setStyle(style).show();
        return this;
    },
    setBounds: function(bounds) {
        for (prop in bounds) {
            bounds[prop] = parseInt(bounds[prop],10) + 'px';
        }
        this.element.setStyle(bounds);
        return this;
    },
    setSize: function(width, height) {
        this.element.style.width = parseInt(width,10) + "px";
        this.element.style.height = parseInt(height,10) + "px";
        return this;
    },
    setPosition: function(top, left) {
        this.element.style.top = parseInt(top,10) + "px";
        this.element.style.left = parseInt(left,10) + "px";
        return this;
    },
    destroy: function() {
        if (this.element) this.element.remove();
        return this;
    }
});
ScraplrUI.Abstract.Logger = Class.create({
    level: 'debug'
}); (function() {
    var levels = $w(" debug info warn error ");
    levels.each(function(level, index) {
        ScraplrUI.Abstract.Logger.addMethod(level,
        function(message) {
            if (index >= levels.indexOf(this.level)) this._log({
                level: level,
                message: message,
                date: new Date()
            });
        });
    });
})();
ScraplrUI.NullLogger = Class.create(ScraplrUI.Abstract.Logger, {
    _log: Prototype.emptyFunction
});
ScraplrUI.MemLogger = Class.create(ScraplrUI.Abstract.Logger, {
    initialize: function() {
        this.logs = [];
    },
    _log: function(log) {
        this.logs.push(log);
    }
});
ScraplrUI.ConsoleLogger = Class.create(ScraplrUI.Abstract.Logger, {
    _log: function(log) {
        console[log.level](log.message);
    }
});
ScraplrUI.ElementLogger = Class.create(ScraplrUI.Abstract.Logger, {
    initialize: function(element) {
        this.element = $(element);
    },
    format: '<p>(<span class="date">#{date}</span>) ' + '<span class="level">#{level}</span> : ' + '<span class="message">#{message}</span></p>',
    _log: function(log) {
        var entry = this.format.interpolate({
            level: log.level.toUpperCase(),
            message: log.message.escapeHTML(),
            date: log.date.toLocaleTimeString()
        });
        this.element.insert({
            top: entry
        });
    }
});
document.whenReady(function() {
    if ($('log')) ScraplrUI.logger = new ScraplrUI.ElementLogger('log');
    else if (window.console) ScraplrUI.logger = new ScraplrUI.ConsoleLogger();
    else ScraplrUI.logger = new ScraplrUI.MemLogger();
});
ScraplrUI.PullDown = Class.create(ScraplrUI.Options, {
    options: {
        className: '',
        shadow: false,
        position: 'over',
        cloneWidth: false,
        beforeShow: null,
        afterShow: null,
        beforeUpdate: null,
        afterUpdate: null,
        afterCreate: null
    },
    initialize: function(container, options) {
        this.setOptions(options);
        this.container = $(container);
        this.element = new Element('div', {
            className: 'ScraplrUI-widget-dropdown ' + this.options.className,
            style: 'z-index:30;position:absolute;'
        }).hide();
        if (this.options.shadow) this.shadow = new ScraplrUI.Shadow(this.element, {
            theme: this.options.shadow
        }).hide();
        else this.iframe = Prototype.Browser.IE ? new ScraplrUI.IframeShim() : null;
        this.outsideClickHandler = this.outsideClick.bind(this);
        this.placeHandler = this.place.bind(this);
        this.hideHandler = this.hide.bind(this);
    },
    destroy: function() {
        if (this.active) this.element.remove();
        this.element = null;
        this.stopObserving();
    },
    insert: function(elem) {
        return this.element.insert(elem);
    },
    place: function() {
        this.element.clonePosition(this.container, {
            setHeight: false,
            setWidth: this.options.cloneWidth,
            offsetTop: this.options.position == 'below' ? this.container.offsetHeight: 0
        });
        var w = this.element.getWidth();
        var h = this.element.getHeight();
        var t = parseInt(this.element.style.top,10);
        var l = parseInt(this.element.style.left,10);
        if (this.shadow) this.shadow.setBounds({
            top: t,
            left: l,
            width: w,
            height: h
        });
        if (this.iframe) this.iframe.setPosition(t, l).setSize(w, h);
        return this;
    },
    show: function(event) {
        if (this.active) return this;
        this.active = true;
        if (this.options.beforeShow) this.options.beforeShow(this);
        this.element.hide();
        if (this.iframe) this.iframe.show();
        document.body.insert(this.element);
        if (this.shadow) this.shadow.show();
        this.element.show();
        if (this.options.afterShow) this.options.afterShow(this);
        document.observe('mousedown', this.outsideClickHandler);
        Event.observe(window, 'scroll', this.placeHandler);
        Event.observe(window, 'resize', this.hideHandler);
        return this;
    },
    outsideClick: function(event) {
        if (event.findElement('.ScraplrUI-widget-dropdown')) return;
    },
    hide: function() {
        if (this.active) {
            this.active = false;
            if (this.shadow) this.shadow.hide();
            if (this.iframe) this.iframe.hide();
            this.element.remove();
        }
        this.stopObserving();
        return this;
    },
    stopObserving: function() {
        Event.stopObserving(window, 'resize', this.hideHandler);
        Event.stopObserving(window, 'scroll', this.placeHandler);
        document.stopObserving('click', this.outsideClickHandler);
    }
});
ScraplrUI.Shadow = Class.create(ScraplrUI.Options, {
    options: {
        theme: "mac_shadow",
        focus: false,
        zIndex: 100,
        withIFrameShim: true
    },
    initialize: function(element, options) {
        this.setOptions(options);
        this.element = $(element);
        this.create();
        this.iframe = Prototype.Browser.IE && this.options.withIFrameShim ? new ScraplrUI.IframeShim() : null;
        if (Object.isElement(this.element.parentNode)) this.render();
    },
    destroy: function() {
        if (this.shadow.parentNode) this.remove();
    },
    setPosition: function(top, left) {
        if (this.shadowSize) {
            var shadowStyle = this.shadow.style;
            top = parseInt(top,10) - this.shadowSize.top + this.shadowShift.top;
            left = parseInt(left,10) - this.shadowSize.left + this.shadowShift.left;
            shadowStyle.top = top + 'px';
            shadowStyle.left = left + 'px';
            if (this.iframe) this.iframe.setPosition(top, left);
        }
        return this;
    },
    setSize: function(width, height) {
        if (this.shadowSize) {
            try {
                var w = Math.max(0, parseInt(width,10) + this.shadowSize.width - this.shadowShift.width) + "px";
                this.shadow.style.width = w;
                var h = Math.max(0, parseInt(height,10) - this.shadowShift.height) + "px";
                this.shadowContents[1].childElements().each(function(e) {
                    e.style.height = h;
                });
                this.shadowContents.each(function(item) {
                    item.style.width = w;
                });
                if (this.iframe) this.iframe.setSize(width + this.shadowSize.width - this.shadowShift.width, height + this.shadowSize.height - this.shadowShift.height);
            }
            catch(e) {}
        }
        return this;
    },
    setBounds: function(bounds) {
        return this.setPosition(bounds.top, bounds.left).setSize(bounds.width, bounds.height);
    },
    setZIndex: function(zIndex) {
        this.shadow.style.zIndex = zIndex;
        return this;
    },
    show: function() {
        this.render();
        this.shadow.show();
        if (this.iframe) this.iframe.show();
        return this;
    },
    hide: function() {
        this.shadow.hide();
        if (this.iframe) this.iframe.hide();
        return this;
    },
    remove: function() {
        this.shadow.remove();
        return this;
    },
    focus: function() {
        this.options.focus = true;
        this.updateShadow();
        return this;
    },
    blur: function() {
        this.options.focus = false;
        this.updateShadow();
        return this;
    },
    render: function() {
        if (this.element.parentNode && !Object.isElement(this.shadow.parentNode)) {
            this.element.parentNode.appendChild(this.shadow);
            this.computeSize();
            this.setBounds(Object.extend(this.element.getDimensions(), this.getElementPosition()));
            this.shadow.show();
        }
        return this;
    },
    create: function() {
        var zIndex = this.element.getStyle('zIndex');
        if (!zIndex) this.element.setStyle({
            zIndex: this.options.zIndex
        });
        zIndex = (zIndex || this.options.zIndex) - 1;
        this.shadowContents = new Array(3);
        this.shadowContents[0] = new Element("div").insert(new Element("div", {
            className: "shadow_center_wrapper"
        }).insert(new Element("div", {
            className: "n_shadow"
        }))).insert(new Element("div", {
            className: "shadow_right ne_shadow"
        })).insert(new Element("div", {
            className: "shadow_left nw_shadow"
        }));
        this.shadowContents[1] = new Element("div").insert(new Element("div", {
            className: "shadow_center_wrapper c_shadow"
        })).insert(new Element("div", {
            className: "shadow_right e_shadow"
        })).insert(new Element("div", {
            className: "shadow_left w_shadow"
        }));
        this.centerElements = this.shadowContents[1].childElements();
        this.shadowContents[2] = new Element("div").insert(new Element("div", {
            className: "shadow_center_wrapper"
        }).insert(new Element("div", {
            className: "s_shadow"
        }))).insert(new Element("div", {
            className: "shadow_right se_shadow"
        })).insert(new Element("div", {
            className: "shadow_left sw_shadow"
        }));
        this.shadow = new Element("div", {
            className: "shadow_container " + this.options.theme,
            style: "position:absolute; top:-10000px; left:-10000px; display:none; z-index:" + zIndex
        }).insert(this.shadowContents[0]).insert(this.shadowContents[1]).insert(this.shadowContents[2]);
    },
    computeSize: function() {
        if (this.focusedShadowShift) return;
        this.shadow.show();
        var content = this.shadowContents[1].select("div.c_shadow").first();
        this.unfocusedShadowShift = {};
        this.focusedShadowShift = {};
        $w("top left bottom right").each(function(pos) {
            this.unfocusedShadowShift[pos] = content.getNumStyle("padding-" + pos) || 0;
        }.bind(this));
        this.unfocusedShadowShift.width = this.unfocusedShadowShift.left + this.unfocusedShadowShift.right;
        this.unfocusedShadowShift.height = this.unfocusedShadowShift.top + this.unfocusedShadowShift.bottom;
        $w("top left bottom right").each(function(pos) {
            this.focusedShadowShift[pos] = content.getNumStyle("margin-" + pos) || 0;
        }.bind(this));
        this.focusedShadowShift.width = this.focusedShadowShift.left + this.focusedShadowShift.right;
        this.focusedShadowShift.height = this.focusedShadowShift.top + this.focusedShadowShift.bottom;
        this.shadowShift = this.options.focus ? this.focusedShadowShift: this.unfocusedShadowShift;
        this.shadowSize = {
            top: this.shadowContents[0].childElements()[1].getNumStyle("height"),
            left: this.shadowContents[0].childElements()[1].getNumStyle("width"),
            bottom: this.shadowContents[2].childElements()[1].getNumStyle("height"),
            right: this.shadowContents[0].childElements()[2].getNumStyle("width")
        };
        this.shadowSize.width = this.shadowSize.left + this.shadowSize.right;
        this.shadowSize.height = this.shadowSize.top + this.shadowSize.bottom;
        content.setStyle("padding:0; margin:0");
        this.shadow.hide();
    },
    updateShadow: function() {
        this.shadowShift = this.options.focus ? this.focusedShadowShift: this.unfocusedShadowShift;
        var shadowStyle = this.shadow.style,
        pos = this.getElementPosition(),
        size = this.element.getDimensions();
        shadowStyle.top = pos.top - this.shadowSize.top + this.shadowShift.top + 'px';
        shadowStyle.left = pos.left - this.shadowSize.left + this.shadowShift.left + 'px';
        shadowStyle.width = size.width + this.shadowSize.width - this.shadowShift.width + "px";
        var h = size.height - this.shadowShift.height + "px";
        this.centerElements.each(function(e) {
            e.style.height = h;
        });
        var w = size.width + this.shadowSize.width - this.shadowShift.width + "px";
        this.shadowContents.each(function(item) {
            item.style.width = w;
        });
    },
    getElementPosition: function() {
        return {
            top: this.element.getNumStyle("top"),
            left: this.element.getNumStyle("left")
        };
    }
});
document.whenReady(function() {
    CSS.fixPNG("shadow");
});
ScraplrUI.Window = Class.create(ScraplrUI.Options, {
    options: {
        theme: null,
        shadowTheme: null,
        id: null,
        windowManager: null,
        top: null,
        left: null,
        width: 200,
        height: 300,
        minHeight: 100,
        minWidth: 200,
        maxHeight: null,
        maxWidth: null,
        altitude: "front",
        resizable: false,
        draggable: true,
        wired: false,
        show: Element.show,
        hide: Element.hide,
        superflousEffects: !Object.isUndefined(window.Effect),
        shadow: false,
        activeOnClick: false,
        gridX: 1,
        gridY: 1,
        close: 'hide',
        minimize: 'toggleFold',
        maximize: 'toggleMaximize'
    },
    initialize: function(options) {
        this.setOptions(options);
        this.windowManager = this.options.windowManager || ScraplrUI.defaultWM;
        this.create();
        this.id = this.element.id;
        this.windowManager.register(this);
        this.render();
        if (this.options.activeOnClick) this.overlay.setStyle({
            zIndex: this.lastZIndex + 1
        }).show();
    },
    destroy: function($super) {
        this.hide();
        if (this.centerOptions) Event.stopObserving(this.windowManager.scrollContainer, "scroll", this.centerOptions.handler);
        this.windowManager.unregister(this);
        this.fire('destroyed');
    },
    fire: function(eventName, memo) {
        memo = memo || {};
        memo.window = this;
        return (this.savedElement || this.element).fire('window:' + eventName, memo);
    },
    observe: function(eventName, handler) {
        this.element.observe('window:' + eventName, handler.bind(this));
        return this;
    },
    show: function(modal) {
        if (this.visible) return this;
        this.fire('showing');
        this.effect('show');
        if (modal) {
            this.windowManager.startModalSession(this);
            this.modalSession = true;
        }
        this.addElements();
        this.visible = true;
        new PeriodicalExecuter(function(executer) {
            if (!this.element.visible()) return;
            this.fire('shown');
            executer.stop();
        }.bind(this), 0.1);
        return this;
    },
    hide: function() {
        if (!this.visible) return this;
        this.fire('hiding');
        this.effect('hide');
        if (this.modalSession) {
            this.windowManager.endModalSession(this);
            this.modalSession = false;
        }
        this.windowManager.hide(this);
        new PeriodicalExecuter(function(executer) {
            if (this.element.visible()) return;
            this.visible = false;
            this.element.remove();
            this.fire('hidden');
            executer.stop();
        }.bind(this), 0.1);
        return this;
    },
    close: function() {
        return this.action('close');
    },
    activate: function() {
        return this.bringToFront().focus();
    },
    bringToFront: function() {
        return this.setAltitude('front');
    },
    sendToBack: function() {
        return this.setAltitude('back');
    },
    focus: function() {
        if (this.focused) return this;
        this.windowManager.focus(this);
        this.overlay.hide();
        this.element.addClassName(this.options.theme + '_focused');
        this.focused = true;
        this.fire('focused');
        return this;
    },
    blur: function() {
        if (!this.focused) return this;
        this.windowManager.blur(this);
        this.element.removeClassName(this.options.theme + '_focused');
        if (this.options.activeOnClick) this.overlay.setStyle({
            zIndex: this.lastZIndex + 1
        }).show();
        this.focused = false;
        this.fire('blurred');
        return this;
    },
    maximize: function() {
        if (this.maximized) return this;
        var bounds = this.getBounds();
        if (this.windowManager.maximize(this)) {
            this.disableButton('minimize').setResizable(false).setDraggable(false);
            this.activate();
            this.maximized = true;
            this.savedArea = bounds;
            var newBounds = Object.extend(this.windowManager.viewport.getDimensions(), {
                top: 0,
                left: 0
            });
            this[this.options.superflousEffects && !Prototype.Browser.IE ? "morph": "setBounds"](newBounds);
            this.fire('maximized');
            return this;
        }
    },
    restore: function() {
        if (!this.maximized) return this;
        if (this.windowManager.restore(this)) {
            this[this.options.superflousEffects && !Prototype.Browser.IE ? "morph": "setBounds"](this.savedArea);
            this.enableButton("minimize").setResizable(true).setDraggable(true);
            this.maximized = false;
            this.fire('restored');
            return this;
        }
    },
    toggleMaximize: function() {
        return this.maximized ? this.restore() : this.maximize();
    },
    adapt: function() {
        var dimensions = this.content.getScrollDimensions();
        if (this.options.superflousEffects) this.morph(dimensions, true);
        else this.setSize(dimensions.width, dimensions.height, true);
        return this;
    },
    fold: function() {
        if (!this.folded) {
            var size = this.getSize(true);
            this.folded = true;
            this.savedInnerHeight = size.height;
            if (this.options.superflousEffects) this.morph({
                width: size.width,
                height: 0
            },
            true);
            else this.setSize(size.width, 0, true);
            this.setResizable(false);
            this.fire("fold");
        }
        return this;
    },
    unfold: function() {
        if (this.folded) {
            var size = this.getSize(true);
            this.folded = false;
            if (this.options.superflousEffects) this.morph({
                width: size.width,
                height: this.savedInnerHeight
            },
            true);
            else this.setSize(size.width, this.savedInnerHeight, true);
            this.setResizable(true);
            this.fire("unfold");
        }
        return this;
    },
    toggleFold: function() {
        return this.folded ? this.unfold() : this.fold();
    },
    setHeader: function(header) {
        this.header.update(header);
        return this;
    },
    setContent: function(content) {
        this.content.update(content);
        return this;
    },
    setFooter: function(footer) {
        this.footer.update(footer);
        return this;
    },
    setAjaxContent: function(url, options) {
        if (!options) options = {};
        Object.keys(options).each(function(name) {
            if (Object.isFunction(options[name])) options[name] = options[name].bind(this);
        },
        this);
        var onComplete = options.onComplete;
        options.onComplete = (function(response, json) {
            this.setContent(response.responseText);
            if (Object.isFunction(onComplete)) onComplete(response, json);
        }).bind(this);
        new Ajax.Request(url, options);
        return this;
    },
    getPosition: function() {
        return {
            left: this.options.left,
            top: this.options.top
        };
    },
    setPosition: function(top, left) {
        var pos = this.computePosition(top, left);
        this.options.top = pos.top;
        this.options.left = pos.left;
        var elementStyle = this.element.style;
        elementStyle.top = pos.top + 'px';
        elementStyle.left = pos.left + 'px';
        this.fire('position:changed');
        return this;
    },
    center: function(options) {
        var size = this.getSize(),
        windowManager = this.windowManager,
        viewport = windowManager.viewport;
        viewportArea = viewport.getDimensions(),
        offset = viewport.getScrollOffset();
        if (options && options.auto) {
            this.centerOptions = Object.extend({
                handler: this.recenter.bind(this)
            },
            options);
            Event.observe(this.windowManager.scrollContainer, "scroll", this.centerOptions.handler);
            Event.observe(window, "resize", this.centerOptions.handler);
        }
        options = Object.extend({
            top: (viewportArea.height - size.height) / 2,
            left: (viewportArea.width - size.width) / 2
        },
        options || {});
        return this.setPosition(options.top + offset.top, options.left + offset.left);
    },
    getSize: function(innerSize) {
        if (innerSize) return {
            width: this.options.width - this.borderSize.width,
            height: this.options.height - this.borderSize.height
        };
        else return {
            width: this.options.width,
            height: this.options.height
        };
    },
    setSize: function(width, height, innerSize) {
        var size = this.computeSize(width, height, innerSize);
        var elementStyle = this.element.style,
        contentStyle = this.content.style;
        this.options.width = size.outerWidth;
        this.options.height = size.outerHeight;
        elementStyle.width = size.outerWidth + "px",
        elementStyle.height = size.outerHeight + "px";
        contentStyle.width = size.innerWidth + "px",
        contentStyle.height = size.innerHeight + "px";
        this.overlay.style.height = size.innerHeight + "px";
        this.fire('size:changed');
        return this;
    },
    getBounds: function(innerSize) {
        return Object.extend(this.getPosition(), this.getSize(innerSize));
    },
    setBounds: function(bounds, innerSize) {
        return this.setPosition(bounds.top, bounds.left).setSize(bounds.width, bounds.height, innerSize);
    },
    morph: function(bounds, innerSize) {
        bounds = Object.extend(this.getBounds(innerSize), bounds || {});
        if (this.centerOptions && this.centerOptions.auto) bounds = Object.extend(bounds, this.computeRecenter(bounds));
        if (innerSize) {
            bounds.width += this.borderSize.width;
            bounds.height += this.borderSize.height;
        }
        this.animating = true;
        new ScraplrUI.Window.Effects.Morph(this, bounds, {
            duration: 0.5,
            afterFinish: function() {
                this.animating = false;
            }.bind(this)
        });
        Object.extend(this.options, bounds);
        return this;
    },
    getAltitude: function() {
        return this.windowManager.getAltitude(this);
    },
    setAltitude: function(altitude) {
        if (this.windowManager.setAltitude(this, altitude)) this.fire('altitude:changed');
        return this;
    },
    setResizable: function(resizable) {
        this.options.resizable = resizable;
        var toggleClassName = (resizable ? 'add': 'remove') + 'ClassName';
        this.element[toggleClassName]('resizable').select('div:[class*=_sizer]').invoke(resizable ? 'show': 'hide');
        if (resizable) this.createResizeHandles();
        this.element.select('div.se').first()[toggleClassName]('se_resize_handle');
        return this;
    },
    setDraggable: function(draggable) {
        this.options.draggable = draggable;
        this.element[(draggable ? 'add': 'remove') + 'ClassName']('draggable');
        return this;
    },
    getTheme: function() {
        return this.options.theme || this.windowManager.getTheme();
    },
    setTheme: function(theme, windowManagerTheme) {
        this.element.removeClassName(this.getTheme()).addClassName(theme);
        if (!windowManagerTheme) this.options.theme = theme;
        return this;
    },
    getShadowTheme: function() {
        return this.options.shadowTheme || this.windowManager.getShadowTheme();
    }
});
ScraplrUI.Window.addMethods(ScraplrUI.Window.Buttons);
ScraplrUI.Window.addMethods(ScraplrUI.Window.Shadow);
ScraplrUI.Window.optionsAccessor($w(" minWidth minHeight maxWidth maxHeight gridX gridY altitude "));
ScraplrUI.Window.addMethods({
    style: "position: absolute; top: 0; left: 0; width: 100%; height: 100%; border: 0;",
    action: function(name) {
        var action = this.options[name];
        if (action) Object.isString(action) ? this[action]() : action.call(this, this);
    },
    create: function() {
        function createDiv(className, options) {
            return new Element('div', Object.extend({
                className: className
            },
            options));
        };
        this.element = createDiv("ui-window " + this.getTheme(), {
            id: this.options.id,
            style: "top:-10000px; left:-10000px"
        });
        this.header = createDiv('n move_handle').enableDrag();
        this.content = createDiv('content').appendText(' ');
        this.footer = createDiv('s move_handle').enableDrag();
        var header = createDiv('nw').insert(createDiv('ne').insert(this.header));
        var content = createDiv('w').insert(createDiv('e', {
            style: "position:relative"
        }).insert(this.content));
        var footer = createDiv('sw').insert(createDiv('se' + (this.options.resizable ? " se_resize_handle": "")).insert(this.footer));
        this.element.insert(header).insert(content).insert(footer).identify('ui-window');
        this.header.observe('mousedown', this.activate.bind(this));
        this.setDraggable(this.options.draggable);
        this.setResizable(this.options.resizable);
        this.overlay = new Element('div', {
            style: this.style + "display: none"
        }).observe('mousedown', this.activate.bind(this));
        if (this.options.activeOnClick) this.content.insert({
            before: this.overlay
        });
    },
    createWiredElement: function() {
        this.wiredElement = this.wiredElement || new Element("div", {
            className: this.getTheme() + "_wired",
            style: "display: none; position: absolute; top: 0; left: 0"
        });
    },
    createResizeHandles: function() {
        $w(" n  w  e  s  nw  ne  sw  se ").each(function(id) {
            this.insert(new Element("div", {
                className: id + "_sizer resize_handle",
                drag_prefix: id
            }).enableDrag());
        },
        this.element);
        this.createResizeHandles = Prototype.emptyFunction;
    },
    render: function() {
        this.addElements();
        this.computeBorderSize();
        this.updateButtonsOrder();
        this.element.hide().remove();
        return this.setBounds(this.options);
    },
    addElements: function() {
        this.windowManager.container.appendChild(this.element);
    },
    setZIndex: function(zIndex) {
        if (this.zIndex != zIndex) {
            this.zIndex = zIndex; [this.element].concat(this.element.childElements()).each(function(element) {
                element.style.zIndex = zIndex++;
            });
            this.lastZIndex = zIndex;
        }
        return this;
    },
    effect: function(name, element, options) {
        var effect = this.options[name] || Prototype.emptyFunction;
        effect(element || this.element, options || {});
    },
    computeBorderSize: function() {
        if (this.element) {
            if (Prototype.Browser.IEVersion >= 7) this.content.style.width = "100%";
            var dim = this.element.getDimensions(),
            pos = this.content.positionedOffset();
            this.borderSize = {
                top: pos[1],
                bottom: dim.height - pos[1] - this.content.getHeight(),
                left: pos[0],
                right: dim.width - pos[0] - this.content.getWidth()
            };
            this.borderSize.width = this.borderSize.left + this.borderSize.right;
            this.borderSize.height = this.borderSize.top + this.borderSize.bottom;
            if (Prototype.Browser.IEVersion >= 7) this.content.style.width = "auto";
        }
    },
    computeSize: function(width, height, innerSize) {
        var innerWidth,
        innerHeight,
        outerWidth,
        outerHeight;
        if (innerSize) {
            outerWidth = width + this.borderSize.width;
            outerHeight = height + this.borderSize.height;
        } else {
            outerWidth = width;
            outerHeight = height;
        }
        if (!this.animating) {
            outerWidth = outerWidth.snap(this.options.gridX);
            outerHeight = outerHeight.snap(this.options.gridY);
            if (!this.folded) {
                if (outerWidth < this.options.minWidth) outerWidth = this.options.minWidth;
                if (outerHeight < this.options.minHeight) outerHeight = this.options.minHeight;
            }
            if (this.options.maxWidth && outerWidth > this.options.maxWidth) outerWidth = this.options.maxWidth;
            if (this.options.maxHeight && outerHeight > this.options.maxHeight) outerHeight = this.options.maxHeight;
        }
        if (this.centerOptions && this.centerOptions.auto) this.recenter();
        innerWidth = outerWidth - this.borderSize.width;
        innerHeight = outerHeight - this.borderSize.height;
        return {
            innerWidth: innerWidth,
            innerHeight: innerHeight,
            outerWidth: outerWidth,
            outerHeight: outerHeight
        };
    },
    computePosition: function(top, left) {
        if (this.centerOptions && this.centerOptions.auto) return this.computeRecenter(this.getSize());;
        return {
            top: this.animating ? top: top.snap(this.options.gridY),
            left: this.animating ? left: left.snap(this.options.gridX)
        };
    },
    computeRecenter: function(size) {
        var viewport = this.windowManager.viewport,
        area = viewport.getDimensions(),
        offset = viewport.getScrollOffset(),
        center = {
            top: Object.isUndefined(this.centerOptions.top) ? (area.height - size.height) / 2: this.centerOptions.top,
            left: Object.isUndefined(this.centerOptions.left) ? (area.width - size.width) / 2: this.centerOptions.left
        };
        return {
            top: parseInt(center.top + offset.top,10),
            left: parseInt(center.left + offset.left,10)
        };
    },
    recenter: function(event) {
        var pos = this.computeRecenter(this.getSize());
        this.setPosition(pos.top, pos.left);
    }
});
ScraplrUI.URLWindow = Class.create(ScraplrUI.Window, {
    options: {
        url: 'about:blank'
    },
    afterClassCreate: function() {
        this.undefMethod('setAjaxContent');
    },
    initialize: function($super, options) {
        $super(options);
        this.createIFrame();
    },
    destroy: function($super) {
        this.iframe.src = null;
        $super();
    },
    getUrl: function() {
        return this.iframe.src;
    },
    setUrl: function(url, options) {
        this.iframe.src = url;
        return this;
    },
    createIFrame: function($super) {
        this.iframe = new Element('iframe', {
            style: this.style,
            frameborder: 0,
            src: this.options.url,
            name: this.element.id + "_frame",
            id: this.element.id + "_frame"
        });
        this.content.insert(this.iframe);
    }
});
if (!Object.isUndefined(window.Effect)) {
    ScraplrUI.Window.Effects = ScraplrUI.Window.Effects || {};
    ScraplrUI.Window.Effects.Morph = Class.create(Effect.Base, {
        initialize: function(window, bounds) {
            this.window = window;
            var options = Object.extend({
                fromBounds: this.window.getBounds(),
                toBounds: bounds,
                from: 0,
                to: 1
            },
            arguments[2] || {});
            this.start(options);
        },
        update: function(position) {
            var t = this.options.fromBounds.top + (this.options.toBounds.top - this.options.fromBounds.top) * position;
            var l = this.options.fromBounds.left + (this.options.toBounds.left - this.options.fromBounds.left) * position;
            var ow = this.options.fromBounds.width + (this.options.toBounds.width - this.options.fromBounds.width) * position;
            var oh = this.options.fromBounds.height + (this.options.toBounds.height - this.options.fromBounds.height) * position;
            this.window.setBounds({
                top: t,
                left: l,
                width: ow,
                height: oh
            });
        }
    });
}
ScraplrUI.Window.addMethods({
    startDrag: function(handle) {
        this.initBounds = this.getBounds();
        this.activate();
        if (this.options.wired) {
            this.createWiredElement();
            this.wiredElement.style.cssText = this.element.style.cssText;
            this.element.hide();
            this.saveElement = this.element;
            this.windowManager.container.appendChild(this.wiredElement);
            this.element = this.wiredElement;
        }
        handle.hasClassName('resize_handle') ? this.startResize(handle) : this.startMove();
    },
    endDrag: function() {
        this.element.hasClassName('resized') ? this.endResize() : this.endMove();
        if (this.options.wired) {
            this.saveElement.style.cssText = this.wiredElement.style.cssText;
            this.wiredElement.remove();
            this.element = this.saveElement;
            this.saveElement = false;
        }
    },
    startMove: function() {
        this.drag = this.moveDrag;
        this.element.addClassName('moved');
        this.fire('move:started');
    },
    endMove: function() {
        this.element.removeClassName('moved');
        this.fire('move:ended');
    },
    startResize: function(handle) {
        this.drag = this[handle.readAttribute('drag_prefix') + 'Drag'];
        this.element.addClassName('resized');
        this.fire('resize:started');
    },
    endResize: function() {
        this.element.removeClassName('resized');
        this.fire('resize:ended');
    },
    moveDrag: function(dx, dy) {
        this.setPosition(this.initBounds.top + dy, this.initBounds.left + dx);
    },
    swDrag: function(dx, dy) {
        var initBounds = this.initBounds;
        this.setSize(initBounds.width - dx, initBounds.height + dy).setPosition(initBounds.top, initBounds.left + (initBounds.width - this.getSize().width));
    },
    seDrag: function(dx, dy) {
        this.setSize(this.initBounds.width + dx, this.initBounds.height + dy);
    },
    nwDrag: function(dx, dy) {
        var initBounds = this.initBounds;
        this.setSize(initBounds.width - dx, initBounds.height - dy).setPosition(initBounds.top + (initBounds.height - this.getSize().height), initBounds.left + (initBounds.width - this.getSize().width));
    },
    neDrag: function(dx, dy) {
        var initBounds = this.initBounds;
        this.setSize(initBounds.width + dx, initBounds.height - dy).setPosition(initBounds.top + (initBounds.height - this.getSize().height), initBounds.left);
    },
    wDrag: function(dx, dy) {
        var initBounds = this.initBounds;
        this.setSize(initBounds.width - dx, initBounds.height).setPosition(initBounds.top, initBounds.left + (initBounds.width - this.getSize().width));
    },
    eDrag: function(dx, dy) {
        this.setSize(this.initBounds.width + dx, this.initBounds.height);
    },
    nDrag: function(dx, dy) {
        var initBounds = this.initBounds;
        this.setSize(initBounds.width, initBounds.height - dy).setPosition(initBounds.top + (initBounds.height - this.getSize().height), initBounds.left);
    },
    sDrag: function(dx, dy) {
        this.setSize(this.initBounds.width, this.initBounds.height + dy);
    }
});
ScraplrUI.Window.addMethods({
    methodsAdded: function(base) {
        base.aliasMethodChain('create', 'buttons');
        base.aliasMethodChain('destroy', 'buttons');
    },
    createWithButtons: function() {
        this.createWithoutButtons();
        if (!this.options.resizable) {
            this.options.minimize = false;
            this.options.maximize = false;
        }
        this.buttons = new Element("div", {
            className: "buttons"
        }).observe('click', this.onButtonsClick.bind(this)).observe('mouseover', this.onButtonsHover.bind(this)).observe('mouseout', this.onButtonsOut.bind(this));
        this.element.insert(this.buttons);
        this.defaultButtons.each(function(button) {
            if (this.options[button] !== false) this.addButton(button);
        },
        this);
    },
    destroyWithButtons: function() {
        this.buttons.stopObserving();
        this.destroyWithoutButtons();
    },
    defaultButtons: $w(' minimize maximize close '),
    getButtonElement: function(buttonName) {
        return this.buttons.down("." + buttonName);
    },
    addButton: function(buttonName, action) {
        this.buttons.insert(new Element("a", {
            className: buttonName
        }));
        if (action) this.options[buttonName] = action;
        return this;
    },
    removeButton: function(buttonName) {
        this.getButtonElement(buttonName).remove();
        return this;
    },
    disableButton: function(buttonName) {
        this.getButtonElement(buttonName).addClassName("disabled");
        return this;
    },
    enableButton: function(buttonName) {
        this.getButtonElement(buttonName).removeClassName("disabled");
        return this;
    },
    onButtonsClick: function(event) {
        var element = event.findElement('a:not(.disabled)');
        if (element) {
          this.action(element.className);
        }         
        event.stop();
    },
    onButtonsHover: function(event) {
        this.buttons.addClassName("over");
    },
    onButtonsOut: function(event) {
        this.buttons.removeClassName("over");
    },
    updateButtonsOrder: function() {
        var buttons = this.buttons.childElements();
        buttons.inject(new Array(buttons.length),
        function(array, button) {
            array[parseInt(button.getStyle("padding-top"),10)] = button.setStyle("padding: 0");
            return array;
        }).each(function(button) {
            this.buttons.insert(button);
        },
        this);
    }
});
ScraplrUI.Window.addMethods({
    methodsAdded: function(base) { (function(methods) {
            $w(methods).each(function(m) {
                base.aliasMethodChain(m, 'shadow');
            });
        })(' create addElements setZIndex setPosition setSize setBounds ');
    },
    showShadow: function() {
        if (this.shadow) {
            this.shadow.hide();
            this.effect('show', this.shadow.shadow);
        }
        if (this.iframe) this.iframe.show();
    },
    hideShadow: function() {
        if (this.shadow) this.effect('hide', this.shadow.shadow);
        if (this.iframe) this.iframe.hide();
    },
    removeShadow: function() {
        if (this.shadow) this.shadow.remove();
    },
    focusShadow: function() {
        if (this.shadow) this.shadow.focus();
    },
    blurShadow: function() {
        if (this.shadow) this.shadow.blur();
    },
    createWithShadow: function() {
        this.createWithoutShadow();
        this.observe('showing', this.showShadow).observe('hiding', this.hideShadow).observe('hidden', this.removeShadow).observe('focused', this.focusShadow).observe('blurred', this.blurShadow);
        if (this.options.shadow) this.shadow = new ScraplrUI.Shadow(this.element, {
            theme: this.getShadowTheme(),
            withIFrameShim: false
        });
        this.iframe = Prototype.Browser.IE ? new ScraplrUI.IframeShim({
            parent: this.windowManager.container
        }) : null;
    },
    addElementsWithShadow: function() {
        this.addElementsWithoutShadow();
        if (this.shadow) {
            this.shadow.setBounds(this.options).render();
        }
    },
    setZIndexWithShadow: function(zIndex) {
        if (this.zIndex != zIndex) {
            if (this.shadow) this.shadow.setZIndex(zIndex - 1);
            this.setZIndexWithoutShadow(zIndex);
            this.zIndex = zIndex;
        }
        return this;
    },
    setPositionWithShadow: function(top, left) {
        this.setPositionWithoutShadow(top, left);
        if (this.shadow) {
            var pos = this.getPosition();
            this.shadow.setPosition(pos.top, pos.left);
        }
        if (this.iframe) {
            var pos = this.getPosition();
            this.iframe.setPosition(pos.top, pos.left);
        }
        return this;
    },
    setSizeWithShadow: function(width, height, innerSize) {
        this.setSizeWithoutShadow(width, height, innerSize);
        if (this.shadow) {
            var size = this.getSize();
            this.shadow.setSize(size.width, size.height);
        }
        if (this.iframe) {
            var size = this.getSize();
            this.iframe.setSize(size.width, size.height);
        }
        return this;
    },
    setBoundsWithShadow: function(bounds, innerSize) {
        this.setBoundsWithoutShadow(bounds, innerSize);
        if (this.shadow) this.shadow.setBounds(this.getBounds());
        if (this.iframe) this.iframe.setBounds(this.getBounds());
    }
});
ScraplrUI.Dialog = Class.create(ScraplrUI.Window, {
    options: {
        buttons: null,
        beforeSelect: Prototype.trueFunction,
        close: false,
        resizable: false,
        activeOnClick: false
    },
    disableDialogButton: function(index) {
        var button = this.getDialogButton(index);
        if (button) button.addClassName("disabled");
        return this;
    },
    enableDialogButton: function(index) {
        var button = this.getDialogButton(index);
        if (button) button.removeClassName("disabled");
        return this;
    },
    getDialogButton: function(index) {
        var buttons = this.buttonContainer.childElements();
        if (index >= 0 && index < buttons.length) return buttons[index];
        else return null;
    },
    create: function($super) {
        $super();
        this.buttonContainer = this.createButtons();
        this.dialogContent = new Element('div', {
            className: 'ui-dialog-content'
        });
        this.content.update(this.dialogContent);
        this.content.insert(this.buttonContainer);
    },
    addElements: function($super) {
        $super();
        this.buttonsHeight = this.buttonContainer.getHeight() || this.buttonsHeight;
        this.setDialogSize();
    },
    setContent: function(content, withoutButton) {
        this.dialogContent.update(content);
        if (withoutButton && this.buttonContainer.parentNode) this.buttonContainer.remove();
        else this.content.insert(this.buttonContainer);
        this.setDialogSize();
        return this;
    },
    onSelect: function(e) {
        var element = e.element();
        if (element.callback && !element.hasClassName('disabled')) {
            if (this.options.beforeSelect(element)) element.callback(this);
        }
    },
    createButtons: function(dialogButtons) {
        var buttonContainer = new Element('div', {
            className: 'ui-dialog-buttons'
        }); (this.options.buttons || ScraplrUI.Dialog.okCancelButtons).each(function(item) {
            var button;
            if (item.separator) button = new Element('span', {
                className: 'separator'
            });
            else button = new Element('button', {
                title: item.title,
                className: (item.className || '') + (item.disabled ? ' disabled': '')
            }).observe('click', this.onSelect.bind(this)).update(item.title);
            buttonContainer.insert(button);
            button.callback = item.callback;
        }.bind(this));
        return buttonContainer;
    },
    setDialogSize: function() {
        if (!this.borderSize) return;
        this.buttonsHeight = this.buttonContainer.getHeight() || this.buttonsHeight;
        var style = this.dialogContent.style,
        size = this.getSize(true);
        style.width = size.width + "px",
        style.height = size.height - this.buttonsHeight + "px";
    },
    setSize: function($super, width, height, innerSize) {
        $super(width, height, innerSize);
        this.setDialogSize();
        return this;
    }
});
ScraplrUI.Dialog = Object.extend(ScraplrUI.Dialog, {
    okButton: [{
        title: 'Ok',
        className: 'ok',
        callback: function(win) {
            win.destroy();
        }
    }],
    okCancelButtons: [{
        title: 'Ok',
        className: 'ok',
        callback: function(win) {
            win.destroy();
        }
    },
    {
        title: 'Cancel',
        className: 'cancel',
        callback: function(win) {
            win.destroy();
        }
    }]
});
ScraplrUI.WindowManager = Class.create(ScraplrUI.Options, {
    options: {
        container: null,
        zIndex: 0,
        theme: "alphacube",
        shadowTheme: "mac_shadow",
        showOverlay: Element.show,
        hideOverlay: Element.hide,
        positionningStrategy: function(win, area) {
            ScraplrUI.WindowManager.DumbPositionningStrategy(win, area);
        }
    },
    initialize: function(options) {
        this.setOptions(options);
        this.container = $(this.options.container || document.body);
        if (this.container === $(document.body)) {
            this.viewport = document.viewport;
            this.scrollContainer = window;
        } else {
            this.viewport = this.scrollContainer = this.container;
        }
        this.container.observe('drag:started', this.onStartDrag.bind(this)).observe('drag:updated', this.onDrag.bind(this)).observe('drag:ended', this.onEndDrag.bind(this));
        this.stack = new ScraplrUI.WindowManager.Stack();
        this.modalSessions = 0;
        this.createOverlays();
        this.resizeEvent = this.resize.bind(this);
        Event.observe(window, "resize", this.resizeEvent);
    },
    destroy: function() {
        this.windows().invoke('destroy');
        this.stack.destroy();
        Event.stopObserving(window, "resize", this.resizeEvent);
    },
    setTheme: function(theme) {
        this.stack.windows.select(function(w) {
            return ! w.options.theme;
        }).invoke('setTheme', theme, true);
        this.options.theme = theme;
        return this;
    },
    register: function(win) {
        if (this.getWindow(win.id)) return;
        this.handlePosition(win);
        this.stack.add(win);
        this.restartZIndexes();
    },
    unregister: function(win) {
        this.stack.remove(win);
        if (win == this.focusedWindow) this.focusedWindow = null;
    },
    getWindow: function(element) {
        element = $(element);
        if (!element) return;
        if (!element.hasClassName('ui-window')) element = element.up('.ui-window');
        var id = element.id;
        return this.stack.windows.find(function(win) {
            return win.id == id;
        });
    },
    windows: function() {
        return this.stack.windows.clone();
    },
    getFocusedWindow: function() {
        return this.focusedWindow;
    },
    startModalSession: function(win) {
        if (!this.modalSessions) {
            this.removeOverflow();
            this.modalOverlay.className = win.getTheme() + "_overlay";
            this.container.appendChild(this.modalOverlay);
            if (!this.modalOverlay.opacity) this.modalOverlay.opacity = this.modalOverlay.getOpacity();
            this.modalOverlay.setStyle("height: " + this.viewport.getHeight() + "px");
            this.options.showOverlay(this.modalOverlay, {
                from: 0,
                to: this.modalOverlay.opacity
            });
            if (this.iframe) {
                this.iframe.setBounds({
                    top: 0,
                    left: 0,
                    width: this.viewport.getWidth(),
                    height: this.viewport.getHeight()
                });
                this.iframe.show();
            }
        }
        this.modalOverlay.setStyle({
            zIndex: win.zIndex - 1
        });
        this.modalSessions++;
    },
    endModalSession: function(win) {
        this.modalSessions--;
        if (this.modalSessions) {
            this.modalOverlay.setStyle({
                zIndex: this.stack.getPreviousWindow(win).zIndex - 1
            });
        } else {
            this.resetOverflow();
            this.options.hideOverlay(this.modalOverlay, {
                from: this.modalOverlay.opacity,
                to: 0
            });
            if (this.iframe) this.iframe.hide();
        }
    },
    moveHandleSelector: '.ui-window.draggable .move_handle',
    resizeHandleSelector: '.ui-window.resizable .resize_handle',
    onStartDrag: function(event) {
        var handle = event.element(),
        isMoveHandle = handle.match(this.moveHandleSelector),
        isResizeHandle = handle.match(this.resizeHandleSelector);
        if (isResizeHandle || isMoveHandle) {
            event.stop();
            var win = this.getWindow(Event.element(event).up('div.ui-window'));
            this.container.insert(this.dragOverlay.setStyle({
                zIndex: this.getLastZIndex()
            }));
            try {
                win.startDrag(handle);
                this.draggedWindow = win;
            } catch(ex) {
                log: Prototype.emptyFunction;
            }
        }
    },
    onDrag: function(event) {
        if (this.draggedWindow) {
            event.stop();
            this.draggedWindow.drag(event.memo.dx, event.memo.dy);
        }
    },
    onEndDrag: function(event) {
        if (this.draggedWindow) {
            event.stop();
            this.dragOverlay.remove();
            this.draggedWindow.endDrag();
            this.draggedWindow = null;
        }
    },
    maximize: function(win) {
        this.removeOverflow();
        this.maximizedWindow = win;
        return true;
    },
    restore: function(win) {
        if (this.maximizedWindow) {
            this.resetOverflow();
            this.maximizedWindow = false;
        }
        return true;
    },
    removeOverflow: function() {
        var container = this.container;
        container.savedOverflow = container.style.overflow || "auto";
        container.savedOffset = this.viewport.getScrollOffset();
        container.style.overflow = "hidden";
        this.viewport.setScrollOffset({
            top: 0,
            left: 0
        });
        if (this.container == document.body && Prototype.Browser.IE) this.cssRule = CSS.addRule("html { overflow: hidden }");
    },
    resetOverflow: function() {
        var container = this.container;
        if (container.savedOverflow) {
            if (this.container == document.body && Prototype.Browser.IE) this.cssRule.remove();
            container.style.overflow = container.savedOverflow;
            this.viewport.setScrollOffset(container.savedOffset);
            container.savedOffset = container.savedOverflow = null;
        }
    },
    hide: function(win) {
        var previous = this.stack.getPreviousWindow(win);
        if (previous) previous.focus();
    },
    restartZIndexes: function() {
        var zIndex = this.getZIndex() + 1;
        this.stack.windows.each(function(w) {
            w.setZIndex(zIndex);
            zIndex = w.lastZIndex + 1;
        });
    },
    getLastZIndex: function() {
        return this.getZIndex() + 1;
    },
    overlayStyle: "position: absolute; top: 0; left: 0; display: none; width: 100%;",
    createOverlays: function() {
        this.modalOverlay = new Element("div", {
            style: this.overlayStyle
        });
        this.dragOverlay = new Element("div", {
            style: this.overlayStyle + "height: 100%"
        });
        this.iframe = Prototype.Browser.IE ? new ScraplrUI.IframeShim() : null;
    },
    focus: function(win) {
        if (this.focusedWindow) this.focusedWindow.blur();
        this.focusedWindow = win;
    },
    blur: function(win) {
        if (win == this.focusedWindow) this.focusedWindow = null;
    },
    setAltitude: function(win, altitude) {
        var stack = this.stack;
        if (altitude === "front") {
            if (stack.getFrontWindow() === win) return;
            stack.bringToFront(win);
        } else if (altitude === "back") {
            if (stack.getBackWindow() === win) return;
            stack.sendToBack(win);
        } else {
            if (stack.getPosition(win) == altitude) return;
            stack.setPosition(win, altitude);
        }
        this.restartZIndexes();
        return true;
    },
    getAltitude: function(win) {
        return this.stack.getPosition(win);
    },
    resize: function(event) {
        var area = this.viewport.getDimensions();
        if (this.maximizedWindow) this.maximizedWindow.setSize(area.width, area.height);
        if (this.modalOverlay.visible()) this.modalOverlay.setStyle("height:" + area.height + "px");
    },
    handlePosition: function(win) {
        if (Object.isNumber(win.options.top) && Object.isNumber(win.options.left)) return;
        var strategy = this.options.positionningStrategy,
        area = this.viewport.getDimensions();
        Object.isFunction(strategy) ? strategy(win, area) : strategy.position(win, area);
    }
});
ScraplrUI.WindowManager.DumbPositionningStrategy = function(win, area) {
    size = win.getSize();
    var top = area.height - size.height,
    left = area.width - size.width;
    top = top < 0 ? 0: Math.random() * top;
    left = left < 0 ? 0: Math.random() * left;
    win.setPosition(top, left);
};
ScraplrUI.WindowManager.optionsAccessor('zIndex', 'theme', 'shadowTheme');
ScraplrUI.WindowManager.Stack = Class.create(Enumerable, {
    initialize: function() {
        this.windows = [];
    },
    each: function(iterator) {
        this.windows.each(iterator);
    },
    add: function(win, position) {
        this.windows.splice(position || this.windows.length, 0, win);
    },
    remove: function(win) {
        this.windows = this.windows.without(win);
    },
    sendToBack: function(win) {
        this.remove(win);
        this.windows.unshift(win);
    },
    bringToFront: function(win) {
        this.remove(win);
        this.windows.push(win);
    },
    getPosition: function(win) {
        return this.windows.indexOf(win);
    },
    setPosition: function(win, position) {
        this.remove(win);
        this.windows.splice(position, 0, win);
    },
    getFrontWindow: function() {
        return this.windows.last();
    },
    getBackWindow: function() {
        return this.windows.first();
    },
    getPreviousWindow: function(win) {
        return (win == this.windows.first()) ? null: this.windows[this.windows.indexOf(win) - 1];
    }
});
document.whenReady(function() {
    ScraplrUI.defaultWM = new ScraplrUI.WindowManager();
});
ScraplrUI.ContextMenu = Class.create(ScraplrUI.Options, {
    options: {
        className: 'ui-context_menu',
        beforeShow: Prototype.emptyFunction,
        beforeHide: Prototype.emptyFunction,
        beforeSelect: Prototype.emptyFunction,
        zIndex: 900,
        pageOffset: 25,
        showEffect: false,
        hideEffect: false,
        shadow: "mac_shadow"
    },
    initialize: function(options) {
        this.setOptions(options);
        if (Object.isUndefined(Effect)) {
            this.options.showEffect = this.options.hideEffect = false;
        }
        this.iframe = Prototype.Browser.IE ? new ScraplrUI.IframeShim() : null;
        this.create();
        this.shadow = this.options.shadow ? ScraplrUI.ContextMenu.shadow || new ScraplrUI.Shadow(this.element, {
            theme: this.options.shadow
        }).focus().hide() : null;
        if (this.shadow) ScraplrUI.ContextMenu.shadow = this.shadow;
        this.initObservers();
    },
    create: function() {
        this.element = new Element('div', {
            className: this.options.className,
            style: 'display: none'
        });
        this.element.insert(this.createList(this.options.menuItems));
        $(document.body).insert(this.element.observe('contextmenu', Event.stop));
    },
    createList: function(items) {
        var list = new Element('ul');
        items.each(function(item) {
            list.insert(new Element('li', {
                className: item.separator ? 'separator': ''
            }).insert(!item.separator ? Object.extend(new Element('a', {
                href: '#',
                title: item.name,
                className: (item.className || '') + (item.disabled ? ' disabled': '') + (item.submenu ? ' submenu': '')
            }), {
                _callback: item.callback
            }).observe('click', item.callback ? this.onSelect.bind(this) : Event.stop).observe('contextmenu', Event.stop).update(item.name).insert(item.submenu ? this.createList(item.submenu).wrap({
                className: this.options.className,
                style: 'display:none'
            }) : '') : ''));
        }.bind(this));
        return list;
    },
    initObservers: function() {
        var contextEvent = Prototype.Browser.Opera ? 'click': 'contextmenu';
        document.observe('click',
        function(e) {
            if (this.element.visible() && !e.isRightClick()) {
                this.options.beforeHide();
                if (this.iframe) this.iframe.hide();
                this.hide();
            }
        }.bind(this));
        $$(this.options.selector).invoke('observe', contextEvent,
        function(e) {
            if (Prototype.Browser.Opera && !e.ctrlKey) return;
            this.show(e);
        }.bind(this));
        this.element.select('a.submenu').invoke('observe', 'mouseover',
        function(e) {
            if (this.hasClassName('disabled')) return;
            this.down('.menu').setStyle({
                top: 0,
                left: this.getWidth() + 'px'
            }).show();
        }).invoke('observe', 'mouseout',
        function(e) {
            this.down('.menu').hide();
        });
        if (this.shadow) this.shadow.shadow.observe('contextmenu', Event.stop);
    },
    show: function(e) {
        if (e) e.stop();
        this.options.beforeShow();
        this.fire('showing');
        if (ScraplrUI.ContextMenu.shownMenu) {
            ScraplrUI.ContextMenu.shownMenu.hide();
        }
        ScraplrUI.ContextMenu.shownMenu = this;
        this.position(e);
        if (this.options.showEffect) {
            Effect.Appear(this.element, {
                duration: 0.25,
                afterFinish: function() {
                    this.fire('shown');
                }.bind(this)
            });
        }
        else {
            this.element.show();
            this.fire('shown');
        }
        this.event = e;
        return this;
    },
    position: function(e) {
        var x = Event.pointer(e).x,
        y = Event.pointer(e).y,
        vpDim = document.viewport.getDimensions(),
        vpOff = document.viewport.getScrollOffset(),
        elDim = this.element.getDimensions(),
        elOff = {
            left: ((x + elDim.width + this.options.pageOffset) > vpDim.width ? (vpDim.width - elDim.width - this.options.pageOffset) : x),
            top: ((y - vpOff.top + elDim.height) > vpDim.height && (y - vpOff.top) > elDim.height ? (y - elDim.height) : y)
        },
        elBounds = Object.clone(Object.extend(elOff, elDim));
        for (prop in elOff) {
            elOff[prop] += 'px';
        }
        this.element.setStyle(elOff).setStyle({
            zIndex: this.options.zIndex
        });
        if (this.iframe) {
            this.iframe.setBounds(elBounds).show();
        }
        if (this.shadow) {
            this.shadow.setBounds(elBounds).show();
        }
        return this;
    },
    hide: function() {
        this.options.beforeHide();
        if (this.iframe) this.iframe.hide();
        if (this.shadow) this.shadow.hide();
        if (this.options.hideEffect) {
            Effect.Fade(this.element, {
                duration: 0.25,
                afterFinish: function() {
                    this.fire('hidden');
                }.bind(this)
            });
        }
        else {
            this.element.hide();
            this.fire('hidden');
        }
        return this;
    },
    onSelect: function(e) {
        if (e.target._callback && !e.target.hasClassName('disabled')) {
            this.options.beforeSelect();
            this.fire('selected');
            this.hide();
            e.target._callback(e, this.event);
        }
    },
    fire: function(eventName) {
        this.element.fire('contextmenu:' + eventName);
    }
});
Object.extend(Event, {
    KEY_SPACE: 32,
    KEY_COMA: 188
});
ScraplrUI.AutoComplete = Class.create(ScraplrUI.Options, {
    options: {
        className: "pui-autocomplete",
        max: {
            selection: 10,
            selected: false
        },
        url: false,
        delay: 0.2,
        shadow: false,
        highlight: false,
        tokens: false,
        unique: true
    },
    initialize: function(element, options) {
        this.setOptions(options);
        if (typeof(this.options.tokens) == 'number') this.options.tokens = $A([this.options.tokens]);
        this.element = $(element);
        this.render();
        this.updateInputSize();
        this.nbSelected = 0;
        this.list = [];
        this.keydownHandler = this.keydown.bindAsEventListener(this);
        document.observe('keydown', this.keydownHandler);
    },
    destroy: function() {
        this.autocompletion.destroy();
        this.input.stopObserving();
        document.stopObserving('keypress', this.keydownHandler);
        this.container.remove();
        this.element.show();
    },
    init: function(tokens) {
        tokens = tokens || this.options.tokens;
        var values = this.input.value.split(tokens.first());
        values.each(function(text) {
            if (!text.empty()) this.add(text);
        }.bind(this));
        this.input.clear();
        return this;
    },
    add: function(text, value, options) {
        if (!this.canAddMoreItems()) return;
        var li = new Element("li", Object.extend({
            className: this.getClassName("box")
        },
        options || {}));
        li.observe("click", this.focus.bindAsEventListener(this, li)).observe("mouseover", this.over.bindAsEventListener(this, li)).observe("mouseout", this.out.bindAsEventListener(this, li));
        var close = new Element('a', {
            'href': '#',
            'class': 'closebutton',
            'onclick':'Scraplr.TaskPaper.removeTaskTag(this)'
        });
        li.insert(new Element("span").update(text).insert(close));
        if (value) li.writeAttribute("pui-autocomplete:value", value);
        close.observe("click", this.remove.bind(this, li));
        this.input.parentNode.insert({
            before: li
        });
        this.nbSelected++;
        this.updateSelectedText().updateHiddenField();
        this.updateInputSize();
        if (!this.canAddMoreItems()) this.hideAutocomplete().fire("selection:max_reached");
        else this.hideAutocomplete().fire("input:empty");
        return this.fire("element:added", {
            element: li,
            text: text,
            value: value
        });
    },
    remove: function(element) {
        element.stopObserving();
        element.remove();
        this.nbSelected--;
        this.updateSelectedText().updateHiddenField();
        this.updateInputSize();
        this.input.focus();
        return this.fire("element:removed", {
            element: element
        });
    },
    removeLast: function() {
        var element = this.container.select("li." + this.getClassName("box")).last();
        Scraplr.TaskPaper.removeTaskTag(element.down('a.closebutton'));
        if (element) this.remove(element);
    },
    removeSelected: function(event) {
        if (event.element().readAttribute("type") != "text" && event.keyCode == Event.KEY_BACKSPACE) {
            this.container.select("li." + this.getClassName("box")).each(function(element) {
                if (this.isSelected(element)) this.remove(element);
            }.bind(this));
            if (event) event.stop();
        }
        return this;
    },
    focus: function(event, element) {
        if (event) event.stop();
        if (event && !event.shiftKey) this.deselectAll();
        element = element || this.input;
        if (element == this.input && !this.input.readAttribute("focused")) {
            this.input.writeAttribute("focused", true);
            this.input.focus();
            this.displayCompletion();
        }
        else {
            this.out(event, element);
            element.addClassName(this.getClassName("selected"));
            if (element != this.input) this.blur();
        }
        return this.fire("element:focus", {
            element: element
        });
    },
    blur: function(event, element) {
        if (event) event.stop();
        if (!element) this.input.blur();
        this.hideAutocomplete();
        return this.fire("element:blur", {
            element: element
        });
    },
    over: function(event, element) {
        if (!this.isSelected(element)) element.addClassName(this.getClassName("over"));
        if (event) event.stop();
        return this.fire("element:over", {
            element: element
        });
    },
    out: function(event, element) {
        if (!this.isSelected(element)) element.removeClassName(this.getClassName("over"));
        if (event) event.stop();
        return this.fire("element:out", {
            element: element
        });
    },
    isSelected: function(element) {
        return element.hasClassName(this.getClassName("selected"));
    },
    deselectAll: function() {
        this.container.select("li." + this.getClassName("box")).invoke("removeClassName", this.getClassName("selected"));
        return this;
    },
    setAutocompleteList: function(list) {
        this.list = list;
        return this;
    },
    fire: function(eventName, memo) {
        memo = memo || {};
        memo.autocomplete = this;
        return this.input.fire('autocomplete:' + eventName, memo);
    },
    observe: function(eventName, handler) {
        this.input.observe('autocomplete:' + eventName, handler.bind(this));
        return this;
    },
    stopObserving: function(eventName, handler) {
        this.input.stopObserving('autocomplete:' + eventName, handler);
        return this;
    },
    moveSelection: function(event, element) {
        var current = null;
        if (!this.current) current = this.autocompletionContainer.firstDescendant();
        else if (element == "next") {
            current = this.current[element]() || this.autocompletionContainer.firstDescendant();
        }
        else if (element == "previous") {
            current = this.current[element]() || this.autocompletionContainer.childElements().last();
        }
        else current = element;
        if (this.current) this.current.removeClassName(this.getClassName("current"));
        this.current = current;
        if (this.current) this.current.addClassName(this.getClassName("current"));
    },
    addCurrentSelected: function() {
        if (this.current) {
            var index = this.autocompletionContainer.childElements().indexOf(this.current);
            this.current = null;
            this.input.value = "";
            this.add(this.selectedList[index].text, this.selectedList[index].value); (function() {
                this.input.focus();
            }.bind(this)).defer();
            this.displayCompletion();
        }
    },
    showMessage: function(text) {
        if (text) {
            if (this.hideTimer) {
                clearTimeout(this.hideTimer);
                this.hideTimer = false;
            }
            this.message.update(text);
            this.message.show();
            this.autocompletionContainer.hide();
            this.showAutocomplete();
        }
        else this.hideAutocomplete();
    },
    runRequest: function(search) {
        this.autocompletionContainer.hide();
        this.fire("request:started");
        new Ajax.Request(this.options.url, {
            parameters: {
                search: search,
                max: this.options.max.selection,
                "selected[]": this.selectedValues()
            },
            onComplete: function(transport) {
                this.setAutocompleteList(transport.responseText.evalJSON());
                this.timer = null;
                this.fire("request:completed");
                this.displayCompletion();
            }.bind(this)
        });
    },
    getClassName: function(className) {
        return this.options.className + "-" + className;
    },
    keydown: function(event) {
        if (event.element() != this.input) return;
        this.ignoreKeyup = false;
        if (this.options.max.selected && this.nbSelected == this.options.max.selected) this.ignoreKeyup = true;
        if (this.options.tokens) {
            var tokenFound = this.options.tokens.find(function(token) {
                return event.keyCode == token;
            });
            if (tokenFound) {
                var value = this.input.value.strip();
                this.ignoreKeyup = true;
                var value = this.input.value;
                this.input.clear();
                if (!value.empty()) this.add(value);
            }
        }
        switch (event.keyCode) {
        case Event.KEY_UP:
            this.moveSelection(event, 'previous');
            this.ignoreKeyup = true;
            break;
        case Event.KEY_DOWN:
            this.moveSelection(event, 'next');
            this.ignoreKeyup = true;
            break;
        case Event.KEY_RETURN:
            this.addCurrentSelected();
            this.ignoreKeyup = true;
            break;
        case Event.KEY_BACKSPACE:
            if (this.input.getCaretPosition() == 0) this.removeLast();
            break;
        }
        if (this.ignoreKeyup) {
            event.stop();
            return false;
        }
        else return true;
    },
    keyup: function(event) {
        if (this.ignoreKeyup) {
            this.ignoreKeyup = false;
            return true;
        }
        else {
            this.updateHiddenField();
            this.displayCompletion(event);
            return true;
        }
    },
    updateInputSize: function() {
        var top;
        var w = this.container.select("li." + this.getClassName("box")).inject(0,
        function(sum, element) {
            if (Object.isUndefined(top)) top = element.cumulativeOffset().top;
            else if (top != element.cumulativeOffset().top) {
                top = element.cumulativeOffset().top;
                sum = 0;
            }
            return sum + element.getWidth() + element.getMarginDimensions().width + element.getBorderDimensions().width;
        });
        var margin = this.container.getMarginDimensions().width + this.container.getBorderDimensions().width + this.container.getPaddingDimensions().width;
        var width = this.container.getWidth() - w - margin;
        if (width < 50) width = this.container.getWidth() - margin;
        //this.input.parentNode.style.width = width + "px";
        this.input.parentNode.setStyle('width:'+ width + 'px;');
        //this.input.style.width = width + "px";
        this.input.setStyle('width:'+ width + 'px;');
    },
    displayCompletion: function(event) {
        var value = this.input.value.strip();
        this.current = null;
        if (!this.canAddMoreItems())
        return;
        if (!value.empty()) {
            if (event && this.options.url) {
                if (this.timer)
                clearTimeout(this.timer);
                this.timer = this.runRequest.bind(this, value).delay(this.options.delay);
            }
            else {
                this.message.hide();
                if (this.options.url)
                this.selectedList = this.list;
                else {
                    this.selectedList = this.list.findAll(function(entry) {
                        var strRegExPattern = '^' + value;
                        return entry.text.match(new RegExp(strRegExPattern, 'i'));
                    }).slice(0, this.options.max.selection);
                    if (this.options.unique) {
                        var selected = this.selectedValues();
                        if (!selected.empty())
                        this.selectedList = this.selectedList.findAll(function(entry) {
                            return ! selected.include(entry.value);
                        });
                    }
                }
                this.autocompletionContainer.update("");
                if (this.selectedList.empty()) {
                    this.hideAutocomplete().fire('selection:empty');
                    document.observe('keyup',
                    function(event) {
                        var objElement = Event.element(event);
                        if (event.keyCode === 13) {
                            if (objElement.value !== "") {
                                try{
                                  var objTarget = objElement.up('ul.pui-autocomplete-holder').next(0).identify();
                                }catch(ex){
                                  var objTarget = objElement.up('ul.new-entry-holder').next(0).identify();
                                }
                                Scraplr.TaskPaper.createUnexistTagToken(objTarget, objElement.value);
                                objElement.value = "";
                            }
                        }
                    });
                }
                else {
                    this.selectedList.each(function(entry) {
                        var li = new Element("li").update(this.options.highlight ? entry.text.gsub(value, "<em>" + value + "</em>") : entry.text);
                        li.observe("mouseover", this.moveSelection.bindAsEventListener(this, li)).observe("mousedown", this.addCurrentSelected.bindAsEventListener(this));
                        this.autocompletionContainer.insert(li);
                    }.bind(this));
                    this.autocompletionContainer.show();
                    this.moveSelection("next");
                    this.showAutocomplete();
                }
            }
        }
        else {
            this.hideAutocomplete().fire("input:empty");
            var arrEnteredTags = [];
            document.observe('keyup',
            function(event) {
                var objElement = Event.element(event);
                if (event.keyCode === 13) {
                    if (objElement.value !== "") {
                      try{
                        var objTarget = objElement.up('ul.pui-autocomplete-holder').next(0).identify();
                      }catch(ex){
                        var objTarget = objElement.up('ul.new-entry-holder').next(0).identify();
                      }
                        Scraplr.TaskPaper.createUnexistTagToken(objTarget, objElement.value);
                        objElement.value = "";
                    }
                }
            });
        }
    },
    showAutocomplete: function(event) {
        this.autocompletion.show(event).place(this.container);
        return this;
    },
    hideAutocomplete: function() {
        if (!this.hideTimer) this.hideTimer = (function() {
            this.autocompletionContainer.hide();
            this.autocompletion.hide();
            this.hideTimer = false;
        }).bind(this).defer();
        return this;
    },
    render: function() {
        this.input = this.element.cloneNode(true);
        this.input.writeAttribute("autocomplete", "off");
        this.input.name = "";
        this.input.observe("focus", this.focus.bindAsEventListener(this, this.input)).observe("blur", this.blur.bindAsEventListener(this, this.input)).observe("keyup", this.keyup.bindAsEventListener(this));
        this.container = new Element('ul', {
            className: this.getClassName("holder")
        }).insert(new Element("li", {
            className: this.getClassName("input")
        }).insert(this.input));
        this.autocompletionContainer = new Element("ul", {
            className: this.getClassName("results")
        }).hide();
        this.message = new Element("div", {
            className: this.getClassName("message")
        }).hide();
        this.hidden = new Element("input", {
            type: 'hidden',
            id:'holder'+this.element.id,
            name: this.element.name
        });
        this.element.insert({
            before: this.container
        }).insert({
            before: this.hidden
        });
        this.element.remove();
        this.autocompletion = new ScraplrUI.PullDown(this.container, {
            className: this.getClassName("result"),
            shadow: this.options.shadow,
            position: 'below',
            cloneWidth: true
        });
        this.autocompletion.insert(this.message).insert(this.autocompletionContainer);
    },
    canAddMoreItems: function() {
        return ! (this.options.max.selected && this.nbSelected == this.options.max.selected);
    },
    updateSelectedText: function() {
        var selected = this.container.select("li." + this.getClassName("box"));
        var content = selected.collect(function(element) {
            return element.down("span").firstChild.textContent;
        });
        var separator = this.getSeparatorChar();
        this.selectedText = content.empty() ? false: content.join(separator);
        return this;
    },
    updateHiddenField: function() {
        var separator = this.getSeparatorChar();
        this.hidden.value = this.selectedText ? $A([this.selectedText, this.input.value]).join(separator) : this.input.value;
    },
    selectedValues: function() {
        var selected = this.container.select("li." + this.getClassName("box"));
        return selected.collect(function(element) {
            return element.readAttribute("pui-autocomplete:value");
        });
    },
    getSeparatorChar: function() {
        var separator = this.options.tokens ? this.options.tokens.first() : " ";
        if (separator == Event.KEY_COMA) separator = ',';
        if (separator == Event.KEY_SPACE) separator = ' ';
        return separator;
    }
});
Element.addMethods({
    getCaretPosition: function(element) {
        if (element.createTextRange) {
            var r = document.selection.createRange().duplicate();
            r.moveEnd('character', element.value.length);
            if (r.text === '') return element.value.length;
            return element.value.lastIndexOf(r.text);
        } else return element.selectionStart;
    },
    getAttributeDimensions: function(element, attribut) {
        var dim = $w('top bottom left right').inject({},
        function(dims, key) {
            dims[key] = element.getNumStyle(attribut + "-" + key + (attribut == "border" ? "-width": ""));
            return dims;
        });
        dim.width = dim.left + dim.right;
        dim.height = dim.top + dim.bottom;
        return dim;
    },
    getBorderDimensions: function(element) {
        return element.getAttributeDimensions("border");
    },
    getMarginDimensions: function(element) {
        return element.getAttributeDimensions("margin");
    },
    getPaddingDimensions: function(element) {
        return element.getAttributeDimensions("padding");
    }
});
Object.extend(Date.prototype, {
    addDays: function(days) {
        return new Date(this.getFullYear(), this.getMonth(), this.getDate() + days, this.getHours(), this.getMinutes(), this.getSeconds(), this.getMilliseconds());
    },
    succ: function() {
        return this.addDays(1);
    },
    firstOfMonth: function() {
        return new Date(this.getFullYear(), this.getMonth(), 1);
    },
    endOfMonth: function() {
        return new Date(this.getFullYear(), this.getMonth() + 1, 0);
    },
    getDayOfYear: function() {
        return Math.ceil((this - new Date(this.getFullYear(), 0, 1)) / 86400000);
    },
    strftime: function(grammar) {
        var parts = {},
        i18n = Date.default_i18n;
        var lambda = function(date, part) {
            switch (part) {
            case 'a':
                return i18n.WEEKDAYS_MEDIUM[date.getDay()];
            case 'A':
                return i18n.WEEKDAYS[date.getDay()];
            case 'b':
            case 'h':
                return i18n.MONTHS_SHORT[date.getMonth()];
            case 'B':
                return i18n.MONTHS[date.getMonth()];
            case 'C':
                return Math.floor(date.getFullYear() / 100);
            case 'd':
                return date.getDate().toPaddedString(2);
            case 'e':
                return date.getDate();
            case 'j':
                return date.getDayOfYear();
            case 'm':
                return (date.getMonth() + 1).toPaddedString(2);
            case 'u':
                return date.getDay() || 7;
            case 'w':
                return date.getDay();
            case 'y':
                return date.getFullYear().toString().substring(2);
            case 'Y':
                return date.getFullYear();
            case 'H':
                return date.getHours().toPaddedString(2);
            case 'I':
                return (date.getHours() % 12).toPaddedString(2);
            case 'M':
                return date.getMinutes().toPaddedString(2);
            case 'p':
                return date.getHours() < 12 ? 'am': 'pm';
            case 'S':
                return date.getSeconds().toPaddedString(2);
            case 'n':
                return '\n';
            case 't':
                return '\t';
            case 'D':
                return date.strftime('%m/%d/%y');
            case 'r':
                return date.strftime('%I:%M:%S %p');
            case 'R':
                return date.strftime('%H:%M:%S');
            case 'T':
                return date.strftime('%H:%M:%S');
            case 'c':
                return date.strftime(i18n.FORMAT_DATETIME);
            case 'x':
                return date.strftime(i18n.FORMAT_DATE);
            case 'X':
                return date.strftime(i18n.FORMAT_TIME);
            }
        };
        grammar.scan(/\w+/,
        function(e) {
            var part = e.first();
            parts[part] = lambda(this, part);
        }.bind(this));
        return grammar.interpolate(parts, Date.STRFT_GRAMMER);
    },
    equalsDate: function(other) {
        return (this.getMonth() == other.getMonth() && this.getDate() == other.getDate() && this.getFullYear() == other.getFullYear());
    }
});
Object.extend(Date, {
    STRFT_GRAMMER: /(^|.|\r|\n)(\%(\w+))/,
    default_i18n: {
        MONTHS_SHORT: $w('Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec'),
        MONTHS: $w('January February March April May June July August September October November December'),
        WEEKDAYS_MEDIUM: $w('Sun Mon Tue Wed Thu Fri Sat'),
        WEEKDAYS: $w('Sunday Monday Tuesday Wednesday Thursday Friday Saturday'),
        FORMAT_DATE: '%m/%d/%Y',
        FORMAT_TIME: '%H:%M:%S',
        FORMAT_DATETIME: '%x %X'
    },
    parseString: function(dateString, format) {
        var date = new Date(),
        i18n = Date.default_i18n;
        format = format.replace('%D', '%m/%d/%y');
        format = format.replace('%T', '%H:%M:%S').replace('%r', '%I:%M:%S %p').replace('%R', '%H:%M:%S');
        format = format.replace('%c', i18n.FORMAT_DATETIME).replace('%x', i18n.FORMAT_DATE).replace('%X', i18n.FORMAT_TIME);
        var tokens = format.match(/%./g);
        dateString.split(/[^A-Za-z0-9\u00A1-\uFFFF]+/).each(function(e, i) {
            switch (tokens[i]) {
            case '%a':
            case '%A':
            case '%u':
            case '%w':
                break;
            case '%b':
            case '%h':
                date.setMonth(i18n.MONTHS_SHORT.indexOf(e));
                break;
            case '%B':
                date.setMonth(i18n.MONTHS.indexOf(e));
                break;
            case '%C':
                break;
            case '%d':
            case '%e':
                date.setDate(parseInt(e, 10));
                break;
            case '%j':
                break;
            case '%m':
                date.setMonth(parseInt(e, 10) - 1);
                break;
            case '%w':
                date.setDay(parseInt(e, 10));
                break;
            case '%y':
                var year = parseInt(e, 10);
                if (year < 50) year += 2000;
                if (year < 100) year += 1900;
                date.setYear(year);
                break;
            case '%Y':
                date.setFullYear(parseInt(e, 10));
                break;
            case '%H':
                date.setHours(parseInt(e, 10));
                break;
            case '%I':
                date.setHours(parseInt(e, 10));
                break;
            case '%M':
                date.setMinutes(parseInt(e, 10));
                break;
            case '%p':
                if (e == 'pm') date.setHours(date.getHours() + 12);
                break;
            case '%S':
                date.setSeconds(parseInt(e, 10));
                break;
            }
        });
        return date;
    }
});
Element.addMethods({
    center: function(container, element) {
        element = $(element);
        container = $(container);
        var cBorders = container.borderDimensions(),
        eBorders = element.borderDimensions();
        var height = container.getHeight() - (cBorders.top + cBorders.bottom);
        var width = container.getWidth() - (cBorders.left + cBorders.right);
        var setX = ((width - element.getWidth() - (eBorders.left + eBorders.right)) / 2);
        var setY = ((height - element.getHeight() - (eBorders.top + eBorders.bottom)) / 2);
        setX = (setX < 0) ? 0: setX;
        setY = (setY < 0) ? 0: setY;
        container.relativize();
        return element.setStyle({
            top: setY + 'px',
            left: setX + 'px'
        });
    },
    borderDimensions: function(element) {
        return $w('top bottom left right').inject({},
        function(dims, key) {
            dims[key] = parseFloat(element.getStyle('border-' + key + '-width') || 0);
            return dims;
        });
    }
});
ScraplrUI.Calendar = Class.create(ScraplrUI.Options, {
    options: {
        theme: 'default',
        format: '%m/%d/%Y',
        startWeekday: 0,
        startDate: new Date()
    },
    initialize: function(element, options) {
        this.setOptions(options);
        this.element = $(element);
        this.element.identify();
        this.container = new Element('div').addClassName('ui_calendar_container');
        this.container.setStyle({
            position: 'relative'
        });
        this.element.addClassName(this.options.theme).insert({
            top: this.container
        });
        this.initDate(this.options.startDate);
        this.buildTable();
        this.buildSelector();
        this.update(this.date);
    },
    fire: function(eventName, memo) {
        memo = memo || {};
        memo.calendar = this;
        return this.element.fire('calendar:' + eventName, memo);
    },
    observe: function(eventName, handler) {
        this.element.observe('calendar:' + eventName, handler.bind(this));
        return this;
    },
    stopObserving: function(eventName, handler) {
        this.element.stopObserving('calendar:' + eventName, handler);
        return this;
    },
    generateId: function(name) {
        return this.element.id + name;
    },
    initDate: function(date) {
        this.date = this.convertDate(date);
    },
    convertDate: function(date) {
        if (!date) return null;
        return Object.isString(date) ? Date.parseString(date, this.options.format) : date;
    },
    update: function(newDate) {
        this.updateDaysRow();
        this.date = newDate;
        var today = new Date();
        this.headerSpan.innerHTML = ScraplrUI.Calendar.Options.MONTHS[this.date.getMonth()] + ' ' + this.date.getFullYear();
        var days = $R(this.startDay(this.date), this.lastDay(this.date));
        if (days.size() < 42) {
            days = $R(this.startDay(this.date), this.lastDay(this.date).addDays(42 - days.size()));
        }
        days = $A(days);
        var day,
        cell,
        classNames,
        monthDate,
        index,
        l;
        for (index = 0, l = days.length; index < l; ++index) {
            day = days[index];
            cell = this.cells[index];
            classNames = [];
            cell.date = day;
            monthDate = day.getDate();
            if (day.getMonth() != this.date.getMonth()) {
                classNames.push('non_current');
                cell.innerHTML = monthDate;
            } else {
                cell.innerHTML = '<a href="#">' + monthDate + '</a>';
                if (this.selectedDay && this.selectedDay.equalsDate(day)) classNames.push('selected');
            }
            if (today.equalsDate(day)) classNames.push('today');
            if (cell.hasClassName('weekend')) classNames.push('weekend');
            if (cell.hasClassName('first')) classNames.push('first');
            if (cell.hasClassName('last')) classNames.push('last');
            cell.className = classNames.join(' ');
        }
    },
    updateDaysRow: function() {
        var dayNames = this.dayNames();
        this.daysRow.update('');
        $R(0, 6).each(function(n) {
            this.daysRow.insert({
                bottom: new Element('th', {
                    'class': 'dayname'
                }).update(dayNames[n].truncate(2, ''))
            });
        }.bind(this));
    },
    onCellClick: function(event) {
        event.stop();
        var element = event.element();
        if (element.tagName == 'A') element = element.up('td');
        if (element.hasClassName('non_current')) return;
        var day = element.date;
        this.selectedDay = day;
        $w('selected selected_next selected_prev').each(function(e) {
            this.table.select('.' + e).invoke('removeClassName', e);
        }.bind(this));
        element.addClassName('selected');
        var next = element.next(),
        prev = element.previous();
        if (next) next.addClassName('selected_next');
        if (prev) prev.addClassName('selected_prev');
        this.fire('click', {
            date: day,
            formattedDate: day.strftime(this.options.format)
        });
    },
    onMonthClick: function(event) {
        event.stop();
        this.selector.setStyle({
            position: 'absolute',
            top: 0,
            left: 0,
            'float': 'left',
            zIndex: 3
        });
        this._showSelector();
    },
    _showMask: function() {
        if (Prototype.Browser.IE) {
            var borderDimensions = this.container.borderDimensions();
            if (!this.containerWidth) {
                this.containerWidth = this.container.getWidth() - (borderDimensions.left + borderDimensions.right);
            }
            if (!this.containerHeight) {
                this.containerHeight = this.container.getHeight() - (borderDimensions.top + borderDimensions.bottom);
            }
            this.mask.setStyle({
                width: this.containerWidth + 'px',
                height: this.containerHeight + 'px'
            });
        }
        this.mask.show();
    },
    _hideSelector: function() {
        this.mask.hide();
        this.selector.hide();
    },
    _showSelector: function() {
        this.container.center(this.selector);
        this._showMask();
        this.selector.show();
    },
    buildTable: function() {
        this.table = new Element('table').addClassName('ui_calendar');
        this.table.setStyle({
            position: 'relative'
        });
        this.buildHeader(this.date);
        var tbody = new Element('tbody');
        $R(1, 42).inGroupsOf(7).each(function(week, index) {
            var row = new Element('tr');
            week.each(function(day, i) {
                var cell = new Element('td');
                if (i == 0 || i == 6) cell.addClassName('weekend');
                if (i == 0) cell.addClassName('first');
                if (i == 6) cell.addClassName('last');
                row.insert({
                    bottom: cell
                });
            });
            tbody.insert({
                bottom: row
            });
        }.bind(this));
        this.cells = tbody.select('td');
        this.cells.invoke('observe', 'click', this.onCellClick.bind(this));
        this.table.insert({
            bottom: tbody
        });
        this.container.insert({
            top: this.table
        });
    },
    buildSelector: function() {
        this.selector = new Element('div').addClassName('selector').hide();
        this.mask = new Element('div').hide().addClassName('ui_calendar_mask');//.setOpacity(0.3);
        this.container.insert({
            bottom: this.selector
        }).insert({
            bottom: this.mask
        });
        this.selector.insert({
            top: new Element('label', {
                'for': this.generateId('_select')
            }).update(ScraplrUI.Calendar.Options.LABEL_MONTH)
        });
        var select = new Element('select');
        for (var i = 0; i < 12; i++) {
            select.insert({
                bottom: new Element('option', {
                    value: i
                }).update(ScraplrUI.Calendar.Options.MONTHS[i])
            });
        }
        this.selector.insert({
            bottom: select
        }).insert({
            bottom: new Element('label', {
                'for': this.generateId('_input')
            }).update(ScraplrUI.Calendar.Options.LABEL_YEAR)
        });
        var input = new Element('input', {
            type: 'text',
            size: 4,
            maxLength: 4,
            value: this.date.getFullYear()
        });
        this.selector.insert({
            bottom: input
        });
        var createButton = function(name, onClick) {
            return new Element('span').addClassName('ui_calendar_button').insert({
                top: new Element('button', {
                    type: 'button'
                }).update(name).observe('click', onClick.bind(this))
            });
        };
        var btnCn = createButton('Cancel',
        function(e) {
            this._hideSelector();
        }.bind(this));
        var btnOk = createButton('OK',
        function(e) {
            this._hideSelector();
            this.update(new Date(input.value, select.value, 1));
        }.bind(this));
        this.selector.insert({
            bottom: new Element('div', {
                textAlign: 'center',
                width: '100%'
            }).addClassName('ui_calendar_button_div').insert({
                bottom: btnCn
            }).insert({
                bottom: btnOk
            })
        });
    },
    buildHeader: function(date) {
        var header = new Element('thead');
        this.daysRow = new Element('tr', {
            id: this.generateId('_days_row')
        });
        var initMonthLink = function(link, direction, self) {
            link.observe('click',
            function(e) {
                e.stop();
                self[direction + 'Month'].call(self);
            }).observe('mousedown',
            function(e) {
                var p = new PeriodicalExecuter(function(pe) {
                    self[direction + 'Month'].call(self);
                },
                .25);
                document.observe('mouseup',
                function(e) {
                    p.stop();
                });
            });
        };
        $w('prev next').each(function(d) {
            this[d + 'Link'] = new Element('a').addClassName(d);
            initMonthLink(this[d + 'Link'], d, this);
        }.bind(this));
        this.headerSpan = new Element('a', {}).update(ScraplrUI.Calendar.Options.MONTHS[date.getMonth()] + ' ' + date.getFullYear());
        var headerDiv = new Element('div').addClassName('header').insert({
            bottom: this.prevLink
        }).insert({
            bottom: this.headerSpan
        }).insert({
            bottom: this.nextLink
        });
        this.updateDaysRow();
        header.insert({
            bottom: new Element('tr').insert({
                top: new Element('th', {
                    colspan: 7
                }).update(headerDiv).addClassName('monthname')
            })
        }).insert({
            bottom: this.daysRow
        });
        this.table.insert({
            top: header
        });
    },
    nextMonth: function() {
        this.date.setMonth(this.date.getMonth() + 1);
        this.update(this.date);
    },
    prevMonth: function() {
        this.date.setMonth(this.date.getMonth() - 1);
        this.update(this.date);
    },
    startDay: function(date) {
        var startDate = date.firstOfMonth();
        startDate.setDate( - (startDate.getDay() % 7));
        startDate.setDate(startDate.getDate() + 1 + parseInt(this.options.startWeekday,10));
        return startDate;
    },
    lastDay: function(date) {
        var endDate = date.endOfMonth();
        endDate.setDate(endDate.getDate() + 6 - (endDate.getDay() % 7));
        return endDate;
    },
    dayNames: function() {
        var days = ScraplrUI.Calendar.Options.WEEKDAYS.slice(this.options.startWeekday);
        for (var i = 0; i < this.options.startWeekday; i++) days.push(ScraplrUI.Calendar.Options.WEEKDAYS[i]);
        return days;
    },
    setStartWeekday: function(start) {
        this.options.startWeekday = start;
        this.update(this.date);
    }
});
ScraplrUI.Calendar.Options = {
    MONTHS_SHORT: $w('Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec'),
    MONTHS: $w('January February March April May June July August September October November December'),
    WEEKDAYS_1CHAR: $w('S M T W T F S'),
    WEEKDAYS_SHORT: $w('Su Mo Tu We Th Fr Sa'),
    WEEKDAYS_MEDIUM: $w('Sun Mon Tue Wed Thu Fri Sat'),
    WEEKDAYS: $w('Sunday Monday Tuesday Wednesday Thursday Friday Saturday'),
    FORMAT_DATE: '%m/%d/%Y',
    FORMAT_TIME: '%H:%M:%S',
    FORMAT_DATETIME: '%x %X',
    LABEL_MONTH: "Month",
    LABEL_YEAR: "Year"
};
var ScraplrTabs = Class.create();
ScraplrTabs.prototype = {
    initialize: function(element, options) {
        this.options = Object.extend({
            defaultPanel: '',
            ajaxUrls: {},
            ajaxLoadingText: 'Loading...'
        },
        options || {});
        this.currentTab = '';
        this.element = $(element);
        this.listElements = $A(this.element.getElementsByTagName('LI'));
        for (i = 0; i < this.listElements.length; i++) {
            tabLI = this.listElements[i];
            var itemLinks = tabLI.getElementsByTagName('A');
            tabLI.itemId = itemLinks[0].href.split("#")[1];
            tabLI.linkedPanel = $(tabLI.itemId);
            tabLI.linkedPanel.style.clear = "both";
            if ((this.options.defaultPanel != '') && (this.options.defaultPanel == tabLI.itemId)) {
                this.openPanel(tabLI);
            } else {
                $($(tabLI).linkedPanel).hide();
            }
            $(itemLinks[0]).observe('click',
            function(event) {
                element = Event.findElement(event, 'LI');
                this.openPanel(element);
                Event.stop(event);
            }.bind(this)); 
            $(itemLinks[0]).observe('dblclick',
            function(event) {
                Event.stop(event);
            }.bind(this));
        }
    },
    openPanel: function(tab) {
        tab = $(tab);
        if (this.currentTab != '') {
            this.currentTab.linkedPanel.hide();
            this.currentTab.removeClassName('selectedTaskTab');
        }
        this.currentTab = tab;
        tab.linkedPanel.show();
        tab.addClassName('selectedTaskTab');
        if (tab.hasClassName('taskCommentValue')) {}
        var url = this.options.ajaxUrls[tab.itemId];
        if (url != undefined) {
            tab.linkedPanel.update(this.options.ajaxLoadingText);
            new Ajax.Request(url, {
                onComplete: function(transport) {
                    tab.linkedPanel.update(transport.responseText);
                }
            });
        }
    }
};    

var Prototip = {
    Version: '2.0.1.2'
};

var Tips = {
    options: {
        images: '/public/images/prototip/',
        // image path, can be relative to this file or an absolute url
        zIndex: 101 // raise if required
    }
};

Prototip.Styles = {
    // The default style every other style will inherit from.
    // Used when no style is set through the options on a tooltip.
    'default': {
        border: 6,
        borderColor: '#c7c7c7',
        className: 'default',
        closeButton: false,
        hideAfter: false,
        hideOn: 'mouseleave',
        hook: false,
        //images: 'styles/creamy/'        // Example: different images. An absolute url or relative to the images url defined above.
        radius: 6,
        showOn: 'mousemove',
        stem: {
            //position: 'topLeft',          // Example: optional default stem position, this will also enable the stem
            height: 12,
            width: 15
        }
    },

    'protoblue': {
        className: 'protoblue',
        border: 6,
        borderColor: '#116497',
        radius: 6,
        stem: {
            height: 12,
            width: 15
        }
    },

    'darkgrey': {
        className: 'darkgrey',
        border: 6,
        borderColor: '#363636',
        radius: 6,
        stem: {
            height: 12,
            width: 15
        }
    },

    'creamy': {
        className: 'creamy',
        border: 6,
        borderColor: '#ebe4b4',
        radius: 6,
        stem: {
            height: 12,
            width: 15
        }
    },

    'protogrey': {
        className: 'protogrey',
        border: 6,
        borderColor: '#606060',
        radius: 6,
        stem: {
            height: 12,
            width: 15
        }
    }
};

Object.extend(Prototip, {
    REQUIRED_Prototype: "1.6.0.2",
    start: function () {
        this.require("Prototype");
        if (Tips.options.images.include("://")) {
            Tips.images = Tips.options.images
        }
        else {
            var A = /prototip(?:-[\w\d.]+)?\.js(.*)/;
            Tips.images = (($$("head script[src]").find(function (B) {
                return B.src.match(A)
            }) || {}).src || "").replace(A, "") + Tips.options.images
        }
        if (Prototype.Browser.IE && !document.namespaces.v) {
            document.namespaces.add("v", "urn:schemas-microsoft-com:vml");
            document.observe("dom:loaded", function () {
                document.createStyleSheet().addRule("v\\:*", "behavior: url(#default#VML);")
            })
        }
        Tips.initialize();
        Element.observe(window, "unload", this.unload)
    },
    require: function (A) {
        if ((typeof window[A] == "undefined") || (this.convertVersionString(window[A].Version) < this.convertVersionString(this["REQUIRED_" + A]))) {
            throw ("Prototip requires " + A + " >= " + this["REQUIRED_" + A]);
        }
    },
    convertVersionString: function (A) {
        var B = A.replace(/_.*|\./g, "");
        B = parseInt(B + "0".times(4 - B.length));
        return A.indexOf("_") > -1 ? B - 1 : B
    },
    capture: function (A) {
        if (!Prototype.Browser.IE) {
            A = A.wrap(function (E, D) {
                var C = Object.isElement(this) ? this: this.element,
                B = D.relatedTarget;
                if (B != C && !$A(C.select("*")).member(B)) {
                    E(D)
                }
            })
        }
        return A
    },
    toggleInt: function (A) {
        return (A > 0) ? ( - 1 * A) : (A).abs()
    },
    unload: function () {
        Tips.removeAll()
    }
});
Object.extend(Tips, {
    tips: [],
    visible: [],
    initialize: function () {
        this.zIndexTop = this.zIndex
    },
    useEvent: (function (A) {
        return {
            mouseover: (A ? "mouseenter": "mouseover"),
            mouseout: (A ? "mouseleave": "mouseout"),
            mouseenter: (A ? "mouseenter": "mouseover"),
            mouseleave: (A ? "mouseleave": "mouseout")
        }
    })(Prototype.Browser.IE),
    specialEvent: {
        mouseover: "mouseover",
        mouseout: "mouseout",
        mouseenter: "mouseover",
        mouseleave: "mouseout"
    },
    _inverse: {
        left: "right",
        right: "left",
        top: "bottom",
        bottom: "top",
        middle: "middle",
        horizontal: "vertical",
        vertical: "horizontal"
    },
    _stemTranslation: {
        width: "horizontal",
        height: "vertical"
    },
    inverseStem: function (A) {
        return !! arguments[1] ? this._inverse[A] : A
    },
    fixIE: (function (B) {
        var A = new RegExp("MSIE ([\\d.]+)").exec(B);
        return A ? (parseFloat(A[1]) < 7) : false
    })(navigator.userAgent),
    WebKit419: (Prototype.Browser.WebKit && !document.evaluate),
    add: function (A) {
        this.tips.push(A)
    },
    remove: function (A) {
        var B = this.tips.find(function (C) {
            return C.element == $(A)
        });
        if (B) {
            B.deactivate();
            if (B.tooltip) {
                B.wrapper.remove();
                if (Tips.fixIE) {
                    B.iframeShim.remove()
                }
            }
            this.tips = this.tips.without(B)
        }
        A.prototip = null
    },
    removeAll: function () {
        this.tips.each(function (A) {
            this.remove(A.element)
        }.bind(this))
    },
    raise: function (B) {
        if (B.highest) {
            return
        }
        if (this.visible.length == 0) {
            this.zIndexTop = this.options.zIndex;
            for (var A = 0;
            A < this.tips.length;
            A++) {
                this.tips[A].wrapper.setStyle({
                    zIndex: this.options.zIndex
                })
            }
        }
        B.wrapper.setStyle({
            zIndex: this.zIndexTop++
        });
        if (B.loader) {
            B.loader.setStyle({
                zIndex: this.zIndexTop
            })
        }
        for (var A = 0;
        A < this.tips.length;
        A++) {
            this.tips[A].highest = false
        }
        B.highest = true
    },
    addVisibile: function (A) {
        this.removeVisible(A);
        this.visible.push(A)
    },
    removeVisible: function (A) {
        this.visible = this.visible.without(A)
    },
    hook: function (B, F) {
        B = $(B),
        F = $(F);
        var K = Object.extend({
            offset: {
                x: 0,
                y: 0
            },
            position: false
        },
        arguments[2] || {});
        var D = K.mouse || F.cumulativeOffset();
        D.left += K.offset.x;
        D.top += K.offset.y;
        var C = K.mouse ? [0, 0] : F.cumulativeScrollOffset(),
        A = document.viewport.getScrollOffsets(),
        G = K.mouse ? "mouseHook": "target";
        D.left += ( - 1 * (C[0] - A[0]));
        D.top += ( - 1 * (C[1] - A[1]));
        if (K.mouse) {
            var E = [0, 0];
            E.width = 0;
            E.height = 0
        }
        var I = {
            element: B.getDimensions()
        },
        J = {
            element: Object.clone(D)
        };
        I[G] = K.mouse ? E: F.getDimensions();
        J[G] = Object.clone(D);
        for (var H in J) {
            switch (K[H]) {
            case "topRight":
            case "rightTop":
                J[H].left += I[H].width;
                break;
            case "topMiddle":
                J[H].left += (I[H].width / 2);
                break;
            case "rightMiddle":
                J[H].left += I[H].width;
                J[H].top += (I[H].height / 2);
                break;
            case "bottomLeft":
            case "leftBottom":
                J[H].top += I[H].height;
                break;
            case "bottomRight":
            case "rightBottom":
                J[H].left += I[H].width;
                J[H].top += I[H].height;
                break;
            case "bottomMiddle":
                J[H].left += (I[H].width / 2);
                J[H].top += I[H].height;
                break;
            case "leftMiddle":
                J[H].top += (I[H].height / 2);
                break
            }
        }
        D.left += -1 * (J.element.left - J[G].left);
        D.top += -1 * (J.element.top - J[G].top);
        if (K.position) {
            B.setStyle({
                left: D.left + "px",
                top: D.top + "px"
            })
        }
        return D
    }
});
Tips.initialize();
var Tip = Class.create({
    initialize: function (C, E) {
        this.element = $(C);
        if (!this.element) {
            throw ("Prototip: Element not available, cannot create a tooltip.");
            return
        }
        Tips.remove(this.element);
        var A = (Object.isString(E) || Object.isElement(E)),
        B = A ? arguments[2] || [] : E;
        this.content = A ? E: null;
        if (B.style) {
            B = Object.extend(Object.clone(Prototip.Styles[B.style]), B)
        }
        this.options = Object.extend(Object.extend({
            ajax: false,
            border: 0,
            borderColor: "#000000",
            radius: 0,
            className: Tips.options.className,
            closeButton: Tips.options.closeButtons,
            delay: !(B.showOn && B.showOn == "click") ? 0.14 : false,
            hideAfter: false,
            hideOn: "mouseleave",
            hook: B.hook,
            offset: B.hook ? {
                x: 0,
                y: 0
            }: {
                x: 16,
                y: 16
            },
            fixed: (B.hook && !B.hook.mouse) ? true: false,
            showOn: "mousemove",
            stem: false,
            style: "default",
            target: this.element,
            title: false,
            viewport: (B.hook && !B.hook.mouse) ? false: true,
            width: false
        },
        Prototip.Styles["default"]), B);
        this.target = $(this.options.target);
        this.radius = this.options.radius;
        this.border = (this.radius > this.options.border) ? this.radius: this.options.border;
        if (this.options.images) {
            this.images = this.options.images.include("://") ? this.options.images: Tips.images + this.options.images
        }
        else {
            this.images = Tips.images + "styles/" + (this.options.style || "") + "/"
        }
        if (!this.images.endsWith("/")) {
            this.images += "/"
        }
        if (Object.isString(this.options.stem)) {
            this.options.stem = {
                position: this.options.stem
            }
        }
        if (this.options.stem.position) {
            this.options.stem = Object.extend(Object.clone(Prototip.Styles[this.options.style].stem) || {},
            this.options.stem);
            this.options.stem.position = [this.options.stem.position.match(/[a-z]+/)[0].toLowerCase(), this.options.stem.position.match(/[A-Z][a-z]+/)[0].toLowerCase()];
            this.options.stem.orientation = ["left", "right"].member(this.options.stem.position[0]) ? "horizontal": "vertical";
            this.stemInverse = {
                horizontal: false,
                vertical: false
            }
        }
        if (this.options.ajax) {
            this.options.ajax.options = Object.extend({
                onComplete: Prototype.emptyFunction
            },
            this.options.ajax.options || {})
        }
        this.useEvent = this.element.tagName.toUpperCase() == "AREA" ? Tips.specialEvent: Tips.useEvent;
        if (this.options.hook.mouse) {
            var D = this.options.hook.tip.match(/[a-z]+/)[0].toLowerCase();
            this.mouseHook = Tips._inverse[D] + Tips._inverse[this.options.hook.tip.match(/[A-Z][a-z]+/)[0].toLowerCase()].capitalize()
        }
        this.fixSafari2 = (Tips.WebKit419 && this.radius);
        this.setup();
        Tips.add(this);
        this.activate();
        Prototip.extend(this)
    },
    setup: function () {
        this.wrapper = new Element("div", {
            className: "prototip"
        }).setStyle({
            zIndex: Tips.options.zIndex
        });
        if (this.fixSafari2) {
            this.wrapper.hide = function () {
                this.setStyle("left:-9500px; top:-9500px; visibility:hidden;");
                return this
            };
            this.wrapper.show = function () {
                this.setStyle("visibility:visible");
                return this
            };
            this.wrapper.visible = function () {
                return (this.getStyle("visibility") == "visible" && parseFloat(this.getStyle("top").replace("px", "")) > -9500)
            }
        }
        this.wrapper.hide();
        if (Tips.fixIE) {
            this.iframeShim = new Element("iframe", {
                className: "iframeShim",
                src: "javascript:false;",
                frameBorder: 0
            }).setStyle({
                display: "none",
                zIndex: Tips.options.zIndex - 1,
                opacity: 0
            })
        }
        if (this.options.ajax) {
            this.showDelayed = this.showDelayed.wrap(this.ajaxShow)
        }
        this.tip = new Element("div", {
            className: "content"
        });
        this.title = new Element("div", {
            className: "title"
        }).hide();
        if (this.options.closeButton || (this.options.hideOn.element && this.options.hideOn.element == "closeButton")) {
            this.closeButton = new Element("div", {
                className: "close"
            }).setPngBackground(this.images + "close.png")
        }
    },
    build: function () {
        $(document.body).insert(this.wrapper);
        if (Tips.fixIE) {
            $(document.body).insert(this.iframeShim)
        }
        if (this.options.ajax) {
            $(document.body).insert(this.loader = new Element("div", {
                className: "prototipLoader"
            }).setPngBackground(this.images + "loader.gif").hide())
        }
        var G = "wrapper";
        if (this.options.stem.position) {
            this.stem = new Element("div", {
                className: "prototip_Stem"
            }).setStyle({
                height: this.options.stem[this.options.stem.orientation == "vertical" ? "height": "width"] + "px"
            });
            var B = this.options.stem.orientation == "horizontal";
            this[G].insert(this.stemWrapper = new Element("div", {
                className: "prototip_StemWrapper clearfix"
            }).insert(this.stemBox = new Element("div", {
                className: "prototip_StemBox clearfix"
            })));
            this.stem.insert(this.stemImage = new Element("div", {
                className: "prototip_StemImage"
            }).setStyle({
                height: this.options.stem[B ? "width": "height"] + "px",
                width: this.options.stem[B ? "height": "width"] + "px"
            }));
            if (Tips.fixIE && !this.options.stem.position[1].toUpperCase().include("MIDDLE")) {
                this.stemImage.setStyle({
                    display: "inline"
                })
            }
            G = "stemBox"
        }
        if (this.border) {
            var D = this.border,
            F;
            this[G].insert(this.borderFrame = new Element("ul", {
                className: "borderFrame"
            }).insert(this.borderTop = new Element("li", {
                className: "borderTop borderRow"
            }).setStyle("height: " + D + "px").insert(new Element("div", {
                className: "prototip_CornerWrapper prototip_CornerWrapperTopLeft"
            }).insert(new Element("div", {
                className: "prototip_Corner"
            }))).insert(F = new Element("div", {
                className: "prototip_BetweenCorners"
            }).setStyle({
                height: D + "px"
            }).insert(new Element("div", {
                className: "prototip_Between"
            }).setStyle({
                margin: "0 " + D + "px",
                height: D + "px"
            }))).insert(new Element("div", {
                className: "prototip_CornerWrapper prototip_CornerWrapperTopRight"
            }).insert(new Element("div", {
                className: "prototip_Corner"
            })))).insert(this.borderMiddle = new Element("li", {
                className: "borderMiddle borderRow"
            }).insert(this.borderCenter = new Element("div", {
                className: "borderCenter"
            }).setStyle("padding: 0 " + D + "px"))).insert(this.borderBottom = new Element("li", {
                className: "borderBottom borderRow"
            }).setStyle("height: " + D + "px").insert(new Element("div", {
                className: "prototip_CornerWrapper prototip_CornerWrapperBottomLeft"
            }).insert(new Element("div", {
                className: "prototip_Corner"
            }))).insert(F.cloneNode(true)).insert(new Element("div", {
                className: "prototip_CornerWrapper prototip_CornerWrapperBottomRight"
            }).insert(new Element("div", {
                className: "prototip_Corner"
            })))));
            G = "borderCenter";
            var C = this.borderFrame.select(".prototip_Corner");
            $w("tl tr bl br").each(function (I, H) {
                if (this.radius > 0) {
                    Prototip.createCorner(C[H], I, {
                        backgroundColor: this.options.borderColor,
                        border: D,
                        radius: this.options.radius
                    })
                }
                else {
                    C[H].addClassName("prototip_Fill")
                }
                C[H].setStyle({
                    width: D + "px",
                    height: D + "px"
                }).addClassName("prototip_Corner" + I.capitalize())
            }.bind(this));
            this.borderFrame.select(".prototip_Between", ".borderMiddle", ".prototip_Fill").invoke("setStyle", {
                backgroundColor: this.options.borderColor
            })
        }
        this[G].insert(this.tooltip = new Element("div", {
            className: "tooltip " + this.options.className
        }).insert(this.toolbar = new Element("div", {
            className: "toolbar"
        }).insert(this.title)));
        if (this.options.width) {
            var E = this.options.width;
            if (Object.isNumber(E)) {
                E += "px"
            }
            this.tooltip.setStyle("width:" + E)
        }
        if (this.stem) {
            var A = {};
            A[this.options.stem.orientation == "horizontal" ? "top": "bottom"] = this.stem;
            this.wrapper.insert(A);
            this.positionStem()
        }
        this.tooltip.insert(this.tip);
        if (!this.options.ajax) {
            this._update({
                title: this.options.title,
                content: this.content
            })
        }
    },
    _update: function (E) {
        var A = this.wrapper.getStyle("visibility");
        this.wrapper.setStyle("height:auto; width:auto; visibility:hidden").show();
        if (this.border) {
            this.borderTop.setStyle("height:0");
            this.borderTop.setStyle("height:0")
        }
        if (E.title) {
            this.title.show().update(E.title);
            this.toolbar.show()
        }
        else {
            if (!this.closeButton) {
                this.title.hide();
                this.toolbar.hide()
            }
        }
        if (Object.isElement(E.content)) {
            E.content.show()
        }
        if (Object.isString(E.content) || Object.isElement(E.content)) {
            this.tip.update(E.content)
        }
        this.tooltip.setStyle({
            width: this.tooltip.getWidth() + "px"
        });
        this.wrapper.setStyle("visibility:visible").show();
        this.tooltip.show();
        var C = this.tooltip.getDimensions(),
        B = {
            width: C.width + "px"
        },
        D = [this.wrapper];
        if (Tips.fixIE) {
            D.push(this.iframeShim)
        }
        if (this.closeButton) {
            this.title.show().insert({
                top: this.closeButton
            });
            this.toolbar.show()
        }
        if (E.title || this.closeButton) {
            this.toolbar.setStyle("width: 100%")
        }
        B.height = null;
        this.wrapper.setStyle({
            visibility: A
        });
        this.tip.addClassName("clearfix");
        if (E.title || this.closeButton) {
            this.title.addClassName("clearfix")
        }
        if (this.border) {
            this.borderTop.setStyle("height:" + this.border + "px");
            this.borderTop.setStyle("height:" + this.border + "px");
            B = "width: " + (C.width + 2 * this.border) + "px";
            D.push(this.borderFrame)
        }
        D.invoke("setStyle", B);
        if (this.stem) {
            this.positionStem();
            if (this.options.stem.orientation == "horizontal") {
                this.wrapper.setStyle({
                    width: this.wrapper.getWidth() + this.options.stem.height + "px"
                })
            }
        }
        this.wrapper.hide()
    },
    activate: function () {
        this.eventShow = this.showDelayed.bindAsEventListener(this);
        this.eventHide = this.hide.bindAsEventListener(this);
        if (this.options.fixed && this.options.showOn == "mousemove") {
            this.options.showOn = "mouseover"
        }
        if (this.options.showOn == this.options.hideOn) {
            this.eventToggle = this.toggle.bindAsEventListener(this);
            this.element.observe(this.options.showOn, this.eventToggle)
        }
        if (this.closeButton) {
            this.closeButton.observe("mouseover", function (E) {
                E.setPngBackground(this.images + "close_hover.png")
            }.bind(this, this.closeButton)).observe("mouseout", function (E) {
                E.setPngBackground(this.images + "close.png")
            }.bind(this, this.closeButton))
        }
        var C = {
            element: this.eventToggle ? [] : [this.element],
            target: this.eventToggle ? [] : [this.target],
            tip: this.eventToggle ? [] : [this.wrapper],
            closeButton: [],
            none: []
        };
        var A = this.options.hideOn.element;
        this.hideElement = A || (!this.options.hideOn ? "none": "element");
        this.hideTargets = C[this.hideElement];
        if (!this.hideTargets && A && Object.isString(A)) {
            this.hideTargets = this.tip.select(A)
        }
        var D = {
            mouseenter: "mouseover",
            mouseleave: "mouseout"
        };
        $w("show hide").each(function (H) {
            var G = H.capitalize(),
            F = (this.options[H + "On"].event || this.options[H + "On"]);
            this[H + "Action"] = F;
            if (["mouseenter", "mouseleave", "mouseover", "mouseout"].include(F)) {
                this[H + "Action"] = (this.useEvent[F] || F);
                this["event" + G] = Prototip.capture(this["event" + G])
            }
        }.bind(this));
        if (!this.eventToggle) {
            this.element.observe(this.options.showOn, this.eventShow)
        }
        if (this.hideTargets) {
            this.hideTargets.invoke("observe", this.hideAction, this.eventHide)
        }
        if (!this.options.fixed && this.options.showOn == "click") {
            this.eventPosition = this.position.bindAsEventListener(this);
            this.element.observe("mousemove", this.eventPosition)
        }
        this.buttonEvent = this.hide.wrap(function (G, F) {
            var E = F.findElement(".close");
            if (E) {
                E.blur();
                F.stop();
                G(F)
            }
        }).bindAsEventListener(this);
        if (this.closeButton) {
            this.wrapper.observe("click", this.buttonEvent)
        }
        if (this.options.showOn != "click" && (this.hideElement != "element")) {
            this.eventCheckDelay = Prototip.capture(function () {
                this.clearTimer("show")
            }).bindAsEventListener(this);
            this.element.observe(this.useEvent.mouseout, this.eventCheckDelay)
        }
        var B = [this.element, this.wrapper];
        this.activityEnter = Prototip.capture(function () {
            Tips.raise(this);
            this.cancelHideAfter()
        }).bindAsEventListener(this);
        this.activityLeave = Prototip.capture(this.hideAfter).bindAsEventListener(this);
        B.invoke("observe", this.useEvent.mouseover, this.activityEnter).invoke("observe", this.useEvent.mouseout, this.activityLeave);
        if (this.options.ajax && this.options.showOn != "click") {
            this.ajaxHideEvent = Prototip.capture(this.ajaxHide).bindAsEventListener(this);
            this.element.observe(this.useEvent.mouseout, this.ajaxHideEvent)
        }
    },
    deactivate: function () {
        if (this.options.showOn == this.options.hideOn) {
            this.element.stopObserving(this.options.showOn, this.eventToggle)
        }
        else {
            this.element.stopObserving(this.options.showOn, this.eventShow);
            if (this.hideTargets) {
                this.hideTargets.invoke("stopObserving")
            }
        }
        if (this.eventPosition) {
            this.element.stopObserving("mousemove", this.eventPosition)
        }
        if (this.eventCheckDelay) {
            this.element.stopObserving("mouseout", this.eventCheckDelay)
        }
        this.wrapper.stopObserving();
        this.element.stopObserving(this.useEvent.mouseover, this.activityEnter).stopObserving(this.useEvent.mouseout, this.activityLeave);
        if (this.ajaxHideEvent) {
            this.element.stopObserving(this.useEvent.mouseout, this.ajaxHideEvent)
        }
    },
    ajaxShow: function (C, B) {
        if (!this.tooltip) {
            this.build()
        }
        this.position(B);
        if (this.ajaxContentLoading) {
            return
        }
        else {
            if (this.ajaxContentLoaded) {
                C(B);
                return
            }
        }
        this.ajaxContentLoading = true;
        var D = {
            fakePointer: {
                pointerX: Event.pointerX(B),
                pointerY: Event.pointerY(B)
            }
        };
        var A = Object.clone(this.options.ajax.options);
        A.onComplete = A.onComplete.wrap(function (F, E) {
            this._update({
                title: this.options.title,
                content: E.responseText
            });
            this.position(D);
            (function () {
                F(E);
                var G = (this.loader && this.loader.visible());
                if (this.loader) {
                    this.clearTimer("loader");
                    this.loader.remove();
                    this.loader = null
                }
                if (G) {
                    this.show()
                }
                this.ajaxContentLoaded = true;
                this.ajaxContentLoading = null
            }.bind(this)).delay(0.1)
        }.bind(this));
        this.loaderTimer = Element.show.delay(this.options.delay, this.loader);
        this.wrapper.hide();
        this.ajaxContentLoading = true;
        this.loader.show();
        this.ajaxTimer = (function () {
            new Ajax.Request(this.options.ajax.url, A)
        }.bind(this)).delay(this.options.delay);
        return false
    },
    ajaxHide: function () {
        this.clearTimer("loader")
    },
    showDelayed: function (A) {
        if (!this.tooltip) {
            this.build()
        }
        this.position(A);
        if (this.wrapper.visible()) {
            return
        }
        this.clearTimer("show");
        this.showTimer = this.show.bind(this).delay(this.options.delay)
    },
    clearTimer: function (A) {
        if (this[A + "Timer"]) {
            clearTimeout(this[A + "Timer"])
        }
    },
    show: function () {
      if(!Scraplr.UI.isDragging){
        if (this.wrapper.visible()) {
            return
        }   
        
        if (Tips.fixIE) {
            this.iframeShim.show()
        }
        Tips.addVisibile(this.wrapper);
        this.tooltip.show();
        //if(Scraplr.UADetector.isIE(Scraplr.UADetector.getAgent())){  
          
          this.wrapper.show();
        //} else {
		      //this.wrapper.appear({duration:0.5});
		    //}
        //this.wrapper.show();
        if (this.stem) {
            this.stem.show()
        }
        
        this.element.fire("prototip:shown");
      }
    },
    hideAfter: function (A) {
        if (this.options.ajax) {
            if (this.loader && this.options.showOn != "click") {
                this.loader.hide()
            }
        }
        if (!this.options.hideAfter) {
            return
        }
        this.cancelHideAfter();
        this.hideAfterTimer = this.hide.bind(this).delay(this.options.hideAfter)
    },
    cancelHideAfter: function () {
        if (this.options.hideAfter) {
            this.clearTimer("hideAfter")
        }
    },
    hide: function () {
        this.clearTimer("show");
        this.clearTimer("loader");
        if (!this.wrapper.visible()) {
            return
        }
        this.afterHide()
    },
    afterHide: function () {
        if (Tips.fixIE) {
            this.iframeShim.hide()
        }
        if (this.loader) {
            this.loader.hide()
        }
        
        this.wrapper.hide();
        
        (this.borderFrame || this.tooltip).show();
        Tips.removeVisible(this.wrapper);
        this.element.fire("prototip:hidden")
    },
    toggle: function (A) {
        if (this.wrapper && this.wrapper.visible()) {
            this.hide(A)
        }
        else {
            this.showDelayed(A)
        }
    },
    positionStem: function () {
        var C = this.options.stem,
        B = arguments[0] || this.stemInverse,
        D = Tips.inverseStem(C.position[0], B[C.orientation]),
        F = Tips.inverseStem(C.position[1], B[Tips._inverse[C.orientation]]),
        A = this.radius || 0;
        this.stemImage.setPngBackground(this.images + D + F + ".png");
        if (C.orientation == "horizontal") {
            var E = (D == "left") ? C.height: 0;
            this.stemWrapper.setStyle("left: " + E + "px;");
            this.stemImage.setStyle({
                "float": D
            });
            this.stem.setStyle({
                left: 0,
                top: (F == "bottom" ? "100%": F == "middle" ? "50%": 0),
                marginTop: (F == "bottom" ? -1 * C.width: F == "middle" ? -0.5 * C.width: 0) + (F == "bottom" ? -1 * A: F == "top" ? A: 0) + "px"
            })
        }
        else {
            this.stemWrapper.setStyle(D == "top" ? "margin: 0; padding: " + C.height + "px 0 0 0;": "padding: 0;margin: 0 0 " + C.height + "px 0;");
            this.stem.setStyle(D == "top" ? "top: 0;bottom: auto;": "top: auto;bottom: 0;");
            this.stemImage.setStyle({
                margin: 0,
                "float": F != "middle" ? F: "none"
            });
            if (F == "middle") {
                this.stemImage.setStyle("margin: 0 auto;")
            }
            else {
                this.stemImage.setStyle("margin-" + F + ": " + A + "px;")
            }
            if (Tips.WebKit419) {
                if (D == "bottom") {
                    this.stem.setStyle({
                        position: "relative",
                        clear: "both",
                        top: "auto",
                        bottom: "auto",
                        "float": "left",
                        width: "100%",
                        margin: ( - 1 * C.height) + "px 0 0 0"
                    });
                    this.stem.style.display = "block"
                }
                else {
                    this.stem.setStyle({
                        position: "absolute",
                        "float": "none",
                        margin: 0
                    })
                }
            }
        }
        this.stemInverse = B
    },
    position: function (B) {
        if (!this.tooltip) {
            this.build()
        }
        Tips.raise(this);
        if (Tips.fixIE) {
            var A = this.wrapper.getDimensions();
            if (!this.iframeShimDimensions || this.iframeShimDimensions.height != A.height || this.iframeShimDimensions.width != A.width) {
                this.iframeShim.setStyle({
                    width: A.width + "px",
                    height: A.height + "px"
                })
            }
            this.iframeShimDimensions = A
        }
        if (this.options.hook) {
            var J, H;
            if (this.mouseHook) {
                var K = document.viewport.getScrollOffsets(),
                C = B.fakePointer || {};
                var G, I = 2;
                switch (this.mouseHook.toUpperCase()) {
                case "LEFTTOP":
                case "TOPLEFT":
                    G = {
                        x: 0 - I,
                        y: 0 - I
                    };
                    break;
                case "TOPMIDDLE":
                    G = {
                        x: 0,
                        y: 0 - I
                    };
                    break;
                case "TOPRIGHT":
                case "RIGHTTOP":
                    G = {
                        x: I,
                        y: 0 - I
                    };
                    break;
                case "RIGHTMIDDLE":
                    G = {
                        x: I,
                        y: 0
                    };
                    break;
                case "RIGHTBOTTOM":
                case "BOTTOMRIGHT":
                    G = {
                        x: I,
                        y: I
                    };
                    break;
                case "BOTTOMMIDDLE":
                    G = {
                        x: 0,
                        y: I
                    };
                    break;
                case "BOTTOMLEFT":
                case "LEFTBOTTOM":
                    G = {
                        x: 0 - I,
                        y: I
                    };
                    break;
                case "LEFTMIDDLE":
                    G = {
                        x: 0 - I,
                        y: 0
                    };
                    break
                }
                G.x += this.options.offset.x;
                G.y += this.options.offset.y;
                J = Object.extend({
                    offset: G
                },
                {
                    element: this.options.hook.tip,
                    mouseHook: this.mouseHook,
                    mouse: {
                        top: C.pointerY || Event.pointerY(B) - K.top,
                        left: C.pointerX || Event.pointerX(B) - K.left
                    }
                });
                H = Tips.hook(this.wrapper, this.target, J);
                if (this.options.viewport) {
                    var M = this.getPositionWithinViewport(H),
                    L = M.stemInverse;
                    H = M.position;
                    H.left += L.vertical ? 2 * Prototip.toggleInt(G.x - this.options.offset.x) : 0;
                    H.top += L.vertical ? 2 * Prototip.toggleInt(G.y - this.options.offset.y) : 0;
                    if (this.stem && (this.stemInverse.horizontal != L.horizontal || this.stemInverse.vertical != L.vertical)) {
                        this.positionStem(L)
                    }
                }
                H = {
                    left: H.left + "px",
                    top: H.top + "px"
                };
                this.wrapper.setStyle(H)
            }
            else {
                J = Object.extend({
                    offset: this.options.offset
                },
                {
                    element: this.options.hook.tip,
                    target: this.options.hook.target
                });
                H = Tips.hook(this.wrapper, this.target, Object.extend({
                    position: true
                },
                J))
            }
            if (this.loader) {
                var E = Tips.hook(this.loader, this.target, Object.extend({
                    position: true
                },
                J))
            }
            if (Tips.fixIE) {
                this.iframeShim.setStyle("left:" + H.left + "px;top:" + H.top + "px")
            }
        }
        else {
            var F = this.target.cumulativeOffset(),
            C = B.fakePointer || {},
            H = {
                left: ((this.options.fixed) ? F[0] : C.pointerX || Event.pointerX(B)) + this.options.offset.x,
                top: ((this.options.fixed) ? F[1] : C.pointerY || Event.pointerY(B)) + this.options.offset.y
            };
            if (!this.options.fixed && this.element !== this.target) {
                var D = this.element.cumulativeOffset();
                H.left += -1 * (D[0] - F[0]);
                H.top += -1 * (D[1] - F[1])
            }
            if (!this.options.fixed && this.options.viewport) {
                var M = this.getPositionWithinViewport(H),
                L = M.stemInverse;
                H = M.position;
                if (this.stem && (this.stemInverse.horizontal != L.horizontal || this.stemInverse.vertical != L.vertical)) {
                    this.positionStem(L)
                }
            }
            H = {
                left: H.left + "px",
                top: H.top + "px"
            };
            this.wrapper.setStyle(H);
            if (this.loader) {
                this.loader.setStyle(H)
            }
            if (Tips.fixIE) {
                this.iframeShim.setStyle(H)
            }
        }
    },
    getPositionWithinViewport: function (C) {
        var E = {
            horizontal: false,
            vertical: false
        },
        D = this.wrapper.getDimensions(),
        B = document.viewport.getScrollOffsets(),
        A = document.viewport.getDimensions(),
        G = {
            left: "width",
            top: "height"
        };
        for (var F in G) {
            if ((C[F] + D[G[F]] - B[F]) > A[G[F]]) {
                C[F] = C[F] - (D[G[F]] + (2 * this.options.offset[F == "left" ? "x": "y"]));
                if (this.stem) {
                    E[Tips._stemTranslation[G[F]]] = true
                }
            }
        }
        return {
            position: C,
            stemInverse: E
        }
    }
});
Object.extend(Prototip, {
    createCorner: function (G, H) {
        var F = arguments[2] || this.options,
        B = F.radius,
        E = F.border,
        D = new Element("canvas", {
            className: "cornerCanvas" + H.capitalize(),
            width: E + "px",
            height: E + "px"
        }),
        A = {
            top: (H.charAt(0) == "t"),
            left: (H.charAt(1) == "l")
        };
        if (D && D.getContext && D.getContext("2d")) {
            G.insert(D);
            var C = D.getContext("2d");
            C.fillStyle = F.backgroundColor;
            C.arc((A.left ? B: E - B), (A.top ? B: E - B), B, 0, Math.PI * 2, true);
            C.fill();
            C.fillRect((A.left ? B: 0), 0, E - B, E);
            C.fillRect(0, (A.top ? B: 0), E, E - B)
        }
        else {
            G.insert(new Element("div").setStyle({
                width: E + "px",
                height: E + "px",
                margin: 0,
                padding: 0,
                display: "block",
                position: "relative",
                overflow: "hidden"
            }).insert(new Element("v:roundrect", {
                fillcolor: F.backgroundColor,
                strokeWeight: "1px",
                strokeColor: F.backgroundColor,
                arcSize: (B / E * 0.5).toFixed(2)
            }).setStyle({
                width: 2 * E - 1 + "px",
                height: 2 * E - 1 + "px",
                position: "absolute",
                left: (A.left ? 0 : ( - 1 * E)) + "px",
                top: (A.top ? 0 : ( - 1 * E)) + "px"
            })))
        }
    }
});
Element.addMethods({
    setPngBackground: function (C, B) {
        C = $(C);
        var A = Object.extend({
            align: "top left",
            repeat: "no-repeat",
            sizingMethod: "scale",
            backgroundColor: ""
        },
        arguments[2] || {});
        C.setStyle(Tips.fixIE ? {
            filter: "progid:DXImageTransform.Microsoft.AlphaImageLoader(src='" + B + "'', sizingMethod='" + A.sizingMethod + "')"
        }: {
            background: A.backgroundColor + " url(" + B + ") " + A.align + " " + A.repeat
        });
        return C
    }
});
Prototip.Methods = {
    show: function () {
        Tips.raise(this);
        this.cancelHideAfter();
        var D = {};
        if (this.options.hook) {
            D.fakePointer = {
                pointerX: 0,
                pointerY: 0
            }
        }
        else {
            var A = this.target.cumulativeOffset(),
            C = this.target.cumulativeScrollOffset(),
            B = document.viewport.getScrollOffsets();
            A.left += ( - 1 * (C[0] - B[0]));
            A.top += ( - 1 * (C[1] - B[1]));
            D.fakePointer = {
                pointerX: A.left,
                pointerY: A.top
            }
        }
        if (this.options.ajax) {
            this.ajaxShow(D)
        }
        else {
            this.showDelayed(D)
        }
        this.hideAfter()
    }
};
Prototip.extend = function (A) {
    A.element.prototip = {};
    Object.extend(A.element.prototip, {
        show: Prototip.Methods.show.bind(A),
        hide: A.hide.bind(A),
        remove: Tips.remove.bind(Tips, A.element)
    })
};
Prototip.start();