/* CONCAT of
/2static/script/fecru/ajax.js
/2static/script/fecru/browse.js
/2static/script/fecru/dialog.js
/2static/script/fecru/hover.js
/2static/script/fecru/profile.js
/2static/script/fecru/rss.js
/2static/script/fecru/ui.js
/2static/script/fecru/onReady.js
/2static/script/fecru/star.js
/2static/script/fecru/prefs.js
/2static/script/lib/raphael-1.3.1.min.js
/2static/script/lib/graphael/g.raphael.js
/2static/script/lib/graphael/g.bar.js
/2static/script/lib/graphael/g.pie.js
/2static/script/lib/graphael/g.line.js
/2static/script/lib/graphael/g.dot.js
/2static/script/fecru/raphaelCharts.js
/script/common/global.js
/2static/script/lib/ajs/contentnamesearch.js
/2static/script/lib/ajs/master.js
/2static/script/fe/fisheye-ui.js
/2static/script/fe/fisheye-changeset.js
/2static/script/fe/fisheye-history.js
/2static/script/fe/fisheye-search.js
/script/activitystream.js
/2static/script/cru/util.js
/2static/script/cru/dialog/dialog.js
/2static/script/cru/dialog/dialog-event.js
/2static/script/cru/review/email-comments.js
/2static/script/cru/review/review-history.js
*/
/* START /2static/script/fecru/ajax.js */
if (!AJS.FECRU) {
    AJS.FECRU = {};
}
AJS.FECRU.AJAX = {};
(function($) {

    /**
     * Factory method that creates a new SequentialAjaxExecutor instance.
     */
    AJS.FECRU.AJAX.createSequentialExecutor = function() {
        return new SequentialAjaxExecutor();
    };

    /******************************************************************
     ******************** AJAX METHODS ********************************
     ******************************************************************/

    /**
     * This class represents a serial channel for ajax calls. An ajax call that
     * is submitted only gets run after the previous call has returned from the
     * server.
     * This is used by Crucible comments: when you click "post" while autosave
     * is waiting for the server to create and return the new commentId, the
     * Post action is queued and automatically gets evaluated after autosave
     * has returned (and populated the js comment model with the new commentId).
     */
    function SequentialAjaxExecutor() {

        var busy = false;
        var $jobs = $('<div></div>');   // private var to SequentialAjaxExecutor, never inserted into the document.

        /**
         * Makes an AJAX call, similar to AJS.FECRU.AJAX.ajaxDo(), but with the exception that
         * only one call can be active at any time. When this method is called
         * a second time, while the previous call has not yet finished, the new
         * call will be scheduled and executed automatically when the previous
         * call finishes.
         *
         * @param url
         * @param params
         * @param onCompleteFunc
         */
        this.executeAjax = function(url, params, onCompleteFunc, noDialog) {
            this.executeAjaxJob({
                url : url,
                params : params,
                callback : onCompleteFunc,
                noDialog : noDialog
            });
        };

        /**
         * Similar to SerializedAjaxDispatcher.executeAjax(), but takes a hash with
         * functions that allow callers to evaluate their url and query params
         * just-in-time.
         *
         * @param job
         */
        this.executeAjaxJob = function(job) {
            if (busy) {
                var dispatcher = this;
                $jobs.queue(function() {
                    dispatcher.executeAjaxJob(job);
                });
            } else {
                busy = true;
                AJS.FECRU.AJAX.ajaxDo(
                        $.isFunction(job.url) ? job.url() : job.url,
                        $.isFunction(job.params) ? job.params() : job.params,
                        function(resp) {
                            try {
                                if (job.callback) {
                                    job.callback(resp);
                                }
                            } finally {
                                busy = false;
                                $jobs.dequeue();
                            }
                        },
                        $.isFunction(job.noDialog) ? job.noDialog() : job.noDialog);
            }
        };

        this.isPending = function() {
            return busy;
        };
    }

    AJS.FECRU.AJAX.ajaxDo = function (url, params, onCompleteFunc, noDialog) {
        AJS.FECRU.AJAX.ajaxUpdate(url, params, null, onCompleteFunc, noDialog);
    };

    /**
     * Makes a Ajax request with the specified parameters and runs the onCompleteFunc
     * function when the call returns. Optionally, a DOM element can be passed in
     * which will be updated with the payload of the Ajax response (the JSON response
     * object must contain a "payload" member.
     * Also, the user can pass a second function (onPayloadEvalFunc) that will be
     * called after the payload content has been put in the specified elementToUpdate
     * element. If elementIdToUpdate is null, the onPayloadEvalFunc will not be called.
     *
     * noDialog is optional: if falsy, the error message dialog is shown on error.
     * If it is a callback, it is invoked on error instead of showing the dialog.
     * Otherwise, if it is truthy, the dialog is not shown on error and no action
     * is taken.
     */
    AJS.FECRU.AJAX.ajaxUpdate = function (url, params, elementIdToUpdate, onCompleteFunc, noDialog, feedbackRemove) {
        // Implemementation note:
        // jQuery does an eval(req.responseText) for us internally. We have an onSuccess callback to
        // intercept this, otherwise we end up eval'ing the response text twice (which is poor form).
        // The onComplete callback gives us access to the XmlHttpRequest and allows us to do extra
        // error handling if required.

        var data = false;
        var onSuccess = function(response) {
            data = response;
        };

        var onComplete = function(req) {
            var cleanUpOnFail = function () {
                if (onCompleteFunc) {
                    onCompleteFunc({ worked: false });  //allow function to clean up
                }
            };
            try {
                if (req.status == 0 ) {
                    return;
                }
                if (!data || !isECMA(req)) {
                    noDialog || displayUnexpectedResponse(req);
                    cleanUpOnFail();
                    return;
                }

                var $updateMe;
                if (elementIdToUpdate) {
                    $updateMe = $("#"+elementIdToUpdate);
                }

                if (!data.worked) {
                    if (!noDialog) {
                        appendErrorResponse(data.errorMsg);
                        if (data.userError) {
                            showUserErrorBox();
                        } else {
                            showErrorBox();
                        }
                    }
                } else if ($updateMe && $updateMe.length === 1) {
                    if ($updateMe.is(':input')) {
                        $updateMe.val(data.payload);
                    } else {
                        $updateMe.html(data.payload);
                    }

                    if (feedbackRemove) {
                        removeFeedback($updateMe,feedbackRemove);
                    }
                }
                if (onCompleteFunc) {
                    onCompleteFunc(data);
                }
            } catch (e) {
                console && console.error('ajaxUpdate failure: ' + e);
                displayErrorInResponse(e);
                cleanUpOnFail();
            }
        };

        $.ajax( {
            type: "post",
            url: url,
            data: params,
            dataType: "json",
            traditional: true, // serialize array params as rev=1&rev=2 rather than rev[]=1&rev[]=2
            success: onSuccess,
            complete: onComplete,
            error: noDialog ? ($.isFunction(noDialog) ? noDialog : NOOP) : ajaxFailure
        } );
        return false;
    };

    var NOOP = function () {};

    var ajaxFailure = function (req, textStatus, errorThrown) {
        try {
            if (!isECMA(req)) {
                displayUnexpectedResponse(req);
                return;
            }
            var resp = eval('(' + req.responseText + ')');
            var errorShown = false;
            if (!resp.worked || resp.errorMsg) {
                appendErrorResponse(resp.errorMsg);
                errorShown = true;
            }
            if (errorThrown) {
                appendErrorResponse(errorThrown);
                errorShown = true;
            }
            if (!errorShown) {
                appendErrorResponse("Unknown error");
            }
            showErrorBox();
        } catch (e) {
            console && console.error('ajaxFailure: ' + e);
            displayErrorInResponse(e);
        }
    };

    /******************************************************************
     ******************** HELPER METHODS ******************************
     ******************************************************************/

    var isECMA = function (req) {
        return isContentType(req, /^application\/(ecmascript|json)/);
    };

    var isHTML = function (req) {
        return isContentType(req, /^text\/html/);
    };

    var isContentType = function (req, targetType) {
        var respContentType = req.getResponseHeader("Content-Type");
        return respContentType != null && respContentType.match(targetType);
    };

    var isHTMLFrag = function (req) {
        var responseText = req.responseText;
        return responseText != null && !responseText.match(/<body/i);
    };

    var ourHtmlEscape = function (input) {
        try {
            return input.replace(/&/g, '&amp;').
                    replace(/>/g, '&gt;').
                    replace(/</g, '&lt;').
                    replace(/"/g, '&quot;');
        } catch(e) {
            console && console.error('escape failure: ' + e);
            return input.message || e.message;
        }
    };

    AJS.FECRU.AJAX.startSpin = function (id, className, replace) {
        className = className ? className + " spinner" : "spinner";

        var $el = typeof(id) == 'object' ? $(id) : $("#"+id);
        if ($el.length === 1) {
            var $spinner = $(document.createElement("span"));
            $spinner.addClass(className)
                    .html("<img src='" + fishEyePageContext + "/" + fishEyeSTATICDIR + "/2static/images/blank.gif'>");
            if (replace) {
                $el.replaceWith($spinner);
            } else {
                $spinner.insertAfter($el);
            }
            return true;
        }
        return false;
    };

    AJS.FECRU.AJAX.stopSpin = function (id) {
        var $el = typeof(id) == 'object' ? $(id) : $("#"+id);

        if ($el.length === 1) {
            $el.next(".spinner").remove();
        }
    };

    /******************************************************************
     ******************** ERROR HANDLING ******************************
     ******************************************************************/

    AJS.FECRU.AJAX.checkError = function (resp) {
        return resp.error;
    };

    var appendErrorResponse = function (error) {
        if (error) {
            var $err = $('#errorResponses');
            var errorCount = $err.find('.errorResponse').length;
            $err.show()
                .append($('<div class="errorResponse-count">Error' + (errorCount ? (' ' + ++errorCount) : '') + '</div>'));
            var errorMsg = error + (error.stack ? ":\n" + error.stack : "");
            $err.append($('<div class="errorResponse"></div>')
                .html( ourHtmlEscape(errorMsg) ));
        }
    };

    AJS.FECRU.AJAX.appendErrorResponse = appendErrorResponse;

    var dialog = 0;

    var showErrorBox = function () {
        showNotificationBox('An error has occurred');
        AJS.$('#errorDetails').show();
    };

    AJS.FECRU.AJAX.showErrorBox = showErrorBox;

    var showUserErrorBox = function () {
        showNotificationBox('Cannot perform requested action');
        $('#errorDetails').hide();
    };

    var showNotificationBox = function (title) {
        var util = AJS.CRU.UTIL;

        if (dialog == 0) {
            dialog = AJS.FECRU.DIALOG.create(440, 330, "error-dialog");
            dialog.addHeader(title)
                .addPanel("Information", $('#errorBox'))
                .addButton("Reload", function() { window.location.reload(); })
                .addButton("Close", function() {
                    $('#errorResponses .errorResponse').remove();
                    $('#errorResponses .errorResponse-count').remove();
                    dialog.hide();
                } );
        }

        if (!util.isAnyDialogShowing()) {
            if (util.isAjaxDialogSpinning()) {
                util.stopAjaxDialogSpin();
            }
            dialog.show();
        }
        return false;
    };

    var displayUnexpectedResponse = function (req) {
        if (isHTML(req)) {
            if (isHTMLFrag(req)) {
                appendErrorResponse(req.responseText);
                showErrorBox();
            } else {
                appendErrorResponse($(req.responseText).filter('body').html());
                showErrorBox();
            }
        } else if (!req.responseText) {
            //no-op here: no responseText means most likely server is gone offline.
        } else {
            var resp = eval('(' + req.responseText + ')');
            appendErrorResponse(resp.errorMsg);
            showErrorBox();
        }
    };

    var displayErrorInResponse = function (err) {
        appendErrorResponse(err);
        showErrorBox();
    };
})(AJS.$);
;
/* END /2static/script/fecru/ajax.js */
/* START /2static/script/fecru/browse.js */
if (!AJS.FECRU) {
    AJS.FECRU = {};
}
AJS.FECRU.BROWSE = {};

(function() {
    /**
     * Open a folder, given the node of its span, and then run a post function. Opening the node may mean loading a
     * subtree via Ajax -- if this happens, the post function will be run after the subtree has loaded.
     *
     * @param $node -- a jQuery wrapper around the span tag containing the folder name (which is a sibling of the ul tag containing its children)
     * @param postFn -- a function of no arguments to run after opening the node
     * @param actionName -- an optional string naming the action to call to load a subtree
     * @param ajaxArgsFn -- an optional function to call to provide extra parameters to the subtree load fn
     */
    var toggleFolder = function($node, postFn, pathLinkFn, fileLinkFn, actionName, ajaxArgsFn) {
        if ($node.hasClass("unfilled")) {
            $node.removeClass("unfilled");
            var liId = $node.closest("li.tree-li").attr('id');
            var treeData = AJS.$("#tree-root").data("extraAttrs")[liId];
            var ajaxParameters = {
                path: treeData.path,
                repName: treeData.repname,
                baseUrl: treeData.baseurl,
                noFiles: treeData.nofiles
            };
            var selectedPath = AJS.$("#selectedDirTreeNode").children("input[name='selectedPath']").val();
            if (selectedPath) {
                ajaxParameters.selectedPath = selectedPath;
            }
            if (ajaxArgsFn) {
                var args = ajaxArgsFn($node);
                for (k in args) {
                    ajaxParameters[k] = args[k];
                }
            }
            $node.after(AJS.$("<span class='dirlistSpinner'>&nbsp;</span>").show());
            $node.removeClass("closed").addClass("open");
            var params = AJS.$("#queryStrSuffix").val();
            if (params) {
                ajaxParameters.queryStrSuffix = params;
            }

            if (!actionName) {
                actionName = "loadSubTree";
            }
            AJS.FECRU.AJAX.ajaxDo(fishEyePageContext + "/json/fe/" + actionName + ".do", ajaxParameters, function(resp) {
                if (resp.worked) {
                    // use 'clean' to avoid the regex that jQuery uses to distinguish HTML from ids
                    var replacement = AJS.$.clean([resp.payload], document);
                    $node.parent().replaceWith(replacement);
                    postFn();
                }
            }, false);
        } else {
            var oldClass = "closed";
            var newClass = "open";
            if ($node.hasClass("open")) {
                oldClass = "open";
                newClass = "closed";
            }
            $node.removeClass(oldClass).addClass(newClass);
            $node.siblings("ul." + oldClass).removeClass(oldClass).addClass(newClass);
            postFn();
        }
    };

    var moveSelectedDirTreeIds = function($newLink) {
        AJS.$("#selectedDirTreeNode").attr("id", "");
        AJS.$("#selectedDirTreeLink").attr("id", "");
        $newLink.closest("span").attr("id", "selectedDirTreeNode");
        $newLink.attr("id", "selectedDirTreeLink");
    };

    /**
     * Open the parents of this link and set the selected ids
     * @param $linkNode the link in the directory tree which is selected.
     */
    AJS.FECRU.BROWSE.selectLink = function($linkNode, folderToggledFn, pathLinkFn) {
        var $span = $linkNode.parent();
        var done = function() {
            folderToggledFn($linkNode);
        };
        if ($span.hasClass("closed")) {
            AJS.FECRU.AJAX.startSpin(AJS.$("#filebox"), "", true); // Get's stopped when #fileResults has html() called
            toggleFolder($span, done, pathLinkFn);
        } else {
            done();
        }
        moveSelectedDirTreeIds($linkNode);
    };

    AJS.FECRU.BROWSE.setupDirectoryTree = function(pathLinkFn, fileLinkFn, actionName, ajaxArgsFn) {
        AJS.$("span.tree").live("click", function(event) {
            if (AJS.$(event.target).is("a")) {
                return true; // let the link do its job
            }
            toggleFolder(AJS.$(this), function() {}, pathLinkFn, fileLinkFn, actionName, ajaxArgsFn);
            event.stopPropagation();
            return false;
        });
        if (pathLinkFn) {
            AJS.$("#navigation-tree a.pathLink").live("click", function(event) {
                return pathLinkFn(event);
            });
        }
        if (fileLinkFn) {
            AJS.$("#navigation-tree a.fileLink").live("click", function(event) {
                return fileLinkFn(event);
            });
        }
    };

    AJS.FECRU.BROWSE.initDirectoryTree = function(pathLinkFn, fileLinkFn, actionName, ajaxArgsFn) {
        AJS.FECRU.BROWSE.setupDirectoryTree(pathLinkFn, fileLinkFn, actionName, ajaxArgsFn);
        if (AJS.FE) {
            AJS.FE.setupPanes();
        }
        AJS.$("#content-navigation")
                .find("div.panel-directory")
                .scrollTo(AJS.$("#selectedDirTreeNode"), {
                    axis: 'y'
                });
    };
})();
;
/* END /2static/script/fecru/browse.js */
/* START /2static/script/fecru/dialog.js */
if (!AJS.FECRU) {
    AJS.FECRU = {};
}
AJS.FECRU.DIALOG = {};

(function () {
    var safeDimensions = function (maxWidth, maxHeight, margin) {
        margin = margin || 50;
        return {
            height: Math.min(AJS.$(window).height() - margin, maxHeight),
            width: Math.min(AJS.$(window).width() - margin, maxWidth)
        };
    };

    /**
     * @param optionalClass a string containing a css class that should be applied to the dialog
     */
    AJS.FECRU.DIALOG.create = function (maxWidth, maxHeight, id, optionalClass) {
        var dimensions = safeDimensions(maxWidth, maxHeight);
        var dialog = new AJS.Dialog(dimensions.width, dimensions.height, id);
        // Add the height and width of the dialog as properties of the object
        AJS.$.extend(dialog, {width: dimensions.width, height: dimensions.height});
        if (optionalClass) {
            dialog.addClass(optionalClass)
        }
        return dialog;
    };
})();
;
/* END /2static/script/fecru/dialog.js */
/* START /2static/script/fecru/hover.js */
if (!AJS.FECRU) {
    AJS.FECRU = {};
}
AJS.FECRU.HOVER = (function() {
    var opts = {
        onHover: true,
        showArrow: false,
        fadeTime: 200,
        hideDelay: 500,
        showDelay: 220,
        width: 300,
        offsetX: 10,
        offsetY: 2,
        container: "body",
        cacheContent:false,
        useLiveEvents : true
    };
    var respCache = {};
    var CACHE_FOREVER = -1;
    var displayHandlerDefaultOpts = {
        createUrl:function() {
            throw new Error("must provide createUrl");
        },
        createCacheKey:function() {
            throw new Error("must provide createCacheKey");

        },
        createParams:function() {
            throw new Error("must provide createParams");
        },
        minutesToCache:CACHE_FOREVER,
        cacheType:"__all",
        createTemplateContent:function() {
            return "";
        }
    };
    /**
     * createDisplayHandler returns a function that handles the display of the hover popup, and is used as the display handler
     * to be passed into AJS.InlineDialog as the url parameter (which is overloaded to take a function like this one).
     * @param params The named parameter needs the following properties:
     * @property createUrl : a function that accepts a jquery elem of the trigger of the hover, and returns the url to get
     * the content of the hover.
     * @property createCacheKey : a function that accepts a jquery elem of the trigger, and returns a unique string key for
     * this hover. Used to cache the result of the call created by createUrl
     * @property createParams : a function that accepts a key (created from createCacheKey) and a jquery elem of the trigger,
     * and returns a hash of the parameters to be passed along in the ajax request to the url created by creatUrl.
     * @optionalproperty minutesToCache : number of minutes to cache the result of the call to the url created by createUrl.
     * Defaults to CACHE_FOREVER
     * @optionalproperty cachetype : a unique string that identifies the type of hover being shown. Used by invalidateCache() to
     * clear the cache of this type. can be null.
     * @optionalproperty createTemplateContent : a function that accepts a key, and optionally the trigger and returns a string to be
     * placed in the spinner template.
     */
    var createDisplayHandler = function(params) {
        var options = AJS.$.extend({}, displayHandlerDefaultOpts, params);
        var minutesToCache = options.createUrl,
                cachetype = options.minutesToCache,
                createTemplateContent = options.createTemplateContent;

        return function ($contentDiv, mouseOverTrigger, showPopup) {
            var $trigger = AJS.$(mouseOverTrigger);
            var url = options.createUrl($trigger);
            var key = options.createCacheKey($trigger);
            var cache = respCache[cachetype] || {};
            var cacheHit = cache[key] || {isHit:false};
            var lastFetchTime = cacheHit.lastFetchTime || 0;
            var cacheTimedOut = (minutesToCache !== CACHE_FOREVER) &&
                                ((new Date().getTime() - lastFetchTime) > (minutesToCache * 1000 * 60));
            if (key) {
                $contentDiv.data("targetToDisplay", {key:key});
                var error = function(xmlHttpRequest, textStatus, errorThrown) {
                    $contentDiv.html(escape(textStatus) + "<br>" + escape(errorThrown ? errorThrown : ""));
                };
                var delayedShowSpinner;
                var done = function(resp) {
                    if (resp.worked) {
                        if (delayedShowSpinner) {
                            clearTimeout(delayedShowSpinner);
                        }
                        if ($contentDiv.data("targetToDisplay").key == key) {
                            $contentDiv.html(resp.html);
                            // show the popup if the spinner wasn't shown (either canceled or not supplied)
                            if (!createTemplateContent || delayedShowSpinner) {
                                showPopup();
                            }
                        }
                        // save result to cache
                        cacheHit.resp = resp;
                        cacheHit.lastFetchTime = new Date().getTime();
                        cacheHit.isHit = true;
                        cache[key] = cacheHit;
                        respCache[cachetype] = cache;
                    } else {
                        $contentDiv.html('<div class="hoverpopup">' + resp.errorMsg + '</div>');
                    }
                };
                if ((!cacheHit.isHit) || cacheTimedOut) {
                    AJS.$.ajax({url: url,
                        data:options.createParams(key, $trigger),
                        type:"GET",
                        dataType:"json",
                        success: done,
                        error: error
                    });
                    // show the spinner if the template is provided, delaying it for a bit
                    if (createTemplateContent) {
                        delayedShowSpinner = setTimeout(function () {
                            if ($contentDiv.data("targetToDisplay").key == key) {
                                $contentDiv.html(getSpinnerTemplate(createTemplateContent(key, $trigger)));
                                showPopup();
                            }
                        }, 150);
                    }
                } else {
                    $contentDiv.html(cacheHit.resp.html);
                    showPopup();
                }
            }
        };
    };

    var invalidateCache = function(type, key) {
        if (!key) {
            respCache[type] = undefined;
        } else if (respCache[type]) {
            respCache[type][key] = undefined;
        }
    };

    var addAllLinkPopups = function() {
        addJiraLinkPopups();
        addCruLinkPopups();
        addCsLinkPopups();
        addUserLinkPopups();
        addDeletedUserLinkPopups();
    };

    var addJiraLinkPopups = function() {
        var jiraShowDelay = 300;
        addLinkPopups({
            linkSpanClass:"jiralinkspan",
            url:fishEyePageContext + '/json/action/issue-tooltip.do',
            showDelay:jiraShowDelay
        });
    };

    var addCruLinkPopups = function() {
        addLinkPopups({
            linkSpanClass:"crulinkspan",
            url:fishEyePageContext + '/json/cru/tooltipdata',
            keyParser: function($trigger) {
                var $permaId = $trigger.find('input.permaId');
                if ($permaId.length == 1) {
                    return $permaId.val();
                }
                return $trigger.find('a').text();
            }
        });
    };

    var addCsLinkPopups = function() {
        var changesetKeyPattern = /^(\/\/([^/]+)\/)(.+)$/;
        addLinkPopups({
            linkSpanClass: "cslinkspan",
            url: fishEyePageContext + '/json/action/cstooltipdata.do',
            keyParser: function ($trigger) {
                return '//' + $trigger.find('input.repname').val() + '/' + $trigger.find('input.revstr').val();
            },
            createTemplateContent: function(key) {
                var matches = changesetKeyPattern.exec(key);
                if (matches) {
                    var changesetId = matches[3];
                    var repository = matches[2];
                    //abbrev. the id if too long
                    changesetId = changesetId.length > 10 ? changesetId.substring(0, 6) + "..." : changesetId;
                    return "changest " + changesetId + " in " + repository;
                } else {
                    return "changeset";
                }
            }
        });
    };

    var addLinkPopups = function(params) {
        var defaults = {
            //the css class to bind the hover trigger to
            linkSpanClass:'',
            //the url to request the content of the trigger
            url:'',
            //how much to delay showing of the hover when triggered
            showDelay: opts.showDelay,
            //given the trigger jquery elem, return a unique string usable as a key for the cache
            keyParser:function ($trigger) {
                return $trigger.text();
            },
            //given the key and trigger, return something to display in the spinner message. can be an empty string.
            createTemplateContent : function(key, $trigger) {
                return key;
            }
        };
        var options = AJS.$.extend({}, defaults, params);
        var linkSpanClass = options.linkSpanClass;
        var displayHandler = createDisplayHandler({
            createUrl:function() {
                return options.url;
            },
            createCacheKey:function($trigger) {
                var $keylink = AJS.$("a", $trigger);
                var key = options.keyParser($keylink);
                key = AJS.$.trim(key);
                return key;
            },
            createParams:function(key) {
                return {key:key};
            },
            cacheType:linkSpanClass + "cache",
            createTemplateContent:options.createTemplateContent
        });
        var hoverOpts = AJS.$.extend(false, opts, {showDelay: options.showDelay});
        AJS.InlineDialog("." + linkSpanClass, linkSpanClass + "-popup", displayHandler, hoverOpts);
    };

    var addUserLinkPopups = function() {
        var displayHandler = createDisplayHandler({
            createUrl: function($trigger) {
                return $trigger.attr('href');
            },
            createCacheKey:function($trigger) {
                return $trigger.attr('href');
            },
            createParams:function() {
                return {ajax :"true"};
            },
            cacheType:'userlinks',
            createTemplateContent:function(key, $trigger) {
                var $linkText = $trigger.find(".linkText");
                if ($linkText.length === 0) {
                    //if the linkText doesnt exist, which does happen for avartars with no displayed name
                    return key.replace(/^.*\//, "");//remove all but the username
                } else {
                    return $linkText.text();
                }
            }
        });
        AJS.InlineDialog("a.userorcommitter", "user-hover-inline-dialog", displayHandler, opts);
    };

    var addDeletedUserLinkPopups = function() {
        var hoverContent = function($contents, $trigger, showPopup) {
            $contents.html(
                "<div class='user-hover-info'>"+
                    "<div class='user-hover-avatar'>"+
                        "<img height='48' width='48' alt='Deleted User' src='" + fishEyePageContext + "/avatar/deleted' />"+
                    "</div>"+
                    "<div class='user-hover-details'>"+
                    "<h4><span class='linkText'></span></h4>"+
                    "<em>(deleted user)</em>"+
                "</div>"
            );
            var usernameSpan = $trigger.find(".hidden-username");
            if (usernameSpan) {
                $contents.find("span.linkText").text(usernameSpan.text());
            }
            showPopup();
        };
        AJS.InlineDialog("a.deleteduser", "deleted-user-hover-inline-dialog", hoverContent, opts);
    };

    var getSpinnerTemplate = function(content) {
        // Don't add the content directly into the spinner, as it may contain unescaped HTML content.
        // The workaround is to create a jQuery object and call .text(), and then return the html of the constructed object.
        var $elem = AJS.$('<div class="hoverpopup-throb jirahoverpopup-throb">' +
                '<img src="' + fishEyePageContext + '/' + fishEyeSTATICDIR +
                '/2static/images/spinner_003366.gif" alt="Retrieving details">' +
                '<em class="hoverpopup-throbber-text">Retrieving ' +
                '<span class="hoverpopup-throbber-text-content">' +
                '</span>...</em></div>');
        $elem.find(".hoverpopup-throbber-text-content").text(content);
        return $elem.html();
    };

    return {
        addAllLinkPopups: addAllLinkPopups,
        invalidateCache : invalidateCache,
        CACHE_FOREVER: CACHE_FOREVER
    };
})();;
/* END /2static/script/fecru/hover.js */
/* START /2static/script/fecru/profile.js */
if (!AJS.FECRU) {
    AJS.FECRU = {};
}
if (!AJS.FECRU.PROFILE) {
    AJS.FECRU.PROFILE = (function() {
        var makeDialogFor = function($profileSettingsLink) {
            var windowWidth = AJS.$(window).width();
            var windowHeight = AJS.$(window).height();
            var width = (windowWidth < 1000) ? windowWidth - 120 : 800;
            var height = (windowHeight < 700) ? windowHeight - 100 : 700;

            var HEADER_HEIGHT = 43; //height of the dialog header, in px
            var BUTTON_HEIGHT = 44; //height of the bottons at bottom of dialog, in px.
            var iframeHeight = height - HEADER_HEIGHT - BUTTON_HEIGHT;
            var settingsDialog = AJS.FECRU.DIALOG.create(width, height, 'fecru-profile-settings-dialog');

            var deepProfileSettingsLink = $profileSettingsLink.attr("href") || fishEyePageContext + "/profile";

            // hack: we're adding a random number to the iframe id to work
            // around webkit bug: 24078 (http://lists.macosforge.org/pipermail/webkit-unassigned/2009-February/100941.html)
            var $iframe = AJS.$("<iframe id='fecru-iframe-" + (Math.ceil(Math.random() * 1000)) + "' frameborder='0' src='" + deepProfileSettingsLink + "' style='width:100%;height:" + (iframeHeight) + "px' ></iframe>");

            settingsDialog.addHeader("Settings");
            settingsDialog.addPanel("Display", $iframe);
            settingsDialog.addButton("Close", function (dialog) {
                dialog.hide();

                //todo: fix up reloading?
                if (getDialogURL()) {
                    // Remove "dialog" parameter before reloading
                    var topURL = window.location.href;
                    topURL = topURL.replace(/\?dialog=[^&]*/, "?");
                    topURL = topURL.replace(/&dialog=[^&]*/, "");
                    topURL = topURL.replace(/\?$/, "");
                    window.location.replace(topURL);
                } else {
                    window.location.reload();
                }
            });
            return settingsDialog;
        };

        AJS.$(document).ready(function () {

            var toggleMappingSubmitButton = function() {
                var disable = (AJS.$("#repositoryDropdown").attr("selectedIndex") == 0);
                AJS.$("#addMappingButton").attr("disabled", disable);
            };
            AJS.$("#repositoryDropdown").change(toggleMappingSubmitButton);
            toggleMappingSubmitButton();    // set initial state

            var settingsDialog;
            //todo may be use live events?
            var $profileSettingsLink = AJS.$("a.dialog-settings").click(function(e) {
                e.preventDefault();
                if (!settingsDialog) {
                    settingsDialog = makeDialogFor($profileSettingsLink);
                }
                settingsDialog.show();
            });

            var $form = AJS.$('form.autosubmit');
            $form.find('input,select').change(function () {
                var params = $form.serialize();
                var action = $form.attr('action');
                var $spinner = $form.find('.edit-settings-spinner').show();
                var saved = function () {
                    setTimeout(function() {
                        $spinner.hide();
                    }, 500); //show the spinner for slightly longer, so that the feedback is visible for longer
                };

                AJS.FECRU.AJAX.ajaxDo(action, params, saved);
            });

            // If there's a dialog argument on our URL, open that dialog.
            var dialogURL = getDialogURL();
            if (dialogURL) {
                settingsDialog = makeDialogFor(AJS.$("<a href='" + fishEyePageContext + dialogURL + "' />"));
                settingsDialog.show();
            }

        });

        function getDialogURL() {
            var matches = /[?&]dialog=([^&]*)/.exec(location.search);
            if (matches) {
                return matches[1];
            }
            return null;
        }

        return true; //flag to stop multiple calls which adds multiple dialog boxes.
    })();
}
;
/* END /2static/script/fecru/profile.js */
/* START /2static/script/fecru/rss.js */
if (!AJS.FECRU) {
    AJS.FECRU = {};
}

AJS.FECRU.RSS = (function() {
    var makeDialogFor = function($Link) {
        var deepRSSLink = $Link.attr("href");
        var dlg = AJS.FECRU.DIALOG.create(800, 500, 'rss-settings-dialog');

        var $iframe = AJS.$("<iframe id='fecru-iframe' frameborder='0' src='" + deepRSSLink + "' style='width:100%;height:"+dlg.height+"px'></iframe>");

        dlg.addHeader("Notification Configuration")
           .addPanel("Display", $iframe)
           .addButton("Close", function (dialog) {
                dialog.hide();
                $iframe.attr("src", deepRSSLink);
            });
        return dlg;
    };
    var setupRSSDialog = function () {
        var rssDialog;
        var $rssLink = AJS.$("#dialog-rss").click(function (e) {
            e.preventDefault();
            if (!rssDialog) {
                rssDialog = makeDialogFor($rssLink);
            }
            rssDialog.show();
        });
    };

    AJS.toInit(function() {
        var $context = AJS.$("#rss-form");
        AJS.$("input, select", $context).change(function() {
            $context.submit();
        });
    });

    return {
        setupRSSDialog : setupRSSDialog
    };

})();
;
/* END /2static/script/fecru/rss.js */
/* START /2static/script/fecru/ui.js */
if (!AJS.FECRU) {
    AJS.FECRU = {};
    AJS.FECRU.UI = {};
} else if (!AJS.FECRU.UI) {
    AJS.FECRU.UI = {};
}

(function() {

    var hasSetDropdowns = false;

    AJS.FECRU.UI.setDropdowns = function (useroptions) {
        if (hasSetDropdowns) {
            AJS.log("WARNING: About to set duplicated dropdown live events");
        } else {
            hasSetDropdowns = true;
        }
        var options = {
            selectionHandler: function () {
                // Prevent AUI from reimplementing default browser handling of <a> elements.
                // May need to revisit depending on AUI changes (see CR-FE-1617).
            },
            useLiveEvents : true //using live meant that hovers will no longer need to perform a dropdown bind on load
        };
        if (useroptions) {
            AJS.$.extend(options, useroptions);
        }
        AJS.dropDown.Standard(options);
    };

    var setupFilterInlineDialog = function(trigger, box, unique, offsetX) {
        var PADDING = 2;
        var $filterBox = AJS.$(box);
        if ($filterBox.length) {
            var options = {
                onHover: false,
                showArrow: true,
                fadeTime: 200,
                hideDelay: 200,
                showDelay: 200,
                width: $filterBox.width() + PADDING,
                offsetX: offsetX || 0,
                offsetY: PADDING,
                container: "body",
                cacheContent:true,
                ignoredElements:['.ui-datepicker'] //dont want to hide the filter box when selecting dates
            };
            var contentHandler = function ($contentDiv, mouseOverTrigger, showPopup) {
                $filterBox.appendTo($contentDiv).show();
                showPopup();
            };
            AJS.InlineDialog(trigger, unique, contentHandler, options);
        }
    };

    AJS.FECRU.UI.constraintToggle = function() {
        setupFilterInlineDialog(".constraint-toggle", "#header-constraint", "header-constraint-inline-dialog");
    };

    AJS.FECRU.UI.filterToggle = function(filters) {
        setupFilterInlineDialog(".filter-toggle", filters, "filter-inline-dialog", 6);
    };

    var linkAugment = function () {
        AJS.$("a[rel='help']").attr("target", "_help");

        // details switch in stream
        AJS.$(".details-switch").unbind().click(function () {
            AJS.$(this).toggleClass("details-expanded");

            var $verbose = AJS.$(this).siblings(".details-verbose");
            if ( $verbose.css("display") == "none" ) {//toggle() is broken in IE8, so checking display
                $verbose.show();
            }
            else {
                $verbose.hide();
            }
        });

        // look for all jira inline activity items, and load the content ajax, chained one by one.
        var $jiraActivities = AJS.$("div.details-body div.jira-issue-key");
        // cache the ajax results to prevent multiple ajax calls for the same key
        var cache = {};
        var ajaxLoadJiraActivity = function($target, next) {
            var issueKey = AJS.$("input.jira-issue-key", $target[0]).attr("value");
            if (issueKey) {
                //                AJS.log("issueKey="+issueKey);
                var cacheHit = cache[issueKey];
                if (cacheHit) {
                    $target.html(cacheHit.resp.html);
                    if (next) {
                        next();
                    }
                } else {
                    // this doesn't use AJS.FECRU.ajaxDo because we don't want the standard error handling
                    AJS.$.ajax({
                        url: fishEyePageContext + '/json/action/issue-inline.do',
                        data: {'key':issueKey},
                        type: "GET",
                        dataType: "json",
                        success: function(resp) {
                            $target.html(resp.html);
                            if (next) {
                                next();
                            }
                            //cache using the issueKey - prevent multiple calls for the same keys
                            cache[issueKey] = {resp:resp};
                        },
                        error: function(resp) {
                            $target.html("Error retrieving issue: " + issueKey);
                        }
                    });
                }
            }
        };
        var nextInChain = function(i, max) {
            return function() {
                if (i < max) {
                    ajaxLoadJiraActivity(AJS.$($jiraActivities[i]), nextInChain(i + 1, max));
                }
            };
        };
        var max = $jiraActivities.length;
        var i = 0;
        if (max > 0) {
            ajaxLoadJiraActivity(AJS.$($jiraActivities[i]), nextInChain(i + 1, max));
        }

        // inline hover in stream
        AJS.$(".hover").mouseover(function (event) {
            AJS.$("#hover").addClass("mouseover");
            inlineHover(AJS.$(this), event);
        });

        AJS.$("#hover").mouseout(function() {
            var which = AJS.$(this);
            which.bind("mouseleave", function() {
                AJS.$(which).removeClass("mouseover");
                AJS.$(which).hide();
            });
        });
        AJS.$("hover-close").click(function() {
            AJS.$(this).hide();
        });
        // filter bar in stream
        //	AJS.$(".stream-sift a").click(function () {
        //		AJS.$(".stream-sift li").removeClass("active");
        //		AJS.$(this).parent().addClass("active");
        //TODO: could be useful at a later date?
        //	});

        contentToggle();
    };

    var formAugment = function (which) {
        if (AJS.$.browser.safari) {//overides type=search
            var searchBox = AJS.$("#quick-search-input");
            if (searchBox.length === 1) {
                searchBox[0].type = "text";
            }
        }
    };

    var $focusedTableRow;
    AJS.FECRU.UI.tableRowClick = function (prefix, rowClickFn) {
        AJS.$("#" + prefix + "-table > tbody > tr").live("click", function () {
            var clearFocus = function() {
                if ($focusedTableRow) {
                    $focusedTableRow.removeClass(prefix + "-focus");
                }
            };

            var $this = AJS.$(this);
            if ($this.hasClass(prefix + "-focus")) {
                clearFocus();
                $focusedTableRow = null;
            } else {
                clearFocus();
                $this.addClass(prefix + "-focus");
                $focusedTableRow = $this;
                if (rowClickFn) {
                    rowClickFn(this);
                }
            }
        });
    };

    AJS.FECRU.UI.tableSort = function (prefix, extractionFn) {
        var params = {};
        if (extractionFn) {
            params['textExtraction'] = extractionFn;
        }
        AJS.$("#" + prefix + "-table").tablesorter(params);
    };

    AJS.FECRU.UI.accordion = function () {
        AJS.$('.sidebar-collapse').live("click", function() {
            AJS.$('#content').toggleClass('collapsed-sidebar');
            return false;
        });

        AJS.$(".accordion-head").live("click", function() {
            var $next = AJS.$(this).next();
            var $parent = AJS.$(this).parent();
            if ($next.is(":hidden")) {
                $parent.addClass("active");
                if (!AJS.$.browser.msie) {//fork required as IE doesn't play nicely with jQuery slideDown animation
                    $next.slideDown("fast");
                }
                else {
                    $next.show();
                }
            }
            else {
                if (!AJS.$.browser.msie) {
                    $next.slideUp("fast", function () {
                        $parent.removeClass("active");
                    });
                }
                else {
                    $next.hide();
                    $parent.removeClass("active");
                }
            }
            return false;
        });

        AJS.$("#accordion-toggle").live("click", function() {
            var $accordionToggle = AJS.$(this);
            var $accordionContent = $accordionToggle.find(".accordion-content");
            var $parent = AJS.$(this).parent();
            var isExpanded = $accordionToggle.html() === 'expand';
            if (isExpanded) {
                $parent.addClass("active");
                if (!AJS.$.browser.msie) {//fork required as IE doesn't play nicely with jQuery slideDown animation
                    $accordionContent.slideDown("fast");
                }
                else {
                    $accordionContent.show();
                }

            }
            else {
                 if (!AJS.$.browser.msie) {
                    $accordionContent.slideUp("fast", function () {
                        $parent.removeClass("active");
                    });
                }
                else {
                    $accordionContent.hide();
                    $parent.removeClass("active");
                }
            }
            $accordionToggle.html(isExpanded ? 'expand' : 'collapse'); // reverse (it's now "wasExpanded")
            return false;
        });
    };

    var contentToggle = function () {
        AJS.$("#content-toggle").live("click", function() {
            var $contentToggle = AJS.$("#content-toggle");
            if ($contentToggle.html() == 'expand') {
                AJS.$(".details-verbose").show();
                $contentToggle.html("collapse").addClass("expanded");
            }
            else {
                AJS.$(".details-verbose").hide();
                $contentToggle.html("expand").removeClass("expanded");
            }
            return false;
        });

        AJS.$(".alwaysExpandChangesetsY").live("click", function() {
            AJS.$(".details-verbose").show();
            AJS.$("div.details-switch").addClass("details-expanded");
        });
        AJS.$(".alwaysExpandChangesetsN").live("click", function() {
            AJS.$(".details-verbose").hide();
            AJS.$("div.details-switch").removeClass("details-expanded");
        });
    };

    var inlineHover = function (which, event) {
        if (!AJS.$("#hover").hasClass("mouseover")) {
            return false;//don't fire if the link is no longer hovered
        }

        var source = which.attr("name").split("-")[0];
        var subject = which.attr("name").split("-")[1];
        var content;
        var offset = which.offset();
        var height = which.height();

        if (source === "user") {
            content = hovers.users[subject];
        }
        else if (source === "item") {
            content = hovers.items[subject];
        }

        AJS.$("#hover-content").html(content);
        AJS.$("#hover").css({
            display:"block",
            top: offset.top + height,
            left: offset.left
        });
    };

    AJS.FECRU.UI.augmentFormFields = function (which, className, defaultValue) {
        var $input = AJS.$(which);
        className = className || "focussed";

        if ($input.size()) {//input exists
            defaultValue = defaultValue || $input.val();

            $input.data("value", defaultValue)
            .focus(function() {
                if ($input.val() == $input.data("value")) {
                    $input.val("").addClass(className);
                }
            }).blur(function() {
                if ($input.val().match(/^\s*$/)) { //use the default if only have empty string or space
                    $input.val($input.data("value")).removeClass(className);
                }
            }).keydown(function(e){
                if ((e.which || e.keyCode) === AJS.$.ui.keyCode.ENTER) {// if return is clicked
                    $input.val(AJS.$.trim($input.val()));
                }
            });
            // The input may have been initialized to a non default (eg validation refill).
            if ($input.val() != defaultValue) {
                $input.addClass(className);
            }
        }
    };

    AJS.FECRU.UI.changesetToggle = function (postOpenFn) {
        return function() {
            AJS.$(".stream-delta dt.hasDiff").live("click", function (e) {
                var target = e.target.tagName.toLowerCase();
                if (target == "a" || target == "span") {
                    return;
                }

                var $node = AJS.$(this);
                $node.next().toggle();
                $node.toggleClass("open");
                if (postOpenFn) {
                    postOpenFn(AJS.$(this));
                }
            });
        };
    };
    AJS.FECRU.UI.changesetToggleAll = function () {
        AJS.$("#expand-all").click(function() {
            AJS.$(".stream-delta dd").show();
            AJS.$(".delta-toggle").html("collapse all").addClass("expanded");
            AJS.$(".stream-delta dt.hasDiff").addClass("open");
        });
        AJS.$("#collapse-all").click(function() {
            AJS.$(".stream-delta dd").hide();
            AJS.$(".delta-toggle").html("expand all").removeClass("expanded");
            AJS.$(".stream-delta dt.hasDiff").removeClass("open");
        });
    };

    AJS.FECRU.UI.initStream = function () {
        linkAugment();
        formAugment();
    };

    AJS.FECRU.UI.contentPadBottom = function () {
        var contentPadding = AJS.$("#content").css("padding-bottom");
        var messageHeight = AJS.$("#footer-bar .system-message").height();
        var messagePadding = parseInt(contentPadding, 10) + messageHeight;
        AJS.$("#content").css("padding-bottom", messagePadding);
    };

    /**
     * checks if the dates in the elements ".calendar-date-end" and ".calendar-date-start" are in the correct order
     * and swap them around if they are found to be reversed.
     *
     * @param extractDateStringFn a function to extract the date string into the format 'yy-mm-dd' from the format
     * obtained in the value attribute of the input element
     */
    AJS.FECRU.UI.swapDatesIfReversed = function(extractDateStringFn, context) {
        context = context || "body";
        var endDateInput = AJS.$("input.calendar-date-end", context);
        var startDateInput = AJS.$("input.calendar-date-start", context);
        if (endDateInput.length > 0 && startDateInput.length > 0) {
            var startDateStr = startDateInput.val();
            var endDateStr = endDateInput.val();
            var startDate = AJS.$.datepicker.parseDate('yy-mm-dd', extractDateStringFn(startDateStr));
            var endDate = AJS.$.datepicker.parseDate('yy-mm-dd', extractDateStringFn(endDateStr));

            if (startDateStr && endDateStr && (startDate > endDate)) {
                //do a swap of the date values before submitting if the dates are found to be reversed
                endDateInput.val(startDateStr);
                startDateInput.val(endDateStr);
            }
        }
    };

    AJS.FECRU.UI.setupCalendar = function(addTime) {
        addTime = addTime || true;
        var calDateStart = AJS.$("input.calendar-date-start");
        calDateStart.attr("autocomplete", "off");
        calDateStart.datepicker({
            dateFormat: 'yy-mm-dd',
            onClose: function(dateText) {
                //only do this if the text is the length of a date
                //this will FAIL if anyone changes the date format

                //NOTE: yy-mm-dd in jquery is a 10 character format
                // ie 1987-12-23
                if (addTime && dateText.length === 10) {
                    AJS.$(this).attr("value", dateText + "T00:00:00");
                }
            }
        });
        var calDateEnd = AJS.$("input.calendar-date-end");
        calDateEnd.attr("autocomplete", "off");
        calDateEnd.datepicker({
            dateFormat: 'yy-mm-dd',
            onClose: function(dateText) {
                if (addTime && dateText.length === 10) {
                    AJS.$(this).attr("value", dateText + "T23:59:59");
                }
            }
        });
    };

    AJS.FECRU.UI.warnAboutFirebug = function (onClose) {
        var cookieName = 'hide_fecru_fb_warn';
        var suppressWarning = AJS.$.cookie(cookieName) === 'Y';
        if (!suppressWarning && window.console && window.console.firebug) {
            var product = AJS.$("#product-name").text() || "FishEye + Crucible";
            var $warning = AJS.$("<div id='firebug-warning'><p>Firebug is known to cause performance problems with " +
                                 product + ". Why not disable it?</p><a class='close'>X</a></div>");
            AJS.$("#firebug-warning .close").live("click", function () {
                $warning.slideUp('fast', function() {
                    AJS.$.cookie(cookieName, 'Y', { expires: 365 });
                    if (onClose) {
                        onClose();
                    }
                });
            });
            $warning.prependTo(AJS.$("#cone"));
        }
    };

    AJS.FECRU.UI.toggleSearch = function () {
        AJS.$("h5:[rel='toggle']").live("click", function() {
            if (AJS.$(this).hasClass("show")) {
                AJS.$("#search-more").show();
                AJS.$(this).removeClass("show").addClass("hide");
                AJS.$(this).find("em").text("hide");
            }
            else {
                AJS.$("#search-more").hide();
                AJS.$(this).addClass("show").removeClass("hide");
                AJS.$(this).find("em").text("show");
            }
            return false;
        });
    };

    AJS.FECRU.UI.setCompletedResizeTimeout = function (selector, callback, completionDelay) {
        completionDelay = completionDelay || 100;
        var timeout;

        AJS.$(selector).resize(function () {
            if (completionDelay > 0 && timeout) {
                clearTimeout(timeout);
            }
            timeout = setTimeout(callback, completionDelay);
        });
    };
    
    AJS.FECRU.UI.setupRevisionPopups = function (selector) {
        if (!AJS.$.browser.msie) {
            AJS.$(selector + " tr .revision").live("mouseover", function () {
                var $trigger = AJS.$(this).find(".revision-popup:first").css("visibility", "visible");
                // Hide any old drop downs if they aren't our own. Moving between rows doesn't close the old dropdown,
                // it only hides it.
                var current = AJS.dropDown.current;
                if (current && current.$.closest(".revision-popup")[0] !== $trigger[0]) {
                    current.hide();
                }
            }).live("mouseout", function () {
                AJS.$(this).find(".revision-popup")
                        .css("visibility", "hidden");
            });
        } else {
            AJS.$(selector + " tr .revision").live("mouseover", function () {
                if (!AJS.dropDown.current) {
                    AJS.$(this).find(".revision-popup:first").css("visibility", "visible");
                }
            }).live("mouseout", function () {
                if (!AJS.dropDown.current) {
                    AJS.$(this).find(".revision-popup:first").css("visibility", "hidden");
                }
            });
        }
    };

    /**
     * For any tables that need to have the thead fixed.
     * @param options {object} holds the optional properties
     *        options.id {string} an optional ID for the faux table head
     *        options.className {string} an optional space separated classname/s for the faux table head
     *        options.sortable {boolean} If true, sortable styling will be added to the list
     *        options.resize {boolean} If true, the list items will be resized in line with the TH widths
     *        options.callback {function} A function to be called after the list is built.
    */
    AJS.FECRU.UI.tableHeadLock = function (options) {
        var settings = {
            $table: AJS.$("table.aui-theadfixed")
        };
        AJS.$.extend(settings, options);

        settings.className = (settings.className ? settings.className + " " : "") + "aui-theadfixed";

        AJS.$("div.aui-theadholder").siblings(".aui-theadfixed").remove();

        var $table = settings.$table;

        $table.each(function(){
            var $this = AJS.$(this);
            var $holder = $this.closest(".aui-theadholder");

            settings.$columns = $this.children("thead").find("th");
            settings.$row = $this.children("tbody").children("tr:visible:first");
            settings.$fauxhead = AJS.$("<ul/>").addClass(settings.className);
            var $fauxhead = settings.$fauxhead;

            if (settings.$row.size()) {
                settings.$columns.each(function(){
                    var $this = AJS.$(this);
                    var $html = ($fauxhead.hasClass("forehead-anchorless")) ? AJS.$("<a/>").html($this.html()) : $this.html();
                    var $span = AJS.$("<span/>").html($html);

                    $fauxhead.append(AJS.$("<li/>").append($span).addClass($this.attr("class")));
                });

                $holder.height($holder.height() - $fauxhead.height());

                $this.data("headlock", settings);// adds settings object to data store for access when the target is unidentified

                if (settings.id) {
                    $fauxhead.attr("id", settings.id);
                }

                if (settings.resize) {
                    AJS.FECRU.UI.tableHeadSizer(settings);
                }

                if (settings.sortable) {
                    $fauxhead.addClass("aui-sortable");
                    AJS.FECRU.UI.tableHeadSorter(settings);
                }
            }

            $fauxhead.insertBefore($holder);

            if (settings.callback) {
                settings.callback();
            }
        });
    };

    AJS.FECRU.UI.tableHeadSizer = function (settings) {
        if (!settings) {
            settings = AJS.$("table.aui-theadfixed").data("headlock");
        }
        else {
            if (AJS.$.browser.webkit) {
                var contentWidth = AJS.$("#content-column-panel").width();
                var contentResize = function () {
                    if (AJS.$("#content-column-panel").width() == contentWidth) {
                        setTimeout(contentResize, 500);
                    }
                    else {
                        AJS.FECRU.UI.tableHeadSizer();
                    }
                };
                setTimeout(contentResize, 500);
            }
        }

        if (!settings) {
            return;
        }

        var $row = settings.$row;
        var $cells = $row.children("td");

        var $list = settings.$fauxhead;
        var $items = $list.children("li");

        var count = 0;
        var breadth = 0;

        settings.$columns.each(function(i){
            var $column = AJS.$(this);
            var span = $column.attr("colspan") || 1;
            var width = 0;

            for (var j = 0; j < span; j++) {
                width += $cells.eq(count).outerWidth(true);
                count += 1;
            }

            $items.eq(i).width(width);
            breadth += width;
        });
        $list.css("min-width",breadth);

        AJS.$("#content-resizable").resizable({
            stop: function() {
                AJS.FECRU.UI.tableHeadSizer();
            },
            handles: "e",
            ghost: true
        });
    };

    AJS.FECRU.UI.tableHeadSorter = function (settings) {
        var $items = settings.$fauxhead.children("li");

        $items.unbind("click.tableHeadSorter").unbind("mouseleave.tableHeadSorter")
            .bind("click.tableHeadSorter", function(){
                var $item = AJS.$(this);
                var hrefless = settings.$fauxhead.hasClass("forehead-hrefless");
                var anchorless = settings.$fauxhead.hasClass("forehead-anchorless");
                if (hrefless || anchorless) {
                    if ($item.hasClass("sort-asc")) {
                        $item.removeClass("sort-asc").addClass("sort-current sort-desc");
                    }
                    else {
                        $item.removeClass("sort-desc").addClass("sort-current sort-asc");
                    }

                    var index = $items.index(this);
                    settings.$columns.eq(index).click();
                    $item.siblings().removeClass("sort-asc sort-desc");

                    console.log(settings.$columns.eq(index).html());
                }
                else {
                    $item.toggleClass("sort-asc sort-desc").addClass("sort-current")
                        .siblings().removeClass("sort-asc sort-desc");
                }
            })
            .bind("mouseleave.tableHeadSorter", function(){
                AJS.$(this).removeClass("sort-current");
            });
    };

    /* Wherein we find the combined widths of all the elements in the "Name" column,
       set the column to that width, and call the resize method of the table header
    */
    AJS.FECRU.UI.tableColumnSizer = function (options) {
        var settings = {
            $tables: AJS.$("#project-table"),
            cell: "td.project",
            buffer: 40
        };
        AJS.$.extend(settings, options);

        settings.$tables.each(function(){
            var width = 0;
            var $table = AJS.$(this);
            var $rows = $table.children("tbody").children("tr");
            $rows.each(function () {
                var current = 0;
                var widths = [];
                AJS.$(this).children("td:first").children("span").each(function () {
                    widths.push(AJS.$(this).outerWidth(true));
                    current += AJS.$(this).outerWidth(true);
                 });

                width = (current > width) ? current : width;

            });
            $rows.eq(0).children(settings.cell).width(width + settings.buffer);
        });
        AJS.FECRU.UI.tableHeadSizer();
    };
})();
;
/* END /2static/script/fecru/ui.js */
/* START /2static/script/fecru/onReady.js */
//init
AJS.toInit(function(){

    var fecru = AJS.FECRU;
    var ui = fecru.UI;

    ui.initStream();

    ui.setDropdowns();

    fecru.HOVER.addAllLinkPopups();

    fecru.RSS.setupRSSDialog();

    ui.accordion();
});
;
/* END /2static/script/fecru/onReady.js */
/* START /2static/script/fecru/star.js */
if (!AJS.FECRU) {
    AJS.FECRU = {};
}

(function() {

    /**
     * html for the star edit dialog, to be inserted on demand.
     */
    var DIALOG_DEFAULT = "Describe your favourite";
    var DIALOG_HTML = "<div id='astronomy'>" +
                          "<div id='astronomy-scape'>&nbsp;</div>" +
                          "<div id='astronomy-label'>" +
                              "<h4><span>Update favourite</span></h4>" +
                              "<span class='close'><a href='#' class='close-astronomy' id='close-astronomy'>X</a></span>" +
                              "<form action='#'>" +
                                  "<fieldset class='input'>" +
                                      "<label for='star-labels'>Name</label>" +
                                      "<input type='text' id='star-labels' value='" + DIALOG_DEFAULT +"'>" +
                                  "</fieldset>" +
                                  "<fieldset class='button'>" +
                                      "<ul>" +
                                          "<li><button class='remove' id='remove-astronomy'>Remove</button></li>" +
                                          "<li class='odd'><input type='submit' id='save-star-label' value='Save label'></li>" +
                                      "</ul>" +
                                  "</fieldset>" +
                              "</form>" +
                          "</div>" +
                      "</div>";

    ////// private functions

    var ON_ONLY = true;
    var OFF_ONLY = false;

    function starOnOffClassName(on) {
        return on ? "star-on" : "star-off";
    }

    function starAddRemoveText($star, on) {
        return $star.find(on ? "input.star-textRemove" : "input.star-textAdd").val();
    }

    /**
     * todo: see if this can be swapped with the throbber jquery plugin.
     */
    function makeThrobberControl($link, throbberSetting) {
        /**
         * Starts the throbber, which activates after noLatencyThreshold milliseconds have passed.
         * @return a function that will stop the throbber after minThrobberDisplay milliseconds have passed, ensuring no flicker.
         */
        var startThrob = function () {
            $link.data("throbbing", true);
            var timeout = setTimeout(function () {
                $link.addClass("star-throb");
            }, throbberSetting.noLatencyThreshold);

            return function() {
                $link.data("throbbing", false);
                clearTimeout(timeout);
                setTimeout(function () {
                    $link.removeClass("star-throb");
                }, throbberSetting.minThrobberDisplay);
            };
        };

        /**
         * stores a function to stop the throbber for the given $link
         */
        var stopThrob = undefined;
        return {
            start: function() {
                if (throbberSetting.showThrob && !stopThrob) {
                    stopThrob = startThrob();
                }
            },
            stop: function() {
                if (stopThrob) {
                    stopThrob();
                    stopThrob = undefined;
                }
            },
            isThrobbing: function() {
                return $link.data("throbbing");
            }
        };
    }

    /**
     *
     * @param link the jquery element of the link that was clicked
     * @param dialogControl an object to control the dialog. Its members should be:
     * - showDialog: a function with no arguments, which when invoked will display the dialog
     * - hideDialog: a function with no arguments, which when invoked will hide the dialog
     * - getDialog: a function with no arguments, which returns a jquery object that represents the dialog
     * @param options the list of options. Available options are:
     * - throbber : options for throbbing
     *
     */
    function starClicked(link, dialogControl, options) {
        var starId = getStarId(link);
        var starKeys = {};
        var $link = AJS.$(link);
        var throbberControl = makeThrobberControl($link, options.throbberSetting);
        //if throbbing already, then don't do anything.
        if (throbberControl.isThrobbing()) {
            return;
        }

        $link.children("span.inputs").children("input.starKey").each(function() {
            var key = AJS.$(this);
            starKeys[key.attr("name")] = key.val();
        });
        if (starId != null) {
            if (isDialogShown($link)) {
                // we are unstarring, pop up the edit box
                editStar(starId, dialogControl, throbberControl);
            } else {
                //...or remove directly without anymore interaction
                doRemoveStar(starId, throbberControl);
            }
        } else {
            // we are adding a star
            addStar(starKeys, dialogControl, throbberControl);
        }

        // todo: fix this hack to make hoverpopups refresh after starring
        var isUserStar = starKeys["itemType"] === 'atlassian-user';
        var isCommitterStar = starKeys["itemType"] === 'atlassian-committer';
        if (isUserStar || isCommitterStar) {
            //todo: problem is that some committer names displays user hovers, and so the trigger for the hover
            //todo: is no longer available for use here, so cannot work out what the cache key is from the star.
            //todo: refactor this into a better caching mechanism, may be use the DOM to cache instead.
           AJS.FECRU.HOVER.invalidateCache('userlinks');
        }

    }

    function updateAccordian() {
        var $starList = AJS.$("#accordionStarList");
        if ($starList.length > 0) {
            AJS.FECRU.AJAX.ajaxDo(fishEyePageContext + "/json/profile/starAccordionAjaxBody.do", {},
                    function(resp) {
                        if (resp.worked) {
                            $starList.replaceWith(resp.html);
                        }
                    },
                    false
                    );
        }
        var $numStars = AJS.$("#accordionNumStars");
        if ($numStars.length > 0) {
            AJS.FECRU.AJAX.ajaxDo(fishEyePageContext + "/json/profile/starAccordionAjaxNumber.do", {},
                    function(resp) {
                        if (resp.worked) {
                            $numStars.replaceWith(resp.html);
                        }
                    },
                    false
                    );
        }
    }

    function getIdElement(link) {
        return AJS.$(link).children("span.inputs").children("input[name='id']");
    }

    function getStarId(link) {
        return getIdElement(link).val();
    }

    function allStars(on) {
        return AJS.$("a." + starOnOffClassName(on));
    }

    function setStarAttributes(on, $node) {
        var newClass = starOnOffClassName(on);
        var oldClass = starOnOffClassName(!on);
        $node.removeClass(oldClass)
             .addClass(newClass)
             .children("span.starText").text(starAddRemoveText($node,on));
    }

    /**
     * Turn on the Star with the given keys
     * @param keys
     */
    function addStar(keys, dialogControl, throbberControl) {
        var paramMap = {};
        for (var key in keys) {
            paramMap["key." + key] = keys[key];
        }
        throbberControl.start();
        doStar(fishEyePageContext + "/json/profile/addStarAjax.do", paramMap, function(newId) {
            allStars(OFF_ONLY).each(function() {
                var $node = AJS.$(this);
                var $inputs = $node.children("span.inputs");
                for (var key in keys) {
                    var input = $inputs.children("input.starKey[name='" + key + "']");
                    if (input.length === 0 || input.val() != keys[key]) {
                        return;
                    }
                }
                setStarAttributes(true, $node);
                $inputs.append("<input type='hidden' name='id' value='" + newId + "'>");
            });
            // AJS.log("id = " + newId);
            if (keys["itemType"] == "atlassian-chart" || keys["itemType"] == "atlassian-search" ||
                keys["itemType"] == "atlassian-quicksearch") {
                editStar(newId, dialogControl, throbberControl);
            }
            throbberControl.stop();
        });
    }

    /**
     * Pop up a dialog box for a Star which is in the on state, allowing the user to set its label or to remove it.
     * @param starId the id of the Star to change.
     * @param dialogControl
     */
    function editStar(starId, dialogControl, throbberControl) {
        // pop up our star edit dialog
        var onSuccess = function(resp) {
            if (resp.worked) {
                var __updateLabel = function(label) {
                    doSaveLabel(starId, label, throbberControl);
                };
                var __removeStar = function() {
                    doRemoveStar(starId, throbberControl);
                };
                showStarDialog(dialogControl, __updateLabel, __removeStar, resp.payload);
            } else {
                //resp.worked is false, reset the star to an off star.
                turnOffStar(starId);
            }
            throbberControl.stop();
        };
        throbberControl.start();
        // get the label for this Star, to supply it to the dialog
        AJS.$.post(fishEyePageContext + "/json/profile/getStarLabelAjax.do", {id: starId}, onSuccess, 'json');
    }

    function turnOffStar(starId) {
        allStars(ON_ONLY).each(function() {
            var id = getStarId(this);
            if (starId == id) {
                // represents the same star
                getIdElement(this).remove();
                var $node = AJS.$(this);
                setStarAttributes(false, $node);
            }
        });
    }

    /**
     * Send a new label value to the server.
     * @param id the id of the Star who's label has been changed
     * @param label a String holding the new label text
     */
    function doSaveLabel(id, label, throbberControl) {
        var onDone = function() {
            refreshDropDown();
            throbberControl.stop();
        };
        throbberControl.start();
        AJS.$.post(fishEyePageContext + "/json/profile/setStarLabelAjax.do", {id:id, label:label}, onDone, 'json');
    }

    /**
     * Unstar the star with the given id.
     *
     * @param id the id of the Star to unstar.
     */
    function doRemoveStar(id, throbberControl) {
        throbberControl.start();
        doStar(fishEyePageContext + "/json/profile/removeStarAjax.do", {id: id}, function() {
            turnOffStar(id);
            throbberControl.stop();
        });
    }

    /**
     * Change the state of a set of stars, and refresh the 'my stars' dropdown.
     * @param url the URL to inform the server about the change
     * @param updateStars the function to call to change the state of the stars in the browser
     */
    function doStar(url, params, updateStars) {
        var done = function(resp) {
            if (resp.worked) {
                updateStars(resp.id);
                refreshDropDown();
                updateAccordian();
                return true;
            }
        };
        AJS.FECRU.AJAX.ajaxDo(url, params, done, false);
    }

    function refreshDropDown() {
        var $dropDown = AJS.$("#starDropDownList");
        if ($dropDown.length > 0) {
            var done = function(resp) {
                if (resp.worked) {
                    $dropDown.replaceWith(resp.html);
                }
            };
            AJS.FECRU.AJAX.ajaxDo(fishEyePageContext + "/json/profile/starDropDownAjax.do", {}, done, false);
        }
    }

    //todo: use AUI inline dialog
    function createStarDialog(dialogControl) {
        var $dialog = AJS.$(DIALOG_HTML);
        //intialize the dialog buttons
        AJS.$("#close-astronomy", $dialog).click(function(e) {
            dialogControl.hideDialog();
            e.preventDefault();
        });

        var $starLabel = AJS.$("#star-labels", $dialog);

        $starLabel.keypress(function(e) {
            if ((e.which || e.keyCode) === AJS.$.ui.keyCode.ENTER) {// if return is clicked
                e.preventDefault();                
                AJS.$("#save-star-label", $dialog).trigger('click');
            }
        }).attr("autocomplete", "off");

        return $dialog;
    }

    function showStarDialog(dialogController, updateLabel, removeStar, currentLabel) {
        var $dialog = dialogController.getDialog();
        var $starLabel = AJS.$("#star-labels", $dialog);

        if(currentLabel) {
            $starLabel.attr("value", currentLabel).data("defaultValue", currentLabel).addClass("focussed");
        } else {
            $starLabel.data("defaultValue", $starLabel.val());
        }

        AJS.$("#remove-astronomy", $dialog).unbind().click(function(e) {
            e.preventDefault();
            removeStar();
            dialogController.hideDialog();
            $starLabel.val(DIALOG_DEFAULT).removeClass("focussed");            
        });

        AJS.$("#save-star-label", $dialog).unbind().click(function(e) {
            e.preventDefault();
            var newLabel = $starLabel.val();
            if (newLabel !== $starLabel.data("defaultValue")) {
                $starLabel.data("defaultValue", newLabel);
                updateLabel(newLabel);
            }
            dialogController.hideDialog();
        });
        dialogController.showDialog();
    }

    function isDialogShown(starLink) {
        return starLink.hasClass("showDialog");
    }

    /**
     * bind a live click event to the stars.
     */
    function bindStars() {
        var $dialog = undefined; //stores a reference to the jquery elmem
        var dialogHider = {};
        var PADDING = 5;
        var hoverOptions = {
            onHover: false,
            showArrow: true,
            fadeTime: 200,
            hideDelay: 200,
            showDelay: 0,
            width: 240 + PADDING,
            offsetX: -8, //to get the arrow centred at the star's vertical asix
            offsetY: 0,
            container: "body",
            useLiveEvents: true,
            cacheContent:false,
            initCallback: function() {
                //implementation note: we need a way to hide the dialog on demand - this callback has a hook into
                //the hide function that inline-dialog uses internally, so this is saving a reference to that function
                //for use later.
                var that = this;
                dialogHider.hideDialog = function() {
                    that.hide();
                };
            }
        };
        var onStarClickedHandler = function ($contentDiv, trigger, showPopup) {
            var $link = AJS.$(trigger);
            var dialogControl = {
                showDialog: function() {
                    if (isDialogShown($link)) {
                        if ($dialog === undefined) {
                            $dialog = createStarDialog(dialogHider);
                        }
                        $dialog.appendTo($contentDiv).show();
                        AJS.FECRU.UI.augmentFormFields("#star-labels");
                        showPopup();
                    }
                },
                hideDialog: function() {
                    dialogHider.hideDialog();
                },
                getDialog: function () {
                    if ($dialog === undefined) {
                        $dialog = createStarDialog(dialogHider);
                    }
                    return $dialog;
                }
            };
            var opts = {
                throbberSetting: {
                    showThrob: true,         //todo: customizable throbbing needed?
                    noLatencyThreshold: 150, //if the ajax call takes less than this milliseconds, then no throb shown
                    minThrobberDisplay: 200  //otherwise, show for at least this milliseconds.
                }
            };
            starClicked(trigger, dialogControl, opts);
        };
        AJS.InlineDialog(".starrable", "star-inline-dialog", onStarClickedHandler, hoverOptions);
    }

    ////// onload events for stars
    AJS.toInit(function() {
        bindStars();
    });
})();
;
/* END /2static/script/fecru/star.js */
/* START /2static/script/fecru/prefs.js */
if (!AJS.FECRU) {
    AJS.FECRU = {};
    AJS.FECRU.PREFS = {};
} else if (!AJS.FECRU.PREFS) {
    AJS.FECRU.PREFS = {};
}


AJS.FECRU.PREFS.setPreference = function(name, value, callback) {
    var params = {};
    params[name] = value;
    AJS.FECRU.PREFS.setPreferences(params, callback);
};

AJS.FECRU.PREFS.setPreferences = function(prefs, callback) {
    var params = {};
    for (i in prefs) {
        params["@" + i] = prefs[i];
    }
    AJS.FECRU.AJAX.ajaxDo(fishEyePageContext + "/json/fe/setPreference.do", params, callback, false);
};

AJS.FECRU.PREFS.setupBinaryPrefLinks = function(name, cookieName, initialState, fns, onStateString, offStateString, forceInitialCall, reload) {
    var onSelector = "." + name + onStateString;
    var offSelector = "." + name + offStateString;

    var setPref = function (value, hideSelector, showSelector, callback) {
        AJS.FECRU.PREFS.setPreference(cookieName, value, callback);
        setVisibility(value, hideSelector, showSelector, true);
    }

    var setVisibility = function (value, hideSelector, showSelector, callfunction) {
        AJS.$(hideSelector).hide();
        AJS.$(showSelector).css("display","block");
        if(callfunction || forceInitialCall) {
           fns[value].call();
        }
    }

    var reloadPage = function () {
        if(reload) {
           window.location = window.location;
        }
    }

    AJS.$(onSelector).live("click", function() {
        setPref(onStateString, onSelector, offSelector, reloadPage);
        return false;
    });
    AJS.$(offSelector).live("click", function() {
        setPref(offStateString, offSelector, onSelector, reloadPage);
        return false;
    });
    if (initialState) {
        setVisibility(onStateString, onSelector, offSelector, false);
    } else {
        setVisibility(offStateString, offSelector, onSelector, false);
    }
};
;
/* END /2static/script/fecru/prefs.js */
/* START /2static/script/lib/raphael-1.3.1.min.js */
/*
 * Raphael 1.3.1 - JavaScript Vector Library
 *
 * Copyright (c) 2008 - 2009 Dmitry Baranovskiy (http://raphaeljs.com)
 * Licensed under the MIT (http://www.opensource.org/licenses/mit-license.php) license.
 */
Raphael=(function(){var a=/[, ]+/,aO=/^(circle|rect|path|ellipse|text|image)$/,L=document,au=window,l={was:"Raphael" in au,is:au.Raphael},an=function(){if(an.is(arguments[0],"array")){var d=arguments[0],e=w[aW](an,d.splice(0,3+an.is(d[0],al))),S=e.set();for(var R=0,a0=d[m];R<a0;R++){var E=d[R]||{};aO.test(E.type)&&S[f](e[E.type]().attr(E));}return S;}return w[aW](an,arguments);},aT=function(){},aL="appendChild",aW="apply",aS="concat",at="",am=" ",z="split",F="click dblclick mousedown mousemove mouseout mouseover mouseup"[z](am),Q="hasOwnProperty",az="join",m="length",aY="prototype",aZ=String[aY].toLowerCase,ab=Math,g=ab.max,aI=ab.min,al="number",aA="toString",aw=Object[aY][aA],aQ={},aM=ab.pow,f="push",aU=/^(?=[\da-f]$)/,c=/^url\(['"]?([^\)]+)['"]?\)$/i,x=/^\s*((#[a-f\d]{6})|(#[a-f\d]{3})|rgb\(\s*([\d\.]+\s*,\s*[\d\.]+\s*,\s*[\d\.]+)\s*\)|rgb\(\s*([\d\.]+%\s*,\s*[\d\.]+%\s*,\s*[\d\.]+%)\s*\)|hs[bl]\(\s*([\d\.]+\s*,\s*[\d\.]+\s*,\s*[\d\.]+)\s*\)|hs[bl]\(\s*([\d\.]+%\s*,\s*[\d\.]+%\s*,\s*[\d\.]+%)\s*\))\s*$/i,O=ab.round,v="setAttribute",W=parseFloat,G=parseInt,aN=String[aY].toUpperCase,j={"clip-rect":"0 0 1e9 1e9",cursor:"default",cx:0,cy:0,fill:"#fff","fill-opacity":1,font:'10px "Arial"',"font-family":'"Arial"',"font-size":"10","font-style":"normal","font-weight":400,gradient:0,height:0,href:"http://raphaeljs.com/",opacity:1,path:"M0,0",r:0,rotation:0,rx:0,ry:0,scale:"1 1",src:"",stroke:"#000","stroke-dasharray":"","stroke-linecap":"butt","stroke-linejoin":"butt","stroke-miterlimit":0,"stroke-opacity":1,"stroke-width":1,target:"_blank","text-anchor":"middle",title:"Raphael",translation:"0 0",width:0,x:0,y:0},Z={along:"along","clip-rect":"csv",cx:al,cy:al,fill:"colour","fill-opacity":al,"font-size":al,height:al,opacity:al,path:"path",r:al,rotation:"csv",rx:al,ry:al,scale:"csv",stroke:"colour","stroke-opacity":al,"stroke-width":al,translation:"csv",width:al,x:al,y:al},aP="replace";an.version="1.3.1";an.type=(au.SVGAngle||L.implementation.hasFeature("http://www.w3.org/TR/SVG11/feature#BasicStructure","1.1")?"SVG":"VML");if(an.type=="VML"){var ag=document.createElement("div");ag.innerHTML="<!--[if vml]><br><br><![endif]-->";if(ag.childNodes[m]!=2){return null;}}an.svg=!(an.vml=an.type=="VML");aT[aY]=an[aY];an._id=0;an._oid=0;an.fn={};an.is=function(e,d){d=aZ.call(d);return((d=="object"||d=="undefined")&&typeof e==d)||(e==null&&d=="null")||aZ.call(aw.call(e).slice(8,-1))==d;};an.setWindow=function(d){au=d;L=au.document;};var aD=function(e){if(an.vml){var d=/^\s+|\s+$/g;aD=aj(function(R){var S;R=(R+at)[aP](d,at);try{var a0=new ActiveXObject("htmlfile");a0.write("<body>");a0.close();S=a0.body;}catch(a2){S=createPopup().document.body;}var i=S.createTextRange();try{S.style.color=R;var a1=i.queryCommandValue("ForeColor");a1=((a1&255)<<16)|(a1&65280)|((a1&16711680)>>>16);return"#"+("000000"+a1[aA](16)).slice(-6);}catch(a2){return"none";}});}else{var E=L.createElement("i");E.title="Rapha\xebl Colour Picker";E.style.display="none";L.body[aL](E);aD=aj(function(i){E.style.color=i;return L.defaultView.getComputedStyle(E,at).getPropertyValue("color");});}return aD(e);};an.hsb2rgb=aj(function(a3,a1,a7){if(an.is(a3,"object")&&"h" in a3&&"s" in a3&&"b" in a3){a7=a3.b;a1=a3.s;a3=a3.h;}var R,S,a8;if(a7==0){return{r:0,g:0,b:0,hex:"#000"};}if(a3>1||a1>1||a7>1){a3/=255;a1/=255;a7/=255;}var a0=~~(a3*6),a4=(a3*6)-a0,E=a7*(1-a1),e=a7*(1-(a1*a4)),a9=a7*(1-(a1*(1-a4)));R=[a7,e,E,E,a9,a7,a7][a0];S=[a9,a7,a7,e,E,E,a9][a0];a8=[E,E,a9,a7,a7,e,E][a0];R*=255;S*=255;a8*=255;var a5={r:R,g:S,b:a8},d=(~~R)[aA](16),a2=(~~S)[aA](16),a6=(~~a8)[aA](16);d=d[aP](aU,"0");a2=a2[aP](aU,"0");a6=a6[aP](aU,"0");a5.hex="#"+d+a2+a6;return a5;},an);an.rgb2hsb=aj(function(d,e,a1){if(an.is(d,"object")&&"r" in d&&"g" in d&&"b" in d){a1=d.b;e=d.g;d=d.r;}if(an.is(d,"string")){var a3=an.getRGB(d);d=a3.r;e=a3.g;a1=a3.b;}if(d>1||e>1||a1>1){d/=255;e/=255;a1/=255;}var a0=g(d,e,a1),i=aI(d,e,a1),R,E,S=a0;if(i==a0){return{h:0,s:0,b:a0};}else{var a2=(a0-i);E=a2/a0;if(d==a0){R=(e-a1)/a2;}else{if(e==a0){R=2+((a1-d)/a2);}else{R=4+((d-e)/a2);}}R/=6;R<0&&R++;R>1&&R--;}return{h:R,s:E,b:S};},an);var aE=/,?([achlmqrstvxz]),?/gi;an._path2string=function(){return this.join(",")[aP](aE,"$1");};function aj(E,e,d){function i(){var R=Array[aY].slice.call(arguments,0),a0=R[az]("\u25ba"),S=i.cache=i.cache||{},a1=i.count=i.count||[];if(S[Q](a0)){return d?d(S[a0]):S[a0];}a1[m]>=1000&&delete S[a1.shift()];a1[f](a0);S[a0]=E[aW](e,R);return d?d(S[a0]):S[a0];}return i;}an.getRGB=aj(function(d){if(!d||!!((d=d+at).indexOf("-")+1)){return{r:-1,g:-1,b:-1,hex:"none",error:1};}if(d=="none"){return{r:-1,g:-1,b:-1,hex:"none"};}!(({hs:1,rg:1})[Q](d.substring(0,2))||d.charAt()=="#")&&(d=aD(d));var S,i,E,a2,a3,a0=d.match(x);if(a0){if(a0[2]){a2=G(a0[2].substring(5),16);E=G(a0[2].substring(3,5),16);i=G(a0[2].substring(1,3),16);}if(a0[3]){a2=G((a3=a0[3].charAt(3))+a3,16);E=G((a3=a0[3].charAt(2))+a3,16);i=G((a3=a0[3].charAt(1))+a3,16);}if(a0[4]){a0=a0[4][z](/\s*,\s*/);i=W(a0[0]);E=W(a0[1]);a2=W(a0[2]);}if(a0[5]){a0=a0[5][z](/\s*,\s*/);i=W(a0[0])*2.55;E=W(a0[1])*2.55;a2=W(a0[2])*2.55;}if(a0[6]){a0=a0[6][z](/\s*,\s*/);i=W(a0[0]);E=W(a0[1]);a2=W(a0[2]);return an.hsb2rgb(i,E,a2);}if(a0[7]){a0=a0[7][z](/\s*,\s*/);i=W(a0[0])*2.55;E=W(a0[1])*2.55;a2=W(a0[2])*2.55;return an.hsb2rgb(i,E,a2);}a0={r:i,g:E,b:a2};var e=(~~i)[aA](16),R=(~~E)[aA](16),a1=(~~a2)[aA](16);e=e[aP](aU,"0");R=R[aP](aU,"0");a1=a1[aP](aU,"0");a0.hex="#"+e+R+a1;return a0;}return{r:-1,g:-1,b:-1,hex:"none",error:1};},an);an.getColor=function(e){var i=this.getColor.start=this.getColor.start||{h:0,s:1,b:e||0.75},d=this.hsb2rgb(i.h,i.s,i.b);i.h+=0.075;if(i.h>1){i.h=0;i.s-=0.2;i.s<=0&&(this.getColor.start={h:0,s:1,b:i.b});}return d.hex;};an.getColor.reset=function(){delete this.start;};an.parsePathString=aj(function(d){if(!d){return null;}var i={a:7,c:6,h:1,l:2,m:2,q:4,s:4,t:2,v:1,z:0},e=[];if(an.is(d,"array")&&an.is(d[0],"array")){e=av(d);}if(!e[m]){(d+at)[aP](/([achlmqstvz])[\s,]*((-?\d*\.?\d*(?:e[-+]?\d+)?\s*,?\s*)+)/ig,function(R,E,a1){var a0=[],S=aZ.call(E);a1[aP](/(-?\d*\.?\d*(?:e[-+]?\d+)?)\s*,?\s*/ig,function(a3,a2){a2&&a0[f](+a2);});while(a0[m]>=i[S]){e[f]([E][aS](a0.splice(0,i[S])));if(!i[S]){break;}}});}e[aA]=an._path2string;return e;});an.findDotsAtSegment=function(e,d,be,bc,a0,R,a2,a1,a8){var a6=1-a8,a5=aM(a6,3)*e+aM(a6,2)*3*a8*be+a6*3*a8*a8*a0+aM(a8,3)*a2,a3=aM(a6,3)*d+aM(a6,2)*3*a8*bc+a6*3*a8*a8*R+aM(a8,3)*a1,ba=e+2*a8*(be-e)+a8*a8*(a0-2*be+e),a9=d+2*a8*(bc-d)+a8*a8*(R-2*bc+d),bd=be+2*a8*(a0-be)+a8*a8*(a2-2*a0+be),bb=bc+2*a8*(R-bc)+a8*a8*(a1-2*R+bc),a7=(1-a8)*e+a8*be,a4=(1-a8)*d+a8*bc,E=(1-a8)*a0+a8*a2,i=(1-a8)*R+a8*a1,S=(90-ab.atan((ba-bd)/(a9-bb))*180/ab.PI);(ba>bd||a9<bb)&&(S+=180);return{x:a5,y:a3,m:{x:ba,y:a9},n:{x:bd,y:bb},start:{x:a7,y:a4},end:{x:E,y:i},alpha:S};};var U=aj(function(a5){if(!a5){return{x:0,y:0,width:0,height:0};}a5=H(a5);var a2=0,a1=0,R=[],e=[],E;for(var S=0,a4=a5[m];S<a4;S++){E=a5[S];if(E[0]=="M"){a2=E[1];a1=E[2];R[f](a2);e[f](a1);}else{var a0=aC(a2,a1,E[1],E[2],E[3],E[4],E[5],E[6]);R=R[aS](a0.min.x,a0.max.x);e=e[aS](a0.min.y,a0.max.y);a2=E[5];a1=E[6];}}var d=aI[aW](0,R),a3=aI[aW](0,e);return{x:d,y:a3,width:g[aW](0,R)-d,height:g[aW](0,e)-a3};}),av=function(a0){var E=[];if(!an.is(a0,"array")||!an.is(a0&&a0[0],"array")){a0=an.parsePathString(a0);}for(var e=0,R=a0[m];e<R;e++){E[e]=[];for(var d=0,S=a0[e][m];d<S;d++){E[e][d]=a0[e][d];}}E[aA]=an._path2string;return E;},ad=aj(function(R){if(!an.is(R,"array")||!an.is(R&&R[0],"array")){R=an.parsePathString(R);}var a4=[],a6=0,a5=0,a9=0,a8=0,E=0;if(R[0][0]=="M"){a6=R[0][1];a5=R[0][2];a9=a6;a8=a5;E++;a4[f](["M",a6,a5]);}for(var a1=E,ba=R[m];a1<ba;a1++){var d=a4[a1]=[],a7=R[a1];if(a7[0]!=aZ.call(a7[0])){d[0]=aZ.call(a7[0]);switch(d[0]){case"a":d[1]=a7[1];d[2]=a7[2];d[3]=a7[3];d[4]=a7[4];d[5]=a7[5];d[6]=+(a7[6]-a6).toFixed(3);d[7]=+(a7[7]-a5).toFixed(3);break;case"v":d[1]=+(a7[1]-a5).toFixed(3);break;case"m":a9=a7[1];a8=a7[2];default:for(var a0=1,a2=a7[m];a0<a2;a0++){d[a0]=+(a7[a0]-((a0%2)?a6:a5)).toFixed(3);}}}else{d=a4[a1]=[];if(a7[0]=="m"){a9=a7[1]+a6;a8=a7[2]+a5;}for(var S=0,e=a7[m];S<e;S++){a4[a1][S]=a7[S];}}var a3=a4[a1][m];switch(a4[a1][0]){case"z":a6=a9;a5=a8;break;case"h":a6+=+a4[a1][a3-1];break;case"v":a5+=+a4[a1][a3-1];break;default:a6+=+a4[a1][a3-2];a5+=+a4[a1][a3-1];}}a4[aA]=an._path2string;return a4;},0,av),r=aj(function(R){if(!an.is(R,"array")||!an.is(R&&R[0],"array")){R=an.parsePathString(R);}var a3=[],a5=0,a4=0,a8=0,a7=0,E=0;if(R[0][0]=="M"){a5=+R[0][1];a4=+R[0][2];a8=a5;a7=a4;E++;a3[0]=["M",a5,a4];}for(var a1=E,a9=R[m];a1<a9;a1++){var d=a3[a1]=[],a6=R[a1];if(a6[0]!=aN.call(a6[0])){d[0]=aN.call(a6[0]);switch(d[0]){case"A":d[1]=a6[1];d[2]=a6[2];d[3]=a6[3];d[4]=a6[4];d[5]=a6[5];d[6]=+(a6[6]+a5);d[7]=+(a6[7]+a4);break;case"V":d[1]=+a6[1]+a4;break;case"H":d[1]=+a6[1]+a5;break;case"M":a8=+a6[1]+a5;a7=+a6[2]+a4;default:for(var a0=1,a2=a6[m];a0<a2;a0++){d[a0]=+a6[a0]+((a0%2)?a5:a4);}}}else{for(var S=0,e=a6[m];S<e;S++){a3[a1][S]=a6[S];}}switch(d[0]){case"Z":a5=a8;a4=a7;break;case"H":a5=d[1];break;case"V":a4=d[1];break;default:a5=a3[a1][a3[a1][m]-2];a4=a3[a1][a3[a1][m]-1];}}a3[aA]=an._path2string;return a3;},null,av),aX=function(e,E,d,i){return[e,E,d,i,d,i];},aK=function(e,E,a0,R,d,i){var S=1/3,a1=2/3;return[S*e+a1*a0,S*E+a1*R,S*d+a1*a0,S*i+a1*R,d,i];},K=function(a9,bE,bi,bg,ba,a4,S,a8,bD,bb){var R=ab.PI,bf=R*120/180,d=R/180*(+ba||0),bm=[],bj,bA=aj(function(bF,bI,i){var bH=bF*ab.cos(i)-bI*ab.sin(i),bG=bF*ab.sin(i)+bI*ab.cos(i);return{x:bH,y:bG};});if(!bb){bj=bA(a9,bE,-d);a9=bj.x;bE=bj.y;bj=bA(a8,bD,-d);a8=bj.x;bD=bj.y;var e=ab.cos(R/180*ba),a6=ab.sin(R/180*ba),bo=(a9-a8)/2,bn=(bE-bD)/2;bi=g(bi,ab.abs(bo));bg=g(bg,ab.abs(bn));var by=(bo*bo)/(bi*bi)+(bn*bn)/(bg*bg);if(by>1){bi=ab.sqrt(by)*bi;bg=ab.sqrt(by)*bg;}var E=bi*bi,br=bg*bg,bt=(a4==S?-1:1)*ab.sqrt(ab.abs((E*br-E*bn*bn-br*bo*bo)/(E*bn*bn+br*bo*bo))),bd=bt*bi*bn/bg+(a9+a8)/2,bc=bt*-bg*bo/bi+(bE+bD)/2,a3=ab.asin(((bE-bc)/bg).toFixed(7)),a2=ab.asin(((bD-bc)/bg).toFixed(7));a3=a9<bd?R-a3:a3;a2=a8<bd?R-a2:a2;a3<0&&(a3=R*2+a3);a2<0&&(a2=R*2+a2);if(S&&a3>a2){a3=a3-R*2;}if(!S&&a2>a3){a2=a2-R*2;}}else{a3=bb[0];a2=bb[1];bd=bb[2];bc=bb[3];}var a7=a2-a3;if(ab.abs(a7)>bf){var be=a2,bh=a8,a5=bD;a2=a3+bf*(S&&a2>a3?1:-1);a8=bd+bi*ab.cos(a2);bD=bc+bg*ab.sin(a2);bm=K(a8,bD,bi,bg,ba,0,S,bh,a5,[a2,be,bd,bc]);}a7=a2-a3;var a1=ab.cos(a3),bC=ab.sin(a3),a0=ab.cos(a2),bB=ab.sin(a2),bp=ab.tan(a7/4),bs=4/3*bi*bp,bq=4/3*bg*bp,bz=[a9,bE],bx=[a9+bs*bC,bE-bq*a1],bw=[a8+bs*bB,bD-bq*a0],bu=[a8,bD];bx[0]=2*bz[0]-bx[0];bx[1]=2*bz[1]-bx[1];if(bb){return[bx,bw,bu][aS](bm);}else{bm=[bx,bw,bu][aS](bm)[az]()[z](",");var bk=[];for(var bv=0,bl=bm[m];bv<bl;bv++){bk[bv]=bv%2?bA(bm[bv-1],bm[bv],d).y:bA(bm[bv],bm[bv+1],d).x;}return bk;}},M=function(e,d,E,i,a2,a1,a0,S,a3){var R=1-a3;return{x:aM(R,3)*e+aM(R,2)*3*a3*E+R*3*a3*a3*a2+aM(a3,3)*a0,y:aM(R,3)*d+aM(R,2)*3*a3*i+R*3*a3*a3*a1+aM(a3,3)*S};},aC=aj(function(i,d,R,E,a9,a8,a5,a2){var a7=(a9-2*R+i)-(a5-2*a9+R),a4=2*(R-i)-2*(a9-R),a1=i-R,a0=(-a4+ab.sqrt(a4*a4-4*a7*a1))/2/a7,S=(-a4-ab.sqrt(a4*a4-4*a7*a1))/2/a7,a3=[d,a2],a6=[i,a5],e;ab.abs(a0)>1000000000000&&(a0=0.5);ab.abs(S)>1000000000000&&(S=0.5);if(a0>0&&a0<1){e=M(i,d,R,E,a9,a8,a5,a2,a0);a6[f](e.x);a3[f](e.y);}if(S>0&&S<1){e=M(i,d,R,E,a9,a8,a5,a2,S);a6[f](e.x);a3[f](e.y);}a7=(a8-2*E+d)-(a2-2*a8+E);a4=2*(E-d)-2*(a8-E);a1=d-E;a0=(-a4+ab.sqrt(a4*a4-4*a7*a1))/2/a7;S=(-a4-ab.sqrt(a4*a4-4*a7*a1))/2/a7;ab.abs(a0)>1000000000000&&(a0=0.5);ab.abs(S)>1000000000000&&(S=0.5);if(a0>0&&a0<1){e=M(i,d,R,E,a9,a8,a5,a2,a0);a6[f](e.x);a3[f](e.y);}if(S>0&&S<1){e=M(i,d,R,E,a9,a8,a5,a2,S);a6[f](e.x);a3[f](e.y);}return{min:{x:aI[aW](0,a6),y:aI[aW](0,a3)},max:{x:g[aW](0,a6),y:g[aW](0,a3)}};}),H=aj(function(a9,a4){var R=r(a9),a5=a4&&r(a4),a6={x:0,y:0,bx:0,by:0,X:0,Y:0,qx:null,qy:null},d={x:0,y:0,bx:0,by:0,X:0,Y:0,qx:null,qy:null},a0=function(ba,bb){var i,bc;if(!ba){return["C",bb.x,bb.y,bb.x,bb.y,bb.x,bb.y];}!(ba[0] in {T:1,Q:1})&&(bb.qx=bb.qy=null);switch(ba[0]){case"M":bb.X=ba[1];bb.Y=ba[2];break;case"A":ba=["C"][aS](K[aW](0,[bb.x,bb.y][aS](ba.slice(1))));break;case"S":i=bb.x+(bb.x-(bb.bx||bb.x));bc=bb.y+(bb.y-(bb.by||bb.y));ba=["C",i,bc][aS](ba.slice(1));break;case"T":bb.qx=bb.x+(bb.x-(bb.qx||bb.x));bb.qy=bb.y+(bb.y-(bb.qy||bb.y));ba=["C"][aS](aK(bb.x,bb.y,bb.qx,bb.qy,ba[1],ba[2]));break;case"Q":bb.qx=ba[1];bb.qy=ba[2];ba=["C"][aS](aK(bb.x,bb.y,ba[1],ba[2],ba[3],ba[4]));break;case"L":ba=["C"][aS](aX(bb.x,bb.y,ba[1],ba[2]));break;case"H":ba=["C"][aS](aX(bb.x,bb.y,ba[1],bb.y));break;case"V":ba=["C"][aS](aX(bb.x,bb.y,bb.x,ba[1]));break;case"Z":ba=["C"][aS](aX(bb.x,bb.y,bb.X,bb.Y));break;}return ba;},e=function(ba,bb){if(ba[bb][m]>7){ba[bb].shift();var bc=ba[bb];while(bc[m]){ba.splice(bb++,0,["C"][aS](bc.splice(0,6)));}ba.splice(bb,1);a7=g(R[m],a5&&a5[m]||0);}},E=function(be,bd,bb,ba,bc){if(be&&bd&&be[bc][0]=="M"&&bd[bc][0]!="M"){bd.splice(bc,0,["M",ba.x,ba.y]);bb.bx=0;bb.by=0;bb.x=be[bc][1];bb.y=be[bc][2];a7=g(R[m],a5&&a5[m]||0);}};for(var a2=0,a7=g(R[m],a5&&a5[m]||0);a2<a7;a2++){R[a2]=a0(R[a2],a6);e(R,a2);a5&&(a5[a2]=a0(a5[a2],d));a5&&e(a5,a2);E(R,a5,a6,d,a2);E(a5,R,d,a6,a2);var a1=R[a2],a8=a5&&a5[a2],S=a1[m],a3=a5&&a8[m];a6.x=a1[S-2];a6.y=a1[S-1];a6.bx=W(a1[S-4])||a6.x;a6.by=W(a1[S-3])||a6.y;d.bx=a5&&(W(a8[a3-4])||d.x);d.by=a5&&(W(a8[a3-3])||d.y);d.x=a5&&a8[a3-2];d.y=a5&&a8[a3-1];}return a5?[R,a5]:R;},null,av),p=aj(function(a4){var a3=[];for(var a0=0,a5=a4[m];a0<a5;a0++){var e={},a2=a4[a0].match(/^([^:]*):?([\d\.]*)/);e.color=an.getRGB(a2[1]);if(e.color.error){return null;}e.color=e.color.hex;a2[2]&&(e.offset=a2[2]+"%");a3[f](e);}for(var a0=1,a5=a3[m]-1;a0<a5;a0++){if(!a3[a0].offset){var E=W(a3[a0-1].offset||0),R=0;for(var S=a0+1;S<a5;S++){if(a3[S].offset){R=a3[S].offset;break;}}if(!R){R=100;S=a5;}R=W(R);var a1=(R-E)/(S-a0+1);for(;a0<S;a0++){E+=a1;a3[a0].offset=E+"%";}}}return a3;}),ao=function(){var i,e,R,E,d;if(an.is(arguments[0],"string")||an.is(arguments[0],"object")){if(an.is(arguments[0],"string")){i=L.getElementById(arguments[0]);}else{i=arguments[0];}if(i.tagName){if(arguments[1]==null){return{container:i,width:i.style.pixelWidth||i.offsetWidth,height:i.style.pixelHeight||i.offsetHeight};}else{return{container:i,width:arguments[1],height:arguments[2]};}}}else{if(an.is(arguments[0],al)&&arguments[m]>3){return{container:1,x:arguments[0],y:arguments[1],width:arguments[2],height:arguments[3]};}}},aG=function(d,i){var e=this;for(var E in i){if(i[Q](E)&&!(E in d)){switch(typeof i[E]){case"function":(function(R){d[E]=d===e?R:function(){return R[aW](e,arguments);};})(i[E]);break;case"object":d[E]=d[E]||{};aG.call(this,d[E],i[E]);break;default:d[E]=i[E];break;}}}},ak=function(d,e){d==e.top&&(e.top=d.prev);d==e.bottom&&(e.bottom=d.next);d.next&&(d.next.prev=d.prev);d.prev&&(d.prev.next=d.next);},Y=function(d,e){if(e.top===d){return;}ak(d,e);d.next=null;d.prev=e.top;e.top.next=d;e.top=d;},k=function(d,e){if(e.bottom===d){return;}ak(d,e);d.next=e.bottom;d.prev=null;e.bottom.prev=d;e.bottom=d;},A=function(e,d,i){ak(e,i);d==i.top&&(i.top=e);d.next&&(d.next.prev=e);e.next=d.next;e.prev=d;d.next=e;},aq=function(e,d,i){ak(e,i);d==i.bottom&&(i.bottom=e);d.prev&&(d.prev.next=e);e.prev=d.prev;d.prev=e;e.next=d;},s=function(d){return function(){throw new Error("Rapha\xebl: you are calling to method \u201c"+d+"\u201d of removed object");};},ar=/^r(?:\(([^,]+?)\s*,\s*([^\)]+?)\))?/;if(an.svg){aT[aY].svgns="http://www.w3.org/2000/svg";aT[aY].xlink="http://www.w3.org/1999/xlink";var O=function(d){return +d+(~~d===d)*0.5;},V=function(S){for(var e=0,E=S[m];e<E;e++){if(aZ.call(S[e][0])!="a"){for(var d=1,R=S[e][m];d<R;d++){S[e][d]=O(S[e][d]);}}else{S[e][6]=O(S[e][6]);S[e][7]=O(S[e][7]);}}return S;},aJ=function(i,d){if(d){for(var e in d){if(d[Q](e)){i[v](e,d[e]);}}}else{return L.createElementNS(aT[aY].svgns,i);}};an[aA]=function(){return"Your browser supports SVG.\nYou are running Rapha\xebl "+this.version;};var q=function(d,E){var e=aJ("path");E.canvas&&E.canvas[aL](e);var i=new ax(e,E);i.type="path";aa(i,{fill:"none",stroke:"#000",path:d});return i;};var b=function(E,a7,d){var a4="linear",a1=0.5,S=0.5,a9=E.style;a7=(a7+at)[aP](ar,function(bb,i,bc){a4="radial";if(i&&bc){a1=W(i);S=W(bc);var ba=((S>0.5)*2-1);aM(a1-0.5,2)+aM(S-0.5,2)>0.25&&(S=ab.sqrt(0.25-aM(a1-0.5,2))*ba+0.5)&&S!=0.5&&(S=S.toFixed(5)-0.00001*ba);}return at;});a7=a7[z](/\s*\-\s*/);if(a4=="linear"){var a0=a7.shift();a0=-W(a0);if(isNaN(a0)){return null;}var R=[0,0,ab.cos(a0*ab.PI/180),ab.sin(a0*ab.PI/180)],a6=1/(g(ab.abs(R[2]),ab.abs(R[3]))||1);R[2]*=a6;R[3]*=a6;if(R[2]<0){R[0]=-R[2];R[2]=0;}if(R[3]<0){R[1]=-R[3];R[3]=0;}}var a3=p(a7);if(!a3){return null;}var e=aJ(a4+"Gradient");e.id="r"+(an._id++)[aA](36);aJ(e,a4=="radial"?{fx:a1,fy:S}:{x1:R[0],y1:R[1],x2:R[2],y2:R[3]});d.defs[aL](e);for(var a2=0,a8=a3[m];a2<a8;a2++){var a5=aJ("stop");aJ(a5,{offset:a3[a2].offset?a3[a2].offset:!a2?"0%":"100%","stop-color":a3[a2].color||"#fff"});e[aL](a5);}aJ(E,{fill:"url(#"+e.id+")",opacity:1,"fill-opacity":1});a9.fill=at;a9.opacity=1;a9.fillOpacity=1;return 1;};var N=function(e){var d=e.getBBox();aJ(e.pattern,{patternTransform:an.format("translate({0},{1})",d.x,d.y)});};var aa=function(a6,bf){var a9={"":[0],none:[0],"-":[3,1],".":[1,1],"-.":[3,1,1,1],"-..":[3,1,1,1,1,1],". ":[1,3],"- ":[4,3],"--":[8,3],"- .":[4,3,1,3],"--.":[8,3,1,3],"--..":[8,3,1,3,1,3]},bb=a6.node,a7=a6.attrs,a3=a6.rotate(),S=function(bm,bl){bl=a9[aZ.call(bl)];if(bl){var bj=bm.attrs["stroke-width"]||"1",bh={round:bj,square:bj,butt:0}[bm.attrs["stroke-linecap"]||bf["stroke-linecap"]]||0,bk=[];var bi=bl[m];while(bi--){bk[bi]=bl[bi]*bj+((bi%2)?1:-1)*bh;}aJ(bb,{"stroke-dasharray":bk[az](",")});}};bf[Q]("rotation")&&(a3=bf.rotation);var a2=(a3+at)[z](a);if(!(a2.length-1)){a2=null;}else{a2[1]=+a2[1];a2[2]=+a2[2];}W(a3)&&a6.rotate(0,true);for(var ba in bf){if(bf[Q](ba)){if(!j[Q](ba)){continue;}var a8=bf[ba];a7[ba]=a8;switch(ba){case"rotation":a6.rotate(a8,true);break;case"href":case"title":case"target":var bd=bb.parentNode;if(aZ.call(bd.tagName)!="a"){var E=aJ("a");bd.insertBefore(E,bb);E[aL](bb);bd=E;}bd.setAttributeNS(a6.paper.xlink,ba,a8);break;case"cursor":bb.style.cursor=a8;break;case"clip-rect":var e=(a8+at)[z](a);if(e[m]==4){a6.clip&&a6.clip.parentNode.parentNode.removeChild(a6.clip.parentNode);var i=aJ("clipPath"),bc=aJ("rect");i.id="r"+(an._id++)[aA](36);aJ(bc,{x:e[0],y:e[1],width:e[2],height:e[3]});i[aL](bc);a6.paper.defs[aL](i);aJ(bb,{"clip-path":"url(#"+i.id+")"});a6.clip=bc;}if(!a8){var be=L.getElementById(bb.getAttribute("clip-path")[aP](/(^url\(#|\)$)/g,at));be&&be.parentNode.removeChild(be);aJ(bb,{"clip-path":at});delete a6.clip;}break;case"path":if(a8&&a6.type=="path"){a7.path=V(r(a8));aJ(bb,{d:a7.path});}break;case"width":bb[v](ba,a8);if(a7.fx){ba="x";a8=a7.x;}else{break;}case"x":if(a7.fx){a8=-a7.x-(a7.width||0);}case"rx":if(ba=="rx"&&a6.type=="rect"){break;}case"cx":a2&&(ba=="x"||ba=="cx")&&(a2[1]+=a8-a7[ba]);bb[v](ba,O(a8));a6.pattern&&N(a6);break;case"height":bb[v](ba,a8);if(a7.fy){ba="y";a8=a7.y;}else{break;}case"y":if(a7.fy){a8=-a7.y-(a7.height||0);}case"ry":if(ba=="ry"&&a6.type=="rect"){break;}case"cy":a2&&(ba=="y"||ba=="cy")&&(a2[2]+=a8-a7[ba]);bb[v](ba,O(a8));a6.pattern&&N(a6);break;case"r":if(a6.type=="rect"){aJ(bb,{rx:a8,ry:a8});}else{bb[v](ba,a8);}break;case"src":if(a6.type=="image"){bb.setAttributeNS(a6.paper.xlink,"href",a8);}break;case"stroke-width":bb.style.strokeWidth=a8;bb[v](ba,a8);if(a7["stroke-dasharray"]){S(a6,a7["stroke-dasharray"]);}break;case"stroke-dasharray":S(a6,a8);break;case"translation":var a0=(a8+at)[z](a);a0[0]=+a0[0]||0;a0[1]=+a0[1]||0;if(a2){a2[1]+=a0[0];a2[2]+=a0[1];}t.call(a6,a0[0],a0[1]);break;case"scale":var a0=(a8+at)[z](a);a6.scale(+a0[0]||1,+a0[1]||+a0[0]||1,+a0[2]||null,+a0[3]||null);break;case"fill":var R=(a8+at).match(c);if(R){var i=aJ("pattern"),a5=aJ("image");i.id="r"+(an._id++)[aA](36);aJ(i,{x:0,y:0,patternUnits:"userSpaceOnUse",height:1,width:1});aJ(a5,{x:0,y:0});a5.setAttributeNS(a6.paper.xlink,"href",R[1]);i[aL](a5);var bg=L.createElement("img");bg.style.cssText="position:absolute;left:-9999em;top-9999em";bg.onload=function(){aJ(i,{width:this.offsetWidth,height:this.offsetHeight});aJ(a5,{width:this.offsetWidth,height:this.offsetHeight});L.body.removeChild(this);a6.paper.safari();};L.body[aL](bg);bg.src=R[1];a6.paper.defs[aL](i);bb.style.fill="url(#"+i.id+")";aJ(bb,{fill:"url(#"+i.id+")"});a6.pattern=i;a6.pattern&&N(a6);break;}if(!an.getRGB(a8).error){delete bf.gradient;delete a7.gradient;!an.is(a7.opacity,"undefined")&&an.is(bf.opacity,"undefined")&&aJ(bb,{opacity:a7.opacity});!an.is(a7["fill-opacity"],"undefined")&&an.is(bf["fill-opacity"],"undefined")&&aJ(bb,{"fill-opacity":a7["fill-opacity"]});}else{if((({circle:1,ellipse:1})[Q](a6.type)||(a8+at).charAt()!="r")&&b(bb,a8,a6.paper)){a7.gradient=a8;a7.fill="none";break;}}case"stroke":bb[v](ba,an.getRGB(a8).hex);break;case"gradient":(({circle:1,ellipse:1})[Q](a6.type)||(a8+at).charAt()!="r")&&b(bb,a8,a6.paper);break;case"opacity":case"fill-opacity":if(a7.gradient){var d=L.getElementById(bb.getAttribute("fill")[aP](/^url\(#|\)$/g,at));if(d){var a1=d.getElementsByTagName("stop");a1[a1[m]-1][v]("stop-opacity",a8);}break;}default:ba=="font-size"&&(a8=G(a8,10)+"px");var a4=ba[aP](/(\-.)/g,function(bh){return aN.call(bh.substring(1));});bb.style[a4]=a8;bb[v](ba,a8);break;}}}D(a6,bf);if(a2){a6.rotate(a2.join(am));}else{W(a3)&&a6.rotate(a3,true);}};var h=1.2;var D=function(d,R){if(d.type!="text"||!(R[Q]("text")||R[Q]("font")||R[Q]("font-size")||R[Q]("x")||R[Q]("y"))){return;}var a3=d.attrs,e=d.node,a5=e.firstChild?G(L.defaultView.getComputedStyle(e.firstChild,at).getPropertyValue("font-size"),10):10;if(R[Q]("text")){a3.text=R.text;while(e.firstChild){e.removeChild(e.firstChild);}var E=(R.text+at)[z]("\n");for(var S=0,a4=E[m];S<a4;S++){if(E[S]){var a1=aJ("tspan");S&&aJ(a1,{dy:a5*h,x:a3.x});a1[aL](L.createTextNode(E[S]));e[aL](a1);}}}else{var E=e.getElementsByTagName("tspan");for(var S=0,a4=E[m];S<a4;S++){S&&aJ(E[S],{dy:a5*h,x:a3.x});}}aJ(e,{y:a3.y});var a0=d.getBBox(),a2=a3.y-(a0.y+a0.height/2);a2&&isFinite(a2)&&aJ(e,{y:a3.y+a2});};var ax=function(e,d){var E=0,i=0;this[0]=e;this.id=an._oid++;this.node=e;e.raphael=this;this.paper=d;this.attrs=this.attrs||{};this.transformations=[];this._={tx:0,ty:0,rt:{deg:0,cx:0,cy:0},sx:1,sy:1};!d.bottom&&(d.bottom=this);this.prev=d.top;d.top&&(d.top.next=this);d.top=this;this.next=null;};ax[aY].rotate=function(e,d,E){if(this.removed){return this;}if(e==null){if(this._.rt.cx){return[this._.rt.deg,this._.rt.cx,this._.rt.cy][az](am);}return this._.rt.deg;}var i=this.getBBox();e=(e+at)[z](a);if(e[m]-1){d=W(e[1]);E=W(e[2]);}e=W(e[0]);if(d!=null){this._.rt.deg=e;}else{this._.rt.deg+=e;}(E==null)&&(d=null);this._.rt.cx=d;this._.rt.cy=E;d=d==null?i.x+i.width/2:d;E=E==null?i.y+i.height/2:E;if(this._.rt.deg){this.transformations[0]=an.format("rotate({0} {1} {2})",this._.rt.deg,d,E);this.clip&&aJ(this.clip,{transform:an.format("rotate({0} {1} {2})",-this._.rt.deg,d,E)});}else{this.transformations[0]=at;this.clip&&aJ(this.clip,{transform:at});}aJ(this.node,{transform:this.transformations[az](am)});return this;};ax[aY].hide=function(){!this.removed&&(this.node.style.display="none");return this;};ax[aY].show=function(){!this.removed&&(this.node.style.display="");return this;};ax[aY].remove=function(){if(this.removed){return;}ak(this,this.paper);this.node.parentNode.removeChild(this.node);for(var d in this){delete this[d];}this.removed=true;};ax[aY].getBBox=function(){if(this.removed){return this;}if(this.type=="path"){return U(this.attrs.path);}if(this.node.style.display=="none"){this.show();var E=true;}var a1={};try{a1=this.node.getBBox();}catch(S){}finally{a1=a1||{};}if(this.type=="text"){a1={x:a1.x,y:Infinity,width:0,height:0};for(var d=0,R=this.node.getNumberOfChars();d<R;d++){var a0=this.node.getExtentOfChar(d);(a0.y<a1.y)&&(a1.y=a0.y);(a0.y+a0.height-a1.y>a1.height)&&(a1.height=a0.y+a0.height-a1.y);(a0.x+a0.width-a1.x>a1.width)&&(a1.width=a0.x+a0.width-a1.x);}}E&&this.hide();return a1;};ax[aY].attr=function(){if(this.removed){return this;}if(arguments[m]==0){var R={};for(var E in this.attrs){if(this.attrs[Q](E)){R[E]=this.attrs[E];}}this._.rt.deg&&(R.rotation=this.rotate());(this._.sx!=1||this._.sy!=1)&&(R.scale=this.scale());R.gradient&&R.fill=="none"&&(R.fill=R.gradient)&&delete R.gradient;return R;}if(arguments[m]==1&&an.is(arguments[0],"string")){if(arguments[0]=="translation"){return t.call(this);}if(arguments[0]=="rotation"){return this.rotate();}if(arguments[0]=="scale"){return this.scale();}if(arguments[0]=="fill"&&this.attrs.fill=="none"&&this.attrs.gradient){return this.attrs.gradient;}return this.attrs[arguments[0]];}if(arguments[m]==1&&an.is(arguments[0],"array")){var d={};for(var e in arguments[0]){if(arguments[0][Q](e)){d[arguments[0][e]]=this.attrs[arguments[0][e]];}}return d;}if(arguments[m]==2){var S={};S[arguments[0]]=arguments[1];aa(this,S);}else{if(arguments[m]==1&&an.is(arguments[0],"object")){aa(this,arguments[0]);}}return this;};ax[aY].toFront=function(){if(this.removed){return this;}this.node.parentNode[aL](this.node);var d=this.paper;d.top!=this&&Y(this,d);return this;};ax[aY].toBack=function(){if(this.removed){return this;}if(this.node.parentNode.firstChild!=this.node){this.node.parentNode.insertBefore(this.node,this.node.parentNode.firstChild);k(this,this.paper);var d=this.paper;}return this;};ax[aY].insertAfter=function(d){if(this.removed){return this;}var e=d.node;if(e.nextSibling){e.parentNode.insertBefore(this.node,e.nextSibling);}else{e.parentNode[aL](this.node);}A(this,d,this.paper);return this;};ax[aY].insertBefore=function(d){if(this.removed){return this;}var e=d.node;e.parentNode.insertBefore(this.node,e);aq(this,d,this.paper);return this;};var P=function(e,d,S,R){d=O(d);S=O(S);var E=aJ("circle");e.canvas&&e.canvas[aL](E);var i=new ax(E,e);i.attrs={cx:d,cy:S,r:R,fill:"none",stroke:"#000"};i.type="circle";aJ(E,i.attrs);return i;};var aF=function(i,d,a1,e,S,a0){d=O(d);a1=O(a1);var R=aJ("rect");i.canvas&&i.canvas[aL](R);var E=new ax(R,i);E.attrs={x:d,y:a1,width:e,height:S,r:a0||0,rx:a0||0,ry:a0||0,fill:"none",stroke:"#000"};E.type="rect";aJ(R,E.attrs);return E;};var ai=function(e,d,a0,S,R){d=O(d);a0=O(a0);var E=aJ("ellipse");e.canvas&&e.canvas[aL](E);var i=new ax(E,e);i.attrs={cx:d,cy:a0,rx:S,ry:R,fill:"none",stroke:"#000"};i.type="ellipse";aJ(E,i.attrs);return i;};var o=function(i,a0,d,a1,e,S){var R=aJ("image");aJ(R,{x:d,y:a1,width:e,height:S,preserveAspectRatio:"none"});R.setAttributeNS(i.xlink,"href",a0);i.canvas&&i.canvas[aL](R);var E=new ax(R,i);E.attrs={x:d,y:a1,width:e,height:S,src:a0};E.type="image";return E;};var X=function(e,d,S,R){var E=aJ("text");aJ(E,{x:d,y:S,"text-anchor":"middle"});e.canvas&&e.canvas[aL](E);var i=new ax(E,e);i.attrs={x:d,y:S,"text-anchor":"middle",text:R,font:j.font,stroke:"none",fill:"#000"};i.type="text";aa(i,i.attrs);return i;};var aV=function(e,d){this.width=e||this.width;this.height=d||this.height;this.canvas[v]("width",this.width);this.canvas[v]("height",this.height);return this;};var w=function(){var E=ao[aW](null,arguments),i=E&&E.container,e=E.x,a0=E.y,R=E.width,d=E.height;if(!i){throw new Error("SVG container not found.");}var S=aJ("svg");R=R||512;d=d||342;aJ(S,{xmlns:"http://www.w3.org/2000/svg",version:1.1,width:R,height:d});if(i==1){S.style.cssText="position:absolute;left:"+e+"px;top:"+a0+"px";L.body[aL](S);}else{if(i.firstChild){i.insertBefore(S,i.firstChild);}else{i[aL](S);}}i=new aT;i.width=R;i.height=d;i.canvas=S;aG.call(i,i,an.fn);i.clear();return i;};aT[aY].clear=function(){var d=this.canvas;while(d.firstChild){d.removeChild(d.firstChild);}this.bottom=this.top=null;(this.desc=aJ("desc"))[aL](L.createTextNode("Created with Rapha\xebl"));d[aL](this.desc);d[aL](this.defs=aJ("defs"));};aT[aY].remove=function(){this.canvas.parentNode&&this.canvas.parentNode.removeChild(this.canvas);for(var d in this){this[d]=s(d);}};}if(an.vml){var aH=function(a8){var a5=/[ahqstv]/ig,a0=r;(a8+at).match(a5)&&(a0=H);a5=/[clmz]/g;if(a0==r&&!(a8+at).match(a5)){var e={M:"m",L:"l",C:"c",Z:"x",m:"t",l:"r",c:"v",z:"x"},R=/([clmz]),?([^clmz]*)/gi,S=/-?[^,\s-]+/g;var a4=(a8+at)[aP](R,function(a9,bb,i){var ba=[];i[aP](S,function(bc){ba[f](O(bc));});return e[bb]+ba;});return a4;}var a6=a0(a8),E,a4=[],d;for(var a2=0,a7=a6[m];a2<a7;a2++){E=a6[a2];d=aZ.call(a6[a2][0]);d=="z"&&(d="x");for(var a1=1,a3=E[m];a1<a3;a1++){d+=O(E[a1])+(a1!=a3-1?",":at);}a4[f](d);}return a4[az](am);};an[aA]=function(){return"Your browser doesn\u2019t support SVG. Falling down to VML.\nYou are running Rapha\xebl "+this.version;};var q=function(d,S){var E=ah("group");E.style.cssText="position:absolute;left:0;top:0;width:"+S.width+"px;height:"+S.height+"px";E.coordsize=S.coordsize;E.coordorigin=S.coordorigin;var i=ah("shape"),e=i.style;e.width=S.width+"px";e.height=S.height+"px";i.coordsize=this.coordsize;i.coordorigin=this.coordorigin;E[aL](i);var R=new ax(i,E,S);R.isAbsolute=true;R.type="path";R.path=[];R.Path=at;d&&aa(R,{fill:"none",stroke:"#000",path:d});S.canvas[aL](E);return R;};var aa=function(a3,a8){a3.attrs=a3.attrs||{};var a6=a3.node,a9=a3.attrs,a0=a6.style,E,bd=a3;for(var a1 in a8){if(a8[Q](a1)){a9[a1]=a8[a1];}}a8.href&&(a6.href=a8.href);a8.title&&(a6.title=a8.title);a8.target&&(a6.target=a8.target);a8.cursor&&(a0.cursor=a8.cursor);if(a8.path&&a3.type=="path"){a9.path=a8.path;a6.path=aH(a9.path);}if(a8.rotation!=null){a3.rotate(a8.rotation,true);}if(a8.translation){E=(a8.translation+at)[z](a);t.call(a3,E[0],E[1]);if(a3._.rt.cx!=null){a3._.rt.cx+=+E[0];a3._.rt.cy+=+E[1];a3.setBox(a3.attrs,E[0],E[1]);}}if(a8.scale){E=(a8.scale+at)[z](a);a3.scale(+E[0]||1,+E[1]||+E[0]||1,+E[2]||null,+E[3]||null);}if("clip-rect" in a8){var d=(a8["clip-rect"]+at)[z](a);if(d[m]==4){d[2]=+d[2]+(+d[0]);d[3]=+d[3]+(+d[1]);var a2=a6.clipRect||L.createElement("div"),bc=a2.style,S=a6.parentNode;bc.clip=an.format("rect({1}px {2}px {3}px {0}px)",d);if(!a6.clipRect){bc.position="absolute";bc.top=0;bc.left=0;bc.width=a3.paper.width+"px";bc.height=a3.paper.height+"px";S.parentNode.insertBefore(a2,S);a2[aL](S);a6.clipRect=a2;}}if(!a8["clip-rect"]){a6.clipRect&&(a6.clipRect.style.clip=at);}}if(a3.type=="image"&&a8.src){a6.src=a8.src;}if(a3.type=="image"&&a8.opacity){a6.filterOpacity=" progid:DXImageTransform.Microsoft.Alpha(opacity="+(a8.opacity*100)+")";a0.filter=(a6.filterMatrix||at)+(a6.filterOpacity||at);}a8.font&&(a0.font=a8.font);a8["font-family"]&&(a0.fontFamily='"'+a8["font-family"][z](",")[0][aP](/^['"]+|['"]+$/g,at)+'"');a8["font-size"]&&(a0.fontSize=a8["font-size"]);a8["font-weight"]&&(a0.fontWeight=a8["font-weight"]);a8["font-style"]&&(a0.fontStyle=a8["font-style"]);if(a8.opacity!=null||a8["stroke-width"]!=null||a8.fill!=null||a8.stroke!=null||a8["stroke-width"]!=null||a8["stroke-opacity"]!=null||a8["fill-opacity"]!=null||a8["stroke-dasharray"]!=null||a8["stroke-miterlimit"]!=null||a8["stroke-linejoin"]!=null||a8["stroke-linecap"]!=null){a6=a3.shape||a6;var a7=(a6.getElementsByTagName("fill")&&a6.getElementsByTagName("fill")[0]),ba=false;!a7&&(ba=a7=ah("fill"));if("fill-opacity" in a8||"opacity" in a8){var e=((+a9["fill-opacity"]+1||2)-1)*((+a9.opacity+1||2)-1);e<0&&(e=0);e>1&&(e=1);a7.opacity=e;}a8.fill&&(a7.on=true);if(a7.on==null||a8.fill=="none"){a7.on=false;}if(a7.on&&a8.fill){var i=a8.fill.match(c);if(i){a7.src=i[1];a7.type="tile";}else{a7.color=an.getRGB(a8.fill).hex;a7.src=at;a7.type="solid";if(an.getRGB(a8.fill).error&&(bd.type in {circle:1,ellipse:1}||(a8.fill+at).charAt()!="r")&&b(bd,a8.fill)){a9.fill="none";a9.gradient=a8.fill;}}}ba&&a6[aL](a7);var R=(a6.getElementsByTagName("stroke")&&a6.getElementsByTagName("stroke")[0]),bb=false;!R&&(bb=R=ah("stroke"));if((a8.stroke&&a8.stroke!="none")||a8["stroke-width"]||a8["stroke-opacity"]!=null||a8["stroke-dasharray"]||a8["stroke-miterlimit"]||a8["stroke-linejoin"]||a8["stroke-linecap"]){R.on=true;}(a8.stroke=="none"||R.on==null||a8.stroke==0||a8["stroke-width"]==0)&&(R.on=false);R.on&&a8.stroke&&(R.color=an.getRGB(a8.stroke).hex);var e=((+a9["stroke-opacity"]+1||2)-1)*((+a9.opacity+1||2)-1),a4=(W(a8["stroke-width"])||1)*0.75;e<0&&(e=0);e>1&&(e=1);a8["stroke-width"]==null&&(a4=a9["stroke-width"]);a8["stroke-width"]&&(R.weight=a4);a4&&a4<1&&(e*=a4)&&(R.weight=1);R.opacity=e;a8["stroke-linejoin"]&&(R.joinstyle=a8["stroke-linejoin"]||"miter");R.miterlimit=a8["stroke-miterlimit"]||8;a8["stroke-linecap"]&&(R.endcap=a8["stroke-linecap"]=="butt"?"flat":a8["stroke-linecap"]=="square"?"square":"round");if(a8["stroke-dasharray"]){var a5={"-":"shortdash",".":"shortdot","-.":"shortdashdot","-..":"shortdashdotdot",". ":"dot","- ":"dash","--":"longdash","- .":"dashdot","--.":"longdashdot","--..":"longdashdotdot"};R.dashstyle=a5[Q](a8["stroke-dasharray"])?a5[a8["stroke-dasharray"]]:at;}bb&&a6[aL](R);}if(bd.type=="text"){var a0=bd.paper.span.style;a9.font&&(a0.font=a9.font);a9["font-family"]&&(a0.fontFamily=a9["font-family"]);a9["font-size"]&&(a0.fontSize=a9["font-size"]);a9["font-weight"]&&(a0.fontWeight=a9["font-weight"]);a9["font-style"]&&(a0.fontStyle=a9["font-style"]);bd.node.string&&(bd.paper.span.innerHTML=(bd.node.string+at)[aP](/</g,"&#60;")[aP](/&/g,"&#38;")[aP](/\n/g,"<br>"));bd.W=a9.w=bd.paper.span.offsetWidth;bd.H=a9.h=bd.paper.span.offsetHeight;bd.X=a9.x;bd.Y=a9.y+O(bd.H/2);switch(a9["text-anchor"]){case"start":bd.node.style["v-text-align"]="left";bd.bbx=O(bd.W/2);break;case"end":bd.node.style["v-text-align"]="right";bd.bbx=-O(bd.W/2);break;default:bd.node.style["v-text-align"]="center";break;}}};var b=function(d,a1){d.attrs=d.attrs||{};var a2=d.attrs,a4=d.node.getElementsByTagName("fill"),S="linear",a0=".5 .5";d.attrs.gradient=a1;a1=(a1+at)[aP](ar,function(a6,a7,i){S="radial";if(a7&&i){a7=W(a7);i=W(i);aM(a7-0.5,2)+aM(i-0.5,2)>0.25&&(i=ab.sqrt(0.25-aM(a7-0.5,2))*((i>0.5)*2-1)+0.5);a0=a7+am+i;}return at;});a1=a1[z](/\s*\-\s*/);if(S=="linear"){var e=a1.shift();e=-W(e);if(isNaN(e)){return null;}}var R=p(a1);if(!R){return null;}d=d.shape||d.node;a4=a4[0]||ah("fill");if(R[m]){a4.on=true;a4.method="none";a4.type=(S=="radial")?"gradientradial":"gradient";a4.color=R[0].color;a4.color2=R[R[m]-1].color;var a5=[];for(var E=0,a3=R[m];E<a3;E++){R[E].offset&&a5[f](R[E].offset+am+R[E].color);}a4.colors&&(a4.colors.value=a5[m]?a5[az](","):"0% "+a4.color);if(S=="radial"){a4.focus="100%";a4.focussize=a0;a4.focusposition=a0;}else{a4.angle=(270-e)%360;}}return 1;};var ax=function(R,a0,d){var S=0,i=0,e=0,E=1;this[0]=R;this.id=an._oid++;this.node=R;R.raphael=this;this.X=0;this.Y=0;this.attrs={};this.Group=a0;this.paper=d;this._={tx:0,ty:0,rt:{deg:0},sx:1,sy:1};!d.bottom&&(d.bottom=this);this.prev=d.top;d.top&&(d.top.next=this);d.top=this;this.next=null;};ax[aY].rotate=function(e,d,i){if(this.removed){return this;}if(e==null){if(this._.rt.cx){return[this._.rt.deg,this._.rt.cx,this._.rt.cy][az](am);}return this._.rt.deg;}e=(e+at)[z](a);if(e[m]-1){d=W(e[1]);i=W(e[2]);}e=W(e[0]);if(d!=null){this._.rt.deg=e;}else{this._.rt.deg+=e;}i==null&&(d=null);this._.rt.cx=d;this._.rt.cy=i;this.setBox(this.attrs,d,i);this.Group.style.rotation=this._.rt.deg;return this;};ax[aY].setBox=function(bb,e,d){if(this.removed){return this;}var a5=this.Group.style,R=(this.shape&&this.shape.style)||this.node.style;bb=bb||{};for(var a9 in bb){if(bb[Q](a9)){this.attrs[a9]=bb[a9];}}e=e||this._.rt.cx;d=d||this._.rt.cy;var a7=this.attrs,a1,a0,a2,ba;switch(this.type){case"circle":a1=a7.cx-a7.r;a0=a7.cy-a7.r;a2=ba=a7.r*2;break;case"ellipse":a1=a7.cx-a7.rx;a0=a7.cy-a7.ry;a2=a7.rx*2;ba=a7.ry*2;break;case"rect":case"image":a1=+a7.x;a0=+a7.y;a2=a7.width||0;ba=a7.height||0;break;case"text":this.textpath.v=["m",O(a7.x),", ",O(a7.y-2),"l",O(a7.x)+1,", ",O(a7.y-2)][az](at);a1=a7.x-O(this.W/2);a0=a7.y-this.H/2;a2=this.W;ba=this.H;break;case"path":if(!this.attrs.path){a1=0;a0=0;a2=this.paper.width;ba=this.paper.height;}else{var a8=U(this.attrs.path);a1=a8.x;a0=a8.y;a2=a8.width;ba=a8.height;}break;default:a1=0;a0=0;a2=this.paper.width;ba=this.paper.height;break;}e=(e==null)?a1+a2/2:e;d=(d==null)?a0+ba/2:d;var E=e-this.paper.width/2,a4=d-this.paper.height/2;if(this.type=="path"||this.type=="text"){(a5.left!=E+"px")&&(a5.left=E+"px");(a5.top!=a4+"px")&&(a5.top=a4+"px");this.X=this.type=="text"?a1:-E;this.Y=this.type=="text"?a0:-a4;this.W=a2;this.H=ba;(R.left!=-E+"px")&&(R.left=-E+"px");(R.top!=-a4+"px")&&(R.top=-a4+"px");}else{(a5.left!=E+"px")&&(a5.left=E+"px");(a5.top!=a4+"px")&&(a5.top=a4+"px");this.X=a1;this.Y=a0;this.W=a2;this.H=ba;(a5.width!=this.paper.width+"px")&&(a5.width=this.paper.width+"px");(a5.height!=this.paper.height+"px")&&(a5.height=this.paper.height+"px");(R.left!=a1-E+"px")&&(R.left=a1-E+"px");(R.top!=a0-a4+"px")&&(R.top=a0-a4+"px");(R.width!=a2+"px")&&(R.width=a2+"px");(R.height!=ba+"px")&&(R.height=ba+"px");var S=(+bb.r||0)/aI(a2,ba);if(this.type=="rect"&&this.arcsize.toFixed(4)!=S.toFixed(4)&&(S||this.arcsize)){var a6=ah("roundrect"),bc={},a9=0,a3=this.events&&this.events[m];a6.arcsize=S;a6.raphael=this;this.Group[aL](a6);this.Group.removeChild(this.node);this[0]=this.node=a6;this.arcsize=S;for(var a9 in a7){bc[a9]=a7[a9];}delete bc.scale;this.attr(bc);if(this.events){for(;a9<a3;a9++){this.events[a9].unbind=ae(this.node,this.events[a9].name,this.events[a9].f,this);}}}}};ax[aY].hide=function(){!this.removed&&(this.Group.style.display="none");return this;};ax[aY].show=function(){!this.removed&&(this.Group.style.display="block");return this;};ax[aY].getBBox=function(){if(this.removed){return this;}if(this.type=="path"){return U(this.attrs.path);}return{x:this.X+(this.bbx||0),y:this.Y,width:this.W,height:this.H};};ax[aY].remove=function(){if(this.removed){return;}ak(this,this.paper);this.node.parentNode.removeChild(this.node);this.Group.parentNode.removeChild(this.Group);this.shape&&this.shape.parentNode.removeChild(this.shape);for(var d in this){delete this[d];}this.removed=true;};ax[aY].attr=function(){if(this.removed){return this;}if(arguments[m]==0){var E={};for(var e in this.attrs){if(this.attrs[Q](e)){E[e]=this.attrs[e];}}this._.rt.deg&&(E.rotation=this.rotate());(this._.sx!=1||this._.sy!=1)&&(E.scale=this.scale());E.gradient&&E.fill=="none"&&(E.fill=E.gradient)&&delete E.gradient;return E;}if(arguments[m]==1&&an.is(arguments[0],"string")){if(arguments[0]=="translation"){return t.call(this);}if(arguments[0]=="rotation"){return this.rotate();}if(arguments[0]=="scale"){return this.scale();}if(arguments[0]=="fill"&&this.attrs.fill=="none"&&this.attrs.gradient){return this.attrs.gradient;}return this.attrs[arguments[0]];}if(this.attrs&&arguments[m]==1&&an.is(arguments[0],"array")){var d={};for(var e=0,R=arguments[0][m];e<R;e++){d[arguments[0][e]]=this.attrs[arguments[0][e]];}return d;}var S;if(arguments[m]==2){S={};S[arguments[0]]=arguments[1];}arguments[m]==1&&an.is(arguments[0],"object")&&(S=arguments[0]);if(S){if(S.text&&this.type=="text"){this.node.string=S.text;}aa(this,S);if(S.gradient&&(({circle:1,ellipse:1})[Q](this.type)||(S.gradient+at).charAt()!="r")){b(this,S.gradient);}(this.type!="path"||this._.rt.deg)&&this.setBox(this.attrs);}return this;};ax[aY].toFront=function(){!this.removed&&this.Group.parentNode[aL](this.Group);this.paper.top!=this&&Y(this,this.paper);return this;};ax[aY].toBack=function(){if(this.removed){return this;}if(this.Group.parentNode.firstChild!=this.Group){this.Group.parentNode.insertBefore(this.Group,this.Group.parentNode.firstChild);k(this,this.paper);}return this;};ax[aY].insertAfter=function(d){if(this.removed){return this;}if(d.Group.nextSibling){d.Group.parentNode.insertBefore(this.Group,d.Group.nextSibling);}else{d.Group.parentNode[aL](this.Group);}A(this,d,this.paper);return this;};ax[aY].insertBefore=function(d){if(this.removed){return this;}d.Group.parentNode.insertBefore(this.Group,d.Group);aq(this,d,this.paper);return this;};var P=function(e,d,a1,S){var R=ah("group"),a0=ah("oval"),i=a0.style;R.style.cssText="position:absolute;left:0;top:0;width:"+e.width+"px;height:"+e.height+"px";R.coordsize=e.coordsize;R.coordorigin=e.coordorigin;R[aL](a0);var E=new ax(a0,R,e);E.type="circle";aa(E,{stroke:"#000",fill:"none"});E.attrs.cx=d;E.attrs.cy=a1;E.attrs.r=S;E.setBox({x:d-S,y:a1-S,width:S*2,height:S*2});e.canvas[aL](R);return E;},aF=function(e,a1,a0,a2,E,d){var R=ah("group"),i=ah("roundrect"),a3=(+d||0)/(aI(a2,E));R.style.cssText="position:absolute;left:0;top:0;width:"+e.width+"px;height:"+e.height+"px";R.coordsize=e.coordsize;R.coordorigin=e.coordorigin;R[aL](i);i.arcsize=a3;var S=new ax(i,R,e);S.type="rect";aa(S,{stroke:"#000"});S.arcsize=a3;S.setBox({x:a1,y:a0,width:a2,height:E,r:d});e.canvas[aL](R);return S;},ai=function(d,a2,a1,i,e){var R=ah("group"),E=ah("oval"),a0=E.style;R.style.cssText="position:absolute;left:0;top:0;width:"+d.width+"px;height:"+d.height+"px";R.coordsize=d.coordsize;R.coordorigin=d.coordorigin;R[aL](E);var S=new ax(E,R,d);S.type="ellipse";aa(S,{stroke:"#000"});S.attrs.cx=a2;S.attrs.cy=a1;S.attrs.rx=i;S.attrs.ry=e;S.setBox({x:a2-i,y:a1-e,width:i*2,height:e*2});d.canvas[aL](R);return S;},o=function(e,d,a2,a1,a3,E){var R=ah("group"),i=ah("image"),a0=i.style;R.style.cssText="position:absolute;left:0;top:0;width:"+e.width+"px;height:"+e.height+"px";R.coordsize=e.coordsize;R.coordorigin=e.coordorigin;i.src=d;R[aL](i);var S=new ax(i,R,e);S.type="image";S.attrs.src=d;S.attrs.x=a2;S.attrs.y=a1;S.attrs.w=a3;S.attrs.h=E;S.setBox({x:a2,y:a1,width:a3,height:E});e.canvas[aL](R);return S;},X=function(e,a2,a1,a3){var R=ah("group"),E=ah("shape"),a0=E.style,a4=ah("path"),d=a4.style,i=ah("textpath");R.style.cssText="position:absolute;left:0;top:0;width:"+e.width+"px;height:"+e.height+"px";R.coordsize=e.coordsize;R.coordorigin=e.coordorigin;a4.v=an.format("m{0},{1}l{2},{1}",O(a2),O(a1),O(a2)+1);a4.textpathok=true;a0.width=e.width;a0.height=e.height;i.string=a3+at;i.on=true;E[aL](i);E[aL](a4);R[aL](E);var S=new ax(i,R,e);S.shape=E;S.textpath=a4;S.type="text";S.attrs.text=a3;S.attrs.x=a2;S.attrs.y=a1;S.attrs.w=1;S.attrs.h=1;aa(S,{font:j.font,stroke:"none",fill:"#000"});S.setBox();e.canvas[aL](R);return S;},aV=function(i,d){var e=this.canvas.style;i==+i&&(i+="px");d==+d&&(d+="px");e.width=i;e.height=d;e.clip="rect(0 "+i+" "+d+" 0)";return this;},ah;L.createStyleSheet().addRule(".rvml","behavior:url(#default#VML)");try{!L.namespaces.rvml&&L.namespaces.add("rvml","urn:schemas-microsoft-com:vml");ah=function(d){return L.createElement("<rvml:"+d+' class="rvml">');};}catch(af){ah=function(d){return L.createElement("<"+d+' xmlns="urn:schemas-microsoft.com:vml" class="rvml">');};}var w=function(){var i=ao[aW](null,arguments),d=i.container,a2=i.height,a3,e=i.width,a1=i.x,a0=i.y;if(!d){throw new Error("VML container not found.");}var R=new aT,S=R.canvas=L.createElement("div"),E=S.style;e=e||512;a2=a2||342;e==+e&&(e+="px");a2==+a2&&(a2+="px");R.width=1000;R.height=1000;R.coordsize="1000 1000";R.coordorigin="0 0";R.span=L.createElement("span");R.span.style.cssText="position:absolute;left:-9999em;top:-9999em;padding:0;margin:0;line-height:1;display:inline;";S[aL](R.span);E.cssText=an.format("width:{0};height:{1};position:absolute;clip:rect(0 {0} {1} 0);overflow:hidden",e,a2);if(d==1){L.body[aL](S);E.left=a1+"px";E.top=a0+"px";}else{d.style.width=e;d.style.height=a2;if(d.firstChild){d.insertBefore(S,d.firstChild);}else{d[aL](S);}}aG.call(R,R,an.fn);return R;};aT[aY].clear=function(){this.canvas.innerHTML=at;this.span=L.createElement("span");this.span.style.cssText="position:absolute;left:-9999em;top:-9999em;padding:0;margin:0;line-height:1;display:inline;";this.canvas[aL](this.span);this.bottom=this.top=null;};aT[aY].remove=function(){this.canvas.parentNode.removeChild(this.canvas);for(var d in this){this[d]=s(d);}};}if((/^Apple|^Google/).test(navigator.vendor)&&!(navigator.userAgent.indexOf("Version/4.0")+1)){aT[aY].safari=function(){var d=this.rect(-99,-99,this.width+99,this.height+99);setTimeout(function(){d.remove();});};}else{aT[aY].safari=function(){};}var ae=(function(){if(L.addEventListener){return function(R,i,e,d){var E=function(S){return e.call(d,S);};R.addEventListener(i,E,false);return function(){R.removeEventListener(i,E,false);return true;};};}else{if(L.attachEvent){return function(S,E,i,e){var R=function(a0){return i.call(e,a0||au.event);};S.attachEvent("on"+E,R);var d=function(){S.detachEvent("on"+E,R);return true;};return d;};}}})();for(var ac=F[m];ac--;){(function(d){ax[aY][d]=function(e){if(an.is(e,"function")){this.events=this.events||[];this.events.push({name:d,f:e,unbind:ae(this.shape||this.node,d,e,this)});}return this;};ax[aY]["un"+d]=function(E){var i=this.events,e=i[m];while(e--){if(i[e].name==d&&i[e].f==E){i[e].unbind();i.splice(e,1);!i.length&&delete this.events;return this;}}return this;};})(F[ac]);}ax[aY].hover=function(e,d){return this.mouseover(e).mouseout(d);};ax[aY].unhover=function(e,d){return this.unmouseover(e).unmouseout(d);};aT[aY].circle=function(d,i,e){return P(this,d||0,i||0,e||0);};aT[aY].rect=function(d,R,e,i,E){return aF(this,d||0,R||0,e||0,i||0,E||0);};aT[aY].ellipse=function(d,E,i,e){return ai(this,d||0,E||0,i||0,e||0);};aT[aY].path=function(d){d&&!an.is(d,"string")&&!an.is(d[0],"array")&&(d+=at);return q(an.format[aW](an,arguments),this);};aT[aY].image=function(E,d,R,e,i){return o(this,E||"about:blank",d||0,R||0,e||0,i||0);};aT[aY].text=function(d,i,e){return X(this,d||0,i||0,e||at);};aT[aY].set=function(d){arguments[m]>1&&(d=Array[aY].splice.call(arguments,0,arguments[m]));return new T(d);};aT[aY].setSize=aV;aT[aY].top=aT[aY].bottom=null;aT[aY].raphael=an;function u(){return this.x+am+this.y;}ax[aY].scale=function(a6,a5,E,e){if(a6==null&&a5==null){return{x:this._.sx,y:this._.sy,toString:u};}a5=a5||a6;!+a5&&(a5=a6);var ba,a8,a9,a7,bm=this.attrs;if(a6!=0){var a4=this.getBBox(),a1=a4.x+a4.width/2,R=a4.y+a4.height/2,bl=a6/this._.sx,bk=a5/this._.sy;E=(+E||E==0)?E:a1;e=(+e||e==0)?e:R;var a3=~~(a6/ab.abs(a6)),a0=~~(a5/ab.abs(a5)),be=this.node.style,bo=E+(a1-E)*bl,bn=e+(R-e)*bk;switch(this.type){case"rect":case"image":var a2=bm.width*a3*bl,bd=bm.height*a0*bk;this.attr({height:bd,r:bm.r*aI(a3*bl,a0*bk),width:a2,x:bo-a2/2,y:bn-bd/2});break;case"circle":case"ellipse":this.attr({rx:bm.rx*a3*bl,ry:bm.ry*a0*bk,r:bm.r*aI(a3*bl,a0*bk),cx:bo,cy:bn});break;case"path":var bg=ad(bm.path),bh=true;for(var bj=0,bc=bg[m];bj<bc;bj++){var bf=bg[bj],bi,S=aN.call(bf[0]);if(S=="M"&&bh){continue;}else{bh=false;}if(S=="A"){bf[bg[bj][m]-2]*=bl;bf[bg[bj][m]-1]*=bk;bf[1]*=a3*bl;bf[2]*=a0*bk;bf[5]=+(a3+a0?!!+bf[5]:!+bf[5]);}else{if(S=="H"){for(bi=1,jj=bf[m];bi<jj;bi++){bf[bi]*=bl;}}else{if(S=="V"){for(bi=1,jj=bf[m];bi<jj;bi++){bf[bi]*=bk;}}else{for(bi=1,jj=bf[m];bi<jj;bi++){bf[bi]*=(bi%2)?bl:bk;}}}}}var d=U(bg),ba=bo-d.x-d.width/2,a8=bn-d.y-d.height/2;bg[0][1]+=ba;bg[0][2]+=a8;this.attr({path:bg});break;}if(this.type in {text:1,image:1}&&(a3!=1||a0!=1)){if(this.transformations){this.transformations[2]="scale("[aS](a3,",",a0,")");this.node[v]("transform",this.transformations[az](am));ba=(a3==-1)?-bm.x-(a2||0):bm.x;a8=(a0==-1)?-bm.y-(bd||0):bm.y;this.attr({x:ba,y:a8});bm.fx=a3-1;bm.fy=a0-1;}else{this.node.filterMatrix=" progid:DXImageTransform.Microsoft.Matrix(M11="[aS](a3,", M12=0, M21=0, M22=",a0,", Dx=0, Dy=0, sizingmethod='auto expand', filtertype='bilinear')");be.filter=(this.node.filterMatrix||at)+(this.node.filterOpacity||at);}}else{if(this.transformations){this.transformations[2]=at;this.node[v]("transform",this.transformations[az](am));bm.fx=0;bm.fy=0;}else{this.node.filterMatrix=at;be.filter=(this.node.filterMatrix||at)+(this.node.filterOpacity||at);}}bm.scale=[a6,a5,E,e][az](am);this._.sx=a6;this._.sy=a5;}return this;};ax[aY].clone=function(){var d=this.attr();delete d.scale;delete d.translation;return this.paper[this.type]().attr(d);};var aB=function(d,e){return function(a9,S,a0){a9=H(a9);var a5,a4,E,a1,R="",a8={},a6,a3=0;for(var a2=0,a7=a9.length;a2<a7;a2++){E=a9[a2];if(E[0]=="M"){a5=+E[1];a4=+E[2];}else{a1=n(a5,a4,E[1],E[2],E[3],E[4],E[5],E[6]);if(a3+a1>S){if(e&&!a8.start){a6=an.findDotsAtSegment(a5,a4,E[1],E[2],E[3],E[4],E[5],E[6],(S-a3)/a1);R+=["C",a6.start.x,a6.start.y,a6.m.x,a6.m.y,a6.x,a6.y];if(a0){return R;}a8.start=R;R=["M",a6.x,a6.y+"C",a6.n.x,a6.n.y,a6.end.x,a6.end.y,E[5],E[6]][az]();a3+=a1;a5=+E[5];a4=+E[6];continue;}if(!d&&!e){a6=an.findDotsAtSegment(a5,a4,E[1],E[2],E[3],E[4],E[5],E[6],(S-a3)/a1);return{x:a6.x,y:a6.y,alpha:a6.alpha};}}a3+=a1;a5=+E[5];a4=+E[6];}R+=E;}a8.end=R;a6=d?a3:e?a8:an.findDotsAtSegment(a5,a4,E[1],E[2],E[3],E[4],E[5],E[6],1);a6.alpha&&(a6={x:a6.x,y:a6.y,alpha:a6.alpha});return a6;};},n=aj(function(E,d,a0,S,a6,a5,a4,a3){var R={x:0,y:0},a2=0;for(var a1=0;a1<1.01;a1+=0.01){var e=M(E,d,a0,S,a6,a5,a4,a3,a1);a1&&(a2+=ab.sqrt(aM(R.x-e.x,2)+aM(R.y-e.y,2)));R=e;}return a2;});var ap=aB(1),C=aB(),J=aB(0,1);ax[aY].getTotalLength=function(){if(this.type!="path"){return;}return ap(this.attrs.path);};ax[aY].getPointAtLength=function(d){if(this.type!="path"){return;}return C(this.attrs.path,d);};ax[aY].getSubpath=function(i,e){if(this.type!="path"){return;}if(ab.abs(this.getTotalLength()-e)<0.000001){return J(this.attrs.path,i).end;}var d=J(this.attrs.path,e,1);return i?J(d,i).end:d;};an.easing_formulas={linear:function(d){return d;},"<":function(d){return aM(d,3);},">":function(d){return aM(d-1,3)+1;},"<>":function(d){d=d*2;if(d<1){return aM(d,3)/2;}d-=2;return(aM(d,3)+2)/2;},backIn:function(e){var d=1.70158;return e*e*((d+1)*e-d);},backOut:function(e){e=e-1;var d=1.70158;return e*e*((d+1)*e+d)+1;},elastic:function(i){if(i==0||i==1){return i;}var e=0.3,d=e/4;return aM(2,-10*i)*ab.sin((i-d)*(2*ab.PI)/e)+1;},bounce:function(E){var e=7.5625,i=2.75,d;if(E<(1/i)){d=e*E*E;}else{if(E<(2/i)){E-=(1.5/i);d=e*E*E+0.75;}else{if(E<(2.5/i)){E-=(2.25/i);d=e*E*E+0.9375;}else{E-=(2.625/i);d=e*E*E+0.984375;}}}return d;}};var I={length:0},aR=function(){var a2=+new Date;for(var be in I){if(be!="length"&&I[Q](be)){var bj=I[be];if(bj.stop){delete I[be];I[m]--;continue;}var a0=a2-bj.start,bb=bj.ms,ba=bj.easing,bf=bj.from,a7=bj.diff,E=bj.to,a6=bj.t,a9=bj.prev||0,a1=bj.el,R=bj.callback,a8={},d;if(a0<bb){var S=an.easing_formulas[ba]?an.easing_formulas[ba](a0/bb):a0/bb;for(var bc in bf){if(bf[Q](bc)){switch(Z[bc]){case"along":d=S*bb*a7[bc];E.back&&(d=E.len-d);var bd=C(E[bc],d);a1.translate(a7.sx-a7.x||0,a7.sy-a7.y||0);a7.x=bd.x;a7.y=bd.y;a1.translate(bd.x-a7.sx,bd.y-a7.sy);E.rot&&a1.rotate(a7.r+bd.alpha,bd.x,bd.y);break;case"number":d=+bf[bc]+S*bb*a7[bc];break;case"colour":d="rgb("+[B(O(bf[bc].r+S*bb*a7[bc].r)),B(O(bf[bc].g+S*bb*a7[bc].g)),B(O(bf[bc].b+S*bb*a7[bc].b))][az](",")+")";break;case"path":d=[];for(var bh=0,a5=bf[bc][m];bh<a5;bh++){d[bh]=[bf[bc][bh][0]];for(var bg=1,bi=bf[bc][bh][m];bg<bi;bg++){d[bh][bg]=+bf[bc][bh][bg]+S*bb*a7[bc][bh][bg];}d[bh]=d[bh][az](am);}d=d[az](am);break;case"csv":switch(bc){case"translation":var a4=a7[bc][0]*(a0-a9),a3=a7[bc][1]*(a0-a9);a6.x+=a4;a6.y+=a3;d=a4+am+a3;break;case"rotation":d=+bf[bc][0]+S*bb*a7[bc][0];bf[bc][1]&&(d+=","+bf[bc][1]+","+bf[bc][2]);break;case"scale":d=[+bf[bc][0]+S*bb*a7[bc][0],+bf[bc][1]+S*bb*a7[bc][1],(2 in E[bc]?E[bc][2]:at),(3 in E[bc]?E[bc][3]:at)][az](am);break;case"clip-rect":d=[];var bh=4;while(bh--){d[bh]=+bf[bc][bh]+S*bb*a7[bc][bh];}break;}break;}a8[bc]=d;}}a1.attr(a8);a1._run&&a1._run.call(a1);}else{if(E.along){var bd=C(E.along,E.len*!E.back);a1.translate(a7.sx-(a7.x||0)+bd.x-a7.sx,a7.sy-(a7.y||0)+bd.y-a7.sy);E.rot&&a1.rotate(a7.r+bd.alpha,bd.x,bd.y);}(a6.x||a6.y)&&a1.translate(-a6.x,-a6.y);E.scale&&(E.scale=E.scale+at);a1.attr(E);delete I[be];I[m]--;a1.in_animation=null;an.is(R,"function")&&R.call(a1);}bj.prev=a0;}}an.svg&&a1&&a1.paper.safari();I[m]&&setTimeout(aR);},B=function(d){return d>255?255:(d<0?0:d);},t=function(d,i){if(d==null){return{x:this._.tx,y:this._.ty,toString:u};}this._.tx+=+d;this._.ty+=+i;switch(this.type){case"circle":case"ellipse":this.attr({cx:+d+this.attrs.cx,cy:+i+this.attrs.cy});break;case"rect":case"image":case"text":this.attr({x:+d+this.attrs.x,y:+i+this.attrs.y});break;case"path":var e=ad(this.attrs.path);e[0][1]+=+d;e[0][2]+=+i;this.attr({path:e});break;}return this;};ax[aY].animateWith=function(e,i,d,R,E){I[e.id]&&(i.start=I[e.id].start);return this.animate(i,d,R,E);};ax[aY].animateAlong=ay();ax[aY].animateAlongBack=ay(1);function ay(d){return function(E,i,e,S){var R={back:d};an.is(e,"function")?(S=e):(R.rot=e);E&&E.constructor==ax&&(E=E.attrs.path);E&&(R.along=E);return this.animate(R,i,S);};}ax[aY].onAnimation=function(d){this._run=d||0;return this;};ax[aY].animate=function(be,a5,a4,E){if(an.is(a4,"function")||!a4){E=a4||null;}var a9={},e={},a2={};for(var a6 in be){if(be[Q](a6)){if(Z[Q](a6)){a9[a6]=this.attr(a6);(a9[a6]==null)&&(a9[a6]=j[a6]);e[a6]=be[a6];switch(Z[a6]){case"along":var bc=ap(be[a6]),a7=C(be[a6],bc*!!be.back),R=this.getBBox();a2[a6]=bc/a5;a2.tx=R.x;a2.ty=R.y;a2.sx=a7.x;a2.sy=a7.y;e.rot=be.rot;e.back=be.back;e.len=bc;be.rot&&(a2.r=W(this.rotate())||0);break;case"number":a2[a6]=(e[a6]-a9[a6])/a5;break;case"colour":a9[a6]=an.getRGB(a9[a6]);var a8=an.getRGB(e[a6]);a2[a6]={r:(a8.r-a9[a6].r)/a5,g:(a8.g-a9[a6].g)/a5,b:(a8.b-a9[a6].b)/a5};break;case"path":var S=H(a9[a6],e[a6]);a9[a6]=S[0];var a3=S[1];a2[a6]=[];for(var bb=0,a1=a9[a6][m];bb<a1;bb++){a2[a6][bb]=[0];for(var ba=1,bd=a9[a6][bb][m];ba<bd;ba++){a2[a6][bb][ba]=(a3[bb][ba]-a9[a6][bb][ba])/a5;}}break;case"csv":var d=(be[a6]+at)[z](a),a0=(a9[a6]+at)[z](a);switch(a6){case"translation":a9[a6]=[0,0];a2[a6]=[d[0]/a5,d[1]/a5];break;case"rotation":a9[a6]=(a0[1]==d[1]&&a0[2]==d[2])?a0:[0,d[1],d[2]];a2[a6]=[(d[0]-a9[a6][0])/a5,0,0];break;case"scale":be[a6]=d;a9[a6]=(a9[a6]+at)[z](a);a2[a6]=[(d[0]-a9[a6][0])/a5,(d[1]-a9[a6][1])/a5,0,0];break;case"clip-rect":a9[a6]=(a9[a6]+at)[z](a);a2[a6]=[];var bb=4;while(bb--){a2[a6][bb]=(d[bb]-a9[a6][bb])/a5;}break;}e[a6]=d;}}}}this.stop();this.in_animation=1;I[this.id]={start:be.start||+new Date,ms:a5,easing:a4,from:a9,diff:a2,to:e,el:this,callback:E,t:{x:0,y:0}};++I[m]==1&&aR();return this;};ax[aY].stop=function(){I[this.id]&&I[m]--;delete I[this.id];return this;};ax[aY].translate=function(d,e){return this.attr({translation:d+" "+e});};ax[aY][aA]=function(){return"Rapha\xebl\u2019s object";};an.ae=I;var T=function(d){this.items=[];this[m]=0;if(d){for(var e=0,E=d[m];e<E;e++){if(d[e]&&(d[e].constructor==ax||d[e].constructor==T)){this[this.items[m]]=this.items[this.items[m]]=d[e];this[m]++;}}}};T[aY][f]=function(){var R,d;for(var e=0,E=arguments[m];e<E;e++){R=arguments[e];if(R&&(R.constructor==ax||R.constructor==T)){d=this.items[m];this[d]=this.items[d]=R;this[m]++;}}return this;};T[aY].pop=function(){delete this[this[m]--];return this.items.pop();};for(var y in ax[aY]){if(ax[aY][Q](y)){T[aY][y]=(function(d){return function(){for(var e=0,E=this.items[m];e<E;e++){this.items[e][d][aW](this.items[e],arguments);}return this;};})(y);}}T[aY].attr=function(e,a0){if(e&&an.is(e,"array")&&an.is(e[0],"object")){for(var d=0,S=e[m];d<S;d++){this.items[d].attr(e[d]);}}else{for(var E=0,R=this.items[m];E<R;E++){this.items[E].attr[aW](this.items[E],arguments);}}return this;};T[aY].animate=function(S,e,a2,a1){(an.is(a2,"function")||!a2)&&(a1=a2||null);var d=this.items[m],E=d,a0=this,R;a1&&(R=function(){!--d&&a1.call(a0);});this.items[--E].animate(S,e,a2||R,R);while(E--){this.items[E].animateWith(this.items[d-1],S,e,a2||R,R);}return this;};T[aY].insertAfter=function(e){var d=this.items[m];while(d--){this.items[d].insertAfter(e);}return this;};T[aY].getBBox=function(){var d=[],a0=[],e=[],R=[];for(var E=this.items[m];E--;){var S=this.items[E].getBBox();d[f](S.x);a0[f](S.y);e[f](S.x+S.width);R[f](S.y+S.height);}d=aI[aW](0,d);a0=aI[aW](0,a0);return{x:d,y:a0,width:g[aW](0,e)-d,height:g[aW](0,R)-a0};};an.registerFont=function(e){if(!e.face){return e;}this.fonts=this.fonts||{};var E={w:e.w,face:{},glyphs:{}},i=e.face["font-family"];for(var a0 in e.face){if(e.face[Q](a0)){E.face[a0]=e.face[a0];}}if(this.fonts[i]){this.fonts[i][f](E);}else{this.fonts[i]=[E];}if(!e.svg){E.face["units-per-em"]=G(e.face["units-per-em"],10);for(var R in e.glyphs){if(e.glyphs[Q](R)){var S=e.glyphs[R];E.glyphs[R]={w:S.w,k:{},d:S.d&&"M"+S.d[aP](/[mlcxtrv]/g,function(a1){return{l:"L",c:"C",x:"z",t:"m",r:"l",v:"c"}[a1]||"M";})+"z"};if(S.k){for(var d in S.k){if(S[Q](d)){E.glyphs[R].k[d]=S.k[d];}}}}}}return e;};aT[aY].getFont=function(a2,a3,e,R){R=R||"normal";e=e||"normal";a3=+a3||{normal:400,bold:700,lighter:300,bolder:800}[a3]||400;var S=an.fonts[a2];if(!S){var E=new RegExp("(^|\\s)"+a2[aP](/[^\w\d\s+!~.:_-]/g,at)+"(\\s|$)","i");for(var d in an.fonts){if(an.fonts[Q](d)){if(E.test(d)){S=an.fonts[d];break;}}}}var a0;if(S){for(var a1=0,a4=S[m];a1<a4;a1++){a0=S[a1];if(a0.face["font-weight"]==a3&&(a0.face["font-style"]==e||!a0.face["font-style"])&&a0.face["font-stretch"]==R){break;}}}return a0;};aT[aY].print=function(R,E,d,a1,a2,bb){bb=bb||"middle";var a7=this.set(),ba=(d+at)[z](at),a8=0,a4=at,bc;an.is(a1,"string")&&(a1=this.getFont(a1));if(a1){bc=(a2||16)/a1.face["units-per-em"];var e=a1.face.bbox.split(a),a0=+e[0],a3=+e[1]+(bb=="baseline"?e[3]-e[1]+(+a1.face.descent):(e[3]-e[1])/2);for(var a6=0,S=ba[m];a6<S;a6++){var a5=a6&&a1.glyphs[ba[a6-1]]||{},a9=a1.glyphs[ba[a6]];a8+=a6?(a5.w||a1.w)+(a5.k&&a5.k[ba[a6]]||0):0;a9&&a9.d&&a7[f](this.path(a9.d).attr({fill:"#000",stroke:"none",translation:[a8,0]}));}a7.scale(bc,bc,a0,a3).translate(R-a0,E-a3);}return a7;};an.format=function(i){var e=an.is(arguments[1],"array")?[0][aS](arguments[1]):arguments,d=/\{(\d+)\}/g;i&&an.is(i,"string")&&e[m]-1&&(i=i[aP](d,function(R,E){return e[++E]==null?at:e[E];}));return i||at;};an.ninja=function(){var d=Raphael;if(l.was){Raphael=l.is;}else{delete Raphael;}return d;};an.el=ax[aY];return an;})();;
/* END /2static/script/lib/raphael-1.3.1.min.js */
/* START /2static/script/lib/graphael/g.raphael.js */
/*
 * g.Raphael 0.2 - Charting library, based on Raphaël
 *
 * Copyright (c) 2009 Dmitry Baranovskiy (http://g.raphaeljs.com)
 * Licensed under the MIT (http://www.opensource.org/licenses/mit-license.php) license.
 */


(function () {
    Raphael.fn.g = Raphael.fn.g || {};
    Raphael.fn.g.markers = {
        disc: "disc",
        o: "disc",
        square: "square",
        s: "square",
        triangle: "triangle",
        t: "triangle",
        star: "star",
        "*": "star",
        cross: "cross",
        x: "cross",
        plus: "plus",
        "+": "plus",
        arrow: "arrow",
        "->": "arrow"
    };
    Raphael.fn.g.txtattr = {font: "12px Arial, sans-serif"};
    Raphael.fn.g.colors = [];
    var hues = [.6, .2, .05, .1333, .75, 0];
    for (var i = 0; i < 10; i++) {
        if (i < hues.length) {
            Raphael.fn.g.colors.push("hsb(" + hues[i] + ", .75, .75)");
        } else {
            Raphael.fn.g.colors.push("hsb(" + hues[i - hues.length] + ", 1, .5)");
        }
    }
    Raphael.fn.g.text = function (x, y, text) {
        return this.text(x, y, text).attr(this.g.txtattr);
    };
    Raphael.fn.g.labelise = function (label, val, total) {
        if (label) {
            return (label + "").replace(/(##+(?:\.#+)?)|(%%+(?:\.%+)?)/g, function (all, value, percent) {
                if (value) {
                    return (+val).toFixed(value.replace(/^#+\.?/g, "").length);
                }
                if (percent) {
                    return (val * 100 / total).toFixed(percent.replace(/^%+\.?/g, "").length) + "%";
                }
            });
        } else {
            return (+val).toFixed(0);
        }
    };

    Raphael.fn.g.finger = function (x, y, width, height, dir, ending, isPath) {
        // dir 0 for horisontal and 1 for vertical
        if ((dir && !height) || (!dir && !width)) {
            return isPath ? "" : this.path({});
        }
        ending = {square: "square", sharp: "sharp", soft: "soft"}[ending] || "round";
        var path;
        height = Math.round(height);
        width = Math.round(width);
        x = Math.round(x);
        y = Math.round(y);
        switch (ending) {
            case "round":
                if (!dir) {
                    var r = Math.floor(height / 2);
                    if (width < r) {
                        r = width;
                        path = ["M", x + .5, y + .5 - Math.floor(height / 2), "l", 0, 0, "a", r, Math.floor(height / 2), 0, 0, 1, 0, height, "l", 0, 0, "z"];
                    } else {
                        path = ["M", x + .5, y + .5 - r, "l", width - r, 0, "a", r, r, 0, 1, 1, 0, height, "l", r - width, 0, "z"];
                    }
                } else {
                    var r = Math.floor(width / 2);
                    if (height < r) {
                        r = height;
                        path = ["M", x - Math.floor(width / 2), y, "l", 0, 0, "a", Math.floor(width / 2), r, 0, 0, 1, width, 0, "l", 0, 0, "z"];
                    } else {
                        path = ["M", x - r, y, "l", 0, r - height, "a", r, r, 0, 1, 1, width, 0, "l", 0, height - r, "z"];
                    }
                }
                break;
            case "sharp":
                if (!dir) {
                    var half = Math.floor(height / 2);
                    path = ["M", x + .5, y + .5 + half, "l", 0, -height, Math.max(width - half, 0), 0, Math.min(half, width), half, -Math.min(half, width), half + (half * 2 < height), "z"];
                } else {
                    var half = Math.floor(width / 2);
                    path = ["M", x + half, y, "l", -width, 0, 0, -Math.max(height - half, 0), half, -Math.min(half, height), half + (half * 2 < height), Math.min(half, height), half, "z"];
                }
                break;
            case "square":
                if (!dir) {
                    path = ["M", x + .5, y + .5 + Math.floor(height / 2), "l", 0, -height, width, 0, 0, height, "z"];
                } else {
                    path = ["M", x + Math.floor(width / 2), y, "l", -width, 0, 0, -height, width, 0, "z"];
                }
                break;
            case "soft":
                var r;
                if (!dir) {
                    r = Math.min(width, Math.round(height / 5));
                    path = ["M", x + .5, y + .5 - Math.floor(height / 2), "l", width - r, 0, "a", r, r, 0, 0, 1, r, r, "l", 0, height - r * 2, "a", r, r, 0, 0, 1, -r, r, "l", r - width, 0, "z"];
                } else {
                    r = Math.min(Math.round(width / 5), height);
                    path = ["M", x - Math.floor(width / 2), y, "l", 0, r - height, "a", r, r, 0, 0, 1, r, -r, "l", width - 2 * r, 0, "a", r, r, 0, 0, 1, r, r, "l", 0, height - r, "z"];
                }
        }
        if (isPath) {
            return path.join(",");
        } else {
            return this.path({}, path);
        }
    };

    // Symbols
    Raphael.fn.g.disc = function (cx, cy, r) {
        return this.circle(cx, cy, r);
    };
    Raphael.fn.g.line = function (cx, cy, r) {
        return this.rect(cx - r, cy - r / 5, 2 * r, 2 * r / 5);
    };
    Raphael.fn.g.square = function (cx, cy, r) {
        r = r * .7;
        return this.rect(cx - r, cy - r, 2 * r, 2 * r);
    };
    Raphael.fn.g.triangle = function (cx, cy, r) {
        r *= 1.75;
        return this.path({}, "M".concat(cx, ",", cy, "m0-", r * .58, "l", r * .5, ",", r * .87, "-", r, ",0z"));
    };
    Raphael.fn.g.star = function (cx, cy, r, r2) {
        r2 = r2 || r * .5;
        var points = ["M", cx, cy + r2, "L"],
                R;
        for (var i = 1; i < 10; i++) {
            R = i % 2 ? r : r2;
            points = points.concat([(cx + R * Math.sin(i * Math.PI * .2)).toFixed(3), (cy + R * Math.cos(i * Math.PI * .2)).toFixed(3)]);
        }
        points.push("z");
        return this.path({}, points);
    };
    Raphael.fn.g.cross = function (cx, cy, r) {
        r = r / 2.5;
        return this.path({}, "M".concat(cx - r, ",", cy, "l", [-r, -r, r, -r, r, r, r, -r, r, r, -r, r, r, r, -r, r, -r, -r, -r, r, -r, -r, "z"]));
    };
    Raphael.fn.g.plus = function (cx, cy, r) {
        r = r / 2;
        return this.path({}, "M".concat(cx - r / 2, ",", cy - r / 2, "l", [0, -r, r, 0, 0, r, r, 0, 0, r, -r, 0, 0, r, -r, 0, 0, -r, -r, 0, 0, -r, "z"]));
    };
    Raphael.fn.g.arrow = function (cx, cy, r) {
        return this.path({}, "M".concat(cx - r * .7, ",", cy - r * .4, "l", [r * .6, 0, 0, -r * .4, r, r * .8, -r, r * .8, 0, -r * .4, -r * .6, 0], "z"));
    };

    // Tooltips
    Raphael.fn.g.tag = function (x, y, text, angle, r) {
        angle = angle || 0;
        r = r == null ? 5 : r;
        text = text == null ? "$9.99" : text;
        var R = .5522 * r,
                res = this.set(),
                d = 3;
        res.push(this.path({fill: "#000", stroke: "none"}));
        res.push(this.text(x, y, text).attr(this.g.txtattr).attr({fill: "#fff"}));
        res.update = function () {
            this.rotate(0, x, y);
            var bb = this[1].getBBox();
            if (bb.height >= r * 2) {
                this[0].attr({path: ["M", x, y + r, "a", r, r, 0, 1, 1, 0, -r * 2, r, r, 0, 1, 1, 0, r * 2, "m", 0, -r * 2 - d, "a", r + d, r + d, 0, 1, 0, 0, (r + d) * 2, "L", x + r + d, y + bb.height / 2 + d, "l", bb.width + 2 * d, 0, 0, -bb.height - 2 * d, -bb.width - 2 * d, 0, "L", x, y - r - d].join(",")});
            } else {
                var dx = Math.sqrt(Math.pow(r + d, 2) - Math.pow(bb.height / 2 + d, 2));
                // ["c", -R, 0, -r, R - r, -r, -r, 0, -R, r - R, -r, r, -r, R, 0, r, r - R, r, r, 0, R, R - r, r, -r, r]
                // "a", r, r, 0, 1, 1, 0, -r * 2, r, r, 0, 1, 1, 0, r * 2,
                this[0].attr({path: ["M", x, y + r, "c", -R, 0, -r, R - r, -r, -r, 0, -R, r - R, -r, r, -r, R, 0, r, r - R, r, r, 0, R, R - r, r, -r, r, "M", x + dx, y - bb.height / 2 - d, "a", r + d, r + d, 0, 1, 0, 0, bb.height + 2 * d, "l", r + d - dx + bb.width + 2 * d, 0, 0, -bb.height - 2 * d, "L", x + dx, y - bb.height / 2 - d].join(",")});
            }
            this[1].attr({x: x + r + d + bb.width / 2, y: y});
            angle = (360 - angle) % 360;
            this.rotate(angle, x, y);
            angle > 90 && angle < 270 && this[1].attr({x: x - r - d - bb.width / 2, y: y, rotation: [180 + angle, x, y]});
            return this;
        };
        res.update();
        return res;
    };
    Raphael.fn.g.popupit = function (x, y, set, dir, size) {
        dir = dir == null ? 2 : dir;
        size = size || 5;
        x = Math.round(x) + .5;
        y = Math.round(y) + .5;
        var bb = set.getBBox(),
                w = Math.round(bb.width / 2),
                h = Math.round(bb.height / 2),
                dx = [0, w + size * 2, 0, -w - size * 2],
                dy = [-h * 2 - size * 3, -h - size, 0, -h - size],
                p = ["M", x - dx[dir], y - dy[dir], "l", -size, (dir == 2) * -size, -Math.max(w - size, 0), 0, "a", size, size, 0, 0, 1, -size, -size,
                    "l", 0, -Math.max(h - size, 0), (dir == 3) * -size, -size, (dir == 3) * size, -size, 0, -Math.max(h - size, 0), "a", size, size, 0, 0, 1, size, -size,
                    "l", Math.max(w - size, 0), 0, size, !dir * -size, size, !dir * size, Math.max(w - size, 0), 0, "a", size, size, 0, 0, 1, size, size,
                    "l", 0, Math.max(h - size, 0), (dir == 1) * size, size, (dir == 1) * -size, size, 0, Math.max(h - size, 0), "a", size, size, 0, 0, 1, -size, size,
                    "l", -Math.max(w - size, 0), 0, "z"].join(","),
                xy = [
                    {
                        x: x,
                        y: y + size * 2 + h
                    },
                    {
                        x: x - size * 2 - w,
                        y: y
                    },
                    {
                        x: x,
                        y: y - size * 2 - h
                    },
                    {
                        x: x + size * 2 + w,
                        y: y
                    }
                ][dir];
        set.translate(xy.x - w - bb.x, xy.y - h - bb.y);
        return this.path({fill: "#000", stroke: "none"}, p).insertBefore(set.node ? set : set[0]);
    };
    Raphael.fn.g.popup = function (x, y, text, dir, size) {
        dir = dir == null ? 2 : dir;
        size = size || 5;
        text = text || "$9.99";
        var res = this.set(),
                d = 3;
        res.push(this.path({fill: "#000", stroke: "none"}));
        res.push(this.text(x, y, text).attr(this.g.txtattr).attr({fill: "#fff"}));
        res.update = function (X, Y, withAnimation) {
            X = X || x;
            Y = Y || y;
            var bb = this[1].getBBox(),
                    w = bb.width / 2,
                    h = bb.height / 2,
                    dx = [0, w + size * 2, 0, -w - size * 2],
                    dy = [-h * 2 - size * 3, -h - size, 0, -h - size],
                    p = ["M", X - dx[dir], Y - dy[dir], "l", -size, (dir == 2) * -size, -Math.max(w - size, 0), 0, "a", size, size, 0, 0, 1, -size, -size,
                        "l", 0, -Math.max(h - size, 0), (dir == 3) * -size, -size, (dir == 3) * size, -size, 0, -Math.max(h - size, 0), "a", size, size, 0, 0, 1, size, -size,
                        "l", Math.max(w - size, 0), 0, size, !dir * -size, size, !dir * size, Math.max(w - size, 0), 0, "a", size, size, 0, 0, 1, size, size,
                        "l", 0, Math.max(h - size, 0), (dir == 1) * size, size, (dir == 1) * -size, size, 0, Math.max(h - size, 0), "a", size, size, 0, 0, 1, -size, size,
                        "l", -Math.max(w - size, 0), 0, "z"].join(","),
                    xy = [
                        {
                            x: X,
                            y: Y + size * 2 + h
                        },
                        {
                            x: X - size * 2 - w,
                            y: Y
                        },
                        {
                            x: X,
                            y: Y - size * 2 - h
                        },
                        {
                            x: X + size * 2 + w,
                            y: Y
                        }
                    ][dir];
            if (withAnimation) {
                this[0].animate({path: p}, 500, ">");
                this[1].animate(xy, 500, ">");
            } else {
                this[0].attr({path: p});
                this[1].attr(xy);
            }
            return this;
        };
        return res.update(x, y);
    };
    Raphael.fn.g.flag = function (x, y, text, angle) {
        angle = angle || 0;
        text = text || "$9.99";
        var res = this.set(),
                d = 3;
        res.push(this.path({fill: "#000", stroke: "none"}));
        res.push(this.text(x, y, text).attr(this.g.txtattr).attr({fill: "#fff"}));
        res.update = function (x, y) {
            this.rotate(0, x, y);
            var bb = this[1].getBBox(),
                    h = bb.height / 2;
            this[0].attr({path: ["M", x, y, "l", h + d, -h - d, bb.width + 2 * d, 0, 0, bb.height + 2 * d, -bb.width - 2 * d, 0, "z"].join(",")});
            this[1].attr({x: x + h + d + bb.width / 2, y: y});
            angle = 360 - angle;
            this.rotate(angle, x, y);
            angle > 90 && angle < 270 && this[1].attr({x: x - r - d - bb.width / 2, y: y, rotation: [180 + angle, x, y]});
            return this;
        };
        return res.update(x, y);
    };
    Raphael.fn.g.label = function (x, y, text) {
        var res = this.set();
        res.push(this.rect(x, y, 10, 10).attr({stroke: "none", fill: "#000"}));
        res.push(this.text(x, y, text).attr(this.g.txtattr).attr({fill: "#fff"}));
        res.update = function () {
            var bb = this[1].getBBox(),
                    r = Math.min(bb.width + 10, bb.height + 10) / 2;
            this[0].attr({x: bb.x - r / 2, y: bb.y - r / 2, width: bb.width + r, height: bb.height + r, r: r});
        };
        res.update();
        return res;
    };
    Raphael.fn.g.labelit = function (set) {
        var bb = set.getBBox(),
                r = Math.min(20, bb.width + 10, bb.height + 10) / 2;
        return this.rect(bb.x - r / 2, bb.y - r / 2, bb.width + r, bb.height + r, r).attr({stroke: "none", fill: "#000"}).insertBefore(set[0]);
    };
    Raphael.fn.g.drop = function (x, y, text, size, angle) {
        size = size || 30;
        angle = angle || 0;
        var res = this.set();
        res.push(this.path({}, ["M", x, y, "l", size, 0, "A", size * .4, size * .4, 0, 1, 0, x + size * .7, y - size * .7, "z"]).attr({fill: "#000", stroke: "none", rotation: [22.5 - angle, x, y]}));
        angle = (angle + 90) * Math.PI / 180;
        res.push(this.text(x + size * Math.sin(angle), y + size * Math.cos(angle), text).attr(this.g.txtattr).attr({"font-size": size * 12 / 30, fill: "#fff"}));
        res.drop = res[0];
        res.text = res[1];
        return res;
    };
    Raphael.fn.g.blob = function (x, y, text, angle) {
        var angle = (+angle + 1 ? angle : 45) + 90,
                size = 12,
                rad = Math.PI / 180,
                fontSize = size * 12 / 12;
        var res = this.set();
        res.push(this.path({fill: "#000", stroke: "none"}));
        res.push(this.text(x + size * Math.sin((angle) * rad), y + size * Math.cos((angle) * rad) - fontSize / 2, text).attr(this.g.txtattr).attr({"font-size": fontSize, fill: "#fff"}));
        res.update = function (X, Y, withAnimation) {
            X = X || x;
            Y = Y || y;
            var bb = this[1].getBBox(),
                    w = Math.max(bb.width + fontSize, size * 25 / 12),
                    h = Math.max(bb.height + fontSize, size * 25 / 12),
                    x2 = X + size * Math.sin((angle - 22.5) * rad),
                    y2 = Y + size * Math.cos((angle - 22.5) * rad),
                    x1 = X + size * Math.sin((angle + 22.5) * rad),
                    y1 = Y + size * Math.cos((angle + 22.5) * rad),
                    dx = (x1 - x2) / 2,
                    dy = (y1 - y2) / 2,
                    rx = w / 2,
                    ry = h / 2,
                    k = -Math.sqrt(Math.abs(rx * rx * ry * ry - rx * rx * dy * dy - ry * ry * dx * dx) / (rx * rx * dy * dy + ry * ry * dx * dx)),
                    cx = k * rx * dy / ry + (x1 + x2) / 2,
                    cy = k * -ry * dx / rx + (y1 + y2) / 2;
            if (withAnimation) {
                this.animate({x: cx, y: cy, path: ["M", x, y, "L", x1, y1, "A", rx, ry, 0, 1, 1, x2, y2, "z"].join(",")}, 500, ">");
            } else {
                this.attr({x: cx, y: cy, path: ["M", x, y, "L", x1, y1, "A", rx, ry, 0, 1, 1, x2, y2, "z"].join(",")});
            }
            return this;
        };
        res.update(x, y);
        return res;
    };

    Raphael.fn.g.colorValue = function (value, total, s, b) {
        return "hsb(" + [Math.min((1 - value / total) * .4, 1), s || .75, b || .75] + ")";
    };

    Raphael.fn.g.snapEnds = function (from, to, steps) {
        var f = from,
                t = to;
        if (f == t) {
            return {from: f, to: t, power: 0};
        }
        function round(a) {
            return Math.abs(a - .5) < .25 ? Math.floor(a) + .5 : Math.round(a);
        }

        var d = (t - f) / steps,
                r = Math.floor(d),
                R = r,
                i = 0;
        if (r) {
            while (R) {
                i--;
                R = Math.floor(d * Math.pow(10, i)) / Math.pow(10, i);
            }
            i ++;
        } else {
            while (!r) {
                i = i || 1;
                r = Math.floor(d * Math.pow(10, i)) / Math.pow(10, i);
                i++;
            }
            i && i--;
        }
        var t = round(to * Math.pow(10, i)) / Math.pow(10, i);
        if (t < to) {
            t = round((to + .5) * Math.pow(10, i)) / Math.pow(10, i);
        }
        var f = round((from - (i > 0 ? 0 : .5)) * Math.pow(10, i)) / Math.pow(10, i);
        return {from: f, to: t, power: i};
    };
    Raphael.fn.g.axis = function (x, y, length, from, to, steps, orientation, labels, type, dashsize) {
        dashsize = dashsize == null ? 2 : dashsize;
        type = type || "t";
        steps = steps || 10;
        var path = type == "|" || type == " " ? ["M", x + .5, y, "l", 0, .001] : orientation == 1 || orientation == 3 ? ["M", x + .5, y, "l", 0, -length] : ["M", x, y + .5, "l", length, 0],
                ends = this.g.snapEnds(from, to, steps),
                f = ends.from,
                t = ends.to,
                i = ends.power,
                j = 0,
                text = this.set();
        d = (t - f) / steps;
        var label = f,
                rnd = i > 0 ? i : 0;
        dx = length / steps;
        if (+orientation == 1 || +orientation == 3) {
            var Y = y,
                    addon = (orientation - 1 ? 1 : -1) * (dashsize + 3 + !!(orientation - 1));
            while (Y >= y - length) {
                type != "-" && type != " " && (path = path.concat(["M", x - (type == "+" || type == "|" ? dashsize : !(orientation - 1) * dashsize * 2), Y + .5, "l", dashsize * 2 + 1, 0]));
                text.push(this.text(x + addon, Y, (labels && labels[j++]) || (Math.round(label) == label ? label : +label.toFixed(rnd))).attr(this.g.txtattr).attr({"text-anchor": orientation - 1 ? "start" : "end"}));
                label += d;
                Y -= dx;
            }
            if (Math.round(Y + dx - (y - length))) {
                type != "-" && type != " " && (path = path.concat(["M", x - (type == "+" || type == "|" ? dashsize : !(orientation - 1) * dashsize * 2), y - length + .5, "l", dashsize * 2 + 1, 0]));
                text.push(this.text(x + addon, y - length, (labels && labels[j]) || (Math.round(label) == label ? label : +label.toFixed(rnd))).attr(this.g.txtattr).attr({"text-anchor": orientation - 1 ? "start" : "end"}));
            }
        } else {
            var X = x,
                    label = f,
                    rnd = i > 0 ? i : 0,
                    addon = (orientation ? -1 : 1) * (dashsize + 9 + !orientation),
                    dx = length / steps,
                    txt = 0,
                    prev = 0;
            while (X <= x + length) {
                type != "-" && type != " " && (path = path.concat(["M", X + .5, y - (type == "+" ? dashsize : !!orientation * dashsize * 2), "l", 0, dashsize * 2 + 1]));
                text.push(txt = this.text(X, y + addon, (labels && labels[j++]) || (Math.round(label) == label ? label : +label.toFixed(rnd))).attr(this.g.txtattr));
                var bb = txt.getBBox();
                if (prev >= bb.x - 5) {
                    text.pop(text.length - 1).remove();
                } else {
                    prev = bb.x + bb.width;
                }
                label += d;
                X += dx;
            }
            if (Math.round(X - dx - x - length)) {
                type != "-" && type != " " && (path = path.concat(["M", x + length + .5, y - (type == "+" ? dashsize : !!orientation * dashsize * 2), "l", 0, dashsize * 2 + 1]));
                text.push(this.text(x + length, y + addon, (labels && labels[j]) || (Math.round(label) == label ? label : +label.toFixed(rnd))).attr(this.g.txtattr));
            }
        }
        var res = this.path({}, path);
        res.text = text;
        res.all = this.set([res, text]);
        res.remove = function () {
            this.text.remove();
            this.constructor.prototype.remove.call(this);
        };
        return res;
    };

    Raphael.el.lighter = function (times) {
        times = times || 2;
        var fs = [this.attrs.fill, this.attrs.stroke];
        this.fs = this.fs || [fs[0], fs[1]];
        fs[0] = Raphael.rgb2hsb(Raphael.getRGB(fs[0]).hex);
        fs[1] = Raphael.rgb2hsb(Raphael.getRGB(fs[1]).hex);
        fs[0].b = Math.min(fs[0].b * times, 1);
        fs[0].s = fs[0].s / times;
        fs[1].b = Math.min(fs[1].b * times, 1);
        fs[1].s = fs[1].s / times;
        this.attr({fill: "hsb(" + [fs[0].h, fs[0].s, fs[0].b] + ")", stroke: "hsb(" + [fs[1].h, fs[1].s, fs[1].b] + ")"});
    };
    Raphael.el.darker = function (times) {
        times = times || 2;
        var fs = [this.attrs.fill, this.attrs.stroke];
        this.fs = this.fs || [fs[0], fs[1]];
        fs[0] = Raphael.rgb2hsb(Raphael.getRGB(fs[0]).hex);
        fs[1] = Raphael.rgb2hsb(Raphael.getRGB(fs[1]).hex);
        fs[0].s = Math.min(fs[0].s * times, 1);
        fs[0].b = fs[0].b / times;
        fs[1].s = Math.min(fs[1].s * times, 1);
        fs[1].b = fs[1].b / times;
        this.attr({fill: "hsb(" + [fs[0].h, fs[0].s, fs[0].b] + ")", stroke: "hsb(" + [fs[1].h, fs[1].s, fs[1].b] + ")"});
    };
    Raphael.el.original = function () {
        if (this.fs) {
            this.attr({fill: this.fs[0], stroke: this.fs[1]});
            delete this.fs;
        }
    };
})();
;
/* END /2static/script/lib/graphael/g.raphael.js */
/* START /2static/script/lib/graphael/g.bar.js */
/*
 * g.Raphael 0.2 - Charting library, based on Raphaël
 *
 * Copyright (c) 2009 Dmitry Baranovskiy (http://g.raphaeljs.com)
 * Licensed under the MIT (http://www.opensource.org/licenses/mit-license.php) license.
 */

Raphael.fn.g.barchart = function (x, y, width, height, values, isVertical, opts) {
    opts = opts || {};
    var type = {round: "round", sharp: "sharp", soft: "soft"}[opts.type] || "square",
            gutter = parseFloat(opts.gutter || "20%"),
            chart = this.set(),
            bars = this.set(),
            covers = this.set(),
            covers2 = this.set(),
            total = Math.max.apply(Math, values),
            stacktotal = [],
            paper = this,
            multi = 0,
            colors = opts.colors || this.g.colors,
            len = values.length;
    if (this.raphael.isArray(values[0])) {
        total = [];
        multi = len;
        len = 0;
        for (var i = values.length; i--;) {
            total.push(Math.max.apply(Math, values[i]));
            len = Math.max(len, values[i].length);
        }
        if (opts.stacked) {
            for (var i = len; i--;) {
                var tot = 0;
                for (var j = values.length; j--;) {
                    tot += + values[j][i] || 0;
                }
                stacktotal.push(tot);
            }
        }
        for (var i = values.length; i--;) {
            if (values[i].length < len) {
                for (var j = len; j--;) {
                    values[i].push(0);
                }
            }
        }
        total = Math.max.apply(Math, opts.stacked ? stacktotal : total);
    }

    total = (opts.to) || total;
    if (!isVertical) {
        var barwidth = width / (len * (100 + gutter) + gutter) * 100,
                barhgutter = barwidth * gutter / 100,
                barvgutter = typeof opts.vgutter == "undefined" ? 20 : opts.vgutter,
                stack = [],
                X = x + barhgutter,
                Y = (height - 2 * barvgutter) / total;
        if (!opts.stretch) {
            barhgutter = Math.round(barhgutter);
            barwidth = Math.floor(barwidth);
        }
        !opts.stacked && (barwidth /= multi || 1);
        for (var i = 0; i < len; i++) {
            stack = [];
            for (var j = 0; j < multi; j++) {
                var h = Math.round((multi ? values[j][i] : values[i]) * Y),
                        top = y + height - barvgutter - h,
                        bar;
                bars.push(bar = this.g.finger(Math.round(X + barwidth / 2), top + h, barwidth, opts.init ? 0 : h, true, type).attr({stroke: "none", fill: colors[multi ? j : i]}));
                bar.y = top;
                bar.x = Math.round(X + barwidth / 2);
                bar.w = barwidth;
                bar.h = h;
                bar.value = multi ? values[j][i] : values[i];
                opts.init && bar.animate({path: this.g.finger(Math.round(X + barwidth / 2), top + h, barwidth, h, true, type, 1)}, (+opts.init - 1) || 1000, ">");
                if (!opts.stacked) {
                    X += barwidth;
                } else {
                    stack.push(bar);
                }
            }
            if (opts.stacked) {
                var cvr;
                covers2.push(cvr = this.rect(stack[0].x - stack[0].w / 2, y, barwidth, height).attr({stroke: "none", fill: "#000", opacity: 0}));
                cvr.bars = [];
                for (var s = 0, ss = stack.length; s < ss; s++) {
                    cvr.bars.push(stack[s]);
                }
                stack.sort(function (a, b) {
                    return a.value - b.value;
                });
                var size = 0;
                for (var s = stack.length; s--;) {
                    stack[s].toFront();
                }
                for (var s = 0, ss = stack.length; s < ss; s++) {
                    var bar = stack[s],
                            cover,
                            h = (size + bar.value) * Y,
                            path = this.g.finger(bar.x, y + height - barvgutter - !!size * .5, barwidth, h, true, type, 1);
                    size && opts.init && bar.animate({path: path}, (+opts.init - 1) || 1000, ">");
                    size && !opts.init && bar.attr({path: path});
                    bar.h = h;
                    bar.y = y + height - barvgutter - !!size * .5 - h;
                    covers.push(cover = this.rect(bar.x - bar.w / 2, bar.y, barwidth, bar.value * Y).attr({stroke: "none", fill: "#000", opacity: 0}));
                    cover.bar = bar;
                    size += bar.value;
                }

                X += barwidth;
            }
            X += barhgutter;
        }
        covers2.toFront();
        X = x + barhgutter;
        if (!opts.stacked) {
            for (var i = 0; i < len; i++) {
                for (var j = 0; j < multi; j++) {
                    var cover;
                    covers.push(cover = this.rect(Math.round(X), y + barvgutter, barwidth, height - barvgutter).attr({stroke: "none", fill: "#000", opacity: 0}));
                    cover.bar = bars[i * (multi || 1) + j];
                    X += barwidth;
                }
                X += barhgutter;
            }
        }
        chart.label = function (labels, isBottom) {
            labels = labels || [];
            this.labels = paper.set();
            var L, l = -Infinity;
            if (opts.stacked) {
                for (var i = 0; i < len; i++) {
                    var tot = 0;
                    for (var j = 0; j < multi; j++) {
                        tot += multi ? values[j][i] : values[i];
                        if (j == multi - 1) {
                            var label = paper.g.labelise(labels[i], tot, total);
                            L = paper.g.text(bars[i * (multi || 1) + j].x, y + height - barvgutter / 2, label).insertBefore(covers[i * (multi || 1) + j]);
                            var bb = L.getBBox();
                            if (bb.x - 7 < l) {
                                L.remove();
                            } else {
                                this.labels.push(L);
                                l = bb.x + bb.width;
                            }
                        }
                    }
                }
            } else {
                for (var i = 0; i < len; i++) {
                    for (var j = 0; j < multi; j++) {
                        var label = paper.g.labelise(multi ? labels[j] && labels[j][i] : labels[i], multi ? values[j][i] : values[i], total);
                        L = paper.g.text(bars[i * (multi || 1) + j].x, isBottom ? y + height - barvgutter / 2 : bars[i * (multi || 1) + j].y - 10, label).insertBefore(covers[i * (multi || 1) + j]);
                        var bb = L.getBBox();
                        if (bb.x - 7 < l) {
                            L.remove();
                        } else {
                            this.labels.push(L);
                            l = bb.x + bb.width;
                        }
                    }
                }
            }
            return this;
        };
    } else {
        var barheight = Math.floor(height / (len * (100 + gutter) + gutter) * 100),
                bargutter = Math.floor(barheight * gutter / 100),
                stack = [],
                Y = y + bargutter,
                X = (width - 1) / total;
        !opts.stacked && (barheight /= multi || 1);
        for (var i = 0; i < len; i++) {
            stack = [];
            for (var j = 0; j < multi; j++) {
                var val = multi ? values[j][i] : values[i],
                        bar;
                bars.push(bar = this.g.finger(x, Y + barheight / 2, opts.init ? 0 : Math.round(val * X), barheight - 1, false, type).attr({stroke: colors[multi > 1 ? j : i], fill: colors[multi > 1 ? j : i]}));
                bar.x = x + Math.round(val * X);
                bar.y = Y + barheight / 2;
                bar.w = Math.round(val * X);
                bar.h = barheight;
                bar.value = +val;
                opts.init && bar.animate({path: this.g.finger(x, Y + barheight / 2, Math.round(val * X), barheight - 1, false, type, 1)}, (+opts.init - 1) || 500, ">");
                if (!opts.stacked) {
                    Y += barheight;
                } else {
                    stack.push(bar);
                }
            }
            if (opts.stacked) {
                stack.sort(function (a, b) {
                    return a.value - b.value;
                });
                var size = 0;
                for (var s = stack.length; s--;) {
                    stack[s].toFront();
                }
                for (var s = 0, ss = stack.length; s < ss; s++) {
                    var bar = stack[s],
                            cover,
                            val = Math.round((size + bar.value) * X),
                            path = this.g.finger(x, bar.y, val, barheight - 1, false, type, 1);
                    size && opts.init && bar.animate({path: path}, (+opts.init - 1) || 1000, ">");
                    size && !opts.init && bar.attr({path: path});
                    bar.w = val;
                    bar.x = x + val;
                    covers.push(cover = this.rect(x + size * X, bar.y - bar.h / 2, bar.value * X, barheight).attr({stroke: "none", fill: "#000", opacity: 0}));
                    cover.bar = bar;
                    size += bar.value;
                }
                Y += barheight;
            }
            Y += bargutter;
        }
        Y = y + bargutter;
        if (!opts.stacked) {
            for (var i = 0; i < len; i++) {
                for (var j = 0; j < multi; j++) {
                    covers.push(this.rect(x, Y, width, barheight).attr({stroke: "none", fill: "#000", opacity: 0}));
                    Y += barheight;
                }
                Y += bargutter;
            }
        }
        chart.label = function (labels, isRight) {
            labels = labels || [];
            this.labels = paper.set();
            for (var i = 0; i < len; i++) {
                for (var j = 0; j < multi; j++) {
                    var label = paper.g.labelise(multi ? labels[j] && labels[j][i] : labels[i], multi ? values[j][i] : values[i], total);
                    var X = isRight ? bars[i * (multi || 1) + j].x - barheight / 2 + 3 : x + 5,
                            A = isRight ? "end" : "start",
                            L;
                    this.labels.push(L = paper.g.text(X, bars[i * (multi || 1) + j].y, label).attr({"text-anchor": A}).insertBefore(covers[0]));
                    if (L.getBBox().x < x + 5) {
                        L.attr({x: x + 5, "text-anchor": "start"});
                    } else {
                        bars[i * (multi || 1) + j].label = L;
                    }
                }
            }
            return this;
        };
    }
    chart.hover = function (fin, fout) {
        covers2.hide();
        covers.show();
        fout = fout || function () {
        };
        covers.mouseover(fin).mouseout(fout);
        return this;
    };
    chart.hoverColumn = function (fin, fout) {
        covers.hide();
        covers2.show();
        fout = fout || function () {
        };
        covers2.mouseover(fin).mouseout(fout);
        return this;
    };
    chart.click = function (f) {
        covers2.hide();
        covers.show();
        covers.click(f);
        return this;
    };
    chart.clickColumn = function (f) {
        covers.hide();
        covers2.show();
        covers2.click(f);
        return this;
    };
    chart.push(bars, covers, covers2);
    chart.bars = bars;
    chart.covers = covers;
    return chart;
};
;
/* END /2static/script/lib/graphael/g.bar.js */
/* START /2static/script/lib/graphael/g.pie.js */
/*
 * g.Raphael 0.2 - Charting library, based on Raphaël
 *
 * Copyright (c) 2009 Dmitry Baranovskiy (http://g.raphaeljs.com)
 * Licensed under the MIT (http://www.opensource.org/licenses/mit-license.php) license.
 */

Raphael.fn.g.piechart = function (cx, cy, r, values, opts) {
    opts = opts || {};
    var paper = this,
            sectors = [],
            covers = this.set(),
            chart = this.set(),
            series = this.set(),
            order = [],
            len = values.length,
            angle = 0,
            total = 0,
            others = 0,
            cut = 9,
            defcut = true;
    chart.covers = covers;
    if (len == 1) {
        series.push(this.circle(cx, cy, r).attr({fill: this.g.colors[0], stroke: opt.stroke || "#fff", "stroke-width": opts.strokewidth == null ? 1 : opts.strokewidth}));
        covers.push(this.circle(cx, cy, r).attr({fill: "#000", opacity: 0, "stroke-width": 3}));
        total = values[0];
        values[0] = {value: values[0], order: 0, valueOf: function () {
            return this.value;
        }};
        series[0].middle = {x: cx, y: cy};
        series[0].mangle = 180;
    } else {
        function sector(cx, cy, r, startAngle, endAngle, fill) {
            var rad = Math.PI / 180,
                    x1 = cx + r * Math.cos(-startAngle * rad),
                    x2 = cx + r * Math.cos(-endAngle * rad),
                    xm = cx + r / 2 * Math.cos(-(startAngle + (endAngle - startAngle) / 2) * rad),
                    y1 = cy + r * Math.sin(-startAngle * rad),
                    y2 = cy + r * Math.sin(-endAngle * rad),
                    ym = cy + r / 2 * Math.sin(-(startAngle + (endAngle - startAngle) / 2) * rad),
                    res = ["M", cx, cy, "L", x1, y1, "A", r, r, 0, +(Math.abs(endAngle - startAngle) > 180), 1, x2, y2, "z"];
            res.middle = {x: xm, y: ym};
            return res;
        }

        for (var i = 0; i < len; i++) {
            total += values[i];
            values[i] = {value: values[i], order: i, valueOf: function () {
                return this.value;
            }};
        }
        values.sort(function (a, b) {
            return b.value - a.value;
        });
        for (var i = 0; i < len; i++) {
            if (defcut && values[i] * 360 / total <= 1.5) {
                cut = i;
                defcut = false;
            }
            if (i > cut) {
                defcut = false;
                values[cut].value += values[i];
                values[cut].others = true;
                others = values[cut].value;
            }
        }
        len = Math.min(cut + 1, values.length);
        others && values.splice(len) && (values[cut].others = true);
        for (var i = 0; i < len; i++) {
            var mangle = angle - 360 * values[i] / total / 2;
            if (!i) {
                angle = 90 - mangle;
                mangle = angle - 360 * values[i] / total / 2;
            }
            if (opts.init) {
                var ipath = sector(cx, cy, 1, angle, angle - 360 * values[i] / total).join(",");
            }
            var path = sector(cx, cy, r, angle, angle -= 360 * values[i] / total);
            var p = this.path({fill: opts.colors && opts.colors[i] || this.g.colors[i] || "#666", stroke: opts.stroke || "#fff", "stroke-width": opts.strokewidth == null ? 1 : opts.strokewidth, "stroke-linejoin": "round"}, opts.init ? ipath : path.join(","));
            p.value = values[i];
            p.middle = path.middle;
            p.mangle = mangle;
            sectors.push(p);
            series.push(p);
            opts.init && p.animate({path: path.join(",")}, (+opts.init - 1) || 1000, ">");
        }
        for (var i = 0; i < len; i++) {
            var p = paper.path({fill: "#000", opacity: 0, "stroke-width": 3}, sectors[i].attr("path"));
            opts.href && opts.href[i] && p.attr({href: opts.href[i]});
            p.attr = function () {
            };
            covers.push(p);
            series.push(p);
        }
    }

    chart.hover = function (fin, fout) {
        fout = fout || function () {
        };
        var that = this;
        for (var i = 0; i < len; i++) {
            (function (sector, cover, j) {
                var o = {
                    sector: sector,
                    cover: cover,
                    cx: cx,
                    cy: cy,
                    mx: sector.middle.x,
                    my: sector.middle.y,
                    mangle: sector.mangle,
                    r: r,
                    value: values[j],
                    total: total,
                    label: that.labels && that.labels[j]
                };
                cover.mouseover(function () {
                    fin.call(o);
                }).mouseout(function () {
                    fout.call(o);
                });
            })(series[i], covers[i], i);
        }
        return this;
    };
    chart.click = function (f) {
        var that = this;
        for (var i = 0; i < len; i++) {
            (function (sector, cover, j) {
                var o = {
                    sector: sector,
                    cover: cover,
                    cx: cx,
                    cy: cy,
                    mx: sector.middle.x,
                    my: sector.middle.y,
                    mangle: sector.mangle,
                    r: r,
                    value: values[j],
                    total: total,
                    label: that.labels && that.labels[j]
                };
                cover.click(function () {
                    f.call(o);
                });
            })(series[i], covers[i], i);
        }
        return this;
    };
    chart.inject = function (element) {
        element.insertBefore(covers[0]);
    };
    var legend = function (labels, otherslabel, mark, dir) {
        var x = cx + r + r / 5,
                y = cy,
                h = y + 10;
        labels = labels || [];
        dir = (dir && dir.toLowerCase && dir.toLowerCase()) || "east";
        mark = paper.g.markers[mark && mark.toLowerCase()] || "disc";
        chart.labels = paper.set();
        for (var i = 0; i < len; i++) {
            var clr = series[i].attr("fill"),
                    j = values[i].order,
                    txt;
            values[i].others && (labels[j] = otherslabel || "Others");
            labels[j] = paper.g.labelise(labels[j], values[i], total);
            chart.labels.push(paper.set());
            chart.labels[i].push(paper.g[mark](x + 5, h, 5).attr({fill: clr, stroke: "none"}));
            chart.labels[i].push(txt = paper.text(x + 20, h, labels[j] || values[j]).attr(paper.g.txtattr).attr({fill: opts.legendcolor || "#000", "text-anchor": "start"}));
            covers[i].label = chart.labels[i];
            h += txt.getBBox().height * 1.2;
        }
        var bb = chart.labels.getBBox(),
                tr = {
                    east: [0, -bb.height / 2],
                    west: [-bb.width - 2 * r - 20, -bb.height / 2],
                    north: [-r - bb.width / 2, -r - bb.height - 10],
                    south: [-r - bb.width / 2, r + 10]
                }[dir];
        chart.labels.translate.apply(chart.labels, tr);
        chart.push(chart.labels);
    };
    if (opts.legend) {
        legend(opts.legend, opts.legendothers, opts.legendmark, opts.legendpos);
    }
    chart.push(series, covers);
    chart.series = series;
    chart.covers = covers;
    return chart;
};
;
/* END /2static/script/lib/graphael/g.pie.js */
/* START /2static/script/lib/graphael/g.line.js */
/*
 * g.Raphael 0.2 - Charting library, based on Raphaël
 *
 * Copyright (c) 2009 Dmitry Baranovskiy (http://g.raphaeljs.com)
 * Licensed under the MIT (http://www.opensource.org/licenses/mit-license.php) license.
 */

Raphael.fn.g.linechart = function (x, y, width, height, valuesx, valuesy, opts) {
    function shrink(values, dim) {
        var k = values.length / dim,
                j = 0,
                l = k,
                sum = 0,
                res = [];
        while (j < values.length) {
            l--;
            if (l < 0) {
                sum += values[j] * (1 + l);
                res.push(sum / k);
                sum = values[j++] * -l;
                l += k;
            } else {
                sum += values[j++];
            }
        }
        return res;
    }

    opts = opts || {};
    if (!this.raphael.isArray(valuesx[0])) {
        valuesx = [valuesx];
    }
    if (!this.raphael.isArray(valuesy[0])) {
        valuesy = [valuesy];
    }
    var allx = Array.prototype.concat.apply([], valuesx),
            ally = Array.prototype.concat.apply([], valuesy),
            xdim = this.g.snapEnds(Math.min.apply(Math, allx), Math.max.apply(Math, allx), valuesx[0].length - 1),
            minx = xdim.from,
            maxx = xdim.to,
            gutter = opts.gutter || 10,
            kx = (width - gutter * 2) / (maxx - minx),
            ydim = this.g.snapEnds(Math.min.apply(Math, ally), Math.max.apply(Math, ally), valuesy[0].length - 1),
            miny = ydim.from,
            maxy = ydim.to,
            ky = (height - gutter * 2) / (maxy - miny),
            len = Math.max(valuesx[0].length, valuesy[0].length),
            symbol = opts.symbol || "",
            colors = opts.colors || Raphael.fn.g.colors,
            that = this,
            columns = null,
            dots = null,
            chart = this.set(),
            path = [];

    for (var i = 0, ii = valuesy.length; i < ii; i++) {
        len = Math.max(len, valuesy[i].length);
    }
    var shades = this.set();
    for (var i = 0, ii = valuesy.length; i < ii; i++) {
        if (opts.shade) {
            shades.push(this.path({stroke: "none", fill: colors[i], opacity: opts.nostroke ? 1 : .3}));
        }
        if (valuesy[i].length > width - 2 * gutter) {
            valuesy[i] = shrink(valuesy[i], width - 2 * gutter);
            len = width - 2 * gutter;
        }
        if (valuesx[i] && valuesx[i].length > width - 2 * gutter) {
            valuesx[i] = shrink(valuesx[i], width - 2 * gutter);
        }
    }
    var axis = this.set();
    if (opts.axis) {
        var ax = (opts.axis + "").split(/[,\s]+/);
        +ax[0] && axis.push(this.g.axis(x + gutter, y + gutter, width - 2 * gutter, minx, maxx, opts.axisxstep || Math.floor((width - 2 * gutter) / 20), 2));
        +ax[1] && axis.push(this.g.axis(x + width - gutter, y + height - gutter, height - 2 * gutter, miny, maxy, opts.axisystep || Math.floor((height - 2 * gutter) / 20), 3));
        +ax[2] && axis.push(this.g.axis(x + gutter, y + height - gutter, width - 2 * gutter, minx, maxx, opts.axisxstep || Math.floor((width - 2 * gutter) / 20), 0));
        +ax[3] && axis.push(this.g.axis(x + gutter, y + height - gutter, height - 2 * gutter, miny, maxy, opts.axisystep || Math.floor((height - 2 * gutter) / 20), 1));
    }
    var lines = this.set(),
            symbols = this.set(),
            line;
    for (var i = 0, ii = valuesy.length; i < ii; i++) {
        if (!opts.nostroke) {
            lines.push(line = this.path({
                stroke: colors[i],
                "stroke-width": opts.width || 2,
                "stroke-linejoin": "round",
                "stroke-linecap": "round",
                "stroke-dasharray": opts.dash || ""
            }));
        }
        var sym = this.raphael.isArray(symbol) ? symbol[i] : symbol;
        path = [];
        for (var j = 0, jj = valuesy[i].length; j < jj; j++) {
            var X = x + gutter + ((valuesx[i] || valuesx[0])[j] - minx) * kx;
            var Y = y + height - gutter - (valuesy[i][j] - miny) * ky;
            (Raphael.isArray(sym) ? sym[j] : sym) && symbols.push(this.g[Raphael.fn.g.markers[this.raphael.isArray(sym) ? sym[j] : sym]](X, Y, (opts.width || 2) * 3).attr({fill: colors[i], stroke: "none"}));
            path = path.concat([j ? "L" : "M", X, Y]);
        }
        if (opts.shade) {
            shades[i].attr({path: path.concat(["L", X, y + height - gutter, "L",  x + gutter + ((valuesx[i] || valuesx[0])[0] - minx) * kx, y + height - gutter, "z"]).join(",")});
        }
        !opts.nostroke && line.attr({path: path.join(",")});
    }
    function createColumns() {
        // unite Xs together
        var Xs = [];
        for (var i = 0, ii = valuesx.length; i < ii; i++) {
            Xs = Xs.concat(valuesx[i]);
        }
        Xs.sort();
        // remove duplicates
        var Xs2 = [],
                xs = [];
        for (var i = 0, ii = Xs.length; i < ii; i++) {
            Xs[i] != Xs[i - 1] && Xs2.push(Xs[i]) && xs.push(x + gutter + (Xs[i] - minx) * kx);
        }
        Xs = Xs2;
        ii = Xs.length;
        var cvrs = that.set();
        for (var i = 0; i < ii; i++) {
            var X = xs[i] - (xs[i] - (xs[i - 1] || x)) / 2,
                    w = ((xs[i + 1] || x + width) - xs[i]) / 2 + (xs[i] - (xs[i - 1] || x)) / 2,
                    C;
            cvrs.push(C = that.rect(X - 1, y, w + 1, height).attr({stroke: "none", fill: "#000", opacity: 0}));
            C.values = [];
            C.y = [];
            C.x = xs[i];
            C.axis = Xs[i];
            C.xIndex = i;
            for (var j = 0, jj = valuesy.length; j < jj; j++) {
                Xs2 = valuesx[j] || valuesx[0];
                for (var k = 0, kk = Xs2.length; k < kk; k++) {
                    if (Xs2[k] == Xs[i]) {
                        C.values.push(valuesy[j][k]);
                        C.y.push(y + height - gutter - (valuesy[j][k] - miny) * ky);
                    }
                }
            }
        }
        columns = cvrs;
    }

    function createDots() {
        var cvrs = that.set(),
                C;
        for (var i = 0, ii = valuesy.length; i < ii; i++) {
            for (var j = 0, jj = valuesy[i].length; j < jj; j++) {
                var X = x + gutter + ((valuesx[i] || valuesx[0])[j] - minx) * kx,
                        nearX = x + gutter + ((valuesx[i] || valuesx[0])[j ? j - 1 : 1] - minx) * kx,
                        Y = y + height - gutter - (valuesy[i][j] - miny) * ky;
                cvrs.push(C = that.circle(X, Y, Math.abs(nearX - X) / 2).attr({stroke: "none", fill: "#000", opacity: 0}));
                C.x = X;
                C.y = Y;
                C.value = valuesy[i][j];
                C.axis = (valuesx[i] || valuesx[0])[j];
            }
        }
        dots = cvrs;
    }

    chart.push(axis, columns, dots, lines, shades, symbols);
    chart.hoverColumn = function (fin, fout) {
        !columns && createColumns();
        columns.mouseover(fin).mouseout(fout);
        return this;
    };
    chart.clickColumn = function (f) {
        !columns && createColumns();
        columns.click(f);
        return this;
    };
    chart.hrefColumn = function (cols) {
        var hrefs = that.raphael.isArray(arguments[0]) ? arguments[0] : arguments;
        if (!(arguments.length - 1) && typeof cols == "object") {
            for (var x in cols) {
                for (var i = 0, ii = columns.length; i < ii; i++) if (columns[i].axis == x) {
                    columns[i].attr("href", cols[x]);
                }
            }
        }
        !columns && createColumns();
        for (var i = 0, ii = hrefs.length; i < ii; i++) {
            columns[i] && columns[i].attr("href", hrefs[i]);
        }
        return this;
    };
    chart.hoverDot = function (fin, fout) {
        !dots && createDots();
        dots.mouseover(fin).mouseout(fout);
        return this;
    };
    chart.clickDot = function (f) {
        !dots && createDots();
        dots.click(f);
        return this;
    };
    return chart;
};
;
/* END /2static/script/lib/graphael/g.line.js */
/* START /2static/script/lib/graphael/g.dot.js */
/*
 * g.Raphael 0.2 - Charting library, based on Raphaël
 *
 * Copyright (c) 2009 Dmitry Baranovskiy (http://g.raphaeljs.com)
 * Licensed under the MIT (http://www.opensource.org/licenses/mit-license.php) license.
 */

Raphael.fn.g.dotchart = function (x, y, width, height, valuesx, valuesy, size, opts) {
    function drawAxis(ax) {
        +ax[0] && (ax[0] = paper.g.axis(x + gutter, y + gutter, width - 2 * gutter, minx, maxx, opts.axisxstep || Math.floor((width - 2 * gutter) / 20), 2, opts.axisxlabels || null, opts.axisxtype || "t"));
        +ax[1] && (ax[1] = paper.g.axis(x + width - gutter, y + height - gutter, height - 2 * gutter, miny, maxy, opts.axisystep || Math.floor((height - 2 * gutter) / 20), 3, opts.axisylabels || null, opts.axisytype || "t"));
        +ax[2] && (ax[2] = paper.g.axis(x + gutter, y + height - gutter + maxR, width - 2 * gutter, minx, maxx, opts.axisxstep || Math.floor((width - 2 * gutter) / 20), 0, opts.axisxlabels || null, opts.axisxtype || "t"));
        +ax[3] && (ax[3] = paper.g.axis(x + gutter - maxR, y + height - gutter, height - 2 * gutter, miny, maxy, opts.axisystep || Math.floor((height - 2 * gutter) / 20), 1, opts.axisylabels || null, opts.axisytype || "t"));
    }

    opts = opts || {};
    var xdim = this.g.snapEnds(Math.min.apply(Math, valuesx), Math.max.apply(Math, valuesx), valuesx.length - 1),
            minx = xdim.from,
            maxx = xdim.to,
            gutter = opts.gutter || 10,
            ydim = this.g.snapEnds(Math.min.apply(Math, valuesy), Math.max.apply(Math, valuesy), valuesy.length - 1),
            miny = ydim.from,
            maxy = ydim.to,
            len = Math.max(valuesx.length, valuesy.length, size.length),
            symbol = this.g.markers[opts.symbol] || "disc",
            res = this.set(),
            series = this.set(),
            max = opts.max || 100,
            top = Math.max.apply(Math, size),
            R = [],
            paper = this,
            k = Math.sqrt(top / Math.PI) * 2 / max;

    for (var i = 0; i < len; i++) {
        R[i] = Math.min(Math.sqrt(size[i] / Math.PI) * 2 / k, max);
    }
    gutter = Math.max.apply(Math, R.concat(gutter));
    var axis = this.set(),
            maxR = Math.max.apply(Math, R);
    if (opts.axis) {
        var ax = (opts.axis + "").split(/[,\s]+/);
        drawAxis(ax);
        var g = [], b = [];
        for (var i = 0, ii = ax.length; i < ii; i++) {
            var bb = ax[i].all ? ax[i].all.getBBox()[["height", "width"][i % 2]] : 0;
            g[i] = bb + gutter;
            b[i] = bb;
        }
        gutter = Math.max.apply(Math, g.concat(gutter));
        for (var i = 0, ii = ax.length; i < ii; i++) if (ax[i].all) {
            ax[i].remove();
            ax[i] = 1;
        }
        drawAxis(ax);
        for (var i = 0, ii = ax.length; i < ii; i++) if (ax[i].all) {
            axis.push(ax[i].all);
        }
        res.axis = axis;
    }
    var kx = (width - gutter * 2) / ((maxx - minx) || 1),
            ky = (height - gutter * 2) / ((maxy - miny) || 1);
    for (var i = 0, ii = valuesy.length; i < ii; i++) {
        var sym = this.raphael.isArray(symbol) ? symbol[i] : symbol,
                X = x + gutter + (valuesx[i] - minx) * kx,
                Y = y + height - gutter - (valuesy[i] - miny) * ky;
        sym && R[i] && series.push(this.g[sym](X, Y, R[i]).attr({fill: opts.heat ? this.g.colorValue(R[i], maxR) : Raphael.fn.g.colors[0], "fill-opacity": opts.opacity ? R[i] / max : 1, stroke: "none"}));
    }
    var covers = this.set();
    for (var i = 0, ii = valuesy.length; i < ii; i++) {
        var X = x + gutter + (valuesx[i] - minx) * kx,
                Y = y + height - gutter - (valuesy[i] - miny) * ky;
        covers.push(this.circle(X, Y, maxR).attr({fill: "#000", stroke: "none", opacity: 0}));
        opts.href && opts.href[i] && covers[i].attr({href: opts.href[i]});
        covers[i].r = +R[i].toFixed(3);
        covers[i].x = +X.toFixed(3);
        covers[i].y = +Y.toFixed(3);
        covers[i].X = valuesx[i];
        covers[i].Y = valuesy[i];
        covers[i].value = size[i] || 0;
        covers[i].dot = series[i];
    }
    res.covers = covers;
    res.series = series;
    res.push(series, axis, covers);
    res.hover = function (fin, fout) {
        covers.mouseover(fin).mouseout(fout);
        return this;
    };
    res.click = function (f) {
        covers.click(f);
        return this;
    };
    res.href = function (map) {
        var cover;
        for (var i = covers.length; i--;) {
            cover = covers[i];
            if (cover.X == map.x && cover.Y == map.y && cover.value == map.value) {
                cover.attr({href: map.href});
            }
        }
    };
    return res;
};
;
/* END /2static/script/lib/graphael/g.dot.js */
/* START /2static/script/fecru/raphaelCharts.js */
if (!AJS.FECRU) {
    AJS.FECRU = {};
}
AJS.FECRU.RAPHAELCHARTS = {};

(function () {

    var LINE_COLOURS_PIE = ["#478ec7","#769810","#d8561f","#d7e52e","#0c4383","#5fbe41","#f5832b","#edef00","#0c87c9","#ad2a15"];
    var LINE_COLOURS_XY = ["#d7561f","#478ec7","#769810","#dee439","#5fbe41","#0c4383","#f5832b","#edef00","#0c87c9","#ad2a15","#aebf47"];
    var REPOSITORY_COLOURS = ["#d7561f","#769810","#dee439","#5fbe41","#0c4383","#f5832b","#edef00","#0c87c9","#ad2a15","#aebf47"];

    var months = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];

    var getClr = function(i) {
        return LINE_COLOURS_XY[i % LINE_COLOURS_XY.length];
    };

    function getMinY(bars) {
        var minY = bars[0].y;
        for (var i = 1, ii = bars.length; i < ii; i++) {
            minY = Math.min(bars[i].y, minY);
        }
        return minY;
    }

    function getMaxY(bars) {
        var maxY = 0;
        for (var i = 0, ii = bars.length; i < ii; i++) {
            maxY = Math.max(bars[i].y + bars[i].h, maxY);
        }
        return maxY;
    }

    function toComp(num) {
        var prefix = ["",,, "K",,, "M",,, "G",,, "T",,, "P",,, "E",,, "Z",,, "Y"];
        for (var i = 0, ii = prefix.length; i < ii; i++) {
            if (typeof prefix[i] == "string") {
                if (num < Math.pow(10, i + 3)) {
                    return Math.round(num / Math.pow(10, i)) + prefix[i];
                }
            }
        }
        return Math.round(num / Math.pow(10, i - 1)) + prefix[prefix.length - 1];
    }

    function initDiv(divName) {
        AJS.$("body").append('<div id="' + divName + '-child" style="display:block;position:absolute; left:-4000px;">&nbsp;</div>');
        return divName + '-child';
    }

    function finaliseDiv(divName) {
        var div = AJS.$('#' + divName + '-child');
        div.removeAttr("style");

        //div.insertAfter(AJS.$('#' + divName));
        AJS.$('#' + divName).replaceWith(div);

    }

    function drawLocCharts(xValuesLoc, yValuesLoc, seriesLabelsShort, seriesLabelsLong, pageUrl) {

        var txt = {"text-anchor": "start", "font-size": 11, fill: "#666"};
        var popup;
        var dateText;

        var formattedDates = [];
        for (var i = 0, ii = xValuesLoc.length; i < ii; i++) {
            formattedDates[i] = formatDate(xValuesLoc[i]);
        }

        function hin2() {
            axisSet.hide();
            if (popup) {
                popup.remove();
                tooltipSet.hide();
                popup = null;
            }
            if (dateText) {
                dateText.remove();
                dateText = null;
            }
            for (var i = 0, ii = this.values.length; i < ii; i++) {
                tooltipSet[i].attr({text: seriesLabelsShort[i] + ": " + toComp(this.values[i])});
            }
            tooltipSet.show();
            if (this.line) {
                this.line.show();
            } else {
                this.line = rMain.set();
                this.line.push(rMain.path({opacity: .3}, "M" + [Math.round(this.x) + .5, 10, "l", 0, 130]).insertBefore(this));
                for (var j = 0, jj = this.values.length; j < jj; j++) {
                    this.line.push(rMain.circle(Math.round(this.x) + .5, this.y[j], 3).insertBefore(this).attr({fill: getClr(j), stroke: "#fff"}));
                }
            }
            popup = rMain.set();
            var maxy = Math.max.apply(0, this.y);
            var miny = Math.min.apply(0, this.y);
            popup.push(rMain.g.popupit(this.x, miny + (maxy - miny) / 2, tooltipSet, (this.x < 200) * 2 + 1).insertBefore(this).attr({fill: "#fff", stroke: "#666"}));
            tooltipSet.insertBefore(this);
            dateText = rMain.text(this.x, 150, (formattedDates[this.xIndex]));
        }

        function hout() {
            if (popup) {
                popup.remove();
                tooltipSet.hide();
                axisSet.show();
                popup = null;
                this.line.hide();
            }
            if (dateText) {
                dateText.remove();
                dateText = null;
            }
        }

        function fclick() {
            if (pageUrl) {
                var oldMaxDate = pageUrl.match(/maxDate=.*?(?=&|$)/);
                var newMaxDate = 'maxDate=' + xValuesLoc[this.xIndex];
                if (oldMaxDate) {
                    pageUrl = pageUrl.replace(oldMaxDate, newMaxDate);
                } else {
                    pageUrl += (pageUrl.indexOf("?") == -1) ? "?" : "&";
                    pageUrl += newMaxDate;
                }
                window.location = pageUrl;
            }
        }

        // draw sparkline

        var divSparkline = "locChartSparkline";
        var divSparklineChild = initDiv(divSparkline);

        var rSparkline = Raphael(divSparklineChild, 280, 37);
        rSparkline.g.linechart(0, 0, 280, 32, xValuesLoc, yValuesLoc, {colors: LINE_COLOURS_XY, width: 2});


        // draw main chart

        var divMain = "locChartMain";
        var divMainChild = initDiv(divMain);

        var bump = 20;
        var mainHeight = 150 + 10 + 20 * seriesLabelsLong.length;

        var rMain = Raphael(divMainChild, 280, mainHeight);

        // set up tooltip text
        var tooltipSet = rMain.set();
        for (var l = 0, ll = seriesLabelsLong.length; l < ll; l++) {
            tooltipSet.push(rMain.text(100, 30 + l * 15, "").attr({fill: getClr(l), "font-weight": 800}));
        }
        tooltipSet.hide();

        // draw legend
        for (var i = 0, ii = seriesLabelsLong.length; i < ii; i++) {
            rMain.g.disc(20, 150 + 20 * i + bump, 7).attr({stroke: "none", fill: getClr(i)});
            rMain.text(30, 150 + 20 * i + bump, seriesLabelsLong[i]).attr({"text-anchor": "start"});
        }

        rMain.path({opacity: .3}, "M" + [0, 130, "l", 276, 0]);

        // draw date axis
        var axisSet = rMain.set();

        var step = (276 - 9) / xValuesLoc.length;

        var dateWidth = 60;
        var lastX = - dateWidth / 2;

        for (var i = 0, ii = xValuesLoc.length; i < ii; i++) {

            var xVal = 12 + i * step;
            if (xVal - lastX > dateWidth) {
                lastX = xVal;
                //                axisSet.push(rMain.path({opacity: .3}, "M" + [xVal, 140, "l", 0, 5]));
                axisSet.push(rMain.text(xVal, 150, (formattedDates[i])));
            }
        }

        rMain.g.linechart(0, 10, 276, 130, xValuesLoc, yValuesLoc, {colors: LINE_COLOURS_XY, width: 2}).hoverColumn(hin2, hout).clickColumn(fclick);

        finaliseDiv(divSparkline);
        finaliseDiv(divMain);

    }

    function formatDate(ms) {

        var d = new Date(ms);

        var s = "";
        if (d.getDate() < 10) {
            s += '0';
        }
        s += (d.getDate()) + " ";

        s += months[d.getMonth()] + " ";

        var year = d.getYear() - 100;
        if (year < 10) {
            s += '0';
        }
        s += year;

        return s;
    }

    function drawCommitCharts(seriesLabels, sparklineLabels, sparklineData, commitsByDay, commitsByHour,
                              activityCalendarYears, activityCalendarData) {

        var dayLabels = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
        var hourLabels = ["00","01","02","03","04","05","06","07","08","09","10","11","12","13","14","15","16","17","18","19","20","21","22","23"];
        var monthLabels = ["J","F","M","A","M","J","J","A","S","O","N","D"];

        var divSparklineName = "sparklineSmall";
        var divSparklineChild = initDiv(divSparklineName);

        var rSmall = Raphael(divSparklineChild, 276, 32);
        rSmall.g.barchart(0, 0, 276, 32, sparklineData, false, {stacked: 1, vgutter: 0, colors: LINE_COLOURS_XY});
        rSmall.path({opacity: .3}, "M" + [0, 32, "l", 276, 0]);

        var divMainName = "mainCanvas";
        var divMainChild = initDiv(divMainName);
        var calendarHeight = 80 + 13 * activityCalendarYears.length;
        var mainHeight = 310 + calendarHeight;

        var r = Raphael(divMainChild, 300, mainHeight);

        // intialise tooltips
        var tooltipSet = r.set();
        for (var l = 0; l < seriesLabels.length; l++) {
            tooltipSet.push(r.text(100, 30 + l * 15, "").attr({fill: getClr(l), "font-weight": 800}));
        }
        tooltipSet.hide();

        var txt = {"text-anchor": "start", "font-size": 11, fill: "#666"};
        r.text(10, 10, "52 week commits volume").attr(txt);
        r.text(10, 110, "Commits by day").attr(txt);
        r.text(10, 200, "Commits by hour").attr(txt);
        r.text(10, 290, "Commit calendar").attr(txt);

        var hin = function() {
            for (var i = 0, ii = this.bars.length; i < ii; i++) {
                tooltipSet[i].attr({text: seriesLabels[i] + ": " + this.bars[i].value});
            }
            tooltipSet.show();
            this.popup = r.g.popupit(this.bars[0].x, getMinY(this.bars), tooltipSet, 2).insertBefore(this).attr({fill: "#fff", stroke: "#666"});
            tooltipSet.insertBefore(this);
        };

        var hin_below = function() {
            for (var i = 0, ii = this.bars.length; i < ii; i++) {
                tooltipSet[i].attr({text:  seriesLabels[i] + ": " + this.bars[i].value});
            }
            tooltipSet.show();
            this.popup = r.g.popupit(this.bars[0].x, getMaxY(this.bars), tooltipSet, 0).insertBefore(this).attr({fill: "#fff", stroke: "#666"});
            tooltipSet.insertBefore(this);
        };

        var hout = function() {
            if (this.popup) {
                this.popup.remove();
                tooltipSet.hide();
                delete this.popup;
            }
        };


        r.g.barchart(0, 20, 276, 70, sparklineData, false, {stacked: 1, vgutter: 0, colors: LINE_COLOURS_XY});
        r.path({opacity: .3}, "M" + [0, 90, "l", 276, 0]);

        r.g.barchart(20, 110, 235, 70, commitsByDay, false, {stacked: 1, vgutter: 15, colors: LINE_COLOURS_XY, "font-size": 11, fill: "#666"}).label(dayLabels).hoverColumn(hin, hout);

        r.g.barchart(25, 200, 230, 70, commitsByHour, false, {stacked: 1, vgutter: 15, colors: LINE_COLOURS_XY, "font-size": 11, fill: "#666"}).label(hourLabels).hoverColumn(hin, hout);


        var data = [],
                xs = [],
                ys = [];

        for (var i = 0, ii = activityCalendarData.length; i < ii; i++) {
            for (var j = 0, jj = activityCalendarData[j].length; j < jj; j++) {
                xs.push(j);
                ys.push(ii - i);
                data.push(activityCalendarData[i][j]);
            }
        }

        r.g.dotchart(0, 300, 270, calendarHeight, xs, ys, data, {symbol: "o", max: 6, heat: true, axis: "0 0 1 1", axisxstep: monthLabels.length - 1, axisystep: activityCalendarYears.length - 1, axisxlabels: monthLabels, axisxtype: " ", axisytype: " ", axisylabels: activityCalendarYears.reverse()}).hover(function () {
            this.tag = this.tag || r.g.tag(this.x, this.y, this.value, 0, this.r + 2).insertBefore(this);
            this.tag.show();
        }, function () {
            this.tag && this.tag.hide();
        }).axis.attr({"font-size": 10, fill: "#666"});

        finaliseDiv(divMainName);
        finaliseDiv(divSparklineName);
    }

    AJS.FECRU.RAPHAELCHARTS.commitSparkline = function(divName, width, height, queryParams, contextPath) {

        AJS.$.getJSON(contextPath + '/fe/commitSparkline.do', queryParams,
                function(_52) {
                    var r = Raphael(divName, width, height);

                    r.g.barchart(0, 10, width, height - 10, _52.data, false, {stacked: 1, vgutter: 0, colors: LINE_COLOURS_XY});
                });

    };

    AJS.FECRU.RAPHAELCHARTS.sidebarCharts = function(data, url) {
        drawLocCharts(data.locDataX, data.locDataY, data.seriesLabelsShort, data.seriesLabelsLong, url);
        drawCommitCharts(data.seriesLabelsShort, data.sparklineLabels, data.sparklineData, data.commitsByDay, data.commitsByHour, data.activityCalendarYears, data.activityCalendarData);
    };

    AJS.FECRU.RAPHAELCHARTS.sidebarChartsAjax = function(queryParams, contextPath, url) {

        AJS.$.getJSON(contextPath + '/fe/sidebarChartsJson.do', queryParams,
                function(data) {
                    AJS.FECRU.RAPHAELCHARTS.sidebarCharts(data, url);
                });
    };


})();

;
/* END /2static/script/fecru/raphaelCharts.js */
/* START /script/common/global.js */
//crucible global.js
var $dropdown	= 0;
var dropdownTimer = 0;

function openDropDown(id) {
    clearTimeout(dropdownTimer);

    // Close the old menu if it's not the same menu as is already open
    if ($dropdown && (id instanceof AJS.$ ? ($dropdown.get(0) !== id.get(0)) : ($dropdown.attr("id") !== id))) {
        $dropdown.fadeOut(100);
    }

    // get new menu and show it
    var $menu = (id instanceof AJS.$) ? id : AJS.$("#" + id);
    $dropdown = $menu.show();
}

function closeDropDown() {
    if ($dropdown && $dropdown.length === 1) {
        dropdownTimer = setTimeout(function(){
            $dropdown.fadeOut(100);
        }, 200);
    }
}

function simpleSwap(toHide, toShow) {
    if (typeof(toHide) == 'object') {
        AJS.$(toHide).hide();
    } else {
        AJS.$("#"+toHide).hide();
    }
    if (typeof(toShow) == 'object') {
        AJS.$(toShow).show();
    } else {
        AJS.$("#"+toShow).show();
    }
}
function expandAll(ids, prefix) {
    toggleAll(ids, true, false, prefix);
}
function collapseAll(ids, prefix) {
    toggleAll(ids, false, true, prefix);
}
function expandSelected(allIds, selectedIds, prefix) {
    //first collapse all
    toggleAll(allIds, false, true, prefix);
    toggleAll(selectedIds, true, false, prefix);
}
function toggleType(ids, prefix) {
    toggleAll(ids, false, false, prefix);
}
function toggleBasic(nodeName) {
    toggleNodeAndImage(nodeName, false, false);
}
function collapseBasic(nodeName) {
    toggleNodeAndImage(nodeName, false, true);
}
function expandBasic(nodeName) {
    toggleNodeAndImage(nodeName, true, false);
}
function toggleAll(ids, forceOpen, forceClose, prefix) {
    prefix = prefix || '';
    for (var i = 0; i < ids.length; i++) {
        var theNode = prefix + ids[i];
        toggleNodeAndImage(theNode, forceOpen, forceClose);
    }
    return false;
}

/**
 * jQuery uses [,] and : as special characters for selectors. These characters are still valid in HTML ids.
 * Use this method to return a sanitized version of the id for use in jQuery selectors.
 * @param id id to sanitize
 */
function sanitizeId(id) {
    return id.replace(/:/g,"\\:").replace(/\./g,"\\.");
}

function toggleNodeAndImage(nodeName, forceOpen, forceClose) {
    if (!nodeName) {
        return;
    }
    nodeName = sanitizeId(nodeName);
    var $node = AJS.$("#"+nodeName);
    if ($node.length === 0) {
        return;
    }

    var img = AJS.$("#"+nodeName+'img');
    if (img.length === 1) {
        var swapImage = true;
    }

    // Don't use is(":hidden") here, because we need to force things closed even if their parents are closed
    var shouldOpen = $node.css("display") === 'none';
    shouldOpen = (!forceClose) && (forceOpen || shouldOpen);
    if (shouldOpen) {
        $node.show();
        if (swapImage) {
            img.attr("src", fishEyePageContext + '/' + fishEyeSTATICDIR + '/images/arrow_open.gif' );
        }
    } else {
        $node.hide();
        if (swapImage) {
            img.attr("src", fishEyePageContext + '/' + fishEyeSTATICDIR + '/images/arrow_closed.gif' );
        }
    }
}

function rollover(obj) {
    if (obj.tagName == 'IMG') {
        var imgsrc = obj.src.replace(/\.gif$/, '');
        obj.src = imgsrc + '_over.gif';
    }
    return false;
}

function rollout(obj) {
    if (obj.tagName == 'IMG') {
        obj.src = obj.src.replace(/\_over\.gif$/, '.gif');
    }
    return false;
}

function toggleOverflow(handle, element) {
    var $el = AJS.$(element);
    var $hd = AJS.$(handle);
    if ($el.css("overflow") == 'hidden' || $el.css("overflow") == '') {
        $el.css({'overflow':'visible', 'height':'auto'});
        $hd.attr("src", $hd.attr("src").replace(/expand\.gif$/, 'collapse.gif') );
    } else {
        $el.css({'overflow':'hidden', 'height':'1.3em'});
        $hd.attr( "src", $hd.attr("src").replace(/collapse\.gif$/, 'expand.gif') );
    }
}


function submitDefaultForm(command) {
    document.defaultForm.command.value = command;
    document.defaultForm.submit();
}

function toggleWording(handle) {
    var $handleEl = AJS.$(handle);
    var handleText = $handleEl.html().substring(0, 4);
    switch (handleText) {
        case 'Show':
            $handleEl.html($handleEl.html().replace(/^Show/, 'Hide'));
            return true;
        case 'Hide':
            $handleEl.html($handleEl.html().replace(/^Hide/, 'Show'));
            return true;
        case 'Expa':
            $handleEl.html($handleEl.html().replace(/^Expand/, 'Collapse'));
            return true;
        case 'Coll':
            $handleEl.html($handleEl.html().replace(/^Collapse/, 'Expand'));
            return true;
        case 'More':
            $handleEl.html($handleEl.html().replace(/^More/, 'Less'));
            return true;
        case 'Less':
            $handleEl.html($handleEl.html().replace(/^Less/, 'More'));
            return true;
    }
    return false;
}

function show(toShow, bool) {
    AJS.$(toShow).toggle(bool);
}

function submitCruSearch(inputEl) {
    window.location = fishEyePageContext + "/cru/search?query=" + encodeURIComponent(inputEl.value);
}

function searchReviews() {
    var searchVal = document.forms.searchForm["search.text"].value;
    if (searchVal) {
        window.location = fishEyePageContext + "/cru/search?query=" + encodeURIComponent(searchVal);
    } else {
        window.location = fishEyePageContext + "/cru/search";
    }
}
function searchComments() {
    var searchVal = document.forms.searchForm["query"].value;
    if (searchVal) {
        window.location = fishEyePageContext + "/cru/commentSearch?search.text=" + encodeURIComponent(searchVal);
    } else {
        window.location = fishEyePageContext + "/cru/commentSearch";
    }
}

var ovAnk = false; //tracks whether the mouse is over a "real" anchor
function toggleSensitively(id) {
    if (!ovAnk) {
        toggleBasic(id);
    }
}
;
/* END /script/common/global.js */
/* START /2static/script/lib/ajs/contentnamesearch.js */
if(!(/jwebunit/).test(navigator.userAgent.toLowerCase())){
    AJS.FECRU.UI.setupQuicksearch = function () {
            AJS.$("#quick-search-form").submit(function () {
                return AJS.$.trim(AJS.$("#quick-search-input").val()).length > 0;
            });
            AJS.FECRU.UI.augmentFormFields("#quick-search-input");

            var attr = {
                cache_size: 30,
                max_length: 1,
                effect: "appear" // TODO: add "fade" effect
            };
            var dd,
                cache = {},
                cache_stack = [],
                timer;

            if (typeof path == "function") {
	            var getPath = path;
            } else {
            	var getPath = function () {
	                return path;
	            };
            }

        function regexEscape(text) {
            if (!arguments.callee.sRE) {
                var specials = [
                    '/', '.', '*', '+', '?', '|',
                    '(', ')', '[', ']', '{', '}', '\\'
                    ];
                arguments.callee.sRE = new RegExp(
                    '(\\' + specials.join('|\\') + ')', 'g'
                    );
            }
            return text.replace(arguments.callee.sRE, '\\$1');
        }

        var hider = function (list) {
             AJS.$("a span", list).each(function () {
                var $a = AJS.$(this),
                    elpss = AJS("var", "&#8230;"),
                    elwidth = elpss[0].offsetWidth,
                    width = this.parentNode.parentNode.parentNode.parentNode.offsetWidth,
                    isLong = false,
                    rightPadding = 20; // add some padding so the ellipsis doesn't run over the edge of the box

                    // get the hidden space name property from the span
                    var spaceName = AJS.dropDown.getAdditionalPropertyValue($a, "spaceName");
                    if (spaceName != null) {
                        spaceName = "(" + spaceName + ") ";
                    } else {
                        spaceName = "";
                    }
                    AJS.dropDown.removeAllAdditionalProperties($a);

                this.realhtml = this.realhtml || $a.html();
                $a.html("<em>" + this.realhtml + "</em>");
                $a.append(elpss);
                //$a.attr("title", spaceName + this.realhtml.replace(/<\/?strong>/gi, ""));
                this.elpss = elpss;

                AJS.$("em", $a).each(function () {
                    var $label = AJS.$(this);

                    $label.show();
                    if (this.offsetLeft + this.offsetWidth + elwidth > width - rightPadding) {

                        var childNodes = this.childNodes;
                        var success = false;

                        for (var j = childNodes.length - 1; j >= 0; j--) {
                            var childNode = childNodes[j];
                            var truncatedChars = 1;

                            var valueAttr = (childNode.nodeType == 3) ? "nodeValue" : "innerHTML";
                            var nodeText = childNode[valueAttr];

                            do {
                                if (truncatedChars <= nodeText.length) {
                                    childNode[valueAttr] = nodeText.substr(0, nodeText.length - truncatedChars++);
                                } else { // if we cannot fit even one character of the next word, then try truncating the node just previous to this
                                    break;
                                }
                            } while (this.offsetLeft + this.offsetWidth + elwidth > width - rightPadding);

                            if (truncatedChars <= nodeText.length) {
                                // we've managed truncate part of the word and fit it in
                                success = true;
                                break;
                            }
                        }

                        if (success) {
                            isLong = true;
                        } else {
                            $label.hide();
                        }
                    }
                });
                elpss[isLong ? "show" : "hide"]();
            });
        };

        var searchBox = AJS.$("#quick-search-input");
        if (AJS.$.browser.safari && searchBox.length === 1) {
        //    searchBox[0].type = "search";
            searchBox[0].setAttribute("results", 10);
            searchBox[0].setAttribute("placeholder", "search");
        }
        var jsonparser = function (json, resultStatus) {
            var hasErrors = json.statusMessage ? true : false; // right now, we are overloading the existance of a status message to imply an error
            var matches = hasErrors ? [[{html: json.statusMessage, className: "error"}]] : json.matches;

            if (!hasErrors) {
                var query = json.query;
                if (!cache[query + AJS.FECRU.quickSearchUrl] && resultStatus == "success") {
                    cache[query + AJS.FECRU.quickSearchUrl] = json;
                    cache_stack.push(query + AJS.FECRU.quickSearchUrl);
                    if (cache_stack.length > attr.cache_size) {
                        delete cache[cache_stack.shift() + AJS.FECRU.quickSearchUrl];
                    }
                }
            }

            // do not update drop down for earlier requests. We are only interested in displaying the results for the most current search
            if (json.query != searchBox.val() && json.query.charAt(json.query.length-1) != "*") {
                return;
            }

            var old_dd = dd;
            dd = AJS.dropDown(matches, {selectionHandler: function (e, selected) {
                if (selected) {
                    if (selected.get(0).nodeName.toLowerCase() !== "a") {
                        var enclosingLinkHref = selected.find("a").attr("href");
                        if (enclosingLinkHref) {
                            window.location = enclosingLinkHref;
                        }
                    } else {
                        var selectedHref = selected.attr("href");
                        if (selectedHref) {
                            window.location = selectedHref;
                        }
                    }
                }
            }})[0];
            dd.$.attr("id", "quick-nav-drop-down");
            dd.$.insertAfter(searchBox);

            dd.onhide = function (causer) {
                if (causer == "escape") {
                    searchBox.focus();
                }
            };
            if (json.queryTokens) {
                var spans = AJS.$("span", dd.$);
                for (var i = 0, ii = spans.length - 1; i < ii; i++) {
                    (function () {
                        var $this = AJS.$(this),
                                html = $this.html();
                        // highlight matching tokens
                        var match = "(";
                        for (var j = 0, jj = json.queryTokens.length - 1; j <= jj; j++) {
                            match += regexEscape(json.queryTokens[j]);
                            if (j < jj) {
                                match += "|";
                            }
                        }
                        match += ")";
                        html = html.replace(new RegExp(match, "gi"), "<strong>$1</strong>");
                        $this.html(html);
                    }).call(spans[i]);
                }
            }
            hider(dd.$);
            if(typeof onShow == "function") {
                onShow.apply(dd);
            }
            dd.hider = function () {
                hider(dd.$);
            };
            AJS.onTextResize(dd.hider);
            if (old_dd) {
                dd.show();
                dd.method = attr.effect;
                AJS.unbindTextResize(old_dd.hider);
                old_dd.$.remove();
            } else {
                dd.show(attr.effect);
            }
            AJS.$("#busyQnav").hide();
        };
        searchBox.data("oldval", searchBox.val());
        var keyUpHandler = function () {
            var val = searchBox.val();

            var oldVal = searchBox.data("oldval");

            if (val != oldVal) {
                searchBox.data("oldval", val);
                if (!searchBox.hasClass("placeholded")) {
                    clearTimeout(timer);

                    if ((/[\S]{2,}/).test(val)) {
                        if (cache[val + AJS.FECRU.quickSearchUrl]) {
                            jsonparser(cache[val + AJS.FECRU.quickSearchUrl]);
                        } else {
                            var url = AJS.FECRU.quickSearchUrl + "?q=" + AJS.escape(val) + "&type=ajax&searchAllDirs=false";
                            timer = setTimeout(function () { // delay sending a request to give the user a chance to finish typing their search term(s)'
                                AJS.$("#busyQnav").show();
                                return AJS.$.ajax({
                                    type: "GET",
                                    url: url,
                                    data: null,
                                    success: jsonparser,
                                    dataType: "json",
                                    timeout: 20000,
                                    error: function ( xml, status, e ) { // ajax error handler
                                        if (status == "timeout") {
                                            jsonparser({statusMessage: "Query has timed out", query: val}, status);
                                        }
                                    }
                                });
                            }, 600);
                        }
                    } else {
                        AJS.$("#busyQnav").hide();
                        dd && dd.hide();
                    }
                }
            }
        };
        searchBox.keyup(keyUpHandler);
    };
};
/* END /2static/script/lib/ajs/contentnamesearch.js */
/* START /2static/script/lib/ajs/master.js */
// ============================
// = Search field placeholder =
// ============================
AJS.toInit(function ($) {
    var $search = $("#quick-search-query");
    if (!$search.length) {
        return;
    }

    var search = $search.get(0);
    search.placeholder = AJS.params.quickSearchPlaceholder;
    search.placeholded = true;
    search.value = search.placeholder;

    if (!$.browser.safari) {

        $(search).addClass("placeholded");

        $("#quick-search-query").focus(function () {
            if (this.placeholded) {
                this.placeholded = false;
                this.value = "";
                $(this).removeClass("placeholded");
            }
        });

        $("#quick-search-query").blur(function () {
            if (this.placeholder && (/^\s*$/).test(this.value)) {
                this.value = this.placeholder;
                this.placeholded = true;
                $(this).addClass("placeholded");
            }
        });
    } else {
        search.type = "search";
        search.setAttribute("results", 10);
        search.setAttribute("placeholder", AJS.params.quickSearchPlaceholder);
        search.value = "";
    }
});
;
/* END /2static/script/lib/ajs/master.js */
/* START /2static/script/fe/fisheye-ui.js */
if (!AJS.FE) {
    AJS.FE = {};
}

(function() {

    /**
     * Bind a callback that fires when resizing is completed on matching elements.
     *
     * A resize is considered complete after completionDelay ms have passed since
     * the last resize event.
     *
     * This prevents firing the resize event handler multiple times before the resize
     * is actually complete.
     */

    var hasSetupPageFillHeight = false;
    AJS.FE.setupPageFillHeight = function ($changeHeight) {
        if (hasSetupPageFillHeight) {
            return;
        }
        var offset = 34;
        if ($changeHeight) {
            offset = $changeHeight.offset().top;
        }
        $changeHeight = $changeHeight || AJS.$("#atlas");
        hasSetupPageFillHeight = true;
        var onResize = function() {
            var windowHeight = AJS.$(window).height();
            var footerHeight = AJS.$("#footer").outerHeight();
            $changeHeight.css('height', windowHeight - footerHeight - offset);
        };
        onResize();
        AJS.FECRU.UI.setCompletedResizeTimeout(window, onResize);
    };

    var hasSetupSearchPageFillHeight = false;
    AJS.FE.setupSearchPageFillHeight = function () {
        if (hasSetupSearchPageFillHeight) {
            return;
        }
        hasSetupSearchPageFillHeight = true;

        var onResize = function() {
            var view = AJS.$(window).height();
            var above = AJS.$("#content-search").offset().top + AJS.$(".search-control").outerHeight() + AJS.$(".search-console").outerHeight();
            var below = AJS.$("#footer").outerHeight();

            var diff = AJS.$("#search-content").outerHeight() - parseFloat(AJS.$("#search-content").height());//padding, border, etcetera on content
            diff = parseInt(diff + 0.8, 10);//round up

            AJS.$("#search-content").height(
                view - below - above - diff
            );
        };
        onResize();
        AJS.FECRU.UI.setCompletedResizeTimeout(window, onResize);
    };

    /**
     * setup column height maintenance for the page -- does the initial setup and binds a resize handler to maintain it
     */
    var hasSetupColumnFillHeight = false;
    var setupColumnFillHeight = function () {
        if (hasSetupColumnFillHeight) {
            return;
        }
        hasSetupColumnFillHeight = true;

        var onResize = function() {
            var head = AJS.$("#content").offset().top;
            var foot = AJS.$('#footer').outerHeight();
            var windowHeight = AJS.$(window).height();
            var contentHeight = windowHeight - head - foot - 1.3; //window - masthead - roundingerrorhack
            AJS.$("#content").css({
                paddingBottom: 0,
                height: contentHeight
            });

            AJS.$("#content-resizable,#content-shield").css({
                height: contentHeight
            });

            AJS.$("#content-navigation-panel").css({
                height: contentHeight - 2
            });

            AJS.$("#content-navigation").css({
                width: "100%"
            });
            AJS.$("#content-column").css({
                height: contentHeight,
                marginRight: "auto",
                marginLeft: "auto"
            });

            if(AJS.$.browser.msie){
                if (AJS.$.browser.version < 8) {
                    AJS.$("#content-column-panel, #file-view-source-content").css({
                        height: Math.max(0, windowHeight - AJS.$("#content-column-panel").offset().top - foot - 4)
                    });
                } else {
                    AJS.$("#content-column-panel, #file-view-source-content").css({
                        height: Math.max(0, windowHeight - AJS.$("#content-column-panel").offset().top - foot - 2)
                    });
                    if (AJS.$("#section-iframe").length === 1) {
                        AJS.$("#content-column-panel").css({ top: "0" });
                    }
                }
            } else {
                AJS.$("#content-column-panel, #file-view-source-content").css({
                    height: Math.max(0, windowHeight - AJS.$("#content-column-panel").offset().top - foot - 3.3)
                });
            }
        };
        onResize();

        AJS.FECRU.UI.setCompletedResizeTimeout(window, onResize);
    };

    var columnResize = function (min) {
        AJS.$("#content-resizable").resizable({
            start: function(event, ui) {
                collapseReact(event, ui);
            },
            ghost: true,
            handles: "e",
            maxWidth: 600,
            minWidth: min || 0
        });
        var collapseReact = function (){
            if (AJS.$(".collapsed-sidebar #content-resizable").css("width") !== undefined) {
                AJS.log(AJS.$(".collapsed-sidebar #content-resizable #content-navigation").attr("style"));
            }
        };
        AJS.$("#content-resizable .ui-resizable-handle, #content-shield").live("mousedown",function(){
            AJS.$("#content-shield").show();
        }).live("mouseup",function(){
            AJS.$("#content-shield").hide();
        });
    };

    var hasSetupColumnFoot = false;
    var setupColumnFoot = function () {
        if (hasSetupColumnFoot) {
            return;
        }
        hasSetupColumnFoot = true;
        var hasContent = AJS.$("#panel-foot").children("div.holder").children().length > 0;

        if (hasContent) {
            var onResize = function() {
                var height = AJS.$("#content-column-panel").height();
                var panel = Math.floor(height * 0.55);
                var foot = height - panel;

                AJS.$("#content-column-panel").css({
                   height: panel - 6
                });

                AJS.$("#panel-foot").css({
                   height: foot,
                   display: "block"
                });
            };
            onResize();
            AJS.FECRU.UI.setCompletedResizeTimeout(window, onResize);
        }
    };

    var setupWatch = function() {
        var $watchform = AJS.$("#watchform");
        var eventName = "click.watchform";
        if ($watchform.length > 0) {
            var $input = $watchform.children("input");
            AJS.$(".watch-on", $watchform).unbind(eventName).bind(eventName, function(evnt) {
                evnt.preventDefault();
                $input.val('off');
                $watchform.submit();
            });
            AJS.$(".watch-off", $watchform).unbind(eventName).bind(eventName, function(evnt) {
                evnt.preventDefault();
                $input.val('on');
                $watchform.submit();
            });
        }
    };

    var reloadFilePaneAndHeader = function(href, extractionFn) {
        var origUrl = AJS.$("#origUrl").val();
        var $spinner = AJS.$("#file-table-spinner");
        $spinner.show();
        AJS.FECRU.AJAX.ajaxDo(fishEyePageContext + "/json/fe/loadFilePane.do", {href: href, origUrl: origUrl}, function(resp) {
            if (resp.worked) {
                var replacement = AJS.$.clean([resp.fileTable], document);
                AJS.$("#browse-table").replaceWith(replacement);
                AJS.$("#cone").replaceWith(resp.coneDiv);
                AJS.$("#dirlist-taskbar-bar").replaceWith(resp.taskBarBar);
                AJS.$(".content-fixed").replaceWith(resp.contentFixed);
                AJS.FE.resetupTable("browse", null, extractionFn);
                setupWatch();
                AJS.FECRU.RSS.setupRSSDialog();
                AJS.FECRU.UI.setupQuicksearch();
            }
            $spinner.hide();
        }, false);
    };

    /**
     * Find a link in the directory tree by its href
     */
    var $findLink = function(href) {
        return AJS.$("#navigation-tree").find("a.pathLink[href='" + href + "']");
    };

    AJS.FE.browseDirectoryPathLinkFunction = function(event) {
        var $node = AJS.$(event.target);
        var href = $node.attr("href");
        var toggled = function () { };
        var self = AJS.FE.browseDirectoryPathLinkFunction;
        if ($node.hasClass("browse-directory")) {
            // find the node in the tree with the same href as us
            var $selectedLink = $findLink(href);
            AJS.FECRU.BROWSE.selectLink($selectedLink, toggled, self);
        } else {
            AJS.FECRU.BROWSE.selectLink($node, toggled, self);
        }
        reloadFilePaneAndHeader(href, AJS.FE.extractRevisionDetailsSortKeys);
        return false;
    };

    AJS.FE.setupTable = function(prefix, rowClickFn, extractionFn) {
        AJS.FE.resetupTable(prefix, extractionFn);
        AJS.FECRU.UI.tableRowClick(prefix, rowClickFn);

    };
    AJS.FE.resetupTable = function(prefix, extractionFn) {
        columnResize();
        setupColumnFillHeight();
        setupColumnFoot();
        AJS.FECRU.UI.tableSort(prefix, extractionFn);
    };



    AJS.FE.toggleTabs = function () {
        AJS.$(".tearout-tabs li").live("click", function () {
            var active = AJS.$(this).hasClass("tearout-active") ? true : null;
            var tab = AJS.$(this).attr("class").split("-")[1];
            var panel = "#panel-" + tab;

            AJS.$(".tearout-tabs li").each(function () {
                AJS.$(this).removeClass("tearout-active");
                AJS.$(this).children("a").unbind();
            });

            AJS.$(".panel-tearout","#content-navigation").each(function () {
                AJS.$(this).addClass("hidden");
            });
            // preference will be null if we are ignoring preferences
            var preference = AJS.$(this).children("input").val();
            if (active) {
                AJS.$('#content').addClass('collapsed-sidebar');
                // hiding everything
                if (preference) {
                    AJS.FECRU.PREFS.setPreference("shp", "N");
                }
            }
            else{
                AJS.$(this).addClass("tearout-active");
                AJS.$('#content').removeClass('collapsed-sidebar');
                AJS.$(panel).removeClass("hidden");
                if (preference) {
                    AJS.FECRU.PREFS.setPreferences({slp: preference, shp: "Y"});
                }
            }
            // resize the pane when in changeset view
            if (AJS.$('#section-changeset-view').length > 0) {
                AJS.$(window).resize();
            }
        });
    };

    AJS.FE.toggleWatch = function(val) {

    };

    var hasSetupPanes = false;

    AJS.FE.setupPanes = function() {
        if (!hasSetupPanes) {
            columnResize(100);
            setupColumnFillHeight();
            setupColumnFoot();
            hasSetupPanes = true;
        }
    };

    AJS.FE.streamMoreFocus = function () {
        AJS.$("#stream a.more").live("click", function(){
            AJS.$(this).closest(".stream").addClass("stream-focus");
            AJS.$("body").one("click",function(){
                AJS.$("#stream").find(".stream").removeClass("stream-focus");
            });
        });
    };

    AJS.FECRU.UI.refreshSearch = function(url) {
        var repoQuicksearchURL = AJS.FECRU.quickSearchUrl;
        AJS.FECRU.quickSearchUrl = url;
        var searchBox = AJS.$("#quick-search-input");
        searchBox.removeData("oldval");
        searchBox.trigger("keyup");
        AJS.FECRU.quickSearchUrl = repoQuicksearchURL;
        return false;
    };

    AJS.$(document).ready(function () {
        // Give a warning if firebug is running
        AJS.FECRU.UI.warnAboutFirebug(function() {
            AJS.$(window).resize();
        });

        //setup the watch links
        setupWatch();

        if (!AJS.$.support.opacity) {
            AJS.FE.streamMoreFocus();
        }
    });
})();
;
/* END /2static/script/fe/fisheye-ui.js */
/* START /2static/script/fe/fisheye-changeset.js */
if (!AJS.FE.CHANGESET) {
    AJS.FE.CHANGESET = {};
}
(function() {
    /////////////////////
    // private functions
    /////////////////////

    /**
     *from http://www.quirksmode.org/js/cookies.html
     */
    function readCookie(name) {
        var nameEQ = name + "=";
        var ca = document.cookie.split(';');
        for (var i = 0; i < ca.length; i++) {
            var c = ca[i];
            while (c.charAt(0) == ' ') {
                c = c.substring(1, c.length);
            }
            if (c.indexOf(nameEQ) == 0) {
                return c.substring(nameEQ.length, c.length);
            }
        }
        return null;
    }

    /*
     * usefull for appending to urls (esp ajax urls) when the content of the
     * url depends on the user's cookie preferences
     */
    function getCookiePrefToken() {
        try {
            var val = readCookie("crucibleprefs1");
            if (val) {
                val = val.replace(/^D=[0-9]+/, "");
                return encodeURIComponent(val);
            }
        } catch(ex) {
            AJS.log("error getting cookie preference:" +
                            ex.message +
                            "\nin:" + ex.fileName +
                            "\nat:" + ex.lineNumber);
        }
        return "unknown";
    }

    function csDisplaySpinner(revId) {
        var divId = csGetCsDivId(revId);
        AJS.$("#" + divId).html("<div class='loading'><span class='loading'>Loading content</span></div>");
        return divId;
    }

    function csGetCsDivId(revId) {
        return 'showfilesDiv' + revId;
    }

    function scrollToSelectedItem() {
        var index = AJS.$("#selectedIndex").val();
        AJS.$("#content-column-panel").scrollTo(AJS.$("#item" + index));
    }

    /////////////////////
    // exported functions
    /////////////////////

    AJS.FE.CHANGESET.loadDiff = function (revId, url, onCompleteFunc) {
        var $revDiv = AJS.$("#" + revId);
        if ($revDiv.hasClass("unloaded-diff")) {
            var divId = csDisplaySpinner(revId);
            $revDiv.removeClass("unloaded-diff");
            AJS.FECRU.AJAX.ajaxDo(url, {revid: revId, rndtmp: getCookiePrefToken()}, function(resp) {
                if (resp.worked) {
                    // use 'clean' to avoid the regex that jQuery uses to distinguish HTML from ids
                    var replacement = AJS.$.clean([resp.payload], document);
                    AJS.$("#" + divId).html(replacement);
                    scrollToSelectedItem();
                    if (onCompleteFunc) {
                        onCompleteFunc();
                    }
                }
            }, false);
        } else {
            if (onCompleteFunc) {
                onCompleteFunc();
            }
        }
    };

    /**
     * loads a diff asynchronously.
     * @param i
     * @param url
     * @param revIds
     */
    AJS.FE.CHANGESET.loadDiffAsync = function (i, url, revIds) {
        var revIdLength = revIds.length;
        var onComp = function(origRequest) {
            // do the chaining.
            AJS.FE.CHANGESET.loadDiffAsync(i + 1, url, revIds);
        };
        var MAX_TO_LOAD = 100; //so that if viewing changeset of a branch operation then the page wont overload, set a hard limit.
        if ((i < revIdLength) && (i < MAX_TO_LOAD)) {
            var revId = revIds[i];
            AJS.FE.CHANGESET.loadDiff(revId, url, onComp);
        }
    };

    AJS.FE.CHANGESET.setupChangesetDiffLoading = function() {
        AJS.$("a.unloaded-diff").live("click", function (e) {
            e.preventDefault();
            var $node = AJS.$(this);
            var id = $node.closest(".diffPaneChangset").attr("id");
            var baseClUrl = AJS.$("#baseJSONClUrl").val();
            AJS.FE.CHANGESET.loadDiff(id, baseClUrl, null);
        });
    };

    AJS.FE.CHANGESET.loadDiffForDt = function($dt) {
        var $revIdDiv = $dt.next().children("div.unloaded-diff");
        // if it is already loaded we won't find that div
        if ($revIdDiv) {
            var baseClUrl = AJS.$("#baseJSONClUrl").val();
            AJS.FE.CHANGESET.loadDiff($revIdDiv.attr("id"), baseClUrl, null);
        }
    };

    var isInitialised = false;

    /**
     * Set a property to indicate that the live events etc for the changeset page have been set up
     * @param newValue
     */
    AJS.FE.CHANGESET.initialised = function(newValue) {
        if (newValue === undefined) {
            return isInitialised;
        }
        isInitialised = newValue;
        return newValue;
    };

    var reloadChangesetPage = function(page, index) {
        if (page < 1 || page > getNoOfPages()) {
            return;
        }
        var csid = AJS.$("#csid").val();
        var currentPage = getCurrentPageNo();
        if (currentPage == page) {
            AJS.$("#selectedIndex").val(index);
            scrollToSelectedItem();
        } else {
            var $spinner = AJS.$("#file-table-spinner");
            $spinner.show();
            var params = {href: AJS.$("#baseClUrl").val(), csid: csid, pageNum: page, index: index};
            
            AJS.FECRU.AJAX.ajaxDo(fishEyePageContext + "/json/fe/loadChangesetPage.do", params, function(resp) {
                if (resp.worked) {
                    var replacement = AJS.$.clean([resp.changesetPage], document);
                    AJS.$("#panel-target").replaceWith(replacement);
                    setCurrentPageNo(parseInt(resp.currentPageNo, 10));
                    AJS.$("#selectedIndex").val(resp.selectedIndex);
                    scrollToSelectedItem();
                }
                $spinner.hide();
            }, false);
        }
    };

    function getCurrentPageNo() {
        return parseInt(AJS.$("#currentPageNo").val(), 10);
    }

    function getNoOfPages() {
        return parseInt(AJS.$("#noOfPages").val(), 10);
    }

    function setNavClass(selector, enabled) {
        var links = AJS.$("span.cs-page a").filter(selector);
        if (enabled) {
            links.removeClass("disabled");
        } else {
            links.addClass("disabled");
        }
    }

    function setCurrentPageNo(newPageNo) {
        AJS.$("#currentPageNo").val(newPageNo);
        AJS.$("#currentPageNoDisplay").text(newPageNo);
        setNavClass(".pagination-back, .pagination-first", newPageNo != 1);
        setNavClass(".pagination-next, .pagination-last", newPageNo != getNoOfPages());
    }

    function getAttrValue(node, name) {
        return node.children("input[name='" + name + "']").val();
    }

    AJS.FE.CHANGESET.changesetFileLinkFn = function(event) {
        document.location.hash = getAttrValue(AJS.$(event.target), "anchor");
        var $node = AJS.$(event.target);
        reloadChangesetPage(getAttrValue($node, "page"), getAttrValue($node, "index"));
        return false;
    };

    AJS.FE.CHANGESET.changesetPathLinkFn = AJS.FE.CHANGESET.changesetFileLinkFn;

    AJS.FE.CHANGESET.changesetSubTreeArgs = function($node) {
        return {csid: AJS.$("#csid").val()}
    };

    AJS.FE.CHANGESET.setupPaging = function () {
        // We don't put ":not(.disabled)" in the selector otherwise we can't preventDefault for disabled links
        // (as these handlers won't get triggered)
        var handler = function (event, method) {
            event.preventDefault();
            if (!AJS.$(this).is(".disabled")) {
                var args = arguments.length > 2 ? AJS.$.makeArray(arguments).slice(2) : [];
                method && method.apply(this, args);
            }
        };

        AJS.$("span.cs-page a.pagination-back").live("click", function (e) {
            handler.call(this, e, reloadChangesetPage, getCurrentPageNo() - 1, 1);
        });
        AJS.$("span.cs-page a.pagination-next").live("click", function (e) {
            handler.call(this, e, reloadChangesetPage, getCurrentPageNo() + 1, 1);
        });
        AJS.$("span.cs-page a.pagination-first").live("click", function (e) {
            handler.call(this, e, reloadChangesetPage, 1, 1);
        });
        AJS.$("span.cs-page a.pagination-last").live("click", function (e) {
            handler.call(this, e, reloadChangesetPage, getNoOfPages(), 1);
        });
    };


})();
;
/* END /2static/script/fe/fisheye-changeset.js */
/* START /2static/script/fe/fisheye-history.js */
(function() {

    var revisionForRow = function($row) {
        return $row.find("input.history-item").val();
    };

    var rowForRevisionId = function(revisionId) {
        return AJS.$("#rev-id-" + revisionId).closest("tr");
    };

    var showRevisionDiv = function(revisionData) {
        if (!revisionData || !revisionData.detailsPane) {
            showLoadingDiv();
            return;
        }
        var $foot = AJS.$("#panel-foot");
        $foot.html(revisionData.detailsPane);
        $foot.children().show();
        var revision = revisionForRow(rowForRevisionId(revisionData.id));
        if (revision) {
            window.location.hash = "r" + revision;
        }
    };

    var showLoadingDiv = function() {
        AJS.$("#panel-foot").html("<div class='loading'>Loading...</div>");
    };

    // Cache the focused row as it can be too slow to do AJS.$("tr.history-focus");
    var $focusedRow = undefined;
    var setRowFocus = function(revisionData) {
        if ($focusedRow) {
            $focusedRow.removeClass("history-focus");
        }
        $focusedRow = rowForRevisionId(revisionData.id);
        $focusedRow.addClass("history-focus");
    };

    var revisionDataForId = function (revisionId) {
        var arrayIndex = historyTableData.revisionKeyValueIndexMap[revisionId];
        return historyTableData.revisions[arrayIndex];
    };

    var hasSetupDiffCheckboxes = false;
    /**
     * Binds a live event to validate that only two diff checkboxes can be selected at a time.
     *
     * This method will short circuit if it is called more than once (since we don't want to bind the same live event
     * multiple times).
     */
    var setupDiffCheckboxes = function() {
        if (hasSetupDiffCheckboxes) {
            return;
        }
        hasSetupDiffCheckboxes = true;

        var queue = []; // Queue of checkbox ids in the order that they were checked (oldest -> newest)

        AJS.$("#history-table input.history-item").live("click", function(event) {
            var $r1 = AJS.$("#r1");
            var $r2 = AJS.$("#r2");

            var id = this.id;
            var value = this.value;

            var revisionId = historyTableData.revisionToRevisionIdMap[value];
            var revisionData = revisionDataForId(revisionId);
            revisionData.checked = !!this.checked;

            var bumpedCheckboxId = undefined; // The checkbox which has been unchecked

            var queueLength = queue.length;

            // Nothing is checked, push the id onto the back
            if (queueLength == 0) {
                queue.push(id);
                $r1.val(value);
            }
            // Otherwise, we need to bump the last stack value and shuffle down
            else {
                var queuePosition = AJS.$.inArray(id, queue);
                if (queuePosition >= 0) {
                    bumpedCheckboxId = queue.splice(queuePosition, 1); // remove the nth element from the queue
                }
                // Both queue positions are populated
                else if (queueLength == 2) {
                    bumpedCheckboxId = queue.shift(); // remove the 0th element from the queue
                    queue.push(id); // add new element to the queue
                    $r2.val($r1.val());
                    $r1.val(value);
                }
                // Only one checkbox is checked, add it to the end of the queue
                else if (queueLength == 1) {
                    queue.push(id);
                    $r2.val(value);
                }
            }

            // There should only be two checkboxes in the stack at a time
            if (bumpedCheckboxId) {
                var internalRevId = bumpedCheckboxId.replace(/^\D+/, "");
                revisionData = revisionDataForId(internalRevId);
                revisionData.checked = false;
                AJS.$("#"+bumpedCheckboxId).removeAttr("checked");
            }

            event.stopPropagation();
        });
    };

    var setupFocusedRevision = function() {
        var location = document.location.hash;
        var revision, $row;
        if (!location) {
            $row = AJS.$("#history-table").children("tbody").find("tr:first");
            revision = revisionForRow($row);
        } else {
            // remove the '#r' from the anchor
            revision = location.substring(2);
        }
        var revisionId = historyTableData.revisionToRevisionIdMap[revision];
        if (!revisionId) {
            $row = AJS.$("#history-table").children("tbody").find("tr:first");
            revision = revisionForRow($row);
            revisionId = historyTableData.revisionToRevisionIdMap[revision];
        }

        if (revisionId) {
            var revisionData = revisionDataForId(revisionId);
            showRevisionDiv(revisionData);
            setRowFocus(revisionData);
            AJS.$("div.panel-content", "#content").scrollTo(rowForRevisionId(revisionId));
            return revisionId;
        }
        return null;
    };

    var CURRENT_PAGE = 1;
    var REVISIONS_PER_PAGE = 30;

    var historyTableRow = [
        "<tr class='revision-row'>",
            "<td class='history-diff'>",
                "<input type='checkbox' class='history-item' name='diffs'>",
                "<input type='hidden' class='rev-id'>",
            "</td>",
            "<td class='history-revision'>Revision</td>",
            "<td class='history-age'>Date<input type='hidden'></td>",
            "<td class='history-comment'><div class='first-line-only'>&nbsp;</div></td>",
            "<td class='history-lines'>Lines</td>",
            "<td class='history-author'>Author</td>",
            "<td class='history-branch'>Branch</td>",
        "</tr>\n"
        ].join('');

    /**
     * Creates the rows for the history table which is used to display revision data
     */
    var createHistoryTable = function() {
        var $table = AJS.$("#history-table").children("tbody");
        var rows = "";
        var revisionCount = historyTableData.revisions.length;
        for (var i = 0; i < REVISIONS_PER_PAGE && i < revisionCount; i++) {
            rows += historyTableRow;
        }
        $table.html(rows);
        displayFauxTable(true);//reveal the faux table
    };

    /**
     * Copies data from the revisionData object into the table row for display. This method should set all the required
     * HTML goodies, such as ids, names, values and contents.
     * @param $row a jQuery reference to the row to populate
     * @param revisionData an object which contains data to copy into the table row
     */
    var populateRevisionRow = function($row, revisionData) {
        var $cols = $row.children("td");
        var $checkboxCol = $cols.filter("td.history-diff");
        $checkboxCol.children(".history-item")
                        .attr("id", "revision-" + revisionData.id)
                        .attr("value", revisionData.revision)
                        .attr("checked", revisionData.checked);
        $checkboxCol.children(".rev-id")
                        .attr("id", "rev-id-" + revisionData.id)
                        .attr("value", revisionData.revision);
        $cols.filter("td.history-revision").html(revisionData.displayRevision);
        $cols.filter("td.history-age").html(revisionData.prettyDate);
        $cols.filter("td.history-lines").html(revisionData.lines);
        if (revisionData.authorLink) {
            $cols.filter("td.history-author").html(revisionData.authorLink);
        } else {
            $cols.filter("td.history-author").html(revisionData.author);
        }
        if (revisionData.comment) {
            $row.children("td.history-comment").children(".first-line-only").removeClass("loading").html(revisionData.comment);
        } else {
            $row.children("td.history-comment").children(".first-line-only").addClass("loading").html("Loading...");
        }
        $cols.filter("td.history-branch").text(revisionData.branch);
        $row.show();
    };

   var getTotalPageCount = function () {
        return Math.ceil(historyTableData.revisions.length / REVISIONS_PER_PAGE);
    };

    /**
     * Ensures that the navigation buttons (next/previous revisions) are synchronised -- ie, that they are enabled/disabled
     * when required.
     */
    var syncPaginationButtons = function() {
        var $firstLink = AJS.$("#pagination-first");
        var $prevLink = AJS.$("#pagination-back");
        var $nextLink = AJS.$("#pagination-next");
        var $lastLink = AJS.$("#pagination-last");

        var totalPageCount = getTotalPageCount();

        if (totalPageCount <= 1) {
            $firstLink.addClass("disabled");
            $prevLink.addClass("disabled");
            $nextLink.addClass("disabled");
            $lastLink.addClass("disabled");
        } else if (CURRENT_PAGE <= 1) {
            $firstLink.removeClass("disabled");
            $prevLink.removeClass("disabled");
            $nextLink.addClass("disabled");
            $lastLink.addClass("disabled");
        } else if (CURRENT_PAGE >= totalPageCount) {
            $firstLink.addClass("disabled");
            $prevLink.addClass("disabled");
            $nextLink.removeClass("disabled");
            $lastLink.removeClass("disabled");
        } else {
            $firstLink.removeClass("disabled");
            $prevLink.removeClass("disabled");
            $nextLink.removeClass("disabled");
            $lastLink.removeClass("disabled");
        }

        AJS.$("#pagination-text").text(CURRENT_PAGE+"/"+totalPageCount);
    };

    /**
     * Loads revisions into the history table for display. This method will lazily load extra information from the server
     * if it isn't available locally (such as rendered commit messages).
     * @param pageNumber page to render 
     */
    var loadRevisionsPage = function(pageNumber) {
        pageNumber = pageNumber || 1;
        CURRENT_PAGE = pageNumber;

        var totalPageCount = getTotalPageCount();
        if (pageNumber > totalPageCount) {
            pageNumber = totalPageCount;
        }

        var allRevisions = historyTableData.revisions;
        var tableRows = AJS.$("#history-table").find("tr.revision-row");
        var tableRowCount = tableRows.length;
        var startPoint = (pageNumber-1) * REVISIONS_PER_PAGE;
        if (startPoint < 0) {
            startPoint = 0;
        }
        var endPoint = startPoint + REVISIONS_PER_PAGE;

        showLoadingDiv();

        var revIdsToLoad = [];
        for (var c = 0, i = startPoint, revCount = allRevisions.length;
             i < endPoint && c < tableRowCount;
             i++, c++) {
            var $row = AJS.$(tableRows[c]);
            if (i < revCount) {
                var rev = allRevisions[i];
                populateRevisionRow($row, rev);
                if (!rev.loaded) {
                    revIdsToLoad.push(rev.id);
                }
                $row.data("revision-id", rev.id);
            } else {
                $row.hide();
                $row.removeData("revision-id");
            }
        }

        var onDone = function () {
            if (!$focusedRow || $focusedRow.length == 0) {
                setupFocusedRevision();
            }
            showRevisionDetails($focusedRow[0]);
            displayFauxTable();
        };

        syncPaginationButtons();

        loadExtraRevisionData(revIdsToLoad, onDone);
    };

    /**
     * Sends a list of revisions to the server for extra data which is required for displaying complete data for each
     * revision to the user. Upon completion of the ajax call, retrieved data is stored locally for later reuse.
     * @param revisions array of revisions to complete
     * @param onDone callback function 
     */
    var loadExtraRevisionData = function (revisions, onDone) {
        if (!revisions || revisions.length == 0) {
            onDone && onDone();
            return;
        }
        var done = function (resp) {
            var revs = resp.revisions;
            if (!revs) {
                return;
            }
            for (var i = 0, len = revs.length; i < len; i++) {
                var rev = revs[i];
                var comment = rev.comment;
                var authorLink = rev.authorLink;

                // Update the data structure
                var revisionData = revisionDataForId(rev.id);
                revisionData.loaded = true;
                revisionData.comment = comment;
                revisionData.authorLink = authorLink;
                revisionData.detailsPane = rev.detailsPane;

                // Update the row
                var $row = AJS.$("#rev-id-"+rev.id).closest("tr");
                $row.children("td.history-comment").children(".first-line-only").removeClass("loading").html(comment);
                $row.children("td.history-author").html(authorLink);
            }
            onDone && onDone();
        };

        var params = {
            content: true,
            rev: revisions
        };

        var url = window.location.pathname;

        AJS.FECRU.AJAX.ajaxDo(url, params, done);
    };

    /**
     * Create a map between each revision id and it's index in the {{historyTableData.revisions}} array. This method
     * should be called whenever the order of the revisions data is changed, such as on sorting.
     *
     * This data structure is used so that we can efficiently look up revisionData objects and update them as we lazily
     * retrieve expensive data from the server.
     */
    var createRevisionKeyIndexMap = function() {
        var allRevisions = historyTableData.revisions;
        var revisionKeyValueIndexMap = {};
        var revisionToRevisionIdMap = {};
        if (allRevisions) {
            for (var i = 0, len = allRevisions.length; i < len; i++) {
                var rev = allRevisions[i];
                revisionKeyValueIndexMap[rev.id] = i;
                revisionToRevisionIdMap[rev.revision] = rev.id;
            }
        }
        historyTableData.revisionKeyValueIndexMap = revisionKeyValueIndexMap;
        historyTableData.revisionToRevisionIdMap = revisionToRevisionIdMap;
    };

    // These are tablesorter classes
    var sortUpClass = "headerSortUp";
    var sortDownClass = "headerSortDown";

    AJS.FE.setupFileHistoryPage = function () {
        createRevisionKeyIndexMap();

        setupDiffCheckboxes();
        AJS.FECRU.UI.tableRowClick("history", showRevisionDetails);

        createHistoryTable();

        var pageNumberToLoad = 1; // We start at 1
        var revisionIdToLoad = setupFocusedRevision();
        if (revisionIdToLoad) {
            var arrayIndexToLoad = historyTableData.revisionKeyValueIndexMap[revisionIdToLoad];
            pageNumberToLoad = Math.floor(arrayIndexToLoad / REVISIONS_PER_PAGE) + 1; // pageNumber starts at 1
        }

        loadRevisionsPage(pageNumberToLoad);
        
        // Initial sort is by branch
        AJS.$("#history-branch").addClass(sortDownClass);

        AJS.$("#pagination-back").click(function () {
            if (AJS.$(this).hasClass("disabled")) {
                return false;
            }
            loadRevisionsPage(CURRENT_PAGE+1); // next page
            return false;
        });
        AJS.$("#pagination-next").click(function () {
            if (AJS.$(this).hasClass("disabled")) {
                return false;
            }
            loadRevisionsPage(CURRENT_PAGE-1); // previous page
            return false;
        });
        AJS.$("#pagination-last").click(function () {
            if (AJS.$(this).hasClass("disabled")) {
                return false;
            }
            loadRevisionsPage(1); // last page
            return false;
        });
        AJS.$("#pagination-first").click(function () {
            if (AJS.$(this).hasClass("disabled")) {
                return false;
            }
            var totalPageCount = getTotalPageCount();
            loadRevisionsPage(totalPageCount); // first page
            return false;
        });

        // setup local-link links
        AJS.$("a.local-link").live('click', function() {
            var href = AJS.$(this).attr("href");
            var revision = href.substring(href.lastIndexOf("#") + 2);
            var revisionId = historyTableData.revisionToRevisionIdMap[revision];
            var revisionData = revisionDataForId(revisionId);
            if (revisionId) {
                showRevisionDiv(revisionData);
                setRowFocus(revisionData);
            }
        });

        AJS.$("#history-table th.sortable").live("click", sortHistoryPage);
    };

    var sortHistoryPage = function() {
        var $header = AJS.$(this);
        var headerId = $header.attr("id");

        var sortAsc = !$header.hasClass(sortUpClass);

        var sortFn;
        if (headerId === "history-age") {
            sortFn = function (left, right) {
                var r = left.date.getTime() - right.date.getTime();
                return sortAsc ? r : -r;
            };
        } else if (headerId === "history-lines") {
            sortFn = function (left, right) {
                var r, i, len;
                var lCounts = left.lines.split(" ");
                var lLines = 0;
                for (i = 0, len = lCounts.length; i < len; i++) {
                    lLines += parseInt(lCounts[i].replace(/\D/, ""), 10);
                }

                var rCounts = right.lines.split(" ");
                var rLines = 0;
                for (i = 0, len = rCounts.length; i < len; i++) {
                    rLines += parseInt(rCounts[i].replace(/\D/, ""), 10);
                }
                r = lLines - rLines;
                return sortAsc ? r : -r;
            };
        } else if (headerId === "history-author") {
            sortFn = function (left, right) {
                var r;
                var lName = left.authorLiteral.toLowerCase();
                var rName = right.authorLiteral.toLowerCase();
                if (lName == rName) {
                    r = 0;
                } else if (lName < rName) {
                    r = -1;
                } else {
                    r = 1;
                }
                return sortAsc ? r : -r;
            };
        } else if (headerId === "history-branch") {
            sortFn = function (left, right) {
                var r;
                if (left.branch == right.branch) {
                    r = left.date.getTime() < right.date.getTime();
                } else {
                    var lName = left.branch;
                    var rName = right.branch;
                    if (lName == rName) {
                        r = 0;
                    } else if (lName < rName) {
                        r = -1;
                    } else {
                        r = 1;
                    }
                }
                return sortAsc ? r : -r;
            };
        } else {
            AJS.log("Unknown sort column: " + headerId);
        }

        $header.siblings()
                    .andSelf()
                    .removeClass(sortDownClass)
                    .removeClass(sortUpClass);
        $header.addClass(sortAsc ? sortUpClass : sortDownClass);

        historyTableData.revisions.sort(sortFn);

        createRevisionKeyIndexMap();

        loadRevisionsPage(0); // init to front page
    };

    var showRevisionDetails = function(row) {
        if (!row) {
            return;
        }
        var $row = AJS.$(row);
        var revisionId = $row.data("revision-id");
        var revisionData = revisionDataForId(revisionId);
        if (revisionData) {
            showRevisionDiv(revisionData);
            setRowFocus(revisionData);
        }
    };
    
    AJS.FE.fileHistoryPathLinkFn = function(event) {
        event.stopPropagation();
        return true;
    };

    AJS.FE.extractRevisionDetailsSortKeys = function(cell) {
        var $node = AJS.$(cell);
        var $input = $node.children("input");
        if ($input.size() > 0) {
            return $input.val();
        } else {
            return $node.text();
        }
    };

    /* THIS IS BEING RELOCATED TO UI.JS */
    /**
     * Set the faux head for the history table - assign events and set initial widths of cells
     * or calibrate it once the table has been sorted
     */
    var displayFauxTable = function(first) {
        var $tableHeaders = AJS.$("#history-table").children("thead").find("th");
        var $fauxTable = AJS.$("#faux-table");
        var $faux = $fauxTable.find("li");
        var length = $faux.length;

        for (var i = 0; i < length; i++) {
            var $header = AJS.$($tableHeaders[i]);
            var $item = AJS.$($faux[i]);
            $item.width($header.width());
        }

        if (first) {
            $faux.show();
            AJS.$("#faux-table li.header").live("click", sortHistoryPage);
        }
    };


})();
;
/* END /2static/script/fe/fisheye-history.js */
/* START /2static/script/fe/fisheye-search.js */
AJS.FE.setupQuickSearchRepositoryList = function() {
    AJS.$(".search-quick .qs_repobox .rep-select").live("change",
        function (event) {
            var searchString = AJS.$(".search-quick .qs_searchbox .qsInput").val();
            document.location=fishEyePageContext + "/qsearch/" + AJS.$(this).find(":selected").val() + "?q=" + searchString;
        }
    );
};
;
/* END /2static/script/fe/fisheye-search.js */
/* START /script/activitystream.js */
//todo: refactor all these string manips into using real js objects with explicit property names.

function loadActivityStream(viewParam, callbackParams) {
    var pagingMap = constructPagingParamsMap();
    var paramsMap = {
        type:"ajax",
        view:viewParam
    };
    updateActivityStreamImpl(mergeMaps([paramsMap,pagingMap]), "activity", callbackParams);
}

function loadActivityStreamForProject(id, viewParam, callbackParams) {
    var pagingMap = constructPagingParamsMap();
    var paramsMap = {
        type:"ajax",
        view:viewParam,
        project:id
     };
    updateActivityStream(mergeMaps([paramsMap,pagingMap]), callbackParams);
}

function loadFileActivityStream(id, path, repname, viewParam, callbackParams) {
    var pagingMap = constructPagingParamsMap();
    var paramsMap = {
        type:"ajax",
        view:viewParam,
        p:path,
        repname:repname
    };
    updateActivityStreamImpl(mergeMaps([paramsMap,pagingMap]), id, callbackParams);
}

//todo: add max item constraint for changelog view
function loadDirActivityStream(id, path, repname, viewPara, callbackParams) {
    var pagingMap = constructPagingParamsMap();
    var paramsMap = {
        type:"ajax",
        view:viewPara,
        p:path,
        repname:repname
    };
    updateActivityStreamImpl(mergeMaps([paramsMap,pagingMap]), id, callbackParams);
}

function loadDownStreamStarActivity(viewParam, callbackParams) {
    var pagingMap = constructPagingParamsMap();
    var paramsMap = {
        type:"ajax",
        view:viewParam,
        star:"star",
        home:"home"
    };
    updateActivityStream(mergeMaps([paramsMap,pagingMap]), callbackParams);
}

function loadActivityStreamForUser(username, viewParam, callbackParams) {
    var pagingMap = constructPagingParamsMap();
    var paramsMap = {
        type:"ajax",
        view:viewParam,
        username:username
    };
    updateActivityStream(mergeMaps([paramsMap,pagingMap]), callbackParams);
}

function loadActivityStreamForCommitter(committer, repname, viewParam, callbackParams) {
var pagingMap = constructPagingParamsMap();
    var paramsMap = {
        type:"ajax",
        view:viewParam,
        committer:committer,
        repname:repname
    };
    updateActivityStream(mergeMaps([paramsMap,pagingMap]), callbackParams);
}

function updateActivityStream(paramMap, callbackParams) {
    updateActivityStreamImpl(paramMap, 'activity', callbackParams);
}

function pageActivityStream(forwards, id, callbackParams) {
    updateActivityStreamImpl(toParamMap(forwards ? nextPageParams : prevPageParams), id, callbackParams);
    var $elem = AJS.$("#"+id + " #stream");
    var spinnerUrl = fishEyePageContext + "/" + fishEyeSTATICDIR + "/2static/images/spinner_003366.gif";
    $elem.html('<div><img src="' +spinnerUrl+ '" alt="loading" width="13" height="13">loading...</div>');
}


function updateActivityStreamImpl(params, id, callbackParams) {
    var url = fishEyePageContext + '/json/changelog';
    var done = function(resp) {
        var $elem = AJS.$('#' + id);
        try {
            if (resp.worked) {
//                $elem.html(resp.html);
                // the replaceWith call is a hack to get around an IE7 bug, which makes the .html() call fail.
                $elem.replaceWith("<div id='activity'>" + resp.html + "</div>");
                var $toolbar = AJS.$("#activitystream-toolbar");
                $toolbar.replaceWith("<div id='activitystream-toolbar'>" + resp.activityStreamToolbar + "</div>");
                prevPageParams = resp.prevPageParams;
                nextPageParams = resp.nextPageParams;
                thisPageParams = resp.thisPageParams;
                view = resp.view ? resp.view : "all";
                if (resp.onFinishedLoad) {
                    resp.onFinishedLoad(callbackParams);
                }
                AJS.FECRU.UI.initStream();
                var $newElem = AJS.$('#' + id);
                AJS.FECRU.HOVER.addAllLinkPopups($newElem);
            } else {
                handleError(resp, $elem, null);
            }
        } catch (error) {
            handleError(resp, $elem, error);
        }
    };
    AJS.$.getJSON(url, params, done);
}

function handleError(resp, $elem, error) {
    if (error) {
        $elem.html("<div style='cursor:pointer;' onclick=\"AJS.$('#activityStreamErrorBox').toggle();\">" +
                   "Error, cannot complete request. <br>" +
                   "<div id='activityStreamErrorBox' class='ajaxError' style='display:none;cursor:pointer;'>" +
                   "details:" + error.message + "<br>" +
                   "url:" + error.fileName + "<br>" +
                   "line:" + error.lineNumber + "<br>" +
                   "error:<br>" +
                   (function() {
                               var str;
                               for (var k in error) {
                                   str += "error[" + k + "]=" + error[k] + "<br>";
                               }
                               return str;
                   }()) + "<br>" +
                   "</div>" +
                   "</div>");
    } else {
        $elem.html("An activity stream error has occurred.<br>");
    }
}

//implementation of the prototype toQueryParam() method.
//does NOT support queryString of the type key1=val1&key1=val2&key1=val3 -> {key1:[val1, val2, val3]}
function toParamMap(paramString) {
    var KEY = 0;
    var VAL = 1;
    var paramMap = {};
    var cleanedParamString = paramString.charAt(0) == '?' ? paramString.substring(1, paramString.length)
                                                          : paramString;
    var paramPairs = cleanedParamString.split('&');
    for (var i = 0; i < paramPairs.length; i++) {
        if (paramPairs[i].length > 0) {
            var subpair = paramPairs[i].split('=', 2);
            paramMap[subpair[KEY]] = subpair[VAL];
        }
    }
    return paramMap;
}

function constructPagingParamsMap() {
    var originalParams = toParamMap(thisPageParams);
    var keys = ["maxDate","minDate","direction","prevAnchor","nextAnchor"];
    var cleanedParams = {};
    for (var i = 0; i < keys.length; i++) {
        if (originalParams[keys[i]]) {
            cleanedParams[keys[i]] = originalParams[keys[i]];
        }
    }
    return cleanedParams;
}

/**
 *
 * @param maps an array of javascript map to merge together.
 * Be wary that if two maps have the same key, only one of the value is preserved,
 * and it is not specified which one will be preserved.
 */
function mergeMaps(maps) {
    var merged = {};
    for (var i = 0 ; i < maps.length ; i++) {
        var m = maps[i];
        for (var key in m) {
            if (m.hasOwnProperty(key)) {
                merged[key] = m[key];
            }
        }
    }
    return merged;
}

var prevPageParams = "";
var nextPageParams = "";
var thisPageParams = "";
var view = "";
;
/* END /script/activitystream.js */
/* START /2static/script/cru/util.js */
/**
 * Crucible utility functions that can be used from any crucible page.
 */

if (AJS.CRU === undefined) {
    AJS.CRU = {};
}
AJS.CRU.UTIL = {};

(function ($) {
    var cruUtil = AJS.CRU.UTIL;
    /**
     * Crucible base url (without trailing '/').
     *
     * @param permaId optional review id
     */
    cruUtil.urlBase = function (permaId) {
        if (permaId) {
            return fishEyePageContext + '/cru/' + permaId;
        } else {
            return fishEyePageContext + '/cru';
        }
    };

    /**
     * Crucible JSON base url (without trailing '/').
     *
     * @param permaId optional review id
     */
    cruUtil.jsonUrlBase = function (permaId) {
        if (permaId) {
            return fishEyePageContext + '/json/cru/' + permaId;
        } else {
            return fishEyePageContext + '/json/cru';
        }
    };
    
    cruUtil.isReviewPage = function () {
        return typeof review !== 'undefined';
    };


    cruUtil.startAjaxDialogSpin = function () {
        $('body').addClass('ajax-dialog');
        AJS.dim();
    };

    cruUtil.stopAjaxDialogSpin = function () {
        AJS.dim();
        $('body').removeClass('ajax-dialog');
    };

    cruUtil.isAjaxDialogSpinning = function () {
        return cruUtil.isDimmed() && $('body').hasClass('ajax-dialog');
    };

    cruUtil.ajaxDialog = function (url, params) {
        cruUtil.startAjaxDialogSpin();
        AJS.FECRU.AJAX.ajaxDo(url, params || {}, function (resp) {
            if (resp.worked) {
                if (resp.showDialog) {
                    cruUtil.stopAjaxDialogSpin();
                    AJS.CRU.DIALOG.$CONTAINER.html(resp.payload);
                } else if (resp.redirect) {
                    // no need to undim
                    window.location = resp.payload;
                }
            }
            // !resp.worked handled by ajaxDo
        });
        return false;
    };

    cruUtil.stateTransition = function (transition, permaId, params) {
        var util = cruUtil;
        var url = util.jsonUrlBase(permaId) + '/changeStateAjax';
        var ui = AJS.CRU.UI;

        // Make sure there aren't any unsaved inputs on the page, and if there are, then provide a warning
        // and give the user the ability to cancel and save their inputs.
        if (ui) {
            if (!ui.confirmUnsubmittedInputs()) {
                return;
            }
            ui.clearWatchForUnsavedChanges();
        }

        params = params || {};

        $.extend(params, {
            command: transition
        });

        if (util.isReviewPage() && $.inArray(transition, ['action:completeReview', 'action:summarizeReview']) >= 0) {
            // If completing or summarizing we need to warn if the review has been updated.

            util.startAjaxDialogSpin();
            AJS.CRU.REVIEW.UTIL.reviewUpdatedAjax({
                done: function () {
                    var reviewUpdated = $('body').hasClass('review-updated');
                    if (reviewUpdated) {
                        AJS.CRU.REVIEW.UTIL.warnAboutReviewUpdates({ reshowWarning: true });
                    }
                    util.stopAjaxDialogSpin();
                    return util.ajaxDialog(url, $.extend(params, { reviewUpdated: reviewUpdated }));
                }
            });
            return false;

        } else {
            return util.ajaxDialog(url, params);
        }
    };

    cruUtil.editDetailsFormChange = false;
    cruUtil.checkEditForm = function (done) {
        if (cruUtil.editDetailsFormChange) {
            AJS.CRU.REVIEW.UTIL.postEditDetailsForm(done);
        } else {
            if (done) {
                done();
            }
        }
        return false;//do a link action if called from <a>
    };

    cruUtil.command = function (cmd, pid, button) {
        var perma = pid || permaId;
        if (button) {
            button.disabled = true;
        }
        var donext = function() {
            var url = cruUtil.urlBase(perma);
            window.location = url + '/' + cmd;
        };
        //check and post the editDetailsForm if it has changed
        cruUtil.checkEditForm(donext);
    };


    cruUtil.createBlankReview = function (params) {
        var url = cruUtil.jsonUrlBase() + '/createReviewDialog';
        return cruUtil.ajaxDialog(url, params);
    };

    cruUtil.addToReview = function (params) {
        var url = cruUtil.jsonUrlBase() + '/createDialog';
        return cruUtil.ajaxDialog(url, params);
    };

    cruUtil.isAnyDialogShowing = function () {
        return $('body').children('div.dialog:visible').length > 0;
    };

    cruUtil.isDimmed = function () {
        return !!AJS.dim.dim;
    };

})(AJS.$);
;
/* END /2static/script/cru/util.js */
/* START /2static/script/cru/dialog/dialog.js */
if (!AJS.CRU) {
    AJS.CRU = {};
}
AJS.CRU.DIALOG = {};

(function () {

    AJS.CRU.DIALOG.$CONTAINER = AJS.$('<div id="ajax-dialog-container"></div>');
    AJS.$(document).ready(function () {
        AJS.$('body').append(AJS.CRU.DIALOG.$CONTAINER);
    });

    AJS.CRU.DIALOG.ajaxDialog = function (maxWidth, maxHeight, data, id, optionalClass) {
        var dialog = AJS.FECRU.DIALOG.create(maxWidth, maxHeight, id, optionalClass);
        AJS.$.each(
            AJS.$.extend({ dialog: dialog }, data),
            function (k, v) {
                AJS.CRU.DIALOG.$CONTAINER.data(k, v);
            }
        );
        return dialog;
    };

})();
;
/* END /2static/script/cru/dialog/dialog.js */
/* START /2static/script/cru/dialog/dialog-event.js */
(function () {

    var $container = AJS.CRU.DIALOG.$CONTAINER;

    var dialogHandler = function (callbacks) {
        return function (event) {
            var dialog = $container.data('dialog');
            var permaId = $container.data('permaId');

            if (AJS.CRU.UTIL.isReviewPage() && callbacks.review) {
                return callbacks.review(dialog, permaId, event);
            } else if (!AJS.CRU.UTIL.isReviewPage() && callbacks.external) {
                return callbacks.external(dialog, permaId, event);
            }
        };
    };

    var viewFiltersHandler = function (filters) {
        return dialogHandler({
            review: function (dialog) {
                if (AJS.$('body').hasClass('review-updated')) {
                    window.location.hash = 'f-' + filters.join(',');
                    window.location.reload(true);
                } else {
                    AJS.CRU.REVIEW.UTIL.filterAndExpandFrxs(filters);
                    dialog.remove();
                }
            },
            external: function (dialog, permaId) {
                window.location = AJS.CRU.UTIL.urlBase(permaId) + '#f-' + filters.join(',');
            }
        });
    };

    var batchProcessDraftComments = function (action, permaId, onComplete) {
        var url = AJS.CRU.UTIL.jsonUrlBase(permaId) + '/draftCommentsAjax';
        var params = {
            action: action
        };
        var done = function (resp) {
            if (onComplete) {
                onComplete(resp);
            }
        };
        AJS.FECRU.AJAX.ajaxDo(url, params, done, true);
    };

    var draftCommentsHandler = function (action) {
        return dialogHandler({
            review:  function (dialog) {
                AJS.$.each(review.draftComments(), function (i, comment) {
                    commentator[action + 'Comment'](comment.id(), review.id());
                });
                if (action == 'delete') {
                    AJS.$("#dialog-drafts-message").html("Your draft comments have been deleted.");
                } else if (action == 'publish') {
                    AJS.$("#dialog-drafts-message").html("Your draft comments have been posted.");
                }
                AJS.$('#dialog-drafts-links').hide();
            },
            external: function (dialog, permaId) {
                batchProcessDraftComments(action, permaId, function (resp) {
                    if (resp.worked) {
                        AJS.$('#dialog-drafts-links').hide();
                    } else {
                        // TODO Show error message within dialog panel.
                    }
                });
            }
        });
    };

    var resolveUnresolvedJiras = function () {
        var doIt = function(dialog, permaId) {
            AJS.$('#dialog-unresolved-jiras-controls').hide();
            var url = AJS.CRU.UTIL.jsonUrlBase(permaId) + '/resolveAllSubtasksAjax';
            var params = {};
            AJS.$('#dialog-unresolved-jiras-spinner').show();
            var done = function (resp) {
                AJS.$('#dialog-unresolved-jiras-spinner').hide();
                if (resp.worked) {
                    AJS.$('#dialog-unresolved-jiras-title').html("There are no unresolved subtasks.").show();
                } else {
                    AJS.$('#dialog-unresolved-jiras-results').addClass("jira-error").html(resp.errorMsg).show();
                }
            };
            AJS.FECRU.AJAX.ajaxDo(url, params, done, true);
        };
        return dialogHandler({review: doIt, external: doIt});
    };

    AJS.$(document).ready(function () {
        AJS.$('#dialog-view-drafts').live('click', viewFiltersHandler(['draftcomments']));
        AJS.$('#dialog-view-unread-comments').live('click', viewFiltersHandler(['unreadcomments']));
        AJS.$('#dialog-view-incomplete-frxs').live('click', viewFiltersHandler(['incomplete']));
        AJS.$('#dialog-view-unresolved-jiras').live('click', viewFiltersHandler(['unresolvedsubtasks']));

        AJS.$('#dialog-delete-drafts').live('click', draftCommentsHandler('delete'));
        AJS.$('#dialog-post-drafts').live('click', draftCommentsHandler('publish'));
        AJS.$('#dialog-resolve-unresolved-jiras').live('click', resolveUnresolvedJiras());
    });

})();
;
/* END /2static/script/cru/dialog/dialog-event.js */
/* START /2static/script/cru/review/email-comments.js */
if (!AJS.CRU) {
    AJS.CRU = {};
}
if (!AJS.CRU.REVIEW) {
    AJS.CRU.REVIEW = {};
}
if (!AJS.CRU.REVIEW.EMAILCOMMENTS) {
    AJS.CRU.REVIEW.EMAILCOMMENTS = {};
}

(function() {
    var dialog;
    var self = AJS.CRU.REVIEW.EMAILCOMMENTS;

    var nextButton = function () {};
    var previousButton = function () {};

    self.showPage = function (pageId) {

        AJS.$(".email-wizard", "#emailWizardDialog").each(function() {
            if (AJS.$(this).is('#' + pageId)) {
                AJS.$(this).show();
            } else {
                AJS.$(this).hide();
            }
        });
        AJS.$("#emailWizardDialog").find(".previousButton, .nextButton, .cancelButton").each(function() {
            AJS.$(this)
                .attr('disabled', false)
                .show();
        });

        if (pageId === 'recipients') {
            AJS.$(".nextButton", "#emailWizardDialog").html("Next");
            nextButton = function () {
                self.showPage('message');
            };
            AJS.$(".previousButton", "#emailWizardDialog")
                    .hide();
            previousButton = function () {};

        } else if (pageId === 'message') {
            AJS.$(".nextButton", "#emailWizardDialog").html("Send");
            nextButton = function () {
                var $form = AJS.$("#emailWizardDialog form#emailCommentsForm");
                var url = $form.attr('action');
                AJS.$(".nextButton", "#emailWizardDialog").attr("disabled", true);
                AJS.FECRU.AJAX.ajaxUpdate(url, $form.serialize(), "dialogPanel");
            };
            previousButton = function () {
                self.showPage('recipients');
            };

        } else if (pageId === 'sent') {
            AJS.$(".nextButton", "#emailWizardDialog").html("Close");
            nextButton = function () {
                dialog.remove();
            };
            previousButton = function () {
                self.showPage('message');
            };
        }
    };

    self.start = function (permaId) {

        dialog = AJS.CRU.DIALOG.ajaxDialog(900, 600, {}, "emailWizardDialog", "hover-over")
            .addHeader("Email Review Comments")
            .addPanel("Recipients", "<div id='dialogPanel'>Loading...</div>")
            .addButton("Cancel", function(dialog) {
                dialog.remove();
            }, "cancelButton")
            .addButton("Previous", function() {
                previousButton();
            }, "previousButton")
            .addButton("Next", function() {
                nextButton();
            }, "nextButton")
            .show();

        var url = AJS.CRU.UTIL.jsonUrlBase(permaId) + "/allcommentsemail";

        var ajax = AJS.FECRU.AJAX;
        ajax.startSpin("dialogPanel", "email-spinner");
        ajax.ajaxUpdate(url, {}, "dialogPanel", function () {
            ajax.stopSpin(AJS.$('#dialogPanel'));
            AJS.$("form#emailCommentsForm").find("#to").focus();
        });
    };
} )();
;
/* END /2static/script/cru/review/email-comments.js */
/* START /2static/script/cru/review/review-history.js */
if (!AJS.CRU) {
    AJS.CRU = {};
}
if (!AJS.CRU.REVIEW) {
    AJS.CRU.REVIEW = {};
}
if (!AJS.CRU.REVIEW.HISTORY) {
    AJS.CRU.REVIEW.HISTORY = {};
}

AJS.CRU.REVIEW.HISTORY.showPage = function (permaId) {
    AJS.dropDown.current.hide();

    if (!permaId) {
        AJS.log("I don't know which review to give you history for.");
        return;
    }

    var dialog = AJS.FECRU.DIALOG.create(1200, 700, "cru-review-history-dialog");

    var iframeStyle = "style='width:100%;height:" + (dialog.height - 43 - 44 - 23) + "px'";
    var cs = "<iframe frameborder='0' id='reviewHistoryIframe' name='reviewHistoryIframe' src='" + AJS.CRU.UTIL.urlBase(permaId) + "/reviewHistoryWrapper" + "' " + iframeStyle + "></iframe>";

    var header = "History of Review " + permaId;
    dialog.addHeader(header)
            .addPanel("History", cs);

    dialog.addButton("Done", function(dialog) {
        dialog.hide();
    }).show();

    AJS.$("#cru-review-history-dialog").data("dialog", dialog);//stores the object so we can access it from its contents
};

AJS.CRU.REVIEW.HISTORY.setupLinks = function() {
    AJS.$("a.action-link").live("click", function () {
        parent.top.AJS.$("#cru-review-history-dialog").data("dialog").hide();
    });
    AJS.$("a.comment-link").live("click", function () {
        var comment_nav = parent.top.AJS.CRU.COMMENT.NAV;
        var comment_id = AJS.$(this).attr('hash').replace('#c', '');

        var scrollToMap = comment_nav.navigateFindComment({ commentId: comment_id });
        comment_nav.navigateDirectlyToElement({ commentId: comment_id }, scrollToMap);
    });
    AJS.$("a.frx-link").live("click", function () {
        var frx_id = AJS.$(this).attr('hash').replace('#CFR-','');

        parent.top.AJS.CRU.FRX.NAV.gotoFrx({ frxId: frx_id , destination: '' });
    });

};
;
/* END /2static/script/cru/review/review-history.js */
