if (typeof mw == 'undefined') {
    mw = {};
}
mw.usability = {
    'messages': {}
}
mw.usability.load = function (deps, callback) {
    var needJUI = false;
    for (var i = 0; i < deps.length && !needJUI; i++) {
        if (deps[i] == '$j.ui') {
            needJUI = true;
        }
    }
    if (needJUI && typeof $j.ui == 'undefined') {
        $j.getScript(wgScriptPath + '/extensions/UsabilityInitiative/js/js2stopgap/jui.combined.min.js', callback);
    } else {
        callback();
    }
};
mw.usability.addMessages = function (messages) {
    for (var key in messages) {
        this.messages[key] = messages[key];
    }
};
mw.usability.getMsg = function (key, args) {
    if (!(key in this.messages)) {
        return '[' + key + ']';
    }
    var msg = this.messages[key];
    if (typeof args == 'object' || typeof args == 'array') {
        for (var argKey in args) {
            msg = msg.replace('\$' + (parseInt(argKey) + 1), args[argKey]);
        }
    } else if (typeof args == 'string' || typeof args == 'number') {
        msg = msg.replace('$1', args);
    }
    return msg;
};
mw.usability.testBrowser = function (map) {
    var browser = map[$j('body').is('.rtl') ? 'rtl' : 'ltr'][$j.browser.name];
    if (typeof browser == 'boolean') {
        return browser;
    }
    if (typeof browser !== 'object') {
        return true;
    }
    for (var condition in browser) {
        var op = browser[condition][0];
        var val = browser[condition][1];
        if (val === false) {
            return false;
        } else if (typeof val == 'string') {
            if (!(eval('$j.browser.version' + op + '"' + val + '"'))) {
                return false;
            }
        } else if (typeof val == 'number') {
            if (!(eval('$j.browser.versionNumber' + op + val))) {
                return false;
            }
        }
    }
    return true;
};
mw.usability.getMaxTabIndex = function () {
    var maxTI = 0;
    $j('[tabindex]').each(function () {
        var ti = parseInt($j(this).attr('tabindex'));
        if (ti > maxTI) {
            maxTI = ti;
        }
    });
    return maxTI;
};
(function ($) {
    $.whileAsync = function (opts) {
        var delay = Math.abs(opts.delay) || 10,
            bulk = isNaN(opts.bulk) ? 500 : Math.abs(opts.bulk),
            test = opts.test ||
            function () {
                return true;
            },
            loop = opts.loop ||
            function () {},
            end = opts.end ||
            function () {};
        (function () {
            var t = false,
                begin = new Date();
            while (t = test()) {
                loop();
                if (bulk === 0 || (new Date() - begin) > bulk) {
                    break;
                }
            }
            if (t) {
                setTimeout(arguments.callee, delay);
            } else {
                end();
            }
        })();
    }
    $.eachAsync = function (array, opts) {
        var i = 0,
            l = array.length,
            loop = opts.loop ||
            function () {};
        $.whileAsync($.extend(opts, {
            test: function () {
                return i < l;
            },
            loop: function () {
                var val = array[i];
                return loop.call(val, i++, val);
            }
        }));
    }
    $.fn.eachAsync = function (opts) {
        $.eachAsync(this, opts);
        return this;
    }
})(jQuery);
(function ($) {
    var cache = {};
    var matchTextCache = {};
    $.fn.autoEllipsis = function (options) {
        options = $.extend({
            'position': 'center',
            'tooltip': false,
            'restoreText': false,
            'hasSpan': false,
            'matchText': null
        }, options);
        $(this).each(function () {
            var $this = $(this);
            if (options.restoreText) {
                if (!$this.data('autoEllipsis.originalText')) {
                    $this.data('autoEllipsis.originalText', $this.text());
                } else {
                    $this.text($this.data('autoEllipsis.originalText'));
                }
            }
            var $container = $this;
            var $trimmableText = null;
            var $protectedText = null;
            if (options.hasSpan) {
                $trimmableText = $this.children(options.selector);
            } else {
                $trimmableText = $('<span />').css('whiteSpace', 'nowrap').text($this.text());
                $this.empty().append($trimmableText);
            }
            var text = $container.text();
            var trimmableText = $trimmableText.text();
            var w = $container.width();
            var pw = $protectedText ? $protectedText.width() : 0;
            if (!(text in cache)) {
                cache[text] = {};
            }
            if (options.matchText && !(text in matchTextCache)) {
                matchTextCache[text] = {};
            }
            if (options.matchText && !(options.matchText in matchTextCache[text])) {
                matchTextCache[text][options.matchText] = {};
            }
            if (!options.matchText && w in cache[text]) {
                $container.html(cache[text][w]);
                if (options.tooltip) $container.attr('title', text);
                return;
            }
            if (options.matchText && options.matchText in matchTextCache[text] && w in matchTextCache[text][options.matchText]) {
                $container.html(matchTextCache[text][options.matchText][w]);
                if (options.tooltip) $container.attr('title', text);
                return;
            }
            if ($trimmableText.width() + pw > w) {
                switch (options.position) {
                case 'right':
                    var l = 0,
                        r = trimmableText.length;
                    do {
                        var m = Math.ceil((l + r) / 2);
                        $trimmableText.text(trimmableText.substr(0, m) + '...');
                        if ($trimmableText.width() + pw > w) {
                            r = m - 1;
                        } else {
                            l = m;
                        }
                    } while (l < r);
                    $trimmableText.text(trimmableText.substr(0, l) + '...');
                    break;
                case 'center':
                    var i = [Math.round(trimmableText.length / 2), Math.round(trimmableText.length / 2)];
                    var side = 1;
                    while ($trimmableText.outerWidth() + pw > w && i[0] > 0) {
                        $trimmableText.text(trimmableText.substr(0, i[0]) + '...' + trimmableText.substr(i[1]));
                        if (side == 0) {
                            i[0]--;
                            side = 1;
                        } else {
                            i[1]++;
                            side = 0;
                        }
                    }
                    break;
                case 'left':
                    var r = 0;
                    while ($trimmableText.outerWidth() + pw > w && r < trimmableText.length) {
                        $trimmableText.text('...' + trimmableText.substr(r));
                        r++;
                    }
                    break;
                }
            }
            if (options.tooltip) {
                $container.attr('title', text);
            }
            if (options.matchText) {
                $container.highlightText(options.matchText);
                matchTextCache[text][options.matchText][w] = $container.html();
            } else {
                cache[text][w] = $container.html();
            }
        });
    };
})(jQuery);
(function ($) {
    $.browserTest = function (a, z) {
        var u = 'unknown',
            x = 'X',
            m = function (r, h) {
                for (var i = 0; i < h.length; i = i + 1) {
                    r = r.replace(h[i][0], h[i][1]);
                }
                return r;
            },
            c = function (i, a, b, c) {
                var r = {
                    name: m((a.exec(i) || [u, u])[1], b)
                };
                r[r.name] = true;
                r.version = (c.exec(i) || [x, x, x, x])[3];
                if (r.name.match(/safari/) && r.version > 400) {
                    r.version = '2.0';
                }
                if (r.name === 'presto') {
                    r.version = ($.browser.version > 9.27) ? 'futhark' : 'linear_b';
                }
                if (r.name === 'opera' && $.browser.version >= 9.8) {
                    r.version = i.match(/version\/([0-9\.]*)/i)[1] || 10;
                }
                r.versionNumber = parseFloat(r.version, 10) || 0;
                r.versionX = (r.version !== x) ? (r.version + '').substr(0, 1) : x;
                r.className = r.name + r.versionX;
                return r;
            };
        a = (a.match(/Opera|Navigator|Minefield|KHTML|Chrome|PLAYSTATION 3/) ? m(a, [
            [/(Firefox|MSIE|KHTML,\slike\sGecko|Konqueror)/, ''],
            ['Chrome Safari', 'Chrome'],
            ['KHTML', 'Konqueror'],
            ['Minefield', 'Firefox'],
            ['Navigator', 'Netscape'],
            ['PLAYSTATION 3', 'PS3']
        ]) : a).toLowerCase();
        $.browser = $.extend((!z) ? $.browser : {}, c(a, /(camino|chrome|firefox|netscape|konqueror|lynx|msie|opera|safari|ipod|iphone|blackberry|ps3|docomo)/, [], /(camino|chrome|firefox|netscape|netscape6|opera|version|konqueror|lynx|msie|safari|ps3)(\/|\;?\s|)([a-z0-9\.\+]*?)(\;|dev|rel|\)|\s|$)/));
        $.layout = c(a, /(gecko|konqueror|msie|opera|webkit)/, [
            ['konqueror', 'khtml'],
            ['msie', 'trident'],
            ['opera', 'presto']
        ], /(applewebkit|rv|konqueror|msie)(\:|\/|\s)([a-z0-9\.]*?)(\;|\)|\s)/);
        $.os = {
            name: (/(win|mac|linux|sunos|solaris|iphone)/.exec(navigator.platform.toLowerCase()) || [u])[0].replace('sunos', 'solaris')
        };
        if (!z) {
            $('html').addClass([$.os.name, $.browser.name, $.browser.className, $.layout.name, $.layout.className].join(' '));
        }
    };
    $.browserTest(navigator.userAgent);
})(jQuery);
(function ($) {
    $.fn.collapsibleTabs = function ($$options) {
        if (!this.length) return this;
        var $settings = $.extend({}, $.collapsibleTabs.defaults, $$options);
        this.each(function () {
            var $this = $(this);
            $.collapsibleTabs.instances = ($.collapsibleTabs.instances.length == 0 ? $this : $.collapsibleTabs.instances.add($this));
            $this.data('collapsibleTabsSettings', $settings);
            $this.children($settings.collapsible).each(function () {
                $.collapsibleTabs.addData($(this));
            });
        });
        if (!$.collapsibleTabs.boundEvent) {
            $(window).delayedBind('500', 'resize', function () {
                $.collapsibleTabs.handleResize();
            });
        }
        $.collapsibleTabs.handleResize();
        return this;
    };
    $.collapsibleTabs = {
        instances: [],
        boundEvent: null,
        defaults: {
            expandedContainer: '#p-views ul',
            collapsedContainer: '#p-cactions ul',
            collapsible: 'li.collapsible',
            shifting: false,
            expandCondition: function (eleWidth) {
                return ($('#left-navigation').position().left + $('#left-navigation').width()) < ($('#right-navigation').position().left - eleWidth);
            },
            collapseCondition: function () {
                return ($('#left-navigation').position().left + $('#left-navigation').width()) > $('#right-navigation').position().left;
            }
        },
        addData: function ($collapsible) {
            var $settings = $collapsible.parent().data('collapsibleTabsSettings');
            $collapsible.data('collapsibleTabsSettings', {
                'expandedContainer': $settings.expandedContainer,
                'collapsedContainer': $settings.collapsedContainer,
                'expandedWidth': $collapsible.width(),
                'prevElement': $collapsible.prev()
            });
        },
        getSettings: function ($collapsible) {
            var $settings = $collapsible.data('collapsibleTabsSettings');
            if (typeof $settings == 'undefined') {
                $.collapsibleTabs.addData($collapsible);
                $settings = $collapsible.data('collapsibleTabsSettings');
            }
            return $settings;
        },
        handleResize: function (e) {
            $.collapsibleTabs.instances.each(function () {
                var $this = $(this),
                    data = $.collapsibleTabs.getSettings($this);
                if (data.shifting) return;
                if ($this.children(data.collapsible).length > 0 && data.collapseCondition()) {
                    $this.trigger("beforeTabCollapse");
                    $.collapsibleTabs.moveToCollapsed($this.children(data.collapsible + ':last'));
                }
                if ($(data.collapsedContainer + ' ' + data.collapsible).length > 0 && data.expandCondition($.collapsibleTabs.getSettings($(data.collapsedContainer).children(data.collapsible + ":first")).expandedWidth)) {
                    $this.trigger("beforeTabExpand");
                    $.collapsibleTabs.moveToExpanded(data.collapsedContainer + " " + data.collapsible + ':first');
                }
            });
        },
        moveToCollapsed: function (ele) {
            var $moving = $(ele);
            if ($moving.size() == 0) {
                return;
            }
            var data = $.collapsibleTabs.getSettings($moving);
            var dataExp = $.collapsibleTabs.getSettings(data.expandedContainer);
            dataExp.shifting = true;
            $moving.remove().prependTo(data.collapsedContainer).data('collapsibleTabsSettings', data);
            dataExp.shifting = false;
            $.collapsibleTabs.handleResize();
        },
        moveToExpanded: function (ele) {
            var $moving = $(ele);
            if ($moving.size() == 0) {
                return;
            }
            var data = $.collapsibleTabs.getSettings($moving);
            var dataExp = $.collapsibleTabs.getSettings(data.expandedContainer);
            dataExp.shifting = true;
            $moving.remove().insertAfter(data.prevElement).data('collapsibleTabsSettings', data);
            dataExp.shifting = false;
            $.collapsibleTabs.handleResize();
        }
    };
})(jQuery);
(function (jQuery) {
    jQuery.each(['backgroundColor', 'borderBottomColor', 'borderLeftColor', 'borderRightColor', 'borderTopColor', 'color', 'outlineColor'], function (i, attr) {
        jQuery.fx.step[attr] = function (fx) {
            if (fx.state == 0) {
                fx.start = getColor(fx.elem, attr);
                fx.end = getRGB(fx.end);
            }
            fx.elem.style[attr] = "rgb(" + [Math.max(Math.min(parseInt((fx.pos * (fx.end[0] - fx.start[0])) + fx.start[0]), 255), 0), Math.max(Math.min(parseInt((fx.pos * (fx.end[1] - fx.start[1])) + fx.start[1]), 255), 0), Math.max(Math.min(parseInt((fx.pos * (fx.end[2] - fx.start[2])) + fx.start[2]), 255), 0)].join(",") + ")";
        }
    });

    function getRGB(color) {
        var result;
        if (color && color.constructor == Array && color.length == 3) return color;
        if (result = /rgb\(\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*\)/.exec(color)) return [parseInt(result[1]), parseInt(result[2]), parseInt(result[3])];
        if (result = /rgb\(\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*\)/.exec(color)) return [parseFloat(result[1]) * 2.55, parseFloat(result[2]) * 2.55, parseFloat(result[3]) * 2.55];
        if (result = /#([a-fA-F0-9]{2})([a-fA-F0-9]{2})([a-fA-F0-9]{2})/.exec(color)) return [parseInt(result[1], 16), parseInt(result[2], 16), parseInt(result[3], 16)];
        if (result = /#([a-fA-F0-9])([a-fA-F0-9])([a-fA-F0-9])/.exec(color)) return [parseInt(result[1] + result[1], 16), parseInt(result[2] + result[2], 16), parseInt(result[3] + result[3], 16)];
        return colors[jQuery.trim(color).toLowerCase()];
    }

    function getColor(elem, attr) {
        var color;
        do {
            color = jQuery.curCSS(elem, attr);
            if (color != '' && color != 'transparent' || jQuery.nodeName(elem, "body")) break;
            attr = "backgroundColor";
        } while (elem = elem.parentNode);
        return getRGB(color);
    };
    var colors = {
        aqua: [0, 255, 255],
        azure: [240, 255, 255],
        beige: [245, 245, 220],
        black: [0, 0, 0],
        blue: [0, 0, 255],
        brown: [165, 42, 42],
        cyan: [0, 255, 255],
        darkblue: [0, 0, 139],
        darkcyan: [0, 139, 139],
        darkgrey: [169, 169, 169],
        darkgreen: [0, 100, 0],
        darkkhaki: [189, 183, 107],
        darkmagenta: [139, 0, 139],
        darkolivegreen: [85, 107, 47],
        darkorange: [255, 140, 0],
        darkorchid: [153, 50, 204],
        darkred: [139, 0, 0],
        darksalmon: [233, 150, 122],
        darkviolet: [148, 0, 211],
        fuchsia: [255, 0, 255],
        gold: [255, 215, 0],
        green: [0, 128, 0],
        indigo: [75, 0, 130],
        khaki: [240, 230, 140],
        lightblue: [173, 216, 230],
        lightcyan: [224, 255, 255],
        lightgreen: [144, 238, 144],
        lightgrey: [211, 211, 211],
        lightpink: [255, 182, 193],
        lightyellow: [255, 255, 224],
        lime: [0, 255, 0],
        magenta: [255, 0, 255],
        maroon: [128, 0, 0],
        navy: [0, 0, 128],
        olive: [128, 128, 0],
        orange: [255, 165, 0],
        pink: [255, 192, 203],
        purple: [128, 0, 128],
        violet: [128, 0, 128],
        red: [255, 0, 0],
        silver: [192, 192, 192],
        white: [255, 255, 255],
        yellow: [255, 255, 0]
    };
})(jQuery);
jQuery.cookie = function (name, value, options) {
    if (typeof value != 'undefined') {
        options = options || {};
        if (value === null) {
            value = '';
            options.expires = -1;
        }
        var expires = '';
        if (options.expires && (typeof options.expires == 'number' || options.expires.toUTCString)) {
            var date;
            if (typeof options.expires == 'number') {
                date = new Date();
                date.setTime(date.getTime() + (options.expires * 24 * 60 * 60 * 1000));
            } else {
                date = options.expires;
            }
            expires = '; expires=' + date.toUTCString();
        }
        var path = options.path ? '; path=' + (options.path) : '';
        var domain = options.domain ? '; domain=' + (options.domain) : '';
        var secure = options.secure ? '; secure' : '';
        document.cookie = [name, '=', encodeURIComponent(value), expires, path, domain, secure].join('');
    } else {
        var cookieValue = null;
        if (document.cookie && document.cookie != '') {
            var cookies = document.cookie.split(';');
            for (var i = 0; i < cookies.length; i++) {
                var cookie = jQuery.trim(cookies[i]);
                if (cookie.substring(0, name.length + 1) == (name + '=')) {
                    cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
                    break;
                }
            }
        }
        return cookieValue;
    }
};
(function ($) {
    function encodeEvent(event) {
        return event.replace(/-/g, '--').replace(/ /g, '-');
    }
    $.fn.extend({
        delayedBind: function (timeout, event, data, callback) {
            var encEvent = encodeEvent(event);
            return this.each(function () {
                var that = this;
                if (!($(this).data('_delayedBindBound-' + encEvent + '-' + timeout))) {
                    $(this).data('_delayedBindBound-' + encEvent + '-' + timeout, true);
                    $(this).bind(event, function () {
                        var timerID = $(this).data('_delayedBindTimerID-' + encEvent + '-' + timeout);
                        if (typeof timerID != 'undefined') clearTimeout(timerID);
                        timerID = setTimeout(function () {
                            $(that).trigger('_delayedBind-' + encEvent + '-' + timeout);
                        }, timeout);
                        $(this).data('_delayedBindTimerID-' + encEvent + '-' + timeout, timerID);
                    });
                }
                $(this).bind('_delayedBind-' + encEvent + '-' + timeout, data, callback);
            });
        },
        delayedBindCancel: function (timeout, event) {
            var encEvent = encodeEvent(event);
            return this.each(function () {
                var timerID = $(this).data('_delayedBindTimerID-' + encEvent + '-' + timeout);
                if (typeof timerID != 'undefined') clearTimeout(timerID);
            });
        },
        delayedBindUnbind: function (timeout, event, callback) {
            var encEvent = encodeEvent(event);
            return this.each(function () {
                $(this).unbind('_delayedBind-' + encEvent + '-' + timeout, callback);
            });
        }
    });
})(jQuery);
(function ($) {
    $.highlightText = {
        splitAndHighlight: function (node, pat) {
            var patArray = pat.split(" ");
            for (var i = 0; i < patArray.length; i++) {
                if (patArray[i].length == 0) continue;
                $.highlightText.innerHighlight(node, patArray[i]);
            }
            return node;
        },
        innerHighlight: function (node, pat) {
            if (node.nodeType == 3) {
                var pos = node.data.search(new RegExp("\\b" + RegExp.escape(pat), "i"));
                if (pos >= 0) {
                    var spannode = document.createElement('span');
                    spannode.className = 'highlight';
                    var middlebit = node.splitText(pos);
                    middlebit.splitText(pat.length);
                    var middleclone = middlebit.cloneNode(true);
                    spannode.appendChild(middleclone);
                    middlebit.parentNode.replaceChild(spannode, middlebit);
                }
            } else if (node.nodeType == 1 && node.childNodes && !/(script|style)/i.test(node.tagName) && !(node.tagName.toLowerCase() == 'span' && node.className.match(/\bhighlight/))) {
                for (var i = 0; i < node.childNodes.length; ++i) {
                    $.highlightText.innerHighlight(node.childNodes[i], pat);
                }
            }
        }
    };
    $.fn.highlightText = function (matchString) {
        return $(this).each(function () {
            var $this = $(this);
            $this.data('highlightText', {
                originalText: $this.text()
            });
            $.highlightText.splitAndHighlight(this, matchString);
        });
    };
})(jQuery);
(function ($) {
    $.suggestions = {
        cancel: function (context) {
            if (context.data.timerID != null) {
                clearTimeout(context.data.timerID);
            }
            if (typeof context.config.cancel == 'function') {
                context.config.cancel.call(context.data.$textbox);
            }
        },
        restore: function (context) {
            context.data.$textbox.val(context.data.prevText);
        },
        update: function (context, delayed) {
            function maybeFetch() {
                if (context.data.$textbox.val() !== context.data.prevText) {
                    context.data.prevText = context.data.$textbox.val();
                    if (typeof context.config.fetch == 'function') {
                        context.config.fetch.call(context.data.$textbox, context.data.$textbox.val());
                    }
                }
            }
            if (context.data.timerID != null) {
                clearTimeout(context.data.timerID);
            }
            if (delayed) {
                context.data.timerID = setTimeout(maybeFetch, context.config.delay);
            } else {
                maybeFetch();
            }
            $.suggestions.special(context);
        },
        special: function (context) {
            if (typeof context.config.special.render == 'function') {
                setTimeout(function () {
                    $special = context.data.$container.find('.suggestions-special');
                    context.config.special.render.call($special, context.data.$textbox.val());
                }, 1);
            }
        },
        configure: function (context, property, value) {
            switch (property) {
            case 'fetch':
            case 'cancel':
            case 'special':
            case 'result':
            case '$region':
                context.config[property] = value;
                break;
            case 'suggestions':
                context.config[property] = value;
                if (typeof context.data !== 'undefined') {
                    if (context.data.$textbox.val().length == 0) {
                        context.data.$container.hide();
                    } else {
                        context.data.$container.show();
                        var newCSS = {
                            'top': context.config.$region.offset().top + context.config.$region.outerHeight(),
                            'bottom': 'auto',
                            'width': context.config.$region.outerWidth(),
                            'height': 'auto'
                        }
                        if (context.config.positionFromLeft) {
                            newCSS['left'] = context.config.$region.offset().left;
                            newCSS['right'] = 'auto';
                        } else {
                            newCSS['left'] = 'auto';
                            newCSS['right'] = $('body').width() - (context.config.$region.offset().left + context.config.$region.outerWidth());
                        }
                        context.data.$container.css(newCSS);
                        var $results = context.data.$container.children('.suggestions-results');
                        $results.empty();
                        var expWidth = -1;
                        var $autoEllipseMe = $([]);
                        var matchedText = null;
                        for (var i = 0; i < context.config.suggestions.length; i++) {
                            var text = context.config.suggestions[i];
                            var $result = $('<div />').addClass('suggestions-result').attr('rel', i).data('text', context.config.suggestions[i]).mousemove(function (e) {
                                context.data.selectedWithMouse = true;
                                $.suggestions.highlight(context, $(this).closest('.suggestions-results div'), false);
                            }).appendTo($results);
                            if (typeof context.config.result.render == 'function') {
                                context.config.result.render.call($result, context.config.suggestions[i]);
                            } else {
                                if (context.config.highlightInput) {
                                    matchedText = context.data.prevText;
                                }
                                $result.append($('<span />').css('whiteSpace', 'nowrap').text(text));
                                var $span = $result.children('span');
                                if ($span.outerWidth() > $result.width() && $span.outerWidth() > expWidth) {
                                    expWidth = $span.outerWidth() + (context.data.$container.width() - $span.parent().width());
                                }
                                $autoEllipseMe = $autoEllipseMe.add($result);
                            }
                        }
                        if (expWidth > context.data.$container.width()) {
                            var maxWidth = context.config.maxExpandFactor * context.data.$textbox.width();
                            context.data.$container.width(Math.min(expWidth, maxWidth));
                        }
                        $autoEllipseMe.autoEllipsis({
                            hasSpan: true,
                            tooltip: true,
                            matchText: matchedText
                        });
                    }
                }
                break;
            case 'maxRows':
                context.config[property] = Math.max(1, Math.min(100, value));
                break;
            case 'delay':
                context.config[property] = Math.max(0, Math.min(1200, value));
                break;
            case 'maxExpandFactor':
                context.config[property] = Math.max(1, value);
                break;
            case 'submitOnClick':
            case 'positionFromLeft':
            case 'highlightInput':
                context.config[property] = value ? true : false;
                break;
            }
        },
        highlight: function (context, result, updateTextbox) {
            var selected = context.data.$container.find('.suggestions-result-current');
            if (!result.get || selected.get(0) != result.get(0)) {
                if (result == 'prev') {
                    if (selected.is('.suggestions-special')) {
                        result = context.data.$container.find('.suggestions-result:last')
                    } else {
                        result = selected.prev();
                        if (selected.length == 0) {
                            if (context.data.$container.find('.suggestions-special').html() != "") {
                                result = context.data.$container.find('.suggestions-special');
                            } else {
                                result = context.data.$container.find('.suggestions-results div:last');
                            }
                        }
                    }
                } else if (result == 'next') {
                    if (selected.length == 0) {
                        result = context.data.$container.find('.suggestions-results div:first');
                        if (result.length == 0 && context.data.$container.find('.suggestions-special').html() != "") {
                            result = context.data.$container.find('.suggestions-special');
                        }
                    } else {
                        result = selected.next();
                        if (selected.is('.suggestions-special')) {
                            result = $([]);
                        } else if (result.length == 0 && context.data.$container.find('.suggestions-special').html() != "") {
                            result = context.data.$container.find('.suggestions-special');
                        }
                    }
                }
                selected.removeClass('suggestions-result-current');
                result.addClass('suggestions-result-current');
            }
            if (updateTextbox) {
                if (result.length == 0 || result.is('.suggestions-special')) {
                    $.suggestions.restore(context);
                } else {
                    context.data.$textbox.val(result.data('text'));
                    context.data.$textbox.change();
                }
                context.data.$textbox.trigger('change');
            }
        },
        keypress: function (e, context, key) {
            var wasVisible = context.data.$container.is(':visible');
            var preventDefault = false;
            switch (key) {
            case 40:
                if (wasVisible) {
                    $.suggestions.highlight(context, 'next', true);
                    context.data.selectedWithMouse = false;
                } else {
                    $.suggestions.update(context, false);
                }
                preventDefault = true;
                break;
            case 38:
                if (wasVisible) {
                    $.suggestions.highlight(context, 'prev', true);
                    context.data.selectedWithMouse = false;
                }
                preventDefault = wasVisible;
                break;
            case 27:
                context.data.$container.hide();
                $.suggestions.restore(context);
                $.suggestions.cancel(context);
                context.data.$textbox.trigger('change');
                preventDefault = wasVisible;
                break;
            case 13:
                context.data.$container.hide();
                preventDefault = wasVisible;
                selected = context.data.$container.find('.suggestions-result-current');
                if (selected.size() == 0 || context.data.selectedWithMouse) {
                    $.suggestions.cancel(context);
                    context.config.$region.closest('form').submit();
                } else if (selected.is('.suggestions-special')) {
                    if (typeof context.config.special.select == 'function') {
                        context.config.special.select.call(selected, context.data.$textbox);
                    }
                } else {
                    if (typeof context.config.result.select == 'function') {
                        $.suggestions.highlight(context, selected, true);
                        context.config.result.select.call(selected, context.data.$textbox);
                    } else {
                        $.suggestions.highlight(context, selected, true);
                    }
                }
                break;
            default:
                $.suggestions.update(context, true);
                break;
            }
            if (preventDefault) {
                e.preventDefault();
                e.stopImmediatePropagation();
            }
        }
    };
    $.fn.suggestions = function () {
        var returnValue = null;
        var args = arguments;
        $(this).each(function () {
            var context = $(this).data('suggestions-context');
            if (typeof context == 'undefined' || context == null) {
                context = {
                    config: {
                        'fetch': function () {},
                        'cancel': function () {},
                        'special': {},
                        'result': {},
                        '$region': $(this),
                        'suggestions': [],
                        'maxRows': 7,
                        'delay': 120,
                        'submitOnClick': false,
                        'maxExpandFactor': 3,
                        'positionFromLeft': true,
                        'highlightInput': false
                    }
                };
            }
            if (args.length > 0) {
                if (typeof args[0] == 'object') {
                    for (var key in args[0]) {
                        $.suggestions.configure(context, key, args[0][key]);
                    }
                } else if (typeof args[0] == 'string') {
                    if (args.length > 1) {
                        $.suggestions.configure(context, args[0], args[1]);
                    } else if (returnValue == null) {
                        returnValue = (args[0] in context.config ? undefined : context.config[args[0]]);
                    }
                }
            }
            if (typeof context.data == 'undefined') {
                context.data = {
                    'timerID': null,
                    'prevText': null,
                    'visibleResults': 0,
                    'mouseDownOn': $([]),
                    '$textbox': $(this),
                    'selectedWithMouse': false
                };
                var newCSS = {
                    'top': Math.round(context.data.$textbox.offset().top + context.data.$textbox.outerHeight()),
                    'width': context.data.$textbox.outerWidth(),
                    'display': 'none'
                }
                if (context.config.positionFromLeft) {
                    newCSS['left'] = context.config.$region.offset().left;
                    newCSS['right'] = 'auto';
                } else {
                    newCSS['left'] = 'auto';
                    newCSS['right'] = $('body').width() - (context.config.$region.offset().left + context.config.$region.outerWidth());
                }
                context.data.$container = $('<div />').css(newCSS).addClass('suggestions').append($('<div />').addClass('suggestions-results').mousedown(function (e) {
                    context.data.mouseDownOn = $(e.target).closest('.suggestions-results div');
                }).mouseup(function (e) {
                    var $result = $(e.target).closest('.suggestions-results div');
                    var $other = context.data.mouseDownOn;
                    context.data.mouseDownOn = $([]);
                    if ($result.get(0) != $other.get(0)) {
                        return;
                    }
                    $.suggestions.highlight(context, $result, true);
                    context.data.$container.hide();
                    if (typeof context.config.result.select == 'function') {
                        context.config.result.select.call($result, context.data.$textbox);
                    }
                    context.data.$textbox.focus();
                })).append($('<div />').addClass('suggestions-special').mousedown(function (e) {
                    context.data.mouseDownOn = $(e.target).closest('.suggestions-special');
                }).mouseup(function (e) {
                    var $special = $(e.target).closest('.suggestions-special');
                    var $other = context.data.mouseDownOn;
                    context.data.mouseDownOn = $([]);
                    if ($special.get(0) != $other.get(0)) {
                        return;
                    }
                    context.data.$container.hide();
                    if (typeof context.config.special.select == 'function') {
                        context.config.special.select.call($special, context.data.$textbox);
                    }
                    context.data.$textbox.focus();
                }).mousemove(function (e) {
                    context.data.selectedWithMouse = true;
                    $.suggestions.highlight(context, $(e.target).closest('.suggestions-special'), false);
                })).appendTo($('body'));
                $(this).attr('autocomplete', 'off').keydown(function (e) {
                    context.data.keypressed = (e.keyCode == undefined) ? e.which : e.keyCode;
                    context.data.keypressedCount = 0;
                    switch (context.data.keypressed) {
                    case 40:
                        e.preventDefault();
                        e.stopImmediatePropagation();
                        break;
                    case 38:
                    case 27:
                    case 13:
                        if (context.data.$container.is(':visible')) {
                            e.preventDefault();
                            e.stopImmediatePropagation();
                        }
                    }
                }).keypress(function (e) {
                    context.data.keypressedCount++;
                    $.suggestions.keypress(e, context, context.data.keypressed);
                }).keyup(function (e) {
                    if (context.data.keypressedCount == 0) {
                        $.suggestions.keypress(e, context, context.data.keypressed);
                    }
                }).blur(function () {
                    if (context.data.mouseDownOn.length > 0) {
                        return;
                    }
                    context.data.$container.hide();
                    $.suggestions.cancel(context);
                });
            }
            $(this).data('suggestions-context', context);
        });
        return returnValue !== null ? returnValue : $(this);
    };
})(jQuery);
(function ($) {
    $.fn.textSelection = function (command, options) {
        var fn = {
            getContents: function () {
                return this.val();
            },
            getSelection: function () {
                var e = this.get(0);
                var retval = '';
                if ($(e).is(':hidden')) {} else if (document.selection && document.selection.createRange) {
                    e.focus();
                    var range = document.selection.createRange();
                    retval = range.text;
                } else if (e.selectionStart || e.selectionStart == '0') {
                    retval = e.value.substring(e.selectionStart, e.selectionEnd);
                }
                return retval;
            },
            encapsulateSelection: function (options) {
                return this.each(function () {
                    function checkSelectedText() {
                        if (!selText) {
                            selText = options.peri;
                            isSample = true;
                        } else if (options.replace) {
                            selText = options.peri;
                        } else if (selText.charAt(selText.length - 1) == ' ') {
                            selText = selText.substring(0, selText.length - 1);
                            options.post += ' ';
                        }
                    }
                    var isSample = false;
                    if (this.style.display == 'none') {} else if (this.selectionStart || this.selectionStart == '0') {
                        $(this).focus();
                        var selText = $(this).textSelection('getSelection');
                        var startPos = this.selectionStart;
                        var endPos = this.selectionEnd;
                        var scrollTop = this.scrollTop;
                        checkSelectedText();
                        if (options.ownline) {
                            if (startPos != 0 && this.value.charAt(startPos - 1) != "\n") {
                                options.pre = "\n" + options.pre;
                            }
                            if (this.value.charAt(endPos) != "\n") {
                                options.post += "\n";
                            }
                        }
                        this.value = this.value.substring(0, startPos) + options.pre + selText + options.post + this.value.substring(endPos, this.value.length);
                        this.scrollTop = scrollTop;
                        if (window.opera) {
                            options.pre = options.pre.replace(/\r?\n/g, "\r\n");
                            selText = selText.replace(/\r?\n/g, "\r\n");
                            options.post = options.post.replace(/\r?\n/g, "\r\n");
                        }
                        if (isSample && options.selectPeri) {
                            this.selectionStart = startPos + options.pre.length;
                            this.selectionEnd = startPos + options.pre.length + selText.length;
                        } else {
                            this.selectionStart = startPos + options.pre.length + selText.length + options.post.length;
                            this.selectionEnd = this.selectionStart;
                        }
                    } else if (document.selection && document.selection.createRange) {
                        $(this).focus();
                        if (context) {
                            context.fn.restoreStuffForIE();
                        }
                        var selText = $(this).textSelection('getSelection');
                        var scrollTop = this.scrollTop;
                        var range = document.selection.createRange();
                        if (options.ownline && range.moveStart) {
                            var range2 = document.selection.createRange();
                            range2.collapse();
                            range2.moveStart('character', -1);
                            if (range2.text != "\r" && range2.text != "\n" && range2.text != "") {
                                options.pre = "\n" + options.pre;
                            }
                            var range3 = document.selection.createRange();
                            range3.collapse(false);
                            range3.moveEnd('character', 1);
                            if (range3.text != "\r" && range3.text != "\n" && range3.text != "") {
                                options.post += "\n";
                            }
                        }
                        checkSelectedText();
                        range.text = options.pre + selText + options.post;
                        if (isSample && options.selectPeri && range.moveStart) {
                            range.moveStart('character', -options.post.length - selText.length);
                            range.moveEnd('character', -options.post.length);
                        }
                        range.select();
                        this.scrollTop = scrollTop;
                    }
                    $(this).trigger('encapsulateSelection', [options.pre, options.peri, options.post, options.ownline, options.replace]);
                });
            },
            getCaretPosition: function (options) {
                function getCaret(e) {
                    var caretPos = 0,
                        endPos = 0;
                    if ($.browser.msie) {
                        var preFinished = false;
                        var periFinished = false;
                        var postFinished = false;
                        var preText, rawPreText, periText;
                        var rawPeriText, postText, rawPostText;
                        var periRange = document.selection.createRange().duplicate();
                        var preRange = document.body.createTextRange();
                        preRange.moveToElementText(e);
                        preRange.setEndPoint("EndToStart", periRange);
                        var postRange = document.body.createTextRange();
                        postRange.moveToElementText(e);
                        postRange.setEndPoint("StartToEnd", periRange);
                        preText = rawPreText = preRange.text;
                        periText = rawPeriText = periRange.text;
                        postText = rawPostText = postRange.text;
                        do {
                            if (!preFinished) {
                                if (preRange.compareEndPoints("StartToEnd", preRange) == 0) {
                                    preFinished = true;
                                } else {
                                    preRange.moveEnd("character", -1)
                                    if (preRange.text == preText) {
                                        rawPreText += "\r\n";
                                    } else {
                                        preFinished = true;
                                    }
                                }
                            }
                            if (!periFinished) {
                                if (periRange.compareEndPoints("StartToEnd", periRange) == 0) {
                                    periFinished = true;
                                } else {
                                    periRange.moveEnd("character", -1)
                                    if (periRange.text == periText) {
                                        rawPeriText += "\r\n";
                                    } else {
                                        periFinished = true;
                                    }
                                }
                            }
                            if (!postFinished) {
                                if (postRange.compareEndPoints("StartToEnd", postRange) == 0) {
                                    postFinished = true;
                                } else {
                                    postRange.moveEnd("character", -1)
                                    if (postRange.text == postText) {
                                        rawPostText += "\r\n";
                                    } else {
                                        postFinished = true;
                                    }
                                }
                            }
                        } while ((!preFinished || !periFinished || !postFinished));
                        caretPos = rawPreText.replace(/\r\n/g, "\n").length;
                        endPos = caretPos + rawPeriText.replace(/\r\n/g, "\n").length;
                    } else if (e.selectionStart || e.selectionStart == '0') {
                        caretPos = e.selectionStart;
                        endPos = e.selectionEnd;
                    }
                    return options.startAndEnd ? [caretPos, endPos] : caretPos;
                }
                return getCaret(this.get(0));
            },
            setSelection: function (options) {
                return this.each(function () {
                    if ($(this).is(':hidden')) {} else if (this.selectionStart || this.selectionStart == '0') {
                        if (options.start > this.selectionEnd) {
                            this.selectionEnd = options.end;
                            this.selectionStart = options.start;
                        } else {
                            this.selectionStart = options.start;
                            this.selectionEnd = options.end;
                        }
                    } else if (document.body.createTextRange) {
                        var selection = document.body.createTextRange();
                        selection.moveToElementText(this);
                        var length = this.value.length;
                        var newLines = this.value.match(/\n/g);
                        if (newLines) length = length - newLines.length;
                        selection.moveStart('character', options.start);
                        selection.moveEnd('character', -length + options.end);
                        try {
                            selection.select();
                        } catch (e) {}
                    }
                });
            },
            scrollToCaretPosition: function (options) {
                function getLineLength(e) {
                    return Math.floor(e.scrollWidth / ($.os.name == 'linux' ? 7 : 8));
                }

                function getCaretScrollPosition(e) {
                    var text = e.value.replace(/\r/g, "");
                    var caret = $(e).textSelection('getCaretPosition');
                    var lineLength = getLineLength(e);
                    var row = 0;
                    var charInLine = 0;
                    var lastSpaceInLine = 0;
                    for (i = 0; i < caret; i++) {
                        charInLine++;
                        if (text.charAt(i) == " ") {
                            lastSpaceInLine = charInLine;
                        } else if (text.charAt(i) == "\n") {
                            lastSpaceInLine = 0;
                            charInLine = 0;
                            row++;
                        }
                        if (charInLine > lineLength) {
                            if (lastSpaceInLine > 0) {
                                charInLine = charInLine - lastSpaceInLine;
                                lastSpaceInLine = 0;
                                row++;
                            }
                        }
                    }
                    var nextSpace = 0;
                    for (j = caret; j < caret + lineLength; j++) {
                        if (text.charAt(j) == " " || text.charAt(j) == "\n" || caret == text.length) {
                            nextSpace = j;
                            break;
                        }
                    }
                    if (nextSpace > lineLength && caret <= lineLength) {
                        charInLine = caret - lastSpaceInLine;
                        row++;
                    }
                    return ($.os.name == 'mac' ? 13 : ($.os.name == 'linux' ? 15 : 16)) * row;
                }
                return this.each(function () {
                    if ($(this).is(':hidden')) {} else if (this.selectionStart || this.selectionStart == '0') {
                        var scroll = getCaretScrollPosition(this);
                        if (options.force || scroll < $(this).scrollTop() || scroll > $(this).scrollTop() + $(this).height()) $(this).scrollTop(scroll);
                    } else if (document.selection && document.selection.createRange) {
                        var range = document.body.createTextRange();
                        var savedRange = document.selection.createRange();
                        var pos = $(this).textSelection('getCaretPosition');
                        var oldScrollTop = this.scrollTop;
                        range.moveToElementText(this);
                        range.collapse();
                        range.move('character', pos + 1);
                        range.select();
                        if (this.scrollTop != oldScrollTop) this.scrollTop += range.offsetTop;
                        else if (options.force) {
                            range.move('character', -1);
                            range.select();
                        }
                        savedRange.select();
                    }
                    $(this).trigger('scrollToPosition');
                });
            }
        };
        switch (command) {
        case 'encapsulateSelection':
            options = $.extend({
                'pre': '',
                'peri': '',
                'post': '',
                'ownline': false,
                'replace': false,
                'selectPeri': true
            }, options);
            break;
        case 'getCaretPosition':
            options = $.extend({
                'startAndEnd': false
            }, options);
            break;
        case 'setSelection':
            options = $.extend({
                'start': undefined,
                'end': undefined,
                'startContainer': undefined,
                'endContainer': undefined
            }, options);
            if (options.end === undefined) options.end = options.start;
            if (options.endContainer == undefined) options.endContainer = options.startContainer;
            break;
        case 'scrollToCaretPosition':
            options = $.extend({
                'force': false
            }, options);
            break;
        }
        var context = $(this).data('wikiEditor-context');
        var hasIframe = typeof context === 'object' && context && typeof context.$iframe !== 'undefined';
        var needSave = false;
        if (hasIframe && context.savedSelection !== null) {
            context.fn.restoreSelection();
            needSave = true;
        }
        retval = (hasIframe ? context.fn : fn)[command].call(this, options);
        if (hasIframe && needSave) {
            context.fn.saveSelection();
        }
        return retval;
    };
})(jQuery);
(function ($) {
    $.wikiEditor = {
        'modules': {},
        'instances': [],
        'browsers': {
            'ltr': {
                'msie': [
                    ['>=', 7]
                ],
                'firefox': [
                    ['>=', 2]
                ],
                'opera': [
                    ['>=', 9.6]
                ],
                'safari': [
                    ['>=', 3]
                ],
                'chrome': [
                    ['>=', 3]
                ],
                'netscape': [
                    ['>=', 9]
                ],
                'blackberry': false,
                'ipod': false,
                'iphone': false
            },
            'rtl': {
                'msie': [
                    ['>=', 8]
                ],
                'firefox': [
                    ['>=', 2]
                ],
                'opera': [
                    ['>=', 9.6]
                ],
                'safari': [
                    ['>=', 3]
                ],
                'chrome': [
                    ['>=', 3]
                ],
                'netscape': [
                    ['>=', 9]
                ],
                'blackberry': false,
                'ipod': false,
                'iphone': false
            }
        },
        'imgPath': wgScriptPath + '/extensions/UsabilityInitiative/images/wikiEditor/',
        'isSupported': function (module) {
            if (module && typeof module.name != 'undefined' && (module.name == 'toc' || module.name == 'highlight') && typeof wgReallyGiveMeTOC == 'undefined') {
                return module.supported = false;
            }
            var mod = module && 'browsers' in module ? module : $.wikiEditor;
            if (typeof mod.supported !== 'undefined') {
                return mod.supported;
            }
            return mod.supported = mw.usability.testBrowser(mod.browsers);
        },
        'isRequired': function (module, requirement) {
            if (typeof module['req'] !== 'undefined') {
                for (req in module['req']) {
                    if (module['req'][req] == requirement) {
                        return true;
                    }
                }
            }
            return false;
        },
        'autoMsg': function (object, property) {
            if (typeof property == 'object') {
                for (var i in property) {
                    if (property[i] in object || property[i] + 'Msg' in object) {
                        property = property[i];
                        break;
                    }
                }
            }
            if (property in object) {
                return object[property];
            } else if (property + 'Msg' in object) {
                if (typeof object[property + 'Msg'] == 'object') {
                    return mw.usability.getMsg.apply(mw.usability, object[property + 'Msg']);
                } else {
                    return mw.usability.getMsg(object[property + 'Msg']);
                }
            } else {
                return '';
            }
        },
        'autoLang': function (object, lang) {
            return object[lang || wgUserLanguage] || object['default'] || object;
        },
        'autoIcon': function (icon, path, lang) {
            var src = $.wikiEditor.autoLang(icon, lang);
            path = path || $.wikiEditor.imgPath;
            if (src.substr(0, 7) != 'http://' && src.substr(0, 8) != 'https://' && src[0] != '/') {
                src = path + src;
            }
            return src + '?' + wgWikiEditorIconVersion;
        },
        'autoIconOrOffset': function (icon, offset, path, lang) {
            lang = lang || wgUserLanguage;
            if (typeof offset == 'object' && lang in offset) {
                return offset[lang];
            } else if (typeof icon == 'object' && lang in icon) {
                return $.wikiEditor.autoIcon(icon, undefined, lang);
            } else {
                return $.wikiEditor.autoLang(offset, lang);
            }
        }
    };
    $.fn.wikiEditor = function () {
        if (!$j.wikiEditor.isSupported()) {
            return $(this);
        }
        var context = $(this).data('wikiEditor-context');
        if (!context || typeof context == 'undefined') {
            context = {
                '$textarea': $(this),
                'views': {},
                'modules': {},
                'data': {},
                'instance': $.wikiEditor.instances.push($(this)) - 1,
                'offsets': null,
                'htmlToTextMap': {},
                'oldHTML': null,
                'oldDelayedHTML': null,
                'oldDelayedSel': null,
                'savedSelection': null,
                'history': [],
                'historyPosition': -1,
                'oldDelayedHistoryPosition': -1
            };
            context.api = {
                'addModule': function (context, data) {
                    var modules = {};
                    if (typeof data == 'string') {
                        modules[data] = {};
                    } else if (typeof data == 'object') {
                        modules = data;
                    }
                    for (var module in modules) {
                        if (typeof module == 'string' && $.wikiEditor.isSupported($.wikiEditor.modules[module])) {
                            if ('api' in $.wikiEditor.modules[module]) {
                                for (var call in $.wikiEditor.modules[module].api) {
                                    if (!(call in context.api)) {
                                        context.api[call] = $.wikiEditor.modules[module].api[call];
                                    }
                                }
                            }
                            if ('fn' in $.wikiEditor.modules[module] && 'create' in $.wikiEditor.modules[module].fn) {
                                context.modules[module] = {};
                                $.wikiEditor.modules[module].fn.create(context, modules[module]);
                            }
                        }
                    }
                }
            };
            context.evt = {
                'keydown': function (event) {
                    switch (event.which) {
                    case 9:
                        if (event.ctrlKey || event.altKey || event.shiftKey) {
                            return true;
                        } else {
                            var $tabindexList = $j('[tabindex]:visible').sort(function (a, b) {
                                return a.tabIndex - b.tabIndex;
                            });
                            for (var i = 0; i < $tabindexList.length; i++) {
                                if ($tabindexList.eq(i).attr('id') == context.$iframe.attr('id')) {
                                    $tabindexList.get(i + 1).focus();
                                    break;
                                }
                            }
                            return false;
                        }
                        break;
                    case 86:
                        if (event.ctrlKey && $.browser.msie) {
                            context.evt.paste(event);
                        }
                        break;
                    }
                    return true;
                },
                'change': function (event) {
                    event.data.scope = 'division';
                    var newHTML = context.$content.html();
                    if (context.oldHTML != newHTML) {
                        context.fn.purgeOffsets();
                        context.oldHTML = newHTML;
                        event.data.scope = 'realchange';
                    }
                    if (context.$content.children().length == 0) {
                        context.$content.append('<p></p>');
                    }
                    return true;
                },
                'delayedChange': function (event) {
                    event.data.scope = 'division';
                    var newHTML = context.$content.html();
                    if (context.oldDelayedHTML != newHTML) {
                        context.oldDelayedHTML = newHTML;
                        event.data.scope = 'realchange';
                        var cursorPos = context.fn.getCaretPosition();
                        var t = context.fn.getOffset(cursorPos[0]);
                        if (!$.browser.msie && t && t.node.nodeName == '#text' && t.node.parentNode.nodeName.toLowerCase() == 'body') {
                            $(t.node).wrap("<p></p>");
                            context.fn.purgeOffsets();
                            context.fn.setSelection({
                                start: cursorPos[0],
                                end: cursorPos[1]
                            });
                        }
                    }
                    context.fn.updateHistory(event.data.scope == 'realchange');
                    return true;
                },
                'cut': function (event) {
                    setTimeout(function () {
                        context.$content.find('br').each(function () {
                            if ($(this).parent().is('body')) {
                                $(this).wrap($('<p></p>'));
                            }
                        });
                    }, 100);
                    return true;
                },
                'paste': function (event) {
                    var cursorPos = context.fn.getCaretPosition();
                    var offset = 0;
                    var oldLength = context.fn.getContents().length;
                    context.$content.find('*').addClass('wikiEditor');
                    if ($.layout.name !== 'webkit') {
                        context.$content.addClass('pasting');
                    }
                    setTimeout(function () {
                        context.$content.find('script,style,img,input,select,textarea,hr,button,link,meta').remove();
                        var $selection = context.$content.find(':not(.wikiEditor)');
                        var nodeToDelete = [];
                        var firstDirtyNode;
                        if ($selection.length == 0) {
                            firstDirtyNode = context.fn.getOffset(cursorPos[0]).node;
                        } else {
                            firstDirtyNode = $selection.eq(0)[0];
                        }
                        while (firstDirtyNode != null) {
                            while (firstDirtyNode.parentNode.nodeName != 'BODY' && !$(firstDirtyNode.parentNode).hasClass('wikiEditor')) {
                                firstDirtyNode = firstDirtyNode.parentNode;
                            }
                            while (firstDirtyNode.previousSibling != null && !$(firstDirtyNode.previousSibling).hasClass('wikiEditor')) {
                                if ($(firstDirtyNode.previousSibling).hasClass('#comment')) {
                                    $(firstDirtyNode).remove();
                                } else {
                                    firstDirtyNode = firstDirtyNode.previousSibling;
                                }
                            }
                            var $lastDirtyNode = $(firstDirtyNode);
                            var cc = makeContentCollector($.browser, null);
                            while (firstDirtyNode != null && !$(firstDirtyNode).hasClass('wikiEditor')) {
                                cc.collectContent(firstDirtyNode);
                                cc.notifyNextNode(firstDirtyNode.nextSibling);
                                pastedContent = cc.getLines();
                                if ((pastedContent.length <= 1 || pastedContent[pastedContent.length - 1] !== "") && firstDirtyNode.nextSibling) {
                                    nodeToDelete.push(firstDirtyNode);
                                    firstDirtyNode = firstDirtyNode.nextSibling;
                                    cc.collectContent(firstDirtyNode);
                                    cc.notifyNextNode(firstDirtyNode.nextSibling);
                                }
                                nodeToDelete.push(firstDirtyNode);
                                firstDirtyNode = firstDirtyNode.nextSibling;
                            }
                            var ccData = cc.finish();
                            var pastedContent = ccData.lines;
                            if (pastedContent.length == 0 && firstDirtyNode) {
                                offset += $(firstDirtyNode).text().length;
                            }
                            if (nodeToDelete.length > 0) {
                                $lastDirtyNode = $(nodeToDelete[nodeToDelete.length - 1]);
                            }
                            var testVal = '';
                            testVal = $(nodeToDelete[0]).text();
                            var pastedPretty = '';
                            for (var i = 0; i < pastedContent.length; i++) {
                                pastedPretty = pastedContent[i].replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/\r?\n/g, '\\n');
                                match = pastedContent[i].match(/^[\s]+[^\s]/);
                                if (match != null && match.length > 0) {
                                    index = match[0].length;
                                    leadingSpace = match[0].replace(/[\s]/g, '&nbsp;');
                                    pastedPretty = leadingSpace + pastedPretty.substring(index, pastedPretty.length);
                                }
                                $newElement = $('<p class="wikiEditor" ></p>');
                                if (pastedPretty) {
                                    $newElement.html('<span class = "wikiEditor">' + pastedPretty + '</span>');
                                } else {
                                    $newElement.html('<br class="wikiEditor">');
                                }
                                $newElement.insertAfter($lastDirtyNode);
                                offset += pastedPretty.length;
                                $lastDirtyNode = $newElement;
                            }
                            while (nodeToDelete.length > 0) {
                                $(nodeToDelete.pop()).remove();
                            }
                            $selection = context.$content.find(':not(.wikiEditor)');
                            if ($selection.length == 0) {
                                firstDirtyNode = null;
                            } else {
                                firstDirtyNode = $selection.eq(0)[0];
                            }
                        }
                        context.$content.find('.wikiEditor').removeClass('wikiEditor');
                        var restoreTo = cursorPos[1] + offset;
                        context.fn.setSelection({
                            start: restoreTo,
                            end: restoreTo
                        });
                    }, 0);
                    return true;
                },
                'ready': function (event) {
                    context.history.push({
                        'html': context.$content.html(),
                        'sel': context.fn.getCaretPosition()
                    });
                    return true;
                }
            };
            context.fn = {
                'trigger': function (name, event) {
                    if (typeof event == 'undefined') {
                        event = {
                            'type': 'custom'
                        };
                    }
                    if (typeof event.data == 'undefined') {
                        event.data = {};
                    }
                    if (name in context.evt) {
                        if (!context.evt[name](event)) {
                            return false;
                        }
                    }
                    var returnFromModules = null;
                    for (var module in context.modules) {
                        if (module in $.wikiEditor.modules && 'evt' in $.wikiEditor.modules[module] && name in $.wikiEditor.modules[module].evt) {
                            var ret = $.wikiEditor.modules[module].evt[name](context, event);
                            if (ret != null) {
                                if (returnFromModules == null) {
                                    returnFromModules = ret;
                                } else {
                                    returnFromModules = returnFromModules && ret;
                                }
                            }
                        }
                    }
                    if (returnFromModules != null) {
                        return returnFromModules;
                    } else {
                        return true;
                    }
                },
                'addButton': function (options) {
                    context.$controls.show();
                    context.$buttons.show();
                    return $('<button />').text($.wikiEditor.autoMsg(options, 'caption')).click(options.action).appendTo(context.$buttons);
                },
                'addView': function (options) {
                    function addTab(options) {
                        context.$controls.show();
                        context.$tabs.show();
                        return $('<div></div>').attr('rel', 'wikiEditor-ui-view-' + options.name).addClass(context.view == options.name ? 'current' : null).append($('<a></a>').attr('href', '#').mousedown(function () {
                            return false;
                        }).click(function (event) {
                            context.$ui.find('.wikiEditor-ui-view').hide();
                            context.$ui.find('.' + $(this).parent().attr('rel')).show();
                            context.$tabs.find('div').removeClass('current');
                            $(this).parent().addClass('current');
                            $(this).blur();
                            if ('init' in options && typeof options.init == 'function') {
                                options.init(context);
                            }
                            event.preventDefault();
                            return false;
                        }).text($.wikiEditor.autoMsg(options, 'title'))).appendTo(context.$tabs);
                    }
                    if (!context.$tabs.children().size()) {
                        addTab({
                            'name': 'wikitext',
                            'titleMsg': 'wikieditor-wikitext-tab'
                        });
                    }
                    addTab(options);
                    return $('<div></div>').addClass('wikiEditor-ui-view wikiEditor-ui-view-' + options.name).hide().appendTo(context.$ui);
                },
                'highlightLine': function ($element, mode) {
                    if (!$element.is('p')) {
                        $element = $element.closest('p');
                    }
                    $element.css('backgroundColor', '#AACCFF');
                    setTimeout(function () {
                        $element.animate({
                            'backgroundColor': 'white'
                        }, 'slow');
                    }, 100);
                    setTimeout(function () {
                        $element.css('backgroundColor', 'white');
                    }, 1000);
                },
                'htmlToText': function (html) {
                    if (html in context.htmlToTextMap) {
                        return context.htmlToTextMap[html];
                    }
                    var origHTML = html;
                    html = html.replace(/\r?\n/g, "").replace(/&nbsp;/g, " ").replace(/\<br[^\>]*\>\<\/p\>/gi, '</p>').replace(/\<\/p\>\s*\<p[^\>]*\>/gi, "\n").replace(/\<br[^\>]*\>/gi, "\n").replace(/\<\/p\>(\n*)\<p[^\>]*\>/gi, "$1\n").replace(/\<p[^\>]*\><p[^\>]*\>/gi, '<p>').replace(/\<\/p\><\/p\>/gi, '</p>');
                    var leading = html.match(/^\s*/)[0];
                    var trailing = html.match(/\s*$/)[0];
                    html = html.substr(leading.length, html.length - leading.length - trailing.length);
                    var $pre = $('<pre>' + html + '</pre>');
                    $pre.find('.wikiEditor-noinclude').each(function () {
                        $(this).remove();
                    });
                    $pre.find('.wikiEditor-tab').each(function () {
                        $(this).text("\t");
                    });
                    $pre.find('br').each(function () {
                        $(this).replaceWith("\n");
                    });
                    $pre.find('p').each(function () {
                        var text = $(this).text();
                        var t = new context.fn.rawTraverser(this.firstChild, this, $pre.get(0), true).prev();
                        while (t && t.node.nodeName != '#text' && t.node.nodeName != 'BR' && t.node.nodeName != 'P') {
                            t = t.prev();
                        }
                        if (t) {
                            text = "\n" + text;
                        }
                        t = new context.fn.rawTraverser(this.lastChild, this, $pre.get(0), true).next();
                        while (t && t.node.nodeName != '#text' && t.node.nodeName != 'BR' && t.node.nodeName != 'P') {
                            t = t.next();
                        }
                        if (t && !t.inP && t.node.nodeName == '#text' && t.node.nodeValue.charAt(0) != '\n' && t.node.nodeValue.charAt(0) != '\r') {
                            text += "\n";
                        }
                        $(this).text(text);
                    });
                    var retval;
                    if ($.browser.msie) {
                        retval = $('<pre>' + $pre.html() + '</pre>').text().replace(/\r/g, '\n');
                    } else {
                        retval = $pre.text();
                    }
                    return context.htmlToTextMap[origHTML] = leading + retval + trailing;
                },
                'beforeSelection': function (classname, strict) {
                    if (typeof classname == 'undefined') {
                        classname = '';
                    }
                    var e = null,
                        offset = null;
                    if (context.$iframe[0].contentWindow.getSelection) {
                        var selection = context.$iframe[0].contentWindow.getSelection();
                        if (selection.baseNode !== null) {
                            e = selection.getRangeAt(0).startContainer;
                            offset = selection.getRangeAt(0).startOffset;
                        } else {
                            return null;
                        }
                        var body = context.$iframe[0].contentWindow.document.body;
                        if ($.browser.opera && e == body && offset == 1) {
                            return null;
                        }
                    }
                    if (!e && context.$iframe[0].contentWindow.document.selection) {
                        var range = context.$iframe[0].contentWindow.document.selection.createRange();
                        var range2 = context.$iframe[0].contentWindow.document.body.createTextRange();
                        try {
                            range2.setEndPoint('EndToStart', range);
                        } catch (ex) {
                            return null;
                        }
                        var seekPos = context.fn.htmlToText(range2.htmlText).length;
                        var offset = context.fn.getOffset(seekPos);
                        e = offset ? offset.node : null;
                        offset = offset ? offset.offset : null;
                        if (!e) {
                            return null;
                        }
                    }
                    if (e.nodeName != '#text') {
                        var newE = e.firstChild;
                        for (var i = 0; i < offset - 1 && newE; i++) {
                            newE = newE.nextSibling;
                        }
                        while (newE && newE.lastChild) {
                            newE = newE.lastChild;
                        }
                        e = newE || e;
                    }
                    var classStr = ' ' + classname + ' ';
                    while (e) {
                        if (!strict && (!classname || (' ' + e.className + ' ').indexOf(classStr) != -1)) {
                            return $(e);
                        }
                        var next = e.previousSibling;
                        while (next && next.lastChild) {
                            next = next.lastChild;
                        }
                        e = next || e.parentNode;
                        strict = false;
                    }
                    return $([]);
                },
                'rawTraverser': function (node, inP, ancestor, skipNoinclude) {
                    this.node = node;
                    this.inP = inP;
                    this.ancestor = ancestor;
                    this.skipNoinclude = skipNoinclude;
                    this.next = function () {
                        var p = this.node;
                        var nextInP = this.inP;
                        while (p && !p.nextSibling) {
                            p = p.parentNode;
                            if (p == this.ancestor) {
                                p = null;
                            }
                            if (p && p.nodeName == "P") {
                                nextInP = null;
                            }
                        }
                        p = p ? p.nextSibling : null;
                        if (p && p.nodeName == "P") {
                            nextInP = p;
                        }
                        do {
                            if (this.skipNoinclude) {
                                while (p && (' ' + p.className + ' ').indexOf(' wikiEditor-noinclude ') != -1) {
                                    p = p.nextSibling;
                                }
                            }
                            if (p && p.firstChild) {
                                p = p.firstChild;
                                if (p.nodeName == "P") {
                                    nextInP = p;
                                }
                            }
                        } while (p && p.firstChild);
                        return p ? {
                            'node': p,
                            'inP': nextInP,
                            'ancestor': this.ancestor,
                            'skipNoinclude': this.skipNoinclude,
                            'next': this.next,
                            'prev': this.prev
                        } : null;
                    };
                    this.prev = function () {
                        var p = this.node;
                        var prevInP = this.inP;
                        while (p && !p.previousSibling) {
                            p = p.parentNode;
                            if (p == this.ancestor) {
                                p = null;
                            }
                            if (p && p.nodeName == "P") {
                                prevInP = null;
                            }
                        }
                        p = p ? p.previousSibling : null;
                        if (p && p.nodeName == "P") {
                            prevInP = p;
                        }
                        do {
                            if (this.skipNoinclude) {
                                while (p && (' ' + p.className + ' ').indexOf(' wikiEditor-noinclude ') != -1) {
                                    p = p.previousSibling;
                                }
                            }
                            if (p && p.lastChild) {
                                p = p.lastChild;
                                if (p.nodeName == "P") {
                                    prevInP = p;
                                }
                            }
                        } while (p && p.lastChild);
                        return p ? {
                            'node': p,
                            'inP': prevInP,
                            'ancestor': this.ancestor,
                            'skipNoinclude': this.skipNoinclude,
                            'next': this.next,
                            'prev': this.prev
                        } : null;
                    };
                },
                'traverser': function (start) {
                    var startNode = start.jquery ? start.get(0) : start;
                    var node = startNode;
                    var inP = node.nodeName == "P" ? node : null;
                    do {
                        while (node && (' ' + node.className + ' ').indexOf(' wikiEditor-noinclude ') != -1) {
                            node = node.nextSibling;
                        }
                        if (node && node.firstChild) {
                            node = node.firstChild;
                            if (node.nodeName == "P") {
                                inP = node;
                            }
                        }
                    } while (node && node.firstChild);
                    return new context.fn.rawTraverser(node, inP, startNode, true);
                },
                'getOffset': function (offset) {
                    if (!context.offsets) {
                        context.fn.refreshOffsets();
                    }
                    if (offset in context.offsets) {
                        return context.offsets[offset];
                    }
                    var lowerBound = -1;
                    for (var o in context.offsets) {
                        var realO = parseInt(o);
                        if (realO < offset && realO > lowerBound) {
                            lowerBound = realO;
                        }
                    }
                    if (!(lowerBound in context.offsets)) {
                        return null;
                    }
                    var base = context.offsets[lowerBound];
                    return context.offsets[offset] = {
                        'node': base.node,
                        'offset': base.offset + offset - lowerBound,
                        'length': base.length,
                        'lastTextNode': base.lastTextNode
                    };
                },
                'purgeOffsets': function () {
                    context.offsets = null;
                },
                'refreshOffsets': function () {
                    context.offsets = [];
                    var t = context.fn.traverser(context.$content);
                    var pos = 0,
                        lastTextNode = null;
                    while (t) {
                        if (t.node.nodeName != '#text' && t.node.nodeName != 'BR') {
                            t = t.next();
                            continue;
                        }
                        var nextPos = t.node.nodeName == '#text' ? pos + t.node.nodeValue.length : pos + 1;
                        var nextT = t.next();
                        var leavingP = t.node.nodeName == '#text' && t.inP && nextT && (!nextT.inP || nextT.inP != t.inP);
                        context.offsets[pos] = {
                            'node': t.node,
                            'offset': 0,
                            'length': nextPos - pos + (leavingP ? 1 : 0),
                            'lastTextNode': lastTextNode
                        };
                        if (leavingP) {
                            context.offsets[nextPos] = {
                                'node': t.node,
                                'offset': nextPos - pos,
                                'length': nextPos - pos + 1,
                                'lastTextNode': lastTextNode
                            };
                        }
                        pos = nextPos + (leavingP ? 1 : 0);
                        if (t.node.nodeName == '#text') {
                            lastTextNode = t.node;
                        }
                        t = nextT;
                    }
                },
                'saveSelection': function () {
                    if (!$.browser.msie) {
                        return;
                    }
                    if (typeof context.$iframe != 'undefined') {
                        context.$iframe[0].contentWindow.focus();
                        context.savedSelection = context.$iframe[0].contentWindow.document.selection.createRange();
                    } else {
                        context.$textarea.focus();
                        context.savedSelection = document.selection.createRange();
                    }
                },
                'restoreSelection': function () {
                    if (!$.browser.msie || context.savedSelection === null) {
                        return;
                    }
                    if (typeof context.$iframe != 'undefined') {
                        context.$iframe[0].contentWindow.focus();
                    } else {
                        context.$textarea.focus();
                    }
                    context.savedSelection.select();
                    context.savedSelection = null;
                },
                'updateHistory': function (htmlChange) {
                    var newHTML = context.$content.html();
                    var newSel = context.fn.getCaretPosition();
                    if (context.history.length == 0 || (htmlChange && context.oldDelayedHistoryPosition == context.historyPosition)) {
                        context.oldDelayedSel = newSel;
                        if (context.historyPosition < -1) {
                            context.history.splice(context.history.length + context.historyPosition + 1);
                            context.historyPosition = -1;
                        }
                        context.history.push({
                            'html': newHTML,
                            'sel': newSel
                        });
                        while (context.history.length > 10) {
                            context.history.shift();
                        }
                    } else if (context.oldDelayedSel != newSel) {
                        context.oldDelayedSel = newSel;
                        context.history[context.history.length + context.historyPosition].sel = newSel;
                    }
                    context.oldDelayedHistoryPosition = context.historyPosition;
                },
                'setupIframe': function () {
                    context.$iframe = $('<iframe></iframe>').attr({
                        'frameBorder': 0,
                        'border': 0,
                        'tabindex': 1,
                        'src': wgScriptPath + '/extensions/UsabilityInitiative/js/plugins/jquery.wikiEditor.html?' + 'instance=' + context.instance + '&ts=' + (new Date()).getTime() + '&is=content',
                        'id': 'wikiEditor-iframe-' + context.instance
                    }).css({
                        'backgroundColor': 'white',
                        'width': '100%',
                        'height': context.$textarea.height(),
                        'display': 'none',
                        'overflow-y': 'scroll',
                        'overflow-x': 'hidden'
                    }).insertAfter(context.$textarea).load(function () {
                        if (!this.isSecondRun) {
                            context.$iframe[0].contentWindow.document.designMode = 'on';
                            if ($.browser.msie) {
                                this.isSecondRun = true;
                                return;
                            }
                        }
                        context.$content = $(context.$iframe[0].contentWindow.document.body);
                        for (module in context.modules) {
                            context.$content.addClass('wikiEditor-' + module);
                        }
                        var html = context.$textarea.val().replace(/&esc;/g, '&esc;esc;').replace(/\<p\>/g, '&esc;&lt;p&gt;').replace(/\<\/p\>/g, '&esc;&lt;/p&gt;').replace(/\<span class="wikiEditor-tab"\>\<\/span\>/g, '&esc;&lt;span&nbsp;class=&quot;wikiEditor-tab&quot;&gt;&lt;/span&gt;').replace(/&nbsp;/g, '&esc;&amp;nbsp;');
                        if ($.browser.msie) {
                            html = html.replace(/\t/g, '<span class="wikiEditor-tab"></span>');
                            if ($.browser.versionNumber <= 7) {
                                html = html.replace(/ /g, "&nbsp;");
                            } else {
                                html = html.replace(/(^|\n) /g, "$1&nbsp;");
                            }
                        }
                        html = $('<div />').text('<p>' + html.replace(/\r?\n/g, '</p><p>') + '</p>').html().replace(/&amp;nbsp;/g, '&nbsp;').replace(/&lt;p&gt;/g, '<p>').replace(/&lt;\/p&gt;/g, '</p>').replace(/&lt;span( |&nbsp;)class=("|&quot;)wikiEditor-tab("|&quot;)&gt;&lt;\/span&gt;/g, '<span class="wikiEditor-tab"></span>').replace(/<p><\/p>/g, '<p><br></p>').replace(/&amp;esc;&amp;amp;nbsp;/g, '&amp;nbsp;').replace(/&amp;esc;&amp;lt;p&amp;gt;/g, '&lt;p&gt;').replace(/&amp;esc;&amp;lt;\/p&amp;gt;/g, '&lt;/p&gt;').replace(/&amp;esc;&amp;lt;span&amp;nbsp;class=&amp;quot;wikiEditor-tab&amp;quot;&amp;gt;&amp;lt;\/span&amp;gt;/g, '&lt;span class="wikiEditor-tab"&gt;&lt;\/span&gt;').replace(/&amp;esc;esc;/g, '&amp;esc;');
                        context.$content.html(html);
                        if ($('body').is('.rtl')) {
                            context.$content.addClass('rtl').attr('dir', 'rtl');
                        }
                        context.$textarea.attr('disabled', true);
                        context.$textarea.hide();
                        context.$iframe.show();
                        context.fn.trigger('ready');
                        context.oldHTML = context.oldDelayedHTML = context.$content.html();
                        $(context.$iframe[0].contentWindow.document).bind('keydown', function (event) {
                            event.jQueryNode = context.fn.getElementAtCursor();
                            return context.fn.trigger('keydown', event);
                        }).bind('keyup', function (event) {
                            event.jQueryNode = context.fn.getElementAtCursor();
                            return context.fn.trigger('keyup', event);
                        }).bind('keypress', function (event) {
                            event.jQueryNode = context.fn.getElementAtCursor();
                            return context.fn.trigger('keypress', event);
                        }).bind('paste', function (event) {
                            return context.fn.trigger('paste', event);
                        }).bind('cut', function (event) {
                            return context.fn.trigger('cut', event);
                        }).bind('keyup paste mouseup cut encapsulateSelection', function (event) {
                            return context.fn.trigger('change', event);
                        }).delayedBind(250, 'keyup paste mouseup cut encapsulateSelection', function (event) {
                            context.fn.trigger('delayedChange', event);
                        });
                    });
                    context.$textarea.closest('form').submit(function () {
                        context.$textarea.attr('disabled', false);
                        context.$textarea.val(context.$textarea.textSelection('getContents'));
                    });
                    context.fallbackWindowOnBeforeUnload = window.onbeforeunload;
                    window.onbeforeunload = function () {
                        context.$textarea.val(context.$textarea.textSelection('getContents'));
                        if (context.fallbackWindowOnBeforeUnload) {
                            return context.fallbackWindowOnBeforeUnload();
                        }
                    };
                },
                'getElementAtCursor': function () {
                    if (context.$iframe[0].contentWindow.getSelection) {
                        var selection = context.$iframe[0].contentWindow.getSelection();
                        if (selection.rangeCount == 0) {
                            return $([]);
                        }
                        var sc = selection.getRangeAt(0).startContainer;
                        if (sc.nodeName == "#text") sc = sc.parentNode;
                        return $(sc);
                    } else if (context.$iframe[0].contentWindow.document.selection) {
                        var selection = context.$iframe[0].contentWindow.document.selection.createRange();
                        return $(selection.parentElement());
                    }
                },
                'getContents': function () {
                    var html;
                    if ($.browser.msie) {
                        var $c = $(context.$content.get(0).cloneNode(true));
                        $c.find('p').each(function () {
                            if ($(this).html() == '') {
                                $(this).replaceWith('<p></p>');
                            }
                        });
                        html = $c.html();
                    } else {
                        html = context.$content.html();
                    }
                    return context.fn.htmlToText(html);
                },
                'getSelection': function () {
                    var retval;
                    if (context.$iframe[0].contentWindow.getSelection) {
                        retval = context.$iframe[0].contentWindow.getSelection();
                        if ($.browser.opera) {
                            if (retval.rangeCount > 0) {
                                retval = context.fn.htmlToText($('<pre />').append(retval.getRangeAt(0).cloneContents()).html());
                            } else {
                                retval = '';
                            }
                        }
                    } else if (context.$iframe[0].contentWindow.document.selection) {
                        retval = context.$iframe[0].contentWindow.document.selection.createRange();
                    }
                    if (typeof retval.text != 'undefined') {
                        retval = context.fn.htmlToText(retval.htmlText);
                    } else if (typeof retval.toString != 'undefined') {
                        retval = retval.toString();
                    }
                    return retval;
                },
                'encapsulateSelection': function (options) {
                    var selText = $(this).textSelection('getSelection');
                    var selTextArr;
                    var collapseToEnd = false;
                    var selectAfter = false;
                    var setSelectionTo = null;
                    var pre = options.pre,
                        post = options.post;
                    if (!selText) {
                        selText = options.peri;
                        selectAfter = true;
                    } else if (options.peri == selText.replace(/\s+$/, '')) {
                        selText = selText.replace(/\s+$/, '');
                        collapseToEnd = true;
                        selectAfter = true;
                    } else if (options.replace) {
                        selText = options.peri;
                    } else if (selText.charAt(selText.length - 1) == ' ') {
                        selText = selText.substring(0, selText.length - 1);
                        post += ' ';
                    }
                    if (options.splitlines) {
                        selTextArr = selText.split(/\n/);
                    }
                    if (context.$iframe[0].contentWindow.getSelection) {
                        var range = context.$iframe[0].contentWindow.getSelection().getRangeAt(0);
                        if (collapseToEnd) {
                            if (range.endContainer.nodeName == 'BR') {
                                range.setEndBefore(range.endContainer);
                            }
                            range.collapse(false);
                        }
                        if (options.ownline) {
                            var atStart = false,
                                atEnd = false;
                            var body = context.$content.get(0);
                            if (range.startOffset == 0) {
                                atStart = true;
                            } else if (range.startContainer == body) {
                                var n = body.firstChild;
                                for (var i = 0; i < range.startOffset - 1 && n; i++) {
                                    n = n.nextSibling;
                                }
                                if (n && n.nodeName == 'BR') {
                                    atStart = true;
                                } else {
                                    atEnd = true;
                                }
                            }
                            if ((range.endOffset == 0 && range.endContainer.nodeValue == null) || (range.endContainer.nodeName == '#text' && range.endOffset == range.endContainer.nodeValue.length) || (range.endContainer.nodeName == 'P' && range.endContainer.nodeValue == null)) {
                                atEnd = true;
                            }
                            if (!atStart) {
                                pre = "\n" + options.pre;
                            }
                            if (!atEnd) {
                                post += "\n";
                            }
                        }
                        var insertText = "";
                        if (options.splitlines) {
                            for (var j = 0; j < selTextArr.length; j++) {
                                insertText = insertText + pre + selTextArr[j] + post;
                                if (j != selTextArr.length - 1) {
                                    insertText += "\n";
                                }
                            }
                        } else {
                            insertText = pre + selText + post;
                        }
                        var insertLines = insertText.split("\n");
                        range.extractContents();
                        var firstNode = null,
                            lastNode = null;
                        var selSC = null,
                            selEC = null,
                            selSO = null,
                            selEO = null,
                            offset = 0;
                        for (var i = insertLines.length - 1; i >= 0; i--) {
                            firstNode = context.$iframe[0].contentWindow.document.createTextNode(insertLines[i]);
                            range.insertNode(firstNode);
                            lastNode = lastNode || firstNode;
                            var newOffset = offset + insertLines[i].length;
                            if (!selEC && post.length <= newOffset) {
                                selEC = firstNode;
                                selEO = selEC.nodeValue.length - (post.length - offset);
                            }
                            if (selEC && !selSC && pre.length >= insertText.length - newOffset) {
                                selSC = firstNode;
                                selSO = pre.length - (insertText.length - newOffset);
                            }
                            offset = newOffset;
                            if (i > 0) {
                                firstNode = context.$iframe[0].contentWindow.document.createElement('br');
                                range.insertNode(firstNode);
                                newOffset = offset + 1;
                                if (!selEC && post.length <= newOffset) {
                                    selEC = firstNode;
                                    selEO = 1 - (post.length - offset);
                                }
                                if (selEC && !selSC && pre.length >= insertText.length - newOffset) {
                                    selSC = firstNode;
                                    selSO = pre.length - (insertText.length - newOffset);
                                }
                                offset = newOffset;
                            }
                        }
                        if (firstNode) {
                            context.fn.scrollToTop($(firstNode.parentNode));
                        }
                        if (selectAfter) {
                            setSelectionTo = {
                                startContainer: selSC,
                                endContainer: selEC,
                                start: selSO,
                                end: selEO
                            };
                        } else if (lastNode) {
                            setSelectionTo = {
                                startContainer: lastNode,
                                endContainer: lastNode,
                                start: lastNode.nodeValue.length,
                                end: lastNode.nodeValue.length
                            };
                        }
                    } else if (context.$iframe[0].contentWindow.document.selection) {
                        context.$iframe[0].contentWindow.focus();
                        var range = context.$iframe[0].contentWindow.document.selection.createRange();
                        if (options.ownline && range.moveStart) {
                            var range2 = context.$iframe[0].contentWindow.document.selection.createRange();
                            range2.collapse();
                            range2.moveStart('character', -1);
                            if (range2.text != "\r" && range2.text != "\n" && range2.text != "") {
                                pre = "\n" + pre;
                            }
                            var range3 = context.$iframe[0].contentWindow.document.selection.createRange();
                            range3.collapse(false);
                            range3.moveEnd('character', 1);
                            if (range3.text != "\r" && range3.text != "\n" && range3.text != "") {
                                post += "\n";
                            }
                        }
                        if (collapseToEnd) {
                            range.collapse(false);
                        }
                        var insertText = "";
                        if (options.splitlines) {
                            for (var j = 0; j < selTextArr.length; j++) {
                                insertText = insertText + pre + selTextArr[j] + post;
                                if (j != selTextArr.length - 1) {
                                    insertText += "\n";
                                }
                            }
                        } else {
                            insertText = pre + selText + post;
                        }
                        range.pasteHTML(insertText.replace(/\</g, '&lt;').replace(/>/g, '&gt;').replace(/\r?\n/g, '<br />'));
                        if (selectAfter) {
                            range.moveStart('character', -post.length - selText.length);
                            range.moveEnd('character', -post.length);
                            range.select();
                        }
                    }
                    if (setSelectionTo) {
                        context.fn.setSelection(setSelectionTo);
                    }
                    $(context.$iframe[0].contentWindow.document).trigger('encapsulateSelection', [pre, options.peri, post, options.ownline, options.replace]);
                    return context.$textarea;
                },
                'getCaretPosition': function (options) {
                    var startPos = null,
                        endPos = null;
                    if (context.$iframe[0].contentWindow.getSelection) {
                        var selection = context.$iframe[0].contentWindow.getSelection();
                        if (selection.rangeCount == 0) {
                            return [0, 0];
                        }
                        var sc = selection.getRangeAt(0).startContainer,
                            ec = selection.getRangeAt(0).endContainer;
                        var so = selection.getRangeAt(0).startOffset,
                            eo = selection.getRangeAt(0).endOffset;
                        if (sc.nodeName == 'BODY') {
                            var n = sc.firstChild;
                            for (var i = 0; i < so - 1 && n; i++) {
                                n = n.nextSibling;
                            }
                            sc = n;
                            so = 0;
                        }
                        if (ec.nodeName == 'BODY') {
                            var n = ec.firstChild;
                            for (var i = 0; i < eo - 1 && n; i++) {
                                n = n.nextSibling;
                            }
                            ec = n;
                            eo = 0;
                        }
                        while (sc.firstChild) {
                            sc = sc.firstChild;
                        }
                        while (ec.firstChild) {
                            ec = ec.firstChild;
                        }
                        context.fn.getOffset(0);
                        var o;
                        for (o in context.offsets) {
                            if (startPos === null && context.offsets[o].node == sc) {
                                startPos = ~~o + so - context.offsets[o].offset;
                            }
                            if (startPos !== null && context.offsets[o].node == ec) {
                                endPos = ~~o + eo - context.offsets[o].offset;
                                break;
                            }
                        }
                    } else if (context.$iframe[0].contentWindow.document.selection) {
                        var d = context.$iframe[0].contentWindow.document;
                        var postFinished = false;
                        var periFinished = false;
                        var postFinished = false;
                        var preText, rawPreText, periText;
                        var rawPeriText, postText, rawPostText;
                        try {
                            var periRange = d.selection.createRange().duplicate();
                            var preRange = d.body.createTextRange();
                            preRange.setEndPoint("EndToStart", periRange);
                            var postRange = d.body.createTextRange();
                            postRange.setEndPoint("StartToEnd", periRange);
                            preText = rawPreText = preRange.text;
                            periText = rawPeriText = periRange.text;
                            postText = rawPostText = postRange.text;
                            do {
                                if (!postFinished) {
                                    if (preRange.compareEndPoints("StartToEnd", preRange) == 0) {
                                        postFinished = true;
                                    } else {
                                        preRange.moveEnd("character", -1)
                                        if (preRange.text == preText) {
                                            rawPreText += "\r\n";
                                        } else {
                                            postFinished = true;
                                        }
                                    }
                                }
                                if (!periFinished) {
                                    if (periRange.compareEndPoints("StartToEnd", periRange) == 0) {
                                        periFinished = true;
                                    } else {
                                        periRange.moveEnd("character", -1)
                                        if (periRange.text == periText) {
                                            rawPeriText += "\r\n";
                                        } else {
                                            periFinished = true;
                                        }
                                    }
                                }
                                if (!postFinished) {
                                    if (postRange.compareEndPoints("StartToEnd", postRange) == 0) {
                                        postFinished = true;
                                    } else {
                                        postRange.moveEnd("character", -1)
                                        if (postRange.text == postText) {
                                            rawPostText += "\r\n";
                                        } else {
                                            postFinished = true;
                                        }
                                    }
                                }
                            } while ((!postFinished || !periFinished || !postFinished));
                            startPos = rawPreText.replace(/\r\n/g, "\n").length;
                            endPos = startPos + rawPeriText.replace(/\r\n/g, "\n").length;
                        } catch (e) {
                            startPos = endPos = 0;
                        }
                    }
                    return [startPos, endPos];
                },
                'setSelection': function (options) {
                    var sc = options.startContainer,
                        ec = options.endContainer;
                    sc = sc && sc.jquery ? sc[0] : sc;
                    ec = ec && ec.jquery ? ec[0] : ec;
                    if (context.$iframe[0].contentWindow.getSelection) {
                        var start = options.start,
                            end = options.end;
                        if (!sc || !ec) {
                            var s = context.fn.getOffset(start);
                            var e = context.fn.getOffset(end);
                            sc = s ? s.node : null;
                            ec = e ? e.node : null;
                            start = s ? s.offset : null;
                            end = e ? e.offset : null;
                            if (sc != null && sc.nodeName == '#text' && start > sc.nodeValue.length) {
                                start = sc.nodeValue.length - 1;
                            }
                            if (ec != null && ec.nodeName == '#text' && end > ec.nodeValue.length) {
                                end = ec.nodeValue.length - 1;
                            }
                        }
                        if (!sc || !ec) {
                            return context.$textarea;
                        }
                        var sel = context.$iframe[0].contentWindow.getSelection();
                        while (sc.firstChild && sc.nodeName != '#text') {
                            sc = sc.firstChild;
                        }
                        while (ec.firstChild && ec.nodeName != '#text') {
                            ec = ec.firstChild;
                        }
                        var range = context.$iframe[0].contentWindow.document.createRange();
                        range.setStart(sc, start);
                        range.setEnd(ec, end);
                        sel.removeAllRanges();
                        sel.addRange(range);
                        context.$iframe[0].contentWindow.focus();
                    } else if (context.$iframe[0].contentWindow.document.body.createTextRange) {
                        var range = context.$iframe[0].contentWindow.document.body.createTextRange();
                        if (sc) {
                            range.moveToElementText(sc);
                        }
                        range.collapse();
                        range.moveEnd('character', options.start);
                        var range2 = context.$iframe[0].contentWindow.document.body.createTextRange();
                        if (ec) {
                            range2.moveToElementText(ec);
                        }
                        range2.collapse();
                        range2.moveEnd('character', options.end);
                        var matches, counted = 0;
                        while (matches = range.htmlText.match(/\<\/p\>(\<br[^\>]*\>)+\<p\>/gi)) {
                            if (matches.length <= counted) break;
                            range.moveEnd('character', matches.length);
                            counted += matches.length;
                        }
                        range2.moveEnd('character', counted);
                        while (matches = range2.htmlText.match(/\<\/p\>(\<br[^\>]*\>)+\<p\>/gi)) {
                            if (matches.length <= counted) break;
                            range2.moveEnd('character', matches.length);
                            counted += matches.length;
                        }
                        range2.setEndPoint('StartToEnd', range);
                        range2.select();
                    }
                    return context.$textarea;
                },
                'scrollToCaretPosition': function (options) {
                    context.fn.scrollToTop(context.fn.getElementAtCursor(), true);
                },
                'scrollToTop': function ($element, force) {
                    var html = context.$content.closest('html'),
                        body = context.$content.closest('body'),
                        parentHtml = $('html'),
                        parentBody = $('body');
                    var y = $element.offset().top;
                    if (!$.browser.msie && !$element.is('body')) {
                        y = parentHtml.scrollTop() > 0 ? y + html.scrollTop() - parentHtml.scrollTop() : y;
                        y = parentBody.scrollTop() > 0 ? y + body.scrollTop() - parentBody.scrollTop() : y;
                    }
                    var topBound = html.scrollTop() > body.scrollTop() ? html.scrollTop() : body.scrollTop(),
                        bottomBound = topBound + context.$iframe.height();
                    if (force || y < topBound || y > bottomBound) {
                        html.scrollTop(y);
                        body.scrollTop(y);
                    }
                    $element.trigger('scrollToTop');
                },
                'saveStuffForIE': function () {
                    if (!$.browser.msie || context.$iframe) return;
                    var IHateIE = {
                        'scrollTop': context.$textarea.scrollTop(),
                        'pos': context.$textarea.textSelection('getCaretPosition', {
                            startAndEnd: true
                        })
                    };
                    context.$textarea.data('IHateIE', IHateIE);
                },
                'restoreStuffForIE': function () {
                    if (!$.browser.msie || context.$iframe) return;
                    var IHateIE = context.$textarea.data('IHateIE');
                    if (!IHateIE) return;
                    context.$textarea.scrollTop(IHateIE.scrollTop);
                    context.$textarea.textSelection('setSelection', {
                        start: IHateIE.pos[0],
                        end: IHateIE.pos[1]
                    });
                    context.$textarea.data('IHateIE', null);
                }
            };
            context.$textarea.wrapAll($('<div></div>').addClass('wikiEditor-ui')).wrapAll($('<div></div>').addClass('wikiEditor-ui-view wikiEditor-ui-view-wikitext')).wrapAll($('<div></div>').addClass('wikiEditor-ui-left')).wrapAll($('<div></div>').addClass('wikiEditor-ui-bottom')).wrapAll($('<div></div>').addClass('wikiEditor-ui-text'));
            context.$ui = context.$textarea.parent().parent().parent().parent().parent();
            context.$wikitext = context.$textarea.parent().parent().parent().parent();
            context.$wikitext.before($('<div></div>').addClass('wikiEditor-ui-controls').append($('<div></div>').addClass('wikiEditor-ui-tabs').hide()).append($('<div></div>').addClass('wikiEditor-ui-buttons'))).before($('<div style="clear:both;"></div>'));
            context.$controls = context.$ui.find('.wikiEditor-ui-buttons').hide();
            context.$buttons = context.$ui.find('.wikiEditor-ui-buttons');
            context.$tabs = context.$ui.find('.wikiEditor-ui-tabs');
            context.$ui.after($('<div style="clear:both;"></div>'));
            context.$wikitext.append($('<div></div>').addClass('wikiEditor-ui-right'));
            context.$wikitext.find('.wikiEditor-ui-left').prepend($('<div></div>').addClass('wikiEditor-ui-top'));
            context.view = 'wikitext';
            $(window).resize(function (event) {
                context.fn.trigger('resize', event);
            });
        }
        var args = $.makeArray(arguments);
        if (typeof context.$iframe === 'undefined' && args[0] == 'addModule' && typeof args[1] != 'undefined') {
            var modules = args[1];
            if (typeof modules != "object") {
                modules = {};
                modules[args[1]] = '';
            }
            for (module in modules) {
                if (module in $.wikiEditor.modules && $.wikiEditor.isSupported($.wikiEditor.modules[module]) && $.wikiEditor.isRequired($.wikiEditor.modules[module], 'iframe')) {
                    context.fn.setupIframe();
                    break;
                }
            }
        }
        if (args.length > 0) {
            var call = args.shift();
            if (call in context.api) {
                context.api[call](context, typeof args[0] == 'undefined' ? {} : args[0]);
            }
        }
        return $(this).data('wikiEditor-context', context);
    };
})(jQuery);
RegExp.escape = function (s) {
    return s.replace(/([.*+?^${}()|\/\\[\]])/g, '\\$1');
};
(function ($) {
    $.wikiEditor.modules.dialogs = {
        'browsers': {
            'ltr': {
                'msie': [
                    ['>=', 7]
                ],
                'firefox': [
                    ['>=', 2],
                    ['!=', '2.0'],
                    ['!=', '2.0.0.1'],
                    ['!=', '2.0.0.2'],
                    ['!=', '2.0.0.3'],
                    ['!=', '2.0.0.4']
                ],
                'opera': [
                    ['>=', 9.6]
                ],
                'safari': [
                    ['>=', 3]
                ],
                'chrome': [
                    ['>=', 3]
                ]
            },
            'rtl': {
                'msie': [
                    ['>=', 7]
                ],
                'firefox': [
                    ['>=', 2],
                    ['!=', '2.0'],
                    ['!=', '2.0.0.1'],
                    ['!=', '2.0.0.2'],
                    ['!=', '2.0.0.3'],
                    ['!=', '2.0.0.4']
                ],
                'opera': [
                    ['>=', 9.6]
                ],
                'safari': [
                    ['>=', 3]
                ],
                'chrome': [
                    ['>=', 3]
                ]
            }
        },
        api: {
            addDialog: function (context, data) {
                $.wikiEditor.modules.dialogs.fn.create(context, data)
            },
            openDialog: function (context, module) {
                mw.usability.load(['$j.ui', '$j.ui.dialog', '$j.ui.draggable', '$j.ui.resizable'], function () {
                    if (module in $.wikiEditor.modules.dialogs.modules) {
                        var mod = $.wikiEditor.modules.dialogs.modules[module];
                        var $dialog = $('#' + mod.id);
                        if ($dialog.length == 0) {
                            $.wikiEditor.modules.dialogs.fn.reallyCreate(context, mod);
                            $dialog = $('#' + mod.id);
                        }
                        $dialog.closest('.ui-dialog').find('.ui-dialog-titlebar-close').removeClass('ui-state-focus');
                        $dialog.dialog('open');
                    }
                });
            },
            closeDialog: function (context, module) {
                if (module in $.wikiEditor.modules.dialogs.modules) {
                    $('#' + $.wikiEditor.modules.dialogs.modules[module].id).dialog('close');
                }
            }
        },
        fn: {
            create: function (context, config) {
                for (mod in config) {
                    var module = config[mod];
                    var filtered = false;
                    if (typeof module.filters != 'undefined') {
                        for (var i = 0; i < module.filters.length; i++) {
                            if ($(module.filters[i]).length == 0) {
                                filtered = true;
                                break;
                            }
                        }
                    }
                    if (!filtered && $.wikiEditor.isSupported(module) && $('#' + module.id).size() == 0) {
                        $.wikiEditor.modules.dialogs.modules[mod] = module;
                        if (typeof context.$iframe == 'undefined' && $.wikiEditor.isRequired(module, 'iframe')) {
                            context.fn.setupIframe();
                        }
                        context.$textarea.trigger('wikiEditor-dialogs-setup-' + mod);
                    }
                }
            },
            reallyCreate: function (context, module) {
                var configuration = module.dialog;
                configuration.bgiframe = true;
                configuration.autoOpen = false;
                configuration.modal = true;
                configuration.title = $.wikiEditor.autoMsg(module, 'title');
                configuration.newButtons = {};
                for (msg in configuration.buttons)
                configuration.newButtons[mw.usability.getMsg(msg)] = configuration.buttons[msg];
                configuration.buttons = configuration.newButtons;
                var dialogDiv = $('<div />').attr('id', module.id).html(module.html).data('context', context).appendTo($('body')).each(module.init).dialog(configuration);
                $.wikiEditor.modules.dialogs.fn.setTabindexes(dialogDiv.closest('.ui-dialog').find('button').not('[tabindex]'));
                if (!('resizeme' in module) || module.resizeme) {
                    dialogDiv.bind('dialogopen', $.wikiEditor.modules.dialogs.fn.resize).find('.ui-tabs').bind('tabsshow', function () {
                        $(this).closest('.ui-dialog-content').each($.wikiEditor.modules.dialogs.fn.resize);
                    });
                }
                dialogDiv.bind('dialogclose', function () {
                    context.fn.restoreSelection();
                });
                context.$textarea.trigger('wikiEditor-dialogs-loaded-' + mod);
            },
            resize: function () {
                var wrapper = $(this).closest('.ui-dialog');
                var oldWidth = wrapper.width();
                var oldHidden = $(this).find('*').not(':visible');
                oldHidden.each(function () {
                    $(this).data('oldstyle', $(this).attr('style'));
                });
                oldHidden.show();
                var oldWS = $(this).css('white-space');
                $(this).css('white-space', 'nowrap');
                if (wrapper.width() <= $(this).get(0).scrollWidth) {
                    var thisWidth = $(this).data('thisWidth') ? $(this).data('thisWidth') : 0;
                    thisWidth = Math.max($(this).get(0).scrollWidth, thisWidth);
                    $(this).width(thisWidth);
                    $(this).data('thisWidth', thisWidth);
                    var wrapperWidth = $(this).data('wrapperWidth') ? $(this).data('wrapperWidth') : 0;
                    wrapperWidth = Math.max(wrapper.get(0).scrollWidth, wrapperWidth);
                    wrapper.width(wrapperWidth);
                    $(this).data('wrapperWidth', wrapperWidth);
                    $(this).dialog({
                        'width': wrapper.width()
                    });
                    wrapper.css('left', parseInt(wrapper.css('left')) - (wrapper.width() - oldWidth) / 2);
                }
                $(this).css('white-space', oldWS);
                oldHidden.each(function () {
                    $(this).attr('style', $(this).data('oldstyle'));
                });
            },
            setTabindexes: function ($elements) {
                var tabIndex = mw.usability.getMaxTabIndex() + 1;
                $elements.each(function () {
                    $j(this).attr('tabindex', tabIndex++);
                });
            }
        },
        modules: {},
        quickDialog: function (body, settings) {
            $('<div />').text(body).appendTo($('body')).dialog($.extend({
                bgiframe: true,
                modal: true
            }, settings)).dialog('open');
        }
    };
})(jQuery);
(function ($) {
    $.wikiEditor.modules.toolbar = {
        api: {
            addToToolbar: function (context, data) {
                for (type in data) {
                    switch (type) {
                    case 'sections':
                        var $sections = context.modules.toolbar.$toolbar.find('div.sections');
                        var $tabs = context.modules.toolbar.$toolbar.find('div.tabs');
                        for (section in data[type]) {
                            if (section == 'main') {
                                context.modules.toolbar.$toolbar.prepend($.wikiEditor.modules.toolbar.fn.buildSection(context, section, data[type][section]));
                                continue;
                            }
                            $sections.append($.wikiEditor.modules.toolbar.fn.buildSection(context, section, data[type][section]));
                            $tabs.append($.wikiEditor.modules.toolbar.fn.buildTab(context, section, data[type][section]));
                            $section = $sections.find('.section:visible');
                            if ($section.size()) {
                                $sections.animate({
                                    'height': $section.outerHeight()
                                }, 'fast');
                            }
                        }
                        break;
                    case 'groups':
                        if (!('section' in data)) {
                            continue;
                        }
                        var $section = context.modules.toolbar.$toolbar.find('div[rel=' + data.section + '].section');
                        for (group in data[type]) {
                            $section.append($.wikiEditor.modules.toolbar.fn.buildGroup(context, group, data[type][group]));
                        }
                        break;
                    case 'tools':
                        if (!('section' in data && 'group' in data)) {
                            continue;
                        }
                        var $group = context.modules.toolbar.$toolbar.find('div[rel=' + data.section + '].section ' + 'div[rel=' + data.group + '].group');
                        for (tool in data[type]) {
                            $group.append($.wikiEditor.modules.toolbar.fn.buildTool(context, tool, data[type][tool]));
                        }
                        if ($group.children().length) {
                            $group.show();
                        }
                        break;
                    case 'pages':
                        if (!('section' in data)) {
                            continue;
                        }
                        var $pages = context.modules.toolbar.$toolbar.find('div[rel=' + data.section + '].section .pages');
                        var $index = context.modules.toolbar.$toolbar.find('div[rel=' + data.section + '].section .index');
                        for (page in data[type]) {
                            $pages.append($.wikiEditor.modules.toolbar.fn.buildPage(context, page, data[type][page]));
                            $index.append($.wikiEditor.modules.toolbar.fn.buildBookmark(context, page, data[type][page]));
                        }
                        $.wikiEditor.modules.toolbar.fn.updateBookletSelection(context, page, $pages, $index);
                        break;
                    case 'rows':
                        if (!('section' in data && 'page' in data)) {
                            continue;
                        }
                        var $table = context.modules.toolbar.$toolbar.find('div[rel=' + data.section + '].section ' + 'div[rel=' + data.page + '].page table');
                        for (row in data[type]) {
                            $table.append($.wikiEditor.modules.toolbar.fn.buildRow(context, data[type][row]));
                        }
                        break;
                    case 'characters':
                        if (!('section' in data && 'page' in data)) {
                            continue;
                        }
                        $characters = context.modules.toolbar.$toolbar.find('div[rel=' + data.section + '].section ' + 'div[rel=' + data.page + '].page div');
                        var actions = $characters.data('actions');
                        for (character in data[type]) {
                            $characters.append($($.wikiEditor.modules.toolbar.fn.buildCharacter(data[type][character], actions)).mousedown(function (e) {
                                context.fn.saveStuffForIE();
                                e.preventDefault();
                                return false;
                            }).click(function (e) {
                                $.wikiEditor.modules.toolbar.fn.doAction($(this).parent().data('context'), $(this).parent().data('actions')[$(this).attr('rel')]);
                                e.preventDefault();
                                return false;
                            }));
                        }
                        break;
                    default:
                        break;
                    }
                }
            },
            removeFromToolbar: function (context, data) {
                if (typeof data.section == 'string') {
                    var tab = 'div.tabs span[rel=' + data.section + '].tab';
                    var target = 'div[rel=' + data.section + '].section';
                    var group = null;
                    if (typeof data.group == 'string') {
                        target += ' div[rel=' + data.group + '].group';
                        if (typeof data.tool == 'string') {
                            group = target;
                            target += ' span[rel=' + data.tool + '].tool';
                        }
                    } else if (typeof data.page == 'string') {
                        var index = target + ' div.index div[rel=' + data.page + ']';
                        target += ' div.pages div[rel=' + data.page + '].page';
                        if (typeof data.character == 'string') {
                            target += ' a[rel=' + data.character + ']';
                        } else if (typeof data.row == 'number') {
                            target += ' table tr:not(:has(th)):eq(' + data.row + ')';
                        } else {
                            context.modules.toolbar.$toolbar.find(index).remove();
                            $.wikiEditor.modules.toolbar.fn.updateBookletSelection(context, null, context.modules.toolbar.$toolbar.find(target), context.modules.toolbar.$toolbar.find(index));
                        }
                    } else {
                        context.modules.toolbar.$toolbar.find(tab).remove();
                    }
                    context.modules.toolbar.$toolbar.find(target).remove();
                    if (group) {
                        $group = context.modules.toolbar.$toolbar.find(group);
                        if ($group.children().length == 0) {
                            $group.hide();
                        }
                    }
                }
            }
        },
        evt: {
            resize: function (context, event) {
                context.$ui.find('.sections').height(context.$ui.find('.sections .section-visible').outerHeight());
            },
            tocCollapse: function (context, event) {
                $.wikiEditor.modules.toolbar.evt.resize(context, event);
            },
            tocExpand: function (context, event) {
                $.wikiEditor.modules.toolbar.evt.resize(context, event);
            }
        },
        fn: {
            create: function (context, config) {
                if ('$toolbar' in context.modules.toolbar) {
                    return;
                }
                context.modules.toolbar.$toolbar = $('<div />').addClass('wikiEditor-ui-toolbar').attr('id', 'wikiEditor-ui-toolbar');
                $.wikiEditor.modules.toolbar.fn.build(context, config);
                context.$ui.find('.wikiEditor-ui-top').append(context.modules.toolbar.$toolbar);
            },
            doAction: function (context, action, source) {
                if ($.trackAction != undefined && source.closest('.wikiEditor-ui-toolbar').size()) {
                    var rels = [];
                    var step = source;
                    var i = 0;
                    while (!step.hasClass('wikiEditor-ui-toolbar')) {
                        if (i > 25) {
                            break;
                        }
                        i++;
                        var rel = step.attr('rel');
                        if (rel) {
                            rels.push(step.attr('rel'));
                        }
                        step = step.parent();
                    }
                    rels.reverse();
                    var id = rels.join('.');
                    $.trackAction(id);
                }
                switch (action.type) {
                case 'replace':
                case 'encapsulate':
                    var parts = {
                        'pre': $.wikiEditor.autoMsg(action.options, 'pre'),
                        'peri': $.wikiEditor.autoMsg(action.options, 'peri'),
                        'post': $.wikiEditor.autoMsg(action.options, 'post')
                    };
                    var replace = action.type == 'replace';
                    if ('regex' in action.options && 'regexReplace' in action.options) {
                        var selection = context.$textarea.textSelection('getSelection');
                        if (selection != '' && selection.match(action.options.regex)) {
                            parts.peri = selection.replace(action.options.regex, action.options.regexReplace);
                            parts.pre = parts.post = '';
                            replace = true;
                        }
                    }
                    context.$textarea.textSelection('encapsulateSelection', $.extend({}, action.options, parts, {
                        'replace': replace
                    }));
                    if (typeof context.$iframe !== 'undefined') {
                        context.$iframe[0].contentWindow.focus();
                    }
                    break;
                case 'callback':
                    if (typeof action.execute == 'function') {
                        action.execute(context);
                    }
                    break;
                case 'dialog':
                    context.fn.saveSelection();
                    context.$textarea.wikiEditor('openDialog', action.module);
                    break;
                default:
                    break;
                }
            },
            buildGroup: function (context, id, group) {
                var $group = $('<div />').attr({
                    'class': 'group group-' + id,
                    'rel': id
                });
                var label = $.wikiEditor.autoMsg(group, 'label');
                if (label) {
                    $group.append('<div class="label">' + label + '</div>')
                }
                var empty = true;
                if ('tools' in group) {
                    for (tool in group.tools) {
                        var tool = $.wikiEditor.modules.toolbar.fn.buildTool(context, tool, group.tools[tool]);
                        if (tool) {
                            empty = empty && tool.css('display') == 'none';
                            $group.append(tool);
                        }
                    }
                }
                if (empty) {
                    $group.hide();
                }
                return $group;
            },
            buildTool: function (context, id, tool) {
                if ('filters' in tool) {
                    for (filter in tool.filters) {
                        if ($(tool.filters[filter]).size() == 0) {
                            return null;
                        }
                    }
                }
                var label = $.wikiEditor.autoMsg(tool, 'label');
                switch (tool.type) {
                case 'button':
                    var src = $.wikiEditor.autoIcon(tool.icon, $.wikiEditor.imgPath + 'toolbar/');
                    var $button = null;
                    if ('offset' in tool) {
                        var offsetOrIcon = $.wikiEditor.autoIconOrOffset(tool.icon, tool.offset, $.wikiEditor.imgPath + 'toolbar/');
                        if (typeof offsetOrIcon == 'object') {
                            $button = $('<span />').attr({
                                'alt': label,
                                'title': label,
                                'rel': id,
                                'class': 'tool tool-button wikiEditor-toolbar-spritedButton'
                            }).text(label).css('backgroundPosition', offsetOrIcon[0] + 'px ' + offsetOrIcon[1] + 'px');
                        }
                    }
                    if (!$button) {
                        $button = $('<img />').attr({
                            'src': src,
                            'width': 22,
                            'height': 22,
                            'alt': label,
                            'title': label,
                            'rel': id,
                            'class': 'tool tool-button'
                        });
                    }
                    if ('action' in tool) {
                        $button.data('action', tool.action).data('context', context).mousedown(function (e) {
                            context.fn.saveStuffForIE();
                            e.preventDefault();
                            return false;
                        }).click(function (e) {
                            $.wikiEditor.modules.toolbar.fn.doAction($(this).data('context'), $(this).data('action'), $(this));
                            e.preventDefault();
                            return false;
                        });
                        if (tool.action.type == 'dialog' && !(tool.action.module in $.wikiEditor.modules.dialogs.modules)) {
                            $button.hide();
                            context.$textarea.bind('wikiEditor-dialogs-setup-' + tool.action.module, {
                                button: $button
                            }, function (event) {
                                event.data.button.show().parent().show();
                            });
                        }
                    }
                    return $button;
                case 'select':
                    var $select = $('<div />').attr({
                        'rel': id,
                        'class': 'tool tool-select'
                    });
                    var $options = $('<div />').addClass('options');
                    if ('list' in tool) {
                        for (option in tool.list) {
                            var optionLabel = $.wikiEditor.autoMsg(tool.list[option], 'label');
                            $options.append($('<a />').data('action', tool.list[option].action).data('context', context).mousedown(function (e) {
                                context.fn.saveStuffForIE();
                                e.preventDefault();
                                return false;
                            }).click(function (e) {
                                $.wikiEditor.modules.toolbar.fn.doAction($(this).data('context'), $(this).data('action'), $(this));
                                if ($(this).parent().is(':visible')) {
                                    $(this).parent().animate({
                                        'opacity': 'toggle'
                                    }, 'fast');
                                }
                                e.preventDefault();
                                return false;
                            }).text(optionLabel).addClass('option').attr({
                                'rel': option,
                                'href': '#'
                            }));
                        }
                    }
                    $select.append($('<div />').addClass('menu').append($options));
                    $select.append($('<a />').addClass('label').text(label).data('options', $options).attr('href', '#').mousedown(function (e) {
                        e.preventDefault();
                        return false;
                    }).click(function (e) {
                        $(this).data('options').animate({
                            'opacity': 'toggle'
                        }, 'fast');
                        e.preventDefault();
                        return false;
                    }));
                    return $select;
                default:
                    return null;
                }
            },
            buildBookmark: function (context, id, page) {
                var label = $.wikiEditor.autoMsg(page, 'label');
                return $('<div />').text(label).attr('rel', id).data('context', context).mousedown(function (e) {
                    e.preventDefault();
                    return false;
                }).click(function (event) {
                    $(this).parent().parent().find('.page').hide();
                    $(this).parent().parent().find('.page-' + $(this).attr('rel')).show();
                    $(this).siblings().removeClass('current');
                    $(this).addClass('current');
                    var section = $(this).parent().parent().attr('rel');
                    $.cookie('wikiEditor-' + $(this).data('context').instance + '-booklet-' + section + '-page', $(this).attr('rel'), {
                        expires: 30,
                        path: '/'
                    });
                    if ($.trackAction != undefined) {
                        $.trackAction(section + '.' + $(this).attr('rel'));
                    }
                    event.preventDefault();
                    return false;
                })
            },
            buildPage: function (context, id, page) {
                var $page = $('<div />').attr({
                    'class': 'page page-' + id,
                    'rel': id
                });
                switch (page.layout) {
                case 'table':
                    $page.addClass('page-table');
                    var html = '<table cellpadding=0 cellspacing=0 ' + 'border=0 width="100%" class="table table-' + id + '">';
                    if ('headings' in page) {
                        html += $.wikiEditor.modules.toolbar.fn.buildHeading(context, page.headings)
                    }
                    if ('rows' in page) {
                        for (row in page.rows) {
                            html += $.wikiEditor.modules.toolbar.fn.buildRow(context, page.rows[row])
                        }
                    }
                    $page.html(html);
                    break;
                case 'characters':
                    $page.addClass('page-characters');
                    $characters = $('<div />').data('context', context).data('actions', {});
                    var actions = $characters.data('actions');
                    if ('language' in page) {
                        $characters.attr('lang', page.language);
                    }
                    if ('direction' in page) {
                        $characters.attr('dir', page.direction);
                    }
                    if ('characters' in page) {
                        var html = '';
                        for (var i = 0; i < page.characters.length; i++) {
                            html += $.wikiEditor.modules.toolbar.fn.buildCharacter(page.characters[i], actions);
                        }
                        $characters.html(html).children().mousedown(function (e) {
                            context.fn.saveStuffForIE();
                            e.preventDefault();
                            return false;
                        }).click(function (e) {
                            $.wikiEditor.modules.toolbar.fn.doAction($(this).parent().data('context'), $(this).parent().data('actions')[$(this).attr('rel')], $(this));
                            e.preventDefault();
                            return false;
                        });
                    }
                    $page.append($characters);
                    break;
                }
                return $page;
            },
            buildHeading: function (context, headings) {
                var html = '<tr>';
                for (heading in headings) {
                    html += '<th>' + $.wikiEditor.autoMsg(headings[heading], ['html', 'text']) + '</th>';
                }
                return html;
            },
            buildRow: function (context, row) {
                var html = '<tr>';
                for (cell in row) {
                    html += '<td class="cell cell-' + cell + '" valign="top"><span>' + $.wikiEditor.autoMsg(row[cell], ['html', 'text']) + '</span></td>';
                }
                html += '</tr>';
                return html;
            },
            buildCharacter: function (character, actions) {
                if (typeof character == 'string') {
                    character = {
                        'label': character,
                        'action': {
                            'type': 'replace',
                            'options': {
                                'peri': character,
                                'selectPeri': false
                            }
                        }
                    };
                } else if (0 in character && 1 in character) {
                    character = {
                        'label': character[0],
                        'action': {
                            'type': 'replace',
                            'options': {
                                'peri': character[1],
                                'selectPeri': false
                            }
                        }
                    };
                }
                if ('action' in character && 'label' in character) {
                    actions[character.label] = character.action;
                    return '<span rel="' + character.label + '">' + character.label + '</span>';
                }
            },
            buildTab: function (context, id, section) {
                var selected = $.cookie('wikiEditor-' + context.instance + '-toolbar-section');
                if (selected != null) {
                    $.cookie('wikiEditor-' + context.instance + '-toolbar-section', selected, {
                        expires: 30,
                        path: '/'
                    });
                }
                return $('<span />').attr({
                    'class': 'tab tab-' + id,
                    'rel': id
                }).append($('<a />').addClass(selected == id ? 'current' : null).attr('href', '#').text($.wikiEditor.autoMsg(section, 'label')).data('context', context).mouseup(function (e) {
                    $(this).blur();
                }).mousedown(function (e) {
                    e.preventDefault();
                    return false;
                }).click(function (e) {
                    var $sections = $(this).data('context').$ui.find('.sections');
                    var $section = $(this).data('context').$ui.find('.section-' + $(this).parent().attr('rel'));
                    var show = $section.css('display') == 'none';
                    $previousSections = $section.parent().find('.section-visible');
                    $previousSections.css('position', 'absolute');
                    $previousSections.removeClass('section-visible');
                    $previousSections.fadeOut('fast', function () {
                        $(this).css('position', 'relative');
                    });
                    $(this).parent().parent().find('a').removeClass('current');
                    $sections.css('overflow', 'hidden');

                    function animate($this) {
                        $sections.css('display', 'block').animate({
                            'height': $section.outerHeight()
                        }, $section.outerHeight() * 2, function () {
                            $(this).css('overflow', 'visible').css('height', 'auto');
                            context.fn.trigger('resize');
                        });
                    }
                    if (show) {
                        $section.addClass('section-visible');
                        $section.fadeIn('fast');
                        if ($section.hasClass('loading')) {
                            var $this = $(this);
                            $this.addClass('current loading');
                            setTimeout(function () {
                                $section.trigger('loadSection');
                                animate($(this));
                                $this.removeClass('loading');
                            }, 1000);
                        } else {
                            animate($(this));
                            $(this).addClass('current');
                        }
                    } else {
                        $sections.css('height', $section.outerHeight()).animate({
                            'height': 'hide'
                        }, $section.outerHeight() * 2, function () {
                            $(this).css({
                                'overflow': 'visible',
                                'height': 0
                            });
                            context.fn.trigger('resize');
                        });
                    }
                    if ($.trackAction != undefined) {
                        $.trackAction($section.attr('rel') + '.' + (show ? 'show' : 'hide'));
                    }
                    $.cookie('wikiEditor-' + $(this).data('context').instance + '-toolbar-section', show ? $section.attr('rel') : null, {
                        expires: 30,
                        path: '/'
                    });
                    e.preventDefault();
                    return false;
                }));
            },
            buildSection: function (context, id, section) {
                var $section = $('<div />').attr({
                    'class': section.type + ' section section-' + id,
                    'rel': id
                });
                var selected = $.cookie('wikiEditor-' + context.instance + '-toolbar-section');
                var show = selected == id;
                if (typeof section.deferLoad != 'undefined' && section.deferLoad && id !== 'main' && !show) {
                    $section.addClass('loading').append($('<div />').addClass('spinner'));
                    $section.bind('loadSection', function () {
                        $.wikiEditor.modules.toolbar.fn.reallyBuildSection(context, id, section, $section);
                        $section.removeClass('loading');
                    });
                } else {
                    $.wikiEditor.modules.toolbar.fn.reallyBuildSection(context, id, section, $section);
                }
                if (id !== 'main') {
                    $section.css('display', show ? 'block' : 'none');
                    if (show) $section.addClass('section-visible');
                }
                return $section;
            },
            reallyBuildSection: function (context, id, section, $section) {
                context.$textarea.trigger('wikiEditor-toolbar-buildSection-' + $section.attr('rel'), [section]);
                switch (section.type) {
                case 'toolbar':
                    if ('groups' in section) {
                        for (group in section.groups) {
                            $section.append($.wikiEditor.modules.toolbar.fn.buildGroup(context, group, section.groups[group]));
                        }
                    }
                    break;
                case 'booklet':
                    var $pages = $('<div />').addClass('pages');
                    var $index = $('<div />').addClass('index');
                    if ('pages' in section) {
                        for (page in section.pages) {
                            $pages.append($.wikiEditor.modules.toolbar.fn.buildPage(context, page, section.pages[page]));
                            $index.append($.wikiEditor.modules.toolbar.fn.buildBookmark(context, page, section.pages[page]));
                        }
                    }
                    $section.append($index).append($pages);
                    $.wikiEditor.modules.toolbar.fn.updateBookletSelection(context, id, $pages, $index);
                    break;
                }
            },
            updateBookletSelection: function (context, id, $pages, $index) {
                var cookie = 'wikiEditor-' + context.instance + '-booklet-' + id + '-page';
                var selected = $.cookie(cookie);
                if (selected != null) {
                    $.cookie(cookie, selected, {
                        expires: 30,
                        path: '/'
                    });
                }
                var $selectedIndex = $index.find('*[rel=' + selected + ']');
                if ($selectedIndex.size() == 0) {
                    selected = $index.children().eq(0).attr('rel');
                    $.cookie(cookie, selected, {
                        expires: 30,
                        path: '/'
                    });
                }
                $pages.children().hide();
                $pages.find('*[rel=' + selected + ']').show();
                $index.children().removeClass('current');
                $selectedIndex.addClass('current');
            },
            build: function (context, config) {
                var $tabs = $('<div />').addClass('tabs').appendTo(context.modules.toolbar.$toolbar);
                var $sections = $('<div />').addClass('sections').appendTo(context.modules.toolbar.$toolbar);
                context.modules.toolbar.$toolbar.append($('<div />').css('clear', 'both'));
                var sectionQueue = [];
                for (section in config) {
                    if (section == 'main') {
                        context.modules.toolbar.$toolbar.prepend($.wikiEditor.modules.toolbar.fn.buildSection(context, section, config[section]));
                    } else {
                        sectionQueue.push({
                            '$sections': $sections,
                            'context': context,
                            'id': section,
                            'config': config[section]
                        });
                        $tabs.append($.wikiEditor.modules.toolbar.fn.buildTab(context, section, config[section]));
                    }
                }
                $.eachAsync(sectionQueue, {
                    'bulk': 0,
                    'end': function () {
                        var oldValue = $('body').css('position');
                        $('body').css('position', 'static');
                        $('body').css('position', oldValue);
                    },
                    'loop': function (i, s) {
                        s.$sections.append($.wikiEditor.modules.toolbar.fn.buildSection(s.context, s.id, s.config));
                        var $section = s.$sections.find('.section:visible');
                        if ($section.size()) {
                            $sections.animate({
                                'height': $section.outerHeight()
                            }, $section.outerHeight() * 2, function () {
                                context.fn.trigger('resize');
                            });
                        }
                    }
                });
            }
        }
    };
})(jQuery);
