summaryrefslogtreecommitdiff
path: root/extensions/WikiEditor/modules
diff options
context:
space:
mode:
Diffstat (limited to 'extensions/WikiEditor/modules')
-rw-r--r--extensions/WikiEditor/modules/contentCollector.js439
-rw-r--r--extensions/WikiEditor/modules/ext.wikiEditor.css23
-rw-r--r--extensions/WikiEditor/modules/ext.wikiEditor.dialogs.js15
-rw-r--r--extensions/WikiEditor/modules/ext.wikiEditor.highlight.js8
-rw-r--r--extensions/WikiEditor/modules/ext.wikiEditor.js8
-rw-r--r--extensions/WikiEditor/modules/ext.wikiEditor.preview.js8
-rw-r--r--extensions/WikiEditor/modules/ext.wikiEditor.previewDialog.js8
-rw-r--r--extensions/WikiEditor/modules/ext.wikiEditor.publish.js8
-rw-r--r--extensions/WikiEditor/modules/ext.wikiEditor.templateEditor.js12
-rw-r--r--extensions/WikiEditor/modules/ext.wikiEditor.templates.js12
-rw-r--r--extensions/WikiEditor/modules/ext.wikiEditor.tests.toolbar.js246
-rw-r--r--extensions/WikiEditor/modules/ext.wikiEditor.toc.js8
-rw-r--r--extensions/WikiEditor/modules/ext.wikiEditor.toolbar.hideSig.js14
-rw-r--r--extensions/WikiEditor/modules/ext.wikiEditor.toolbar.js14
-rw-r--r--extensions/WikiEditor/modules/images/dialogs/insert-link-error.pngbin0 -> 672 bytes
-rw-r--r--extensions/WikiEditor/modules/images/dialogs/insert-link-exists.pngbin0 -> 335 bytes
-rw-r--r--extensions/WikiEditor/modules/images/dialogs/insert-link-external-rtl.pngbin0 -> 279 bytes
-rw-r--r--extensions/WikiEditor/modules/images/dialogs/insert-link-external.pngbin0 -> 282 bytes
-rw-r--r--extensions/WikiEditor/modules/images/dialogs/insert-link-invalid.pngbin0 -> 607 bytes
-rw-r--r--extensions/WikiEditor/modules/images/dialogs/insert-link-notexists.pngbin0 -> 548 bytes
-rw-r--r--extensions/WikiEditor/modules/images/dialogs/loading-small.gifbin0 -> 4046 bytes
-rw-r--r--extensions/WikiEditor/modules/images/dialogs/loading.gifbin0 -> 4615 bytes
-rw-r--r--extensions/WikiEditor/modules/images/templateEditor/collapse.pngbin0 -> 1022 bytes
-rw-r--r--extensions/WikiEditor/modules/images/templateEditor/dialog-collapsed.pngbin0 -> 291 bytes
-rw-r--r--extensions/WikiEditor/modules/images/templateEditor/dialog-expanded.pngbin0 -> 277 bytes
-rw-r--r--extensions/WikiEditor/modules/images/templateEditor/expand.pngbin0 -> 250 bytes
-rw-r--r--extensions/WikiEditor/modules/images/templateEditor/name-base.pngbin0 -> 177 bytes
-rw-r--r--extensions/WikiEditor/modules/images/templateEditor/text-base.pngbin0 -> 128 bytes
-rw-r--r--extensions/WikiEditor/modules/images/templateEditor/wiki-text.pngbin0 -> 255 bytes
-rw-r--r--extensions/WikiEditor/modules/images/toc/close.pngbin0 -> 369 bytes
-rw-r--r--extensions/WikiEditor/modules/images/toc/grab.pngbin0 -> 131 bytes
-rw-r--r--extensions/WikiEditor/modules/images/toc/grip.pngbin0 -> 159 bytes
-rw-r--r--extensions/WikiEditor/modules/images/toc/open.pngbin0 -> 370 bytes
-rw-r--r--extensions/WikiEditor/modules/images/toolbar/arrow-down.pngbin0 -> 181 bytes
-rw-r--r--extensions/WikiEditor/modules/images/toolbar/arrow-ltr.pngbin0 -> 184 bytes
-rw-r--r--extensions/WikiEditor/modules/images/toolbar/arrow-rtl.pngbin0 -> 192 bytes
-rw-r--r--extensions/WikiEditor/modules/images/toolbar/base.pngbin0 -> 3632 bytes
-rw-r--r--extensions/WikiEditor/modules/images/toolbar/button-sprite.pngbin0 -> 21076 bytes
-rw-r--r--extensions/WikiEditor/modules/images/toolbar/example-image.pngbin0 -> 1124 bytes
-rw-r--r--extensions/WikiEditor/modules/images/toolbar/format-big.pngbin0 -> 725 bytes
-rw-r--r--extensions/WikiEditor/modules/images/toolbar/format-bold-A.pngbin0 -> 633 bytes
-rw-r--r--extensions/WikiEditor/modules/images/toolbar/format-bold-B.pngbin0 -> 688 bytes
-rw-r--r--extensions/WikiEditor/modules/images/toolbar/format-bold-F.pngbin0 -> 531 bytes
-rw-r--r--extensions/WikiEditor/modules/images/toolbar/format-bold-G.pngbin0 -> 637 bytes
-rw-r--r--extensions/WikiEditor/modules/images/toolbar/format-bold-N.pngbin0 -> 702 bytes
-rw-r--r--extensions/WikiEditor/modules/images/toolbar/format-bold-P.pngbin0 -> 597 bytes
-rw-r--r--extensions/WikiEditor/modules/images/toolbar/format-bold-V.pngbin0 -> 588 bytes
-rw-r--r--extensions/WikiEditor/modules/images/toolbar/format-bold-ka.pngbin0 -> 1492 bytes
-rw-r--r--extensions/WikiEditor/modules/images/toolbar/format-bold-ru.pngbin0 -> 925 bytes
-rw-r--r--extensions/WikiEditor/modules/images/toolbar/format-bold.pngbin0 -> 754 bytes
-rw-r--r--extensions/WikiEditor/modules/images/toolbar/format-indent-rtl.pngbin0 -> 1019 bytes
-rw-r--r--extensions/WikiEditor/modules/images/toolbar/format-indent.pngbin0 -> 1037 bytes
-rw-r--r--extensions/WikiEditor/modules/images/toolbar/format-italic-A.pngbin0 -> 556 bytes
-rw-r--r--extensions/WikiEditor/modules/images/toolbar/format-italic-C.pngbin0 -> 485 bytes
-rw-r--r--extensions/WikiEditor/modules/images/toolbar/format-italic-D.pngbin0 -> 588 bytes
-rw-r--r--extensions/WikiEditor/modules/images/toolbar/format-italic-I.pngbin0 -> 406 bytes
-rw-r--r--extensions/WikiEditor/modules/images/toolbar/format-italic-K.pngbin0 -> 625 bytes
-rw-r--r--extensions/WikiEditor/modules/images/toolbar/format-italic-ka.pngbin0 -> 1247 bytes
-rw-r--r--extensions/WikiEditor/modules/images/toolbar/format-italic.pngbin0 -> 644 bytes
-rw-r--r--extensions/WikiEditor/modules/images/toolbar/format-olist-rtl.pngbin0 -> 359 bytes
-rw-r--r--extensions/WikiEditor/modules/images/toolbar/format-olist.pngbin0 -> 365 bytes
-rw-r--r--extensions/WikiEditor/modules/images/toolbar/format-small.pngbin0 -> 713 bytes
-rw-r--r--extensions/WikiEditor/modules/images/toolbar/format-subscript.pngbin0 -> 731 bytes
-rw-r--r--extensions/WikiEditor/modules/images/toolbar/format-superscript.pngbin0 -> 729 bytes
-rw-r--r--extensions/WikiEditor/modules/images/toolbar/format-ulist-rtl.pngbin0 -> 227 bytes
-rw-r--r--extensions/WikiEditor/modules/images/toolbar/format-ulist.pngbin0 -> 223 bytes
-rw-r--r--extensions/WikiEditor/modules/images/toolbar/insert-file.pngbin0 -> 1386 bytes
-rw-r--r--extensions/WikiEditor/modules/images/toolbar/insert-gallery.pngbin0 -> 338 bytes
-rw-r--r--extensions/WikiEditor/modules/images/toolbar/insert-ilink.pngbin0 -> 332 bytes
-rw-r--r--extensions/WikiEditor/modules/images/toolbar/insert-link.pngbin0 -> 545 bytes
-rw-r--r--extensions/WikiEditor/modules/images/toolbar/insert-newline.pngbin0 -> 784 bytes
-rw-r--r--extensions/WikiEditor/modules/images/toolbar/insert-nowiki.pngbin0 -> 1557 bytes
-rw-r--r--extensions/WikiEditor/modules/images/toolbar/insert-redirect-rtl.pngbin0 -> 1319 bytes
-rw-r--r--extensions/WikiEditor/modules/images/toolbar/insert-redirect.pngbin0 -> 1552 bytes
-rw-r--r--extensions/WikiEditor/modules/images/toolbar/insert-reference.pngbin0 -> 371 bytes
-rw-r--r--extensions/WikiEditor/modules/images/toolbar/insert-signature.pngbin0 -> 851 bytes
-rw-r--r--extensions/WikiEditor/modules/images/toolbar/insert-table.pngbin0 -> 343 bytes
-rw-r--r--extensions/WikiEditor/modules/images/toolbar/insert-xlink.pngbin0 -> 413 bytes
-rw-r--r--extensions/WikiEditor/modules/images/toolbar/loading-small.gifbin0 -> 3331 bytes
-rw-r--r--extensions/WikiEditor/modules/images/toolbar/loading.gifbin0 -> 5423 bytes
-rw-r--r--extensions/WikiEditor/modules/images/toolbar/magnify-clip.pngbin0 -> 204 bytes
-rw-r--r--extensions/WikiEditor/modules/images/toolbar/png24/arrow-down.pngbin0 -> 187 bytes
-rw-r--r--extensions/WikiEditor/modules/images/toolbar/png24/arrow-left.pngbin0 -> 192 bytes
-rw-r--r--extensions/WikiEditor/modules/images/toolbar/png24/arrow-right.pngbin0 -> 189 bytes
-rw-r--r--extensions/WikiEditor/modules/images/toolbar/png24/format-big.pngbin0 -> 740 bytes
-rw-r--r--extensions/WikiEditor/modules/images/toolbar/png24/format-bold-A.pngbin0 -> 682 bytes
-rw-r--r--extensions/WikiEditor/modules/images/toolbar/png24/format-bold-B.pngbin0 -> 726 bytes
-rw-r--r--extensions/WikiEditor/modules/images/toolbar/png24/format-bold-F.pngbin0 -> 576 bytes
-rw-r--r--extensions/WikiEditor/modules/images/toolbar/png24/format-bold-G.pngbin0 -> 728 bytes
-rw-r--r--extensions/WikiEditor/modules/images/toolbar/png24/format-bold-N.pngbin0 -> 770 bytes
-rw-r--r--extensions/WikiEditor/modules/images/toolbar/png24/format-bold-P.pngbin0 -> 642 bytes
-rw-r--r--extensions/WikiEditor/modules/images/toolbar/png24/format-bold-V.pngbin0 -> 670 bytes
-rw-r--r--extensions/WikiEditor/modules/images/toolbar/png24/format-bold.pngbin0 -> 682 bytes
-rw-r--r--extensions/WikiEditor/modules/images/toolbar/png24/format-italic-A.pngbin0 -> 613 bytes
-rw-r--r--extensions/WikiEditor/modules/images/toolbar/png24/format-italic-C.pngbin0 -> 551 bytes
-rw-r--r--extensions/WikiEditor/modules/images/toolbar/png24/format-italic-I.pngbin0 -> 449 bytes
-rw-r--r--extensions/WikiEditor/modules/images/toolbar/png24/format-italic-K.pngbin0 -> 719 bytes
-rw-r--r--extensions/WikiEditor/modules/images/toolbar/png24/format-italic.pngbin0 -> 613 bytes
-rw-r--r--extensions/WikiEditor/modules/images/toolbar/png24/format-olist.pngbin0 -> 438 bytes
-rw-r--r--extensions/WikiEditor/modules/images/toolbar/png24/format-small.pngbin0 -> 688 bytes
-rw-r--r--extensions/WikiEditor/modules/images/toolbar/png24/format-subscript.pngbin0 -> 691 bytes
-rw-r--r--extensions/WikiEditor/modules/images/toolbar/png24/format-superscript.pngbin0 -> 698 bytes
-rw-r--r--extensions/WikiEditor/modules/images/toolbar/png24/format-ulist.pngbin0 -> 389 bytes
-rw-r--r--extensions/WikiEditor/modules/images/toolbar/png24/generate.sh12
-rw-r--r--extensions/WikiEditor/modules/images/toolbar/png24/insert-file.pngbin0 -> 929 bytes
-rw-r--r--extensions/WikiEditor/modules/images/toolbar/png24/insert-gallery.pngbin0 -> 1162 bytes
-rw-r--r--extensions/WikiEditor/modules/images/toolbar/png24/insert-link.pngbin0 -> 769 bytes
-rw-r--r--extensions/WikiEditor/modules/images/toolbar/png24/insert-newline.pngbin0 -> 834 bytes
-rw-r--r--extensions/WikiEditor/modules/images/toolbar/png24/insert-nowiki.pngbin0 -> 1585 bytes
-rw-r--r--extensions/WikiEditor/modules/images/toolbar/png24/insert-redirect.pngbin0 -> 1244 bytes
-rw-r--r--extensions/WikiEditor/modules/images/toolbar/png24/insert-reference.pngbin0 -> 1128 bytes
-rw-r--r--extensions/WikiEditor/modules/images/toolbar/png24/insert-signature.pngbin0 -> 673 bytes
-rw-r--r--extensions/WikiEditor/modules/images/toolbar/png24/insert-table.pngbin0 -> 284 bytes
-rw-r--r--extensions/WikiEditor/modules/images/toolbar/png24/search-replace.pngbin0 -> 1088 bytes
-rw-r--r--extensions/WikiEditor/modules/images/toolbar/search-replace.pngbin0 -> 979 bytes
-rw-r--r--extensions/WikiEditor/modules/jquery.wikiEditor.css112
-rw-r--r--extensions/WikiEditor/modules/jquery.wikiEditor.dialogs.config.css204
-rw-r--r--extensions/WikiEditor/modules/jquery.wikiEditor.dialogs.config.js1150
-rw-r--r--extensions/WikiEditor/modules/jquery.wikiEditor.dialogs.css56
-rw-r--r--extensions/WikiEditor/modules/jquery.wikiEditor.dialogs.js217
-rw-r--r--extensions/WikiEditor/modules/jquery.wikiEditor.highlight.js357
-rw-r--r--extensions/WikiEditor/modules/jquery.wikiEditor.html135
-rw-r--r--extensions/WikiEditor/modules/jquery.wikiEditor.iframe.js1387
-rw-r--r--extensions/WikiEditor/modules/jquery.wikiEditor.js559
-rw-r--r--extensions/WikiEditor/modules/jquery.wikiEditor.preview.css26
-rw-r--r--extensions/WikiEditor/modules/jquery.wikiEditor.preview.js164
-rw-r--r--extensions/WikiEditor/modules/jquery.wikiEditor.previewDialog.css35
-rw-r--r--extensions/WikiEditor/modules/jquery.wikiEditor.previewDialog.js131
-rw-r--r--extensions/WikiEditor/modules/jquery.wikiEditor.publish.js146
-rw-r--r--extensions/WikiEditor/modules/jquery.wikiEditor.templateEditor.js865
-rw-r--r--extensions/WikiEditor/modules/jquery.wikiEditor.templates.js69
-rw-r--r--extensions/WikiEditor/modules/jquery.wikiEditor.toc.css177
-rw-r--r--extensions/WikiEditor/modules/jquery.wikiEditor.toc.js667
-rw-r--r--extensions/WikiEditor/modules/jquery.wikiEditor.toolbar.config.js1091
-rw-r--r--extensions/WikiEditor/modules/jquery.wikiEditor.toolbar.css303
-rw-r--r--extensions/WikiEditor/modules/jquery.wikiEditor.toolbar.js775
136 files changed, 9461 insertions, 0 deletions
diff --git a/extensions/WikiEditor/modules/contentCollector.js b/extensions/WikiEditor/modules/contentCollector.js
new file mode 100644
index 00000000..af8d796a
--- /dev/null
+++ b/extensions/WikiEditor/modules/contentCollector.js
@@ -0,0 +1,439 @@
+// THIS FILE HAS BEEN MODIFIED for use with the mediawiki wikiEditor
+// It no longer requires etherpad.collab.ace.easysync2.Changeset
+// THIS FILE WAS ORIGINALLY AN APPJET MODULE: etherpad.collab.ace.contentcollector
+
+/**
+ * Copyright 2009 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS-IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+var _MAX_LIST_LEVEL = 8;
+
+function sanitizeUnicode(s) {
+ return s.replace(/[\uffff\ufffe\ufeff\ufdd0-\ufdef\ud800-\udfff]/g, '?');
+}
+
+function makeContentCollector( browser, domInterface ) {
+ browser = browser || {};
+
+ var dom = domInterface || {
+ isNodeText : function(n) {
+ return (n.nodeType == 3);
+ },
+ nodeTagName : function(n) {
+ return n.tagName;
+ },
+ nodeValue : function(n) {
+ try {
+ return n.nodeValue;
+ } catch ( err ) {
+ return '';
+ }
+ },
+ nodeName : function(n) {
+ return n.nodeName;
+ },
+ nodeNumChildren : function(n) {
+ return n.childNodes.length;
+ },
+ nodeChild : function(n, i) {
+ return n.childNodes.item(i);
+ },
+ nodeProp : function(n, p) {
+ return n[p];
+ },
+ nodeAttr : function(n, a) {
+ return n.getAttribute(a);
+ },
+ optNodeInnerHTML : function(n) {
+ return n.innerHTML;
+ }
+ };
+
+ var _blockElems = {
+ "div" : 1,
+ "p" : 1,
+ "pre" : 1,
+ "li" : 1
+ };
+ function isBlockElement(n) {
+ return !!_blockElems[(dom.nodeTagName(n) || "").toLowerCase()];
+ }
+ function textify(str) {
+ return sanitizeUnicode(str.replace(/[\n\r ]/g, ' ').replace(/\xa0/g,
+ ' ').replace(/\t/g, ' '));
+ }
+ function getAssoc(node, name) {
+ return dom.nodeProp(node, "_magicdom_" + name);
+ }
+
+ var lines = (function() {
+ var textArray = [];
+ var self = {
+ length : function() {
+ return textArray.length;
+ },
+ atColumnZero : function() {
+ return textArray[textArray.length - 1] === "";
+ },
+ startNew : function() {
+ textArray.push("");
+ self.flush(true);
+ },
+ textOfLine : function(i) {
+ return textArray[i];
+ },
+ appendText : function(txt, attrString) {
+ textArray[textArray.length - 1] += txt;
+ // dmesg(txt+" / "+attrString);
+ },
+ textLines : function() {
+ return textArray.slice();
+ },
+ // call flush only when you're done
+ flush : function(withNewline) {
+
+ }
+ };
+ self.startNew();
+ return self;
+ }());
+ var cc = {};
+ function _ensureColumnZero(state) {
+ if (!lines.atColumnZero()) {
+ _startNewLine(state);
+ }
+ }
+ var selection, startPoint, endPoint;
+ var selStart = [ -1, -1 ], selEnd = [ -1, -1 ];
+ var blockElems = {
+ "div" : 1,
+ "p" : 1,
+ "pre" : 1
+ };
+ function _isEmpty(node, state) {
+ // consider clean blank lines pasted in IE to be empty
+ if (dom.nodeNumChildren(node) == 0)
+ return true;
+ if (dom.nodeNumChildren(node) == 1 && getAssoc(node, "shouldBeEmpty")
+ && dom.optNodeInnerHTML(node) == " "
+ && !getAssoc(node, "unpasted")) {
+ if (state) {
+ var child = dom.nodeChild(node, 0);
+ _reachPoint(child, 0, state);
+ _reachPoint(child, 1, state);
+ }
+ return true;
+ }
+ return false;
+ }
+ function _pointHere(charsAfter, state) {
+ var ln = lines.length() - 1;
+ var chr = lines.textOfLine(ln).length;
+ if (chr == 0 && state.listType && state.listType != 'none') {
+ chr += 1; // listMarker
+ }
+ chr += charsAfter;
+ return [ ln, chr ];
+ }
+ function _reachBlockPoint(nd, idx, state) {
+ if (!dom.isNodeText(nd))
+ _reachPoint(nd, idx, state);
+ }
+ function _reachPoint(nd, idx, state) {
+ if (startPoint && nd == startPoint.node && startPoint.index == idx) {
+ selStart = _pointHere(0, state);
+ }
+ if (endPoint && nd == endPoint.node && endPoint.index == idx) {
+ selEnd = _pointHere(0, state);
+ }
+ }
+ function _incrementFlag(state, flagName) {
+ state.flags[flagName] = (state.flags[flagName] || 0) + 1;
+ }
+ function _decrementFlag(state, flagName) {
+ state.flags[flagName]--;
+ }
+ function _enterList(state, listType) {
+ var oldListType = state.listType;
+ state.listLevel = (state.listLevel || 0) + 1;
+ if (listType != 'none') {
+ state.listNesting = (state.listNesting || 0) + 1;
+ }
+ state.listType = listType;
+ return oldListType;
+ }
+ function _exitList(state, oldListType) {
+ state.listLevel--;
+ if (state.listType != 'none') {
+ state.listNesting--;
+ }
+ state.listType = oldListType;
+ }
+ function _produceListMarker(state) {
+
+ }
+ function _startNewLine(state) {
+ if (state) {
+ var atBeginningOfLine = lines.textOfLine(lines.length() - 1).length == 0;
+ if (atBeginningOfLine && state.listType && state.listType != 'none') {
+ _produceListMarker(state);
+ }
+ }
+ lines.startNew();
+ }
+ cc.notifySelection = function(sel) {
+ if (sel) {
+ selection = sel;
+ startPoint = selection.startPoint;
+ endPoint = selection.endPoint;
+ }
+ };
+ cc.collectContent = function(node, state) {
+ if (!state) {
+ state = {
+ flags : {/* name -> nesting counter */}
+ };
+ }
+ var isBlock = isBlockElement(node);
+ var isEmpty = _isEmpty(node, state);
+ if (isBlock)
+ _ensureColumnZero(state);
+ var startLine = lines.length() - 1;
+ _reachBlockPoint(node, 0, state);
+ if (dom.isNodeText(node)) {
+ var txt = dom.nodeValue(node);
+ var rest = '';
+ var x = 0; // offset into original text
+ if (txt.length == 0) {
+ if (startPoint && node == startPoint.node) {
+ selStart = _pointHere(0, state);
+ }
+ if (endPoint && node == endPoint.node) {
+ selEnd = _pointHere(0, state);
+ }
+ }
+ while (txt.length > 0) {
+ var consumed = 0;
+ if (!browser.firefox || state.flags.preMode) {
+ var firstLine = txt.split('\n', 1)[0];
+ consumed = firstLine.length + 1;
+ rest = txt.substring(consumed);
+ txt = firstLine;
+ } else { /* will only run this loop body once */
+ }
+ if (startPoint && node == startPoint.node
+ && startPoint.index - x <= txt.length) {
+ selStart = _pointHere(startPoint.index - x, state);
+ }
+ if (endPoint && node == endPoint.node
+ && endPoint.index - x <= txt.length) {
+ selEnd = _pointHere(endPoint.index - x, state);
+ }
+ var txt2 = txt;
+ if ((!state.flags.preMode) && /^[\r\n]*$/.exec(txt)) {
+ // prevents textnodes containing just "\n" from being
+ // significant
+ // in safari when pasting text, now that we convert them to
+ // spaces instead of removing them, because in other cases
+ // removing "\n" from pasted HTML will collapse words
+ // together.
+ txt2 = "";
+ }
+ var atBeginningOfLine = lines.textOfLine(lines.length() - 1).length == 0;
+ if (atBeginningOfLine) {
+ // newlines in the source mustn't become spaces at beginning
+ // of line box
+ txt2 = txt2.replace(/^\n*/, '');
+ }
+ if (atBeginningOfLine && state.listType
+ && state.listType != 'none') {
+ _produceListMarker(state);
+ }
+ lines.appendText(textify(txt2));
+
+ x += consumed;
+ txt = rest;
+ if (txt.length > 0) {
+ _startNewLine(state);
+ }
+ }
+
+ } else {
+ var cls = dom.nodeProp(node, "className");
+ var tname = (dom.nodeTagName(node) || "").toLowerCase();
+ if (tname == "br") {
+ _startNewLine(state);
+ } else if (tname == "script" || tname == "style") {
+ // ignore
+ } else if (!isEmpty) {
+ var styl = dom.nodeAttr(node, "style");
+
+ var isPre = (tname == "pre");
+ if ((!isPre) && browser.safari) {
+ isPre = (styl && /\bwhite-space:\s*pre\b/i.exec(styl));
+ }
+ if (isPre)
+ _incrementFlag(state, 'preMode');
+ var oldListTypeOrNull = null;
+
+ var nc = dom.nodeNumChildren(node);
+ for ( var i = 0; i < nc; i++) {
+ var c = dom.nodeChild(node, i);
+ //very specific IE case where it inserts <span lang="en"> which we want to ginore.
+ //to reproduce copy content from wordpad andpaste into the middle of a line in IE
+ if ( browser.msie && cls.indexOf('wikiEditor') >= 0 && dom.nodeName(c) == 'SPAN' && dom.nodeAttr(c, 'lang') == "" ) {
+ continue;
+ }
+ cc.collectContent(c, state);
+ }
+
+ if (isPre)
+ _decrementFlag(state, 'preMode');
+
+ if (oldListTypeOrNull) {
+ _exitList(state, oldListTypeOrNull);
+ }
+ }
+ }
+ if (!browser.msie) {
+ _reachBlockPoint(node, 1, state);
+ }
+ if (isBlock) {
+ if (lines.length() - 1 == startLine) {
+ _startNewLine(state);
+ } else {
+ _ensureColumnZero(state);
+ }
+ }
+
+ if (browser.msie) {
+ // in IE, a point immediately after a DIV appears on the next line
+ //_reachBlockPoint(node, 1, state);
+ }
+ };
+ // can pass a falsy value for end of doc
+ cc.notifyNextNode = function(node) {
+ // an "empty block" won't end a line; this addresses an issue in IE with
+ // typing into a blank line at the end of the document. typed text
+ // goes into the body, and the empty line div still looks clean.
+ // it is incorporated as dirty by the rule that a dirty region has
+ // to end a line.
+ if ((!node) || (isBlockElement(node) && !_isEmpty(node))) {
+ _ensureColumnZero(null);
+ }
+ };
+ // each returns [line, char] or [-1,-1]
+ var getSelectionStart = function() {
+ return selStart;
+ };
+ var getSelectionEnd = function() {
+ return selEnd;
+ };
+
+ // returns array of strings for lines found, last entry will be "" if
+ // last line is complete (i.e. if a following span should be on a new line).
+ // can be called at any point
+ cc.getLines = function() {
+ return lines.textLines();
+ };
+
+ // cc.applyHints = function(hints) {
+ // if (hints.pastedLines) {
+ //
+ // }
+ // }
+
+ cc.finish = function() {
+ lines.flush();
+ var lineStrings = cc.getLines();
+
+ if ( lineStrings.length > 0 && !lineStrings[lineStrings.length - 1] ) {
+ lineStrings.length--;
+ }
+
+ var ss = getSelectionStart();
+ var se = getSelectionEnd();
+
+ function fixLongLines() {
+ // design mode does not deal with with really long lines!
+ var lineLimit = 2000; // chars
+ var buffer = 10; // chars allowed over before wrapping
+ var linesWrapped = 0;
+ var numLinesAfter = 0;
+ for ( var i = lineStrings.length - 1; i >= 0; i--) {
+ var oldString = lineStrings[i];
+ if (oldString.length > lineLimit + buffer) {
+ var newStrings = [];
+ while (oldString.length > lineLimit) {
+ // var semiloc = oldString.lastIndexOf(';',
+ // lineLimit-1);
+ // var lengthToTake = (semiloc >= 0 ? (semiloc+1) :
+ // lineLimit);
+ lengthToTake = lineLimit;
+ newStrings.push(oldString.substring(0, lengthToTake));
+ oldString = oldString.substring(lengthToTake);
+
+ }
+ if (oldString.length > 0) {
+ newStrings.push(oldString);
+ }
+ function fixLineNumber(lineChar) {
+ if (lineChar[0] < 0)
+ return;
+ var n = lineChar[0];
+ var c = lineChar[1];
+ if (n > i) {
+ n += (newStrings.length - 1);
+ } else if (n == i) {
+ var a = 0;
+ while (c > newStrings[a].length) {
+ c -= newStrings[a].length;
+ a++;
+ }
+ n += a;
+ }
+ lineChar[0] = n;
+ lineChar[1] = c;
+ }
+ fixLineNumber(ss);
+ fixLineNumber(se);
+ linesWrapped++;
+ numLinesAfter += newStrings.length;
+
+ newStrings.unshift(i, 1);
+ lineStrings.splice.apply(lineStrings, newStrings);
+
+ }
+ }
+ return {
+ linesWrapped : linesWrapped,
+ numLinesAfter : numLinesAfter
+ };
+ }
+ var wrapData = fixLongLines();
+
+ return {
+ selStart : ss,
+ selEnd : se,
+ linesWrapped : wrapData.linesWrapped,
+ numLinesAfter : wrapData.numLinesAfter,
+ lines : lineStrings
+ };
+ };
+
+ return cc;
+}
+
+
diff --git a/extensions/WikiEditor/modules/ext.wikiEditor.css b/extensions/WikiEditor/modules/ext.wikiEditor.css
new file mode 100644
index 00000000..42de5368
--- /dev/null
+++ b/extensions/WikiEditor/modules/ext.wikiEditor.css
@@ -0,0 +1,23 @@
+/*
+ * CSS for WikiEditor
+ */
+
+/* This ID (#editform) could change in MediaWiki */
+form#editform {
+ margin: 0;
+ padding: 0;
+}
+/* These IDs (#wpSummaryLabel and #wpSummary) could change in MediaWiki */
+#wpSummary, #wpSummaryLabel {
+ margin-bottom: 1em;
+}
+/* This ID (#wpTextbox1) could change in MediaWiki */
+.wikiEditor-ui textarea#wpTextbox1 {
+ border: none;
+ padding: 0;
+ margin: -1px;
+ line-height: 1.5em;
+}
+.wikiEditor-ui .wikiEditor-ui-text > textarea#wpTextbox1 {
+ margin: 0;
+}
diff --git a/extensions/WikiEditor/modules/ext.wikiEditor.dialogs.js b/extensions/WikiEditor/modules/ext.wikiEditor.dialogs.js
new file mode 100644
index 00000000..45a19593
--- /dev/null
+++ b/extensions/WikiEditor/modules/ext.wikiEditor.dialogs.js
@@ -0,0 +1,15 @@
+/*
+ * JavaScript for WikiEditor Dialogs
+ */
+
+$( document ).ready( function() {
+ if ( !$.wikiEditor.isSupported( $.wikiEditor.modules.dialogs ) ) {
+ return;
+ }
+
+ // Replace icons
+ $.wikiEditor.modules.dialogs.config.replaceIcons( $( '#wpTextbox1' ) );
+
+ // Add dialogs module
+ $( '#wpTextbox1' ).wikiEditor( 'addModule', $.wikiEditor.modules.dialogs.config.getDefaultConfig() );
+} ); \ No newline at end of file
diff --git a/extensions/WikiEditor/modules/ext.wikiEditor.highlight.js b/extensions/WikiEditor/modules/ext.wikiEditor.highlight.js
new file mode 100644
index 00000000..a6b43851
--- /dev/null
+++ b/extensions/WikiEditor/modules/ext.wikiEditor.highlight.js
@@ -0,0 +1,8 @@
+/*
+ * JavaScript for WikiEditor Highlighting
+ */
+
+$( document ).ready( function() {
+ // Add highlight module
+ $( '#wpTextbox1' ).wikiEditor( 'addModule', 'highlight' );
+} );
diff --git a/extensions/WikiEditor/modules/ext.wikiEditor.js b/extensions/WikiEditor/modules/ext.wikiEditor.js
new file mode 100644
index 00000000..6056e63b
--- /dev/null
+++ b/extensions/WikiEditor/modules/ext.wikiEditor.js
@@ -0,0 +1,8 @@
+/*
+ * JavaScript for WikiEditor
+ */
+
+$( document ).ready( function() {
+ // Initialize wikiEditor
+ $( '#wpTextbox1' ).wikiEditor();
+} );
diff --git a/extensions/WikiEditor/modules/ext.wikiEditor.preview.js b/extensions/WikiEditor/modules/ext.wikiEditor.preview.js
new file mode 100644
index 00000000..3fbca82d
--- /dev/null
+++ b/extensions/WikiEditor/modules/ext.wikiEditor.preview.js
@@ -0,0 +1,8 @@
+/*
+ * JavaScript for WikiEditor Preview module
+ */
+
+$( document ).ready( function() {
+ // Add preview module
+ $( 'textarea#wpTextbox1' ).wikiEditor( 'addModule', 'preview' );
+} );
diff --git a/extensions/WikiEditor/modules/ext.wikiEditor.previewDialog.js b/extensions/WikiEditor/modules/ext.wikiEditor.previewDialog.js
new file mode 100644
index 00000000..67f97e00
--- /dev/null
+++ b/extensions/WikiEditor/modules/ext.wikiEditor.previewDialog.js
@@ -0,0 +1,8 @@
+/*
+ * JavaScript for WikiEditor Preview Dialog
+ */
+
+$( document ).ready( function() {
+ // Add preview module
+ $( 'textarea#wpTextbox1' ).wikiEditor( 'addModule', 'previewDialog' );
+} );
diff --git a/extensions/WikiEditor/modules/ext.wikiEditor.publish.js b/extensions/WikiEditor/modules/ext.wikiEditor.publish.js
new file mode 100644
index 00000000..d874478c
--- /dev/null
+++ b/extensions/WikiEditor/modules/ext.wikiEditor.publish.js
@@ -0,0 +1,8 @@
+/*
+ * JavaScript for WikiEditor Publish module
+ */
+
+$( document ).ready( function() {
+ // Add publish module
+ $( '#wpTextbox1' ).wikiEditor( 'addModule', 'publish' );
+} );
diff --git a/extensions/WikiEditor/modules/ext.wikiEditor.templateEditor.js b/extensions/WikiEditor/modules/ext.wikiEditor.templateEditor.js
new file mode 100644
index 00000000..650910d2
--- /dev/null
+++ b/extensions/WikiEditor/modules/ext.wikiEditor.templateEditor.js
@@ -0,0 +1,12 @@
+/*
+ * JavaScript for WikiEditor Template Editor
+ */
+
+$( document ).ready( function() {
+ // Disable in template namespace
+ if ( mw.config.get( 'wgNamespaceNumber' ) == 10 ) {
+ return true;
+ }
+ // Add template editor module
+ $( 'textarea#wpTextbox1' ).wikiEditor( 'addModule', 'templateEditor' );
+});
diff --git a/extensions/WikiEditor/modules/ext.wikiEditor.templates.js b/extensions/WikiEditor/modules/ext.wikiEditor.templates.js
new file mode 100644
index 00000000..4ac09b54
--- /dev/null
+++ b/extensions/WikiEditor/modules/ext.wikiEditor.templates.js
@@ -0,0 +1,12 @@
+/*
+ * JavaScript for WikiEditor Templates
+ */
+
+$( document ).ready( function() {
+ // Disable for template namespace
+ if ( mw.config.get( 'wgNamespaceNumber' ) == 10 ) {
+ return true;
+ }
+ // Add templates module
+ $( '#wpTextbox1' ).wikiEditor( 'addModule', 'templates' );
+} ); \ No newline at end of file
diff --git a/extensions/WikiEditor/modules/ext.wikiEditor.tests.toolbar.js b/extensions/WikiEditor/modules/ext.wikiEditor.tests.toolbar.js
new file mode 100644
index 00000000..de01f34f
--- /dev/null
+++ b/extensions/WikiEditor/modules/ext.wikiEditor.tests.toolbar.js
@@ -0,0 +1,246 @@
+/**
+ * Test set for the edit toolbar
+ */
+var textareaId = '#wpTextbox1';
+var wikiEditorTests = {
+ // Add emoticons section
+ 'add_sections_toolbar': {
+ 'call': 'addToToolbar',
+ 'data': {
+ 'sections': {
+ 'emoticons': {
+ 'type': 'toolbar',
+ 'label': 'Emoticons'
+ }
+ }
+ },
+ 'test': '*[rel=emoticons].section',
+ 'pre': 0,
+ 'post': 1
+ },
+ // Add faces group to emoticons section
+ 'add_groups': {
+ 'call': 'addToToolbar',
+ 'data': {
+ 'section': 'emoticons',
+ 'groups': {
+ 'faces': {
+ 'label': 'Faces'
+ }
+ }
+ },
+ 'test': '*[rel=emoticons].section *[rel=faces].group',
+ 'pre': 0,
+ 'post': 1
+ },
+ // Add smile tool to faces group of emoticons section
+ 'add_tools': {
+ 'call': 'addToToolbar',
+ 'data': {
+ 'section': 'emoticons',
+ 'group': 'faces',
+ 'tools': {
+ 'smile': {
+ label: 'Smile!',
+ type: 'button',
+ icon: 'http://upload.wikimedia.org/wikipedia/commons/thumb/a/a4/Gnome-face-smile.svg/22px-Gnome-face-smile.svg.png',
+ action: {
+ type: 'encapsulate',
+ options: {
+ pre: ":)"
+ }
+ }
+ }
+ }
+ },
+ 'test': '*[rel=emoticons].section *[rel=faces].group *[rel=smile].tool',
+ 'pre': 0,
+ 'post': 1
+ },
+ // Add info section
+ 'add_sections_booklet': {
+ 'call': 'addToToolbar',
+ 'data': {
+ 'sections': {
+ 'info': {
+ 'type': 'booklet',
+ 'label': 'Info'
+ }
+ }
+ },
+ 'test': '*[rel=info].section',
+ 'pre': 0,
+ 'post': 1
+ },
+ // Add info section
+ 'add_pages_table': {
+ 'call': 'addToToolbar',
+ 'data': {
+ 'section': 'info',
+ 'pages': {
+ 'colors': {
+ 'layout': 'table',
+ 'label': 'Colors',
+ 'headings': [
+ { text: 'Name' },
+ { text: 'Temperature' },
+ { text: 'Swatch' }
+ ]
+ }
+ }
+ },
+ 'test': '*[rel=info].section *[rel=colors].page',
+ 'pre': 0,
+ 'post': 1
+ },
+ // Add colors rows
+ 'add_rows': {
+ 'call': 'addToToolbar',
+ 'data': {
+ 'section': 'info',
+ 'page': 'colors',
+ 'rows': [
+ {
+ 'name': { text: 'Red' },
+ 'temp': { text: 'Warm' },
+ 'swatch': { html: '<div style="width:10px;height:10px;background-color:red;">' }
+ },
+ {
+ 'name': { text: 'Blue' },
+ 'temp': { text: 'Cold' },
+ 'swatch': { html: '<div style="width:10px;height:10px;background-color:blue;">' }
+ },
+ {
+ 'name': { text: 'Silver' },
+ 'temp': { text: 'Neutral' },
+ 'swatch': { html: '<div style="width:10px;height:10px;background-color:silver;">' }
+ }
+ ]
+ },
+ 'test': '*[rel=info].section *[rel=colors].page tr td',
+ 'pre': 0,
+ 'post': 9
+ },
+ // Add
+ 'add_pages_characters': {
+ 'call': 'addToToolbar',
+ 'data': {
+ 'section': 'info',
+ 'pages': {
+ 'emoticons': {
+ 'layout': 'characters',
+ 'label': 'Emoticons'
+ },
+ 'removeme': {
+ 'layout': 'characters',
+ 'label': 'Remove Me!'
+ }
+ }
+ },
+ 'test': '*[rel=info].section *[rel=emoticons].page',
+ 'pre': 0,
+ 'post': 1
+ },
+ // Add
+ 'add_characters': {
+ 'call': 'addToToolbar',
+ 'data': {
+ 'section': 'info',
+ 'page': 'emoticons',
+ 'characters': [ ':)', ':))', ':(', '<3', ';)' ]
+ },
+ 'test': '*[rel=info].section *[rel=emoticons].page *[rel=":)"]',
+ 'pre': 0,
+ 'post': 1
+ },
+ // Remove page
+ 'remove_page': {
+ 'call': 'removeFromToolbar',
+ 'data': {
+ 'section': 'info',
+ 'page': 'removeme'
+ },
+ 'test': '*[rel=info].section *[rel=removeme].page',
+ 'pre': 1,
+ 'post': 0
+ },
+ // Remove :)) from emoticon characters
+ 'remove_character': {
+ 'call': 'removeFromToolbar',
+ 'data': {
+ 'section': 'info',
+ 'page': 'emoticons',
+ 'character': ':))'
+ },
+ 'test': '*[rel=info].section *[rel=emoticons].page *[rel=":))"]',
+ 'pre': 1,
+ 'post': 0
+ },
+ // Remove row from colors table of info section
+ 'remove_row': {
+ 'call': 'removeFromToolbar',
+ 'data': {
+ 'section': 'info',
+ 'page': 'colors',
+ 'row': 0
+ },
+ 'test': '*[rel=info].section *[rel=colors].page tr td',
+ 'pre': 9,
+ 'post': 6
+ }
+};
+$(document).ready( function() {
+ var button = $( '<button>Run wikiEditor Tests!</button>' )
+ .css( {
+ 'position': 'fixed',
+ 'bottom': 0,
+ 'right': 0,
+ 'width': '100%',
+ 'backgroundColor': '#333333',
+ 'opacity': 0.75,
+ 'color': '#DDDDDD',
+ 'padding': '0.5em',
+ 'border': 'none',
+ 'display': 'none'
+ } )
+ .click( function() {
+ if ( $(this).attr( 'enabled' ) == 'false' ) {
+ $(this).slideUp( 'fast' );
+ return false;
+ }
+ var messages = [ 'Running tests for wikiEditor API' ];
+ var $target = $( textareaId );
+ var $ui = $target.data( 'wikiEditor-context' ).$ui;
+ var passes = 0;
+ var tests = 0;
+ for ( var test in wikiEditorTests ) {
+ var pre = $ui.find( wikiEditorTests[test].test ).size() ==
+ wikiEditorTests[test].pre;
+ messages.push ( test + '-pre: ' + ( pre ? 'PASS' : 'FAIL' ) );
+ $target.wikiEditor(
+ wikiEditorTests[test].call,
+ wikiEditorTests[test].data
+ );
+ var post = $ui.find( wikiEditorTests[test].test ).size() ==
+ wikiEditorTests[test].post;
+ messages.push ( test + '-post: ' + ( post ? 'PASS' : 'FAIL' ) );
+ if ( pre && post ) {
+ passes++;
+ }
+ tests++;
+ }
+ if ( window.console !== undefined ) {
+ for ( var i = 0; i < messages.length; i++ ) {
+ console.log( messages[i] );
+ }
+ }
+ $(this)
+ .attr( 'title', messages.join( " | " ) )
+ .text( passes + ' / ' + tests + ' were successful' )
+ .css( 'backgroundColor', passes < tests ? 'red' : 'green' )
+ .attr( 'enabled', 'false' )
+ .blur();
+ } )
+ .appendTo( $( 'body' ) );
+ setTimeout( function() { button.slideDown( 'fast' ) }, 2000 );
+} );
diff --git a/extensions/WikiEditor/modules/ext.wikiEditor.toc.js b/extensions/WikiEditor/modules/ext.wikiEditor.toc.js
new file mode 100644
index 00000000..264e9f86
--- /dev/null
+++ b/extensions/WikiEditor/modules/ext.wikiEditor.toc.js
@@ -0,0 +1,8 @@
+/*
+ * JavaScript for WikiEditor Table of Contents
+ */
+
+$( document ).ready( function() {
+ // Add table of contents module
+ $( '#wpTextbox1' ).wikiEditor( 'addModule', 'toc' );
+} );
diff --git a/extensions/WikiEditor/modules/ext.wikiEditor.toolbar.hideSig.js b/extensions/WikiEditor/modules/ext.wikiEditor.toolbar.hideSig.js
new file mode 100644
index 00000000..3bc0f0b4
--- /dev/null
+++ b/extensions/WikiEditor/modules/ext.wikiEditor.toolbar.hideSig.js
@@ -0,0 +1,14 @@
+/*
+ * Remove the signature button if the main namespace is edited.
+ */
+$( document ).ready( function() {
+ // This module is designed not to depend on ext.wikiEditor or jquery.wikiEditor.
+ // Removing this dependency fixed various bugs, but it does mean that we have to
+ // account for the situation where $.wikiEditor is not present
+ if ( !$.wikiEditor || !$.wikiEditor.isSupported( $.wikiEditor.modules.toolbar ) ) {
+ return;
+ }
+ if ( $( 'body' ).hasClass( 'ns-0' ) ) {
+ $( '#wpTextbox1' ).wikiEditor( 'removeFromToolbar', { 'section': 'main', 'group': 'insert', 'tool': 'signature' } );
+ }
+});
diff --git a/extensions/WikiEditor/modules/ext.wikiEditor.toolbar.js b/extensions/WikiEditor/modules/ext.wikiEditor.toolbar.js
new file mode 100644
index 00000000..53199375
--- /dev/null
+++ b/extensions/WikiEditor/modules/ext.wikiEditor.toolbar.js
@@ -0,0 +1,14 @@
+/*
+ * JavaScript for WikiEditor Toolbar
+ */
+
+$( document ).ready( function() {
+ if ( !$.wikiEditor.isSupported( $.wikiEditor.modules.toolbar ) ) {
+ return;
+ }
+ // The old toolbar is still in place and needs to be removed so there aren't two toolbars
+ $( '#toolbar' ).remove();
+ // Add toolbar module
+ // TODO: Implement .wikiEditor( 'remove' )
+ $( '#wpTextbox1' ).wikiEditor( 'addModule', $.wikiEditor.modules.toolbar.config.getDefaultConfig() );
+} );
diff --git a/extensions/WikiEditor/modules/images/dialogs/insert-link-error.png b/extensions/WikiEditor/modules/images/dialogs/insert-link-error.png
new file mode 100644
index 00000000..a7def768
--- /dev/null
+++ b/extensions/WikiEditor/modules/images/dialogs/insert-link-error.png
Binary files differ
diff --git a/extensions/WikiEditor/modules/images/dialogs/insert-link-exists.png b/extensions/WikiEditor/modules/images/dialogs/insert-link-exists.png
new file mode 100644
index 00000000..76abbba5
--- /dev/null
+++ b/extensions/WikiEditor/modules/images/dialogs/insert-link-exists.png
Binary files differ
diff --git a/extensions/WikiEditor/modules/images/dialogs/insert-link-external-rtl.png b/extensions/WikiEditor/modules/images/dialogs/insert-link-external-rtl.png
new file mode 100644
index 00000000..74fc1ada
--- /dev/null
+++ b/extensions/WikiEditor/modules/images/dialogs/insert-link-external-rtl.png
Binary files differ
diff --git a/extensions/WikiEditor/modules/images/dialogs/insert-link-external.png b/extensions/WikiEditor/modules/images/dialogs/insert-link-external.png
new file mode 100644
index 00000000..04a5aa17
--- /dev/null
+++ b/extensions/WikiEditor/modules/images/dialogs/insert-link-external.png
Binary files differ
diff --git a/extensions/WikiEditor/modules/images/dialogs/insert-link-invalid.png b/extensions/WikiEditor/modules/images/dialogs/insert-link-invalid.png
new file mode 100644
index 00000000..b5521b9d
--- /dev/null
+++ b/extensions/WikiEditor/modules/images/dialogs/insert-link-invalid.png
Binary files differ
diff --git a/extensions/WikiEditor/modules/images/dialogs/insert-link-notexists.png b/extensions/WikiEditor/modules/images/dialogs/insert-link-notexists.png
new file mode 100644
index 00000000..84eb9d68
--- /dev/null
+++ b/extensions/WikiEditor/modules/images/dialogs/insert-link-notexists.png
Binary files differ
diff --git a/extensions/WikiEditor/modules/images/dialogs/loading-small.gif b/extensions/WikiEditor/modules/images/dialogs/loading-small.gif
new file mode 100644
index 00000000..a8e536b0
--- /dev/null
+++ b/extensions/WikiEditor/modules/images/dialogs/loading-small.gif
Binary files differ
diff --git a/extensions/WikiEditor/modules/images/dialogs/loading.gif b/extensions/WikiEditor/modules/images/dialogs/loading.gif
new file mode 100644
index 00000000..52f78205
--- /dev/null
+++ b/extensions/WikiEditor/modules/images/dialogs/loading.gif
Binary files differ
diff --git a/extensions/WikiEditor/modules/images/templateEditor/collapse.png b/extensions/WikiEditor/modules/images/templateEditor/collapse.png
new file mode 100644
index 00000000..acb42b91
--- /dev/null
+++ b/extensions/WikiEditor/modules/images/templateEditor/collapse.png
Binary files differ
diff --git a/extensions/WikiEditor/modules/images/templateEditor/dialog-collapsed.png b/extensions/WikiEditor/modules/images/templateEditor/dialog-collapsed.png
new file mode 100644
index 00000000..ce703020
--- /dev/null
+++ b/extensions/WikiEditor/modules/images/templateEditor/dialog-collapsed.png
Binary files differ
diff --git a/extensions/WikiEditor/modules/images/templateEditor/dialog-expanded.png b/extensions/WikiEditor/modules/images/templateEditor/dialog-expanded.png
new file mode 100644
index 00000000..73832e97
--- /dev/null
+++ b/extensions/WikiEditor/modules/images/templateEditor/dialog-expanded.png
Binary files differ
diff --git a/extensions/WikiEditor/modules/images/templateEditor/expand.png b/extensions/WikiEditor/modules/images/templateEditor/expand.png
new file mode 100644
index 00000000..4c96ffd3
--- /dev/null
+++ b/extensions/WikiEditor/modules/images/templateEditor/expand.png
Binary files differ
diff --git a/extensions/WikiEditor/modules/images/templateEditor/name-base.png b/extensions/WikiEditor/modules/images/templateEditor/name-base.png
new file mode 100644
index 00000000..4b103085
--- /dev/null
+++ b/extensions/WikiEditor/modules/images/templateEditor/name-base.png
Binary files differ
diff --git a/extensions/WikiEditor/modules/images/templateEditor/text-base.png b/extensions/WikiEditor/modules/images/templateEditor/text-base.png
new file mode 100644
index 00000000..9cb0c74d
--- /dev/null
+++ b/extensions/WikiEditor/modules/images/templateEditor/text-base.png
Binary files differ
diff --git a/extensions/WikiEditor/modules/images/templateEditor/wiki-text.png b/extensions/WikiEditor/modules/images/templateEditor/wiki-text.png
new file mode 100644
index 00000000..ea37e731
--- /dev/null
+++ b/extensions/WikiEditor/modules/images/templateEditor/wiki-text.png
Binary files differ
diff --git a/extensions/WikiEditor/modules/images/toc/close.png b/extensions/WikiEditor/modules/images/toc/close.png
new file mode 100644
index 00000000..511fc4ff
--- /dev/null
+++ b/extensions/WikiEditor/modules/images/toc/close.png
Binary files differ
diff --git a/extensions/WikiEditor/modules/images/toc/grab.png b/extensions/WikiEditor/modules/images/toc/grab.png
new file mode 100644
index 00000000..a0d5d7ba
--- /dev/null
+++ b/extensions/WikiEditor/modules/images/toc/grab.png
Binary files differ
diff --git a/extensions/WikiEditor/modules/images/toc/grip.png b/extensions/WikiEditor/modules/images/toc/grip.png
new file mode 100644
index 00000000..05203f38
--- /dev/null
+++ b/extensions/WikiEditor/modules/images/toc/grip.png
Binary files differ
diff --git a/extensions/WikiEditor/modules/images/toc/open.png b/extensions/WikiEditor/modules/images/toc/open.png
new file mode 100644
index 00000000..459aa39b
--- /dev/null
+++ b/extensions/WikiEditor/modules/images/toc/open.png
Binary files differ
diff --git a/extensions/WikiEditor/modules/images/toolbar/arrow-down.png b/extensions/WikiEditor/modules/images/toolbar/arrow-down.png
new file mode 100644
index 00000000..bf2d4fb4
--- /dev/null
+++ b/extensions/WikiEditor/modules/images/toolbar/arrow-down.png
Binary files differ
diff --git a/extensions/WikiEditor/modules/images/toolbar/arrow-ltr.png b/extensions/WikiEditor/modules/images/toolbar/arrow-ltr.png
new file mode 100644
index 00000000..c27c9636
--- /dev/null
+++ b/extensions/WikiEditor/modules/images/toolbar/arrow-ltr.png
Binary files differ
diff --git a/extensions/WikiEditor/modules/images/toolbar/arrow-rtl.png b/extensions/WikiEditor/modules/images/toolbar/arrow-rtl.png
new file mode 100644
index 00000000..12ca1837
--- /dev/null
+++ b/extensions/WikiEditor/modules/images/toolbar/arrow-rtl.png
Binary files differ
diff --git a/extensions/WikiEditor/modules/images/toolbar/base.png b/extensions/WikiEditor/modules/images/toolbar/base.png
new file mode 100644
index 00000000..b55ed07b
--- /dev/null
+++ b/extensions/WikiEditor/modules/images/toolbar/base.png
Binary files differ
diff --git a/extensions/WikiEditor/modules/images/toolbar/button-sprite.png b/extensions/WikiEditor/modules/images/toolbar/button-sprite.png
new file mode 100644
index 00000000..bd8989ff
--- /dev/null
+++ b/extensions/WikiEditor/modules/images/toolbar/button-sprite.png
Binary files differ
diff --git a/extensions/WikiEditor/modules/images/toolbar/example-image.png b/extensions/WikiEditor/modules/images/toolbar/example-image.png
new file mode 100644
index 00000000..a3853683
--- /dev/null
+++ b/extensions/WikiEditor/modules/images/toolbar/example-image.png
Binary files differ
diff --git a/extensions/WikiEditor/modules/images/toolbar/format-big.png b/extensions/WikiEditor/modules/images/toolbar/format-big.png
new file mode 100644
index 00000000..175ea19d
--- /dev/null
+++ b/extensions/WikiEditor/modules/images/toolbar/format-big.png
Binary files differ
diff --git a/extensions/WikiEditor/modules/images/toolbar/format-bold-A.png b/extensions/WikiEditor/modules/images/toolbar/format-bold-A.png
new file mode 100644
index 00000000..22849e62
--- /dev/null
+++ b/extensions/WikiEditor/modules/images/toolbar/format-bold-A.png
Binary files differ
diff --git a/extensions/WikiEditor/modules/images/toolbar/format-bold-B.png b/extensions/WikiEditor/modules/images/toolbar/format-bold-B.png
new file mode 100644
index 00000000..45be1de6
--- /dev/null
+++ b/extensions/WikiEditor/modules/images/toolbar/format-bold-B.png
Binary files differ
diff --git a/extensions/WikiEditor/modules/images/toolbar/format-bold-F.png b/extensions/WikiEditor/modules/images/toolbar/format-bold-F.png
new file mode 100644
index 00000000..e3264767
--- /dev/null
+++ b/extensions/WikiEditor/modules/images/toolbar/format-bold-F.png
Binary files differ
diff --git a/extensions/WikiEditor/modules/images/toolbar/format-bold-G.png b/extensions/WikiEditor/modules/images/toolbar/format-bold-G.png
new file mode 100644
index 00000000..39989b42
--- /dev/null
+++ b/extensions/WikiEditor/modules/images/toolbar/format-bold-G.png
Binary files differ
diff --git a/extensions/WikiEditor/modules/images/toolbar/format-bold-N.png b/extensions/WikiEditor/modules/images/toolbar/format-bold-N.png
new file mode 100644
index 00000000..4d34a5c9
--- /dev/null
+++ b/extensions/WikiEditor/modules/images/toolbar/format-bold-N.png
Binary files differ
diff --git a/extensions/WikiEditor/modules/images/toolbar/format-bold-P.png b/extensions/WikiEditor/modules/images/toolbar/format-bold-P.png
new file mode 100644
index 00000000..e245e71a
--- /dev/null
+++ b/extensions/WikiEditor/modules/images/toolbar/format-bold-P.png
Binary files differ
diff --git a/extensions/WikiEditor/modules/images/toolbar/format-bold-V.png b/extensions/WikiEditor/modules/images/toolbar/format-bold-V.png
new file mode 100644
index 00000000..9dfe649b
--- /dev/null
+++ b/extensions/WikiEditor/modules/images/toolbar/format-bold-V.png
Binary files differ
diff --git a/extensions/WikiEditor/modules/images/toolbar/format-bold-ka.png b/extensions/WikiEditor/modules/images/toolbar/format-bold-ka.png
new file mode 100644
index 00000000..7e37b40f
--- /dev/null
+++ b/extensions/WikiEditor/modules/images/toolbar/format-bold-ka.png
Binary files differ
diff --git a/extensions/WikiEditor/modules/images/toolbar/format-bold-ru.png b/extensions/WikiEditor/modules/images/toolbar/format-bold-ru.png
new file mode 100644
index 00000000..e24afd88
--- /dev/null
+++ b/extensions/WikiEditor/modules/images/toolbar/format-bold-ru.png
Binary files differ
diff --git a/extensions/WikiEditor/modules/images/toolbar/format-bold.png b/extensions/WikiEditor/modules/images/toolbar/format-bold.png
new file mode 100644
index 00000000..bd8e294c
--- /dev/null
+++ b/extensions/WikiEditor/modules/images/toolbar/format-bold.png
Binary files differ
diff --git a/extensions/WikiEditor/modules/images/toolbar/format-indent-rtl.png b/extensions/WikiEditor/modules/images/toolbar/format-indent-rtl.png
new file mode 100644
index 00000000..3cc6c945
--- /dev/null
+++ b/extensions/WikiEditor/modules/images/toolbar/format-indent-rtl.png
Binary files differ
diff --git a/extensions/WikiEditor/modules/images/toolbar/format-indent.png b/extensions/WikiEditor/modules/images/toolbar/format-indent.png
new file mode 100644
index 00000000..cb864df2
--- /dev/null
+++ b/extensions/WikiEditor/modules/images/toolbar/format-indent.png
Binary files differ
diff --git a/extensions/WikiEditor/modules/images/toolbar/format-italic-A.png b/extensions/WikiEditor/modules/images/toolbar/format-italic-A.png
new file mode 100644
index 00000000..9010c6b1
--- /dev/null
+++ b/extensions/WikiEditor/modules/images/toolbar/format-italic-A.png
Binary files differ
diff --git a/extensions/WikiEditor/modules/images/toolbar/format-italic-C.png b/extensions/WikiEditor/modules/images/toolbar/format-italic-C.png
new file mode 100644
index 00000000..cf191fad
--- /dev/null
+++ b/extensions/WikiEditor/modules/images/toolbar/format-italic-C.png
Binary files differ
diff --git a/extensions/WikiEditor/modules/images/toolbar/format-italic-D.png b/extensions/WikiEditor/modules/images/toolbar/format-italic-D.png
new file mode 100644
index 00000000..9120da78
--- /dev/null
+++ b/extensions/WikiEditor/modules/images/toolbar/format-italic-D.png
Binary files differ
diff --git a/extensions/WikiEditor/modules/images/toolbar/format-italic-I.png b/extensions/WikiEditor/modules/images/toolbar/format-italic-I.png
new file mode 100644
index 00000000..af5bf947
--- /dev/null
+++ b/extensions/WikiEditor/modules/images/toolbar/format-italic-I.png
Binary files differ
diff --git a/extensions/WikiEditor/modules/images/toolbar/format-italic-K.png b/extensions/WikiEditor/modules/images/toolbar/format-italic-K.png
new file mode 100644
index 00000000..d8fd479d
--- /dev/null
+++ b/extensions/WikiEditor/modules/images/toolbar/format-italic-K.png
Binary files differ
diff --git a/extensions/WikiEditor/modules/images/toolbar/format-italic-ka.png b/extensions/WikiEditor/modules/images/toolbar/format-italic-ka.png
new file mode 100644
index 00000000..2ca7a136
--- /dev/null
+++ b/extensions/WikiEditor/modules/images/toolbar/format-italic-ka.png
Binary files differ
diff --git a/extensions/WikiEditor/modules/images/toolbar/format-italic.png b/extensions/WikiEditor/modules/images/toolbar/format-italic.png
new file mode 100644
index 00000000..1b47eeed
--- /dev/null
+++ b/extensions/WikiEditor/modules/images/toolbar/format-italic.png
Binary files differ
diff --git a/extensions/WikiEditor/modules/images/toolbar/format-olist-rtl.png b/extensions/WikiEditor/modules/images/toolbar/format-olist-rtl.png
new file mode 100644
index 00000000..1f065399
--- /dev/null
+++ b/extensions/WikiEditor/modules/images/toolbar/format-olist-rtl.png
Binary files differ
diff --git a/extensions/WikiEditor/modules/images/toolbar/format-olist.png b/extensions/WikiEditor/modules/images/toolbar/format-olist.png
new file mode 100644
index 00000000..abaeae5f
--- /dev/null
+++ b/extensions/WikiEditor/modules/images/toolbar/format-olist.png
Binary files differ
diff --git a/extensions/WikiEditor/modules/images/toolbar/format-small.png b/extensions/WikiEditor/modules/images/toolbar/format-small.png
new file mode 100644
index 00000000..9f031e8c
--- /dev/null
+++ b/extensions/WikiEditor/modules/images/toolbar/format-small.png
Binary files differ
diff --git a/extensions/WikiEditor/modules/images/toolbar/format-subscript.png b/extensions/WikiEditor/modules/images/toolbar/format-subscript.png
new file mode 100644
index 00000000..b676d89d
--- /dev/null
+++ b/extensions/WikiEditor/modules/images/toolbar/format-subscript.png
Binary files differ
diff --git a/extensions/WikiEditor/modules/images/toolbar/format-superscript.png b/extensions/WikiEditor/modules/images/toolbar/format-superscript.png
new file mode 100644
index 00000000..7220b3c8
--- /dev/null
+++ b/extensions/WikiEditor/modules/images/toolbar/format-superscript.png
Binary files differ
diff --git a/extensions/WikiEditor/modules/images/toolbar/format-ulist-rtl.png b/extensions/WikiEditor/modules/images/toolbar/format-ulist-rtl.png
new file mode 100644
index 00000000..51510157
--- /dev/null
+++ b/extensions/WikiEditor/modules/images/toolbar/format-ulist-rtl.png
Binary files differ
diff --git a/extensions/WikiEditor/modules/images/toolbar/format-ulist.png b/extensions/WikiEditor/modules/images/toolbar/format-ulist.png
new file mode 100644
index 00000000..3172d4d3
--- /dev/null
+++ b/extensions/WikiEditor/modules/images/toolbar/format-ulist.png
Binary files differ
diff --git a/extensions/WikiEditor/modules/images/toolbar/insert-file.png b/extensions/WikiEditor/modules/images/toolbar/insert-file.png
new file mode 100644
index 00000000..1e5419ad
--- /dev/null
+++ b/extensions/WikiEditor/modules/images/toolbar/insert-file.png
Binary files differ
diff --git a/extensions/WikiEditor/modules/images/toolbar/insert-gallery.png b/extensions/WikiEditor/modules/images/toolbar/insert-gallery.png
new file mode 100644
index 00000000..46a9acc5
--- /dev/null
+++ b/extensions/WikiEditor/modules/images/toolbar/insert-gallery.png
Binary files differ
diff --git a/extensions/WikiEditor/modules/images/toolbar/insert-ilink.png b/extensions/WikiEditor/modules/images/toolbar/insert-ilink.png
new file mode 100644
index 00000000..c4010cfe
--- /dev/null
+++ b/extensions/WikiEditor/modules/images/toolbar/insert-ilink.png
Binary files differ
diff --git a/extensions/WikiEditor/modules/images/toolbar/insert-link.png b/extensions/WikiEditor/modules/images/toolbar/insert-link.png
new file mode 100644
index 00000000..a8d2f2f3
--- /dev/null
+++ b/extensions/WikiEditor/modules/images/toolbar/insert-link.png
Binary files differ
diff --git a/extensions/WikiEditor/modules/images/toolbar/insert-newline.png b/extensions/WikiEditor/modules/images/toolbar/insert-newline.png
new file mode 100644
index 00000000..67a517b4
--- /dev/null
+++ b/extensions/WikiEditor/modules/images/toolbar/insert-newline.png
Binary files differ
diff --git a/extensions/WikiEditor/modules/images/toolbar/insert-nowiki.png b/extensions/WikiEditor/modules/images/toolbar/insert-nowiki.png
new file mode 100644
index 00000000..80889f4d
--- /dev/null
+++ b/extensions/WikiEditor/modules/images/toolbar/insert-nowiki.png
Binary files differ
diff --git a/extensions/WikiEditor/modules/images/toolbar/insert-redirect-rtl.png b/extensions/WikiEditor/modules/images/toolbar/insert-redirect-rtl.png
new file mode 100644
index 00000000..d0200f6e
--- /dev/null
+++ b/extensions/WikiEditor/modules/images/toolbar/insert-redirect-rtl.png
Binary files differ
diff --git a/extensions/WikiEditor/modules/images/toolbar/insert-redirect.png b/extensions/WikiEditor/modules/images/toolbar/insert-redirect.png
new file mode 100644
index 00000000..52931264
--- /dev/null
+++ b/extensions/WikiEditor/modules/images/toolbar/insert-redirect.png
Binary files differ
diff --git a/extensions/WikiEditor/modules/images/toolbar/insert-reference.png b/extensions/WikiEditor/modules/images/toolbar/insert-reference.png
new file mode 100644
index 00000000..8c2c4aa7
--- /dev/null
+++ b/extensions/WikiEditor/modules/images/toolbar/insert-reference.png
Binary files differ
diff --git a/extensions/WikiEditor/modules/images/toolbar/insert-signature.png b/extensions/WikiEditor/modules/images/toolbar/insert-signature.png
new file mode 100644
index 00000000..49cdc957
--- /dev/null
+++ b/extensions/WikiEditor/modules/images/toolbar/insert-signature.png
Binary files differ
diff --git a/extensions/WikiEditor/modules/images/toolbar/insert-table.png b/extensions/WikiEditor/modules/images/toolbar/insert-table.png
new file mode 100644
index 00000000..7897e78d
--- /dev/null
+++ b/extensions/WikiEditor/modules/images/toolbar/insert-table.png
Binary files differ
diff --git a/extensions/WikiEditor/modules/images/toolbar/insert-xlink.png b/extensions/WikiEditor/modules/images/toolbar/insert-xlink.png
new file mode 100644
index 00000000..7bf24c46
--- /dev/null
+++ b/extensions/WikiEditor/modules/images/toolbar/insert-xlink.png
Binary files differ
diff --git a/extensions/WikiEditor/modules/images/toolbar/loading-small.gif b/extensions/WikiEditor/modules/images/toolbar/loading-small.gif
new file mode 100644
index 00000000..ff0688b9
--- /dev/null
+++ b/extensions/WikiEditor/modules/images/toolbar/loading-small.gif
Binary files differ
diff --git a/extensions/WikiEditor/modules/images/toolbar/loading.gif b/extensions/WikiEditor/modules/images/toolbar/loading.gif
new file mode 100644
index 00000000..fab1bb9d
--- /dev/null
+++ b/extensions/WikiEditor/modules/images/toolbar/loading.gif
Binary files differ
diff --git a/extensions/WikiEditor/modules/images/toolbar/magnify-clip.png b/extensions/WikiEditor/modules/images/toolbar/magnify-clip.png
new file mode 100644
index 00000000..00a9cee1
--- /dev/null
+++ b/extensions/WikiEditor/modules/images/toolbar/magnify-clip.png
Binary files differ
diff --git a/extensions/WikiEditor/modules/images/toolbar/png24/arrow-down.png b/extensions/WikiEditor/modules/images/toolbar/png24/arrow-down.png
new file mode 100644
index 00000000..6fd2d63a
--- /dev/null
+++ b/extensions/WikiEditor/modules/images/toolbar/png24/arrow-down.png
Binary files differ
diff --git a/extensions/WikiEditor/modules/images/toolbar/png24/arrow-left.png b/extensions/WikiEditor/modules/images/toolbar/png24/arrow-left.png
new file mode 100644
index 00000000..adcba5bd
--- /dev/null
+++ b/extensions/WikiEditor/modules/images/toolbar/png24/arrow-left.png
Binary files differ
diff --git a/extensions/WikiEditor/modules/images/toolbar/png24/arrow-right.png b/extensions/WikiEditor/modules/images/toolbar/png24/arrow-right.png
new file mode 100644
index 00000000..52d04e6a
--- /dev/null
+++ b/extensions/WikiEditor/modules/images/toolbar/png24/arrow-right.png
Binary files differ
diff --git a/extensions/WikiEditor/modules/images/toolbar/png24/format-big.png b/extensions/WikiEditor/modules/images/toolbar/png24/format-big.png
new file mode 100644
index 00000000..5e60a178
--- /dev/null
+++ b/extensions/WikiEditor/modules/images/toolbar/png24/format-big.png
Binary files differ
diff --git a/extensions/WikiEditor/modules/images/toolbar/png24/format-bold-A.png b/extensions/WikiEditor/modules/images/toolbar/png24/format-bold-A.png
new file mode 100644
index 00000000..429bff1d
--- /dev/null
+++ b/extensions/WikiEditor/modules/images/toolbar/png24/format-bold-A.png
Binary files differ
diff --git a/extensions/WikiEditor/modules/images/toolbar/png24/format-bold-B.png b/extensions/WikiEditor/modules/images/toolbar/png24/format-bold-B.png
new file mode 100644
index 00000000..cd4a9993
--- /dev/null
+++ b/extensions/WikiEditor/modules/images/toolbar/png24/format-bold-B.png
Binary files differ
diff --git a/extensions/WikiEditor/modules/images/toolbar/png24/format-bold-F.png b/extensions/WikiEditor/modules/images/toolbar/png24/format-bold-F.png
new file mode 100644
index 00000000..9f50b490
--- /dev/null
+++ b/extensions/WikiEditor/modules/images/toolbar/png24/format-bold-F.png
Binary files differ
diff --git a/extensions/WikiEditor/modules/images/toolbar/png24/format-bold-G.png b/extensions/WikiEditor/modules/images/toolbar/png24/format-bold-G.png
new file mode 100644
index 00000000..4584a8b2
--- /dev/null
+++ b/extensions/WikiEditor/modules/images/toolbar/png24/format-bold-G.png
Binary files differ
diff --git a/extensions/WikiEditor/modules/images/toolbar/png24/format-bold-N.png b/extensions/WikiEditor/modules/images/toolbar/png24/format-bold-N.png
new file mode 100644
index 00000000..7c14e28e
--- /dev/null
+++ b/extensions/WikiEditor/modules/images/toolbar/png24/format-bold-N.png
Binary files differ
diff --git a/extensions/WikiEditor/modules/images/toolbar/png24/format-bold-P.png b/extensions/WikiEditor/modules/images/toolbar/png24/format-bold-P.png
new file mode 100644
index 00000000..906ee670
--- /dev/null
+++ b/extensions/WikiEditor/modules/images/toolbar/png24/format-bold-P.png
Binary files differ
diff --git a/extensions/WikiEditor/modules/images/toolbar/png24/format-bold-V.png b/extensions/WikiEditor/modules/images/toolbar/png24/format-bold-V.png
new file mode 100644
index 00000000..13b258d7
--- /dev/null
+++ b/extensions/WikiEditor/modules/images/toolbar/png24/format-bold-V.png
Binary files differ
diff --git a/extensions/WikiEditor/modules/images/toolbar/png24/format-bold.png b/extensions/WikiEditor/modules/images/toolbar/png24/format-bold.png
new file mode 100644
index 00000000..429bff1d
--- /dev/null
+++ b/extensions/WikiEditor/modules/images/toolbar/png24/format-bold.png
Binary files differ
diff --git a/extensions/WikiEditor/modules/images/toolbar/png24/format-italic-A.png b/extensions/WikiEditor/modules/images/toolbar/png24/format-italic-A.png
new file mode 100644
index 00000000..ee834ca6
--- /dev/null
+++ b/extensions/WikiEditor/modules/images/toolbar/png24/format-italic-A.png
Binary files differ
diff --git a/extensions/WikiEditor/modules/images/toolbar/png24/format-italic-C.png b/extensions/WikiEditor/modules/images/toolbar/png24/format-italic-C.png
new file mode 100644
index 00000000..d2a9182b
--- /dev/null
+++ b/extensions/WikiEditor/modules/images/toolbar/png24/format-italic-C.png
Binary files differ
diff --git a/extensions/WikiEditor/modules/images/toolbar/png24/format-italic-I.png b/extensions/WikiEditor/modules/images/toolbar/png24/format-italic-I.png
new file mode 100644
index 00000000..a74215d6
--- /dev/null
+++ b/extensions/WikiEditor/modules/images/toolbar/png24/format-italic-I.png
Binary files differ
diff --git a/extensions/WikiEditor/modules/images/toolbar/png24/format-italic-K.png b/extensions/WikiEditor/modules/images/toolbar/png24/format-italic-K.png
new file mode 100644
index 00000000..4e91bcc8
--- /dev/null
+++ b/extensions/WikiEditor/modules/images/toolbar/png24/format-italic-K.png
Binary files differ
diff --git a/extensions/WikiEditor/modules/images/toolbar/png24/format-italic.png b/extensions/WikiEditor/modules/images/toolbar/png24/format-italic.png
new file mode 100644
index 00000000..ee834ca6
--- /dev/null
+++ b/extensions/WikiEditor/modules/images/toolbar/png24/format-italic.png
Binary files differ
diff --git a/extensions/WikiEditor/modules/images/toolbar/png24/format-olist.png b/extensions/WikiEditor/modules/images/toolbar/png24/format-olist.png
new file mode 100644
index 00000000..69a3186d
--- /dev/null
+++ b/extensions/WikiEditor/modules/images/toolbar/png24/format-olist.png
Binary files differ
diff --git a/extensions/WikiEditor/modules/images/toolbar/png24/format-small.png b/extensions/WikiEditor/modules/images/toolbar/png24/format-small.png
new file mode 100644
index 00000000..1b6e22d1
--- /dev/null
+++ b/extensions/WikiEditor/modules/images/toolbar/png24/format-small.png
Binary files differ
diff --git a/extensions/WikiEditor/modules/images/toolbar/png24/format-subscript.png b/extensions/WikiEditor/modules/images/toolbar/png24/format-subscript.png
new file mode 100644
index 00000000..3fc0dce5
--- /dev/null
+++ b/extensions/WikiEditor/modules/images/toolbar/png24/format-subscript.png
Binary files differ
diff --git a/extensions/WikiEditor/modules/images/toolbar/png24/format-superscript.png b/extensions/WikiEditor/modules/images/toolbar/png24/format-superscript.png
new file mode 100644
index 00000000..d5e2d90c
--- /dev/null
+++ b/extensions/WikiEditor/modules/images/toolbar/png24/format-superscript.png
Binary files differ
diff --git a/extensions/WikiEditor/modules/images/toolbar/png24/format-ulist.png b/extensions/WikiEditor/modules/images/toolbar/png24/format-ulist.png
new file mode 100644
index 00000000..3ddd46cc
--- /dev/null
+++ b/extensions/WikiEditor/modules/images/toolbar/png24/format-ulist.png
Binary files differ
diff --git a/extensions/WikiEditor/modules/images/toolbar/png24/generate.sh b/extensions/WikiEditor/modules/images/toolbar/png24/generate.sh
new file mode 100644
index 00000000..ca14ee68
--- /dev/null
+++ b/extensions/WikiEditor/modules/images/toolbar/png24/generate.sh
@@ -0,0 +1,12 @@
+#! /bin/bash
+
+# Compresses all PNGs in the current directory and puts the compressed
+# version in the parent directory
+#
+# Requires pngcrush
+
+for f in *.png
+do
+ pngcrush $f ../$f
+done
+
diff --git a/extensions/WikiEditor/modules/images/toolbar/png24/insert-file.png b/extensions/WikiEditor/modules/images/toolbar/png24/insert-file.png
new file mode 100644
index 00000000..cabc613a
--- /dev/null
+++ b/extensions/WikiEditor/modules/images/toolbar/png24/insert-file.png
Binary files differ
diff --git a/extensions/WikiEditor/modules/images/toolbar/png24/insert-gallery.png b/extensions/WikiEditor/modules/images/toolbar/png24/insert-gallery.png
new file mode 100644
index 00000000..661689ae
--- /dev/null
+++ b/extensions/WikiEditor/modules/images/toolbar/png24/insert-gallery.png
Binary files differ
diff --git a/extensions/WikiEditor/modules/images/toolbar/png24/insert-link.png b/extensions/WikiEditor/modules/images/toolbar/png24/insert-link.png
new file mode 100644
index 00000000..b16eeaba
--- /dev/null
+++ b/extensions/WikiEditor/modules/images/toolbar/png24/insert-link.png
Binary files differ
diff --git a/extensions/WikiEditor/modules/images/toolbar/png24/insert-newline.png b/extensions/WikiEditor/modules/images/toolbar/png24/insert-newline.png
new file mode 100644
index 00000000..d5ec80b6
--- /dev/null
+++ b/extensions/WikiEditor/modules/images/toolbar/png24/insert-newline.png
Binary files differ
diff --git a/extensions/WikiEditor/modules/images/toolbar/png24/insert-nowiki.png b/extensions/WikiEditor/modules/images/toolbar/png24/insert-nowiki.png
new file mode 100644
index 00000000..f872244e
--- /dev/null
+++ b/extensions/WikiEditor/modules/images/toolbar/png24/insert-nowiki.png
Binary files differ
diff --git a/extensions/WikiEditor/modules/images/toolbar/png24/insert-redirect.png b/extensions/WikiEditor/modules/images/toolbar/png24/insert-redirect.png
new file mode 100644
index 00000000..288aa516
--- /dev/null
+++ b/extensions/WikiEditor/modules/images/toolbar/png24/insert-redirect.png
Binary files differ
diff --git a/extensions/WikiEditor/modules/images/toolbar/png24/insert-reference.png b/extensions/WikiEditor/modules/images/toolbar/png24/insert-reference.png
new file mode 100644
index 00000000..9988dbcc
--- /dev/null
+++ b/extensions/WikiEditor/modules/images/toolbar/png24/insert-reference.png
Binary files differ
diff --git a/extensions/WikiEditor/modules/images/toolbar/png24/insert-signature.png b/extensions/WikiEditor/modules/images/toolbar/png24/insert-signature.png
new file mode 100644
index 00000000..0b003916
--- /dev/null
+++ b/extensions/WikiEditor/modules/images/toolbar/png24/insert-signature.png
Binary files differ
diff --git a/extensions/WikiEditor/modules/images/toolbar/png24/insert-table.png b/extensions/WikiEditor/modules/images/toolbar/png24/insert-table.png
new file mode 100644
index 00000000..e506b928
--- /dev/null
+++ b/extensions/WikiEditor/modules/images/toolbar/png24/insert-table.png
Binary files differ
diff --git a/extensions/WikiEditor/modules/images/toolbar/png24/search-replace.png b/extensions/WikiEditor/modules/images/toolbar/png24/search-replace.png
new file mode 100644
index 00000000..de3a75f8
--- /dev/null
+++ b/extensions/WikiEditor/modules/images/toolbar/png24/search-replace.png
Binary files differ
diff --git a/extensions/WikiEditor/modules/images/toolbar/search-replace.png b/extensions/WikiEditor/modules/images/toolbar/search-replace.png
new file mode 100644
index 00000000..cb70b929
--- /dev/null
+++ b/extensions/WikiEditor/modules/images/toolbar/search-replace.png
Binary files differ
diff --git a/extensions/WikiEditor/modules/jquery.wikiEditor.css b/extensions/WikiEditor/modules/jquery.wikiEditor.css
new file mode 100644
index 00000000..6df44565
--- /dev/null
+++ b/extensions/WikiEditor/modules/jquery.wikiEditor.css
@@ -0,0 +1,112 @@
+/*
+ * CSS for WikiEditor jQuery plugin
+ */
+
+.wikiEditor-ui {
+ float: left;
+ position: relative;
+ clear: both;
+ width: 100%;
+ background-color: #E0EEf7;
+ border: solid silver 1px;
+}
+.wikiEditor-ui textarea:focus {
+ outline: none;
+}
+.wikiEditor-ui .wikiEditor-ui-bottom {
+
+}
+.wikiEditor-ui .wikiEditor-ui-text {
+ line-height: 0;
+}
+.wikiEditor-ui .wikiEditor-ui-top {
+ position: relative;
+ border-bottom: solid silver 1px;
+}
+.wikiEditor-ui .wikiEditor-ui-left {
+ float: left;
+ width: 100%;
+}
+.wikiEditor-ui .wikiEditor-ui-right {
+ float: right;
+ background: #F3F3F3;
+ overflow: hidden;
+}
+.wikiEditor-wikitext {
+ float: left;
+ width: 100%;
+}
+.wikiEditor-ui-controls {
+ float: left;
+ width: 100%;
+ background-color: white;
+ margin-top: -1px;
+ border-bottom: solid 1px silver;
+}
+.wikiEditor-ui-tabs {
+ float: left;
+ height: 2.5em;
+ margin-left: -1px;
+ background-color: white;
+ border-left: solid 1px silver;
+ border-top: solid 1px silver;
+}
+.wikiEditor-ui-buttons {
+ float: right;
+ height: 2.5em;
+ margin-right: -1px;
+ background-color: white;
+ padding-left: 1em;
+ border-top: solid 1px white;
+}
+.wikiEditor-ui-buttons button {
+ margin-left: 0.5em;
+}
+.wikiEditor-ui-tabs div {
+ float: left;
+ height: 2.5em;
+ background-color: #f3f3f3;
+ border-right: solid 1px silver;
+ border-bottom: solid 1px silver;
+}
+.wikiEditor-ui-tabs div.current {
+ border-bottom: solid 1px white;
+ background-color: white;
+}
+.wikiEditor-ui-tabs div a {
+ display: inline-block;
+ padding: 0 0.75em;
+ line-height: 2.5em;
+ color: #0645AD;
+}
+.wikiEditor-ui-tabs div.current a {
+ color: #333333;
+}
+.wikiEditor-ui-tabs div.current a:hover {
+ text-decoration: none;
+}
+
+.wikiEditor-view-wikitext {
+ line-height: 1em;
+}
+.wikiEditor-ui-loading {
+ background: #f3f3f3;
+ z-index: 10;
+ position: absolute;
+ top: 0;
+ left: 0;
+ text-align: center;
+ height: 100%;
+ width: 100%;
+ border: 1px solid silver;
+ margin: -1px;
+}
+.wikiEditor-ui-loading span {
+ display: block;
+ height: 24px;
+ width: 24px;
+ /* @embed */
+ background: url(images/toolbar/loading.gif) 0 0 no-repeat;
+ text-indent: -9999px;
+ margin: 0 auto;
+} \ No newline at end of file
diff --git a/extensions/WikiEditor/modules/jquery.wikiEditor.dialogs.config.css b/extensions/WikiEditor/modules/jquery.wikiEditor.dialogs.config.css
new file mode 100644
index 00000000..13302ac3
--- /dev/null
+++ b/extensions/WikiEditor/modules/jquery.wikiEditor.dialogs.config.css
@@ -0,0 +1,204 @@
+/*
+ * CSS for WikiEditor Dialogs
+ */
+
+/* Table Dialog */
+#wikieditor-toolbar-table-dialog fieldset {
+ width: 218px;
+ padding: 0;
+ float: left;
+}
+body.rtl #wikieditor-toolbar-table-dialog fieldset {
+ float: right;
+}
+#wikieditor-toolbar-table-dialog .wikieditor-toolbar-table-preview-wrapper {
+ width: 330px;
+ padding: 0;
+ float: right;
+}
+body.rtl #wikieditor-toolbar-table-dialog .wikieditor-toolbar-table-preview-wrapper {
+ float: left;
+}
+body.rtl .wikiEditor-toolbar-dialog .wikieditor-toolbar-table-preview-wrapper table {
+ margin-left: 1em;
+ margin-right: 0;
+}
+.wikieditor-toolbar-table-preview-content * {
+ cursor: default;
+}
+.wikiEditor-toolbar-dialog .wikieditor-toolbar-table-preview-wrapper table {
+ width: 100% !important;
+}
+.wikiEditor-toolbar-dialog .wikieditor-toolbar-table-preview-content table td {
+ padding: 10px 4px !important;
+ height: auto !important;
+}
+.wikiEditor-toolbar-dialog .wikieditor-toolbar-table-preview-content table th {
+ padding: 7px 3px !important;
+}
+.wikieditor-toolbar-table-dimension-fields .wikieditor-toolbar-field-wrapper {
+ float: left;
+ margin-right: 20px;
+ vertical-align: bottom;
+}
+body.rtl .wikieditor-toolbar-table-dimension-fields .wikieditor-toolbar-field-wrapper {
+ float: right;
+ margin-right: 0px;
+ margin-left: 20px;
+}
+.wikiEditor-toolbar-dialog .ui-dialog-content {
+ padding: 30px 20px 0 !important;
+}
+.wikieditor-toolbar-dialog-wrapper {
+ width: 100%;
+}
+/* Insert Link Dialog */
+#wikieditor-toolbar-link-int-target-status {
+ float: right;
+}
+#wikieditor-toolbar-link-int-target,
+#wikieditor-toolbar-link-int-text {
+ width: 100%;
+}
+#wikieditor-toolbar-tool-link-int-target-label {
+ float: left;
+ line-height: 1.7em;
+}
+#wikieditor-toolbar-link-int-target-status-exists,
+#wikieditor-toolbar-link-int-target-status-notexists,
+#wikieditor-toolbar-link-int-target-status-invalid,
+#wikieditor-toolbar-link-int-target-status-external {
+ padding-left: 30px;
+ background-position: 0 50%;
+ background-repeat: no-repeat;
+}
+#wikieditor-toolbar-link-int-target-status-exists {
+ /* @embed */
+ background-image: url(images/dialogs/insert-link-exists.png);
+}
+#wikieditor-toolbar-link-int-target-status-notexists {
+ /* @embed */
+ background-image: url(images/dialogs/insert-link-notexists.png);
+}
+#wikieditor-toolbar-link-int-target-status-invalid {
+ /* @embed */
+ background-image: url(images/dialogs/insert-link-invalid.png);
+}
+#wikieditor-toolbar-link-int-target-status-external {
+ /* @embed */
+ background-image: url(images/dialogs/insert-link-external.png);
+}
+/* Reference Dialog */
+#wikieditor-toolbar-reference-dialog label {
+ float: left;
+ line-height: 1.7em;
+}
+#wikieditor-toolbar-reference-text {
+ width: 100%;
+}
+/* RTL Changes */
+body.rtl .wikiEditor-toolbar-dialog .ui-dialog-buttonpane button {
+ float: left;
+ margin: 0.5em 0.4em 0.5em 0 !important;
+}
+body.rtl .wikiEditor-toolbar-dialog .ui-dialog-titlebar-close {
+ left: 0.9em;
+ right: auto;
+}
+body.rtl .wikiEditor-toolbar-dialog .ui-dialog-title {
+ float:right;
+}
+body.rtl #wikieditor-toolbar-link-int-target-status {
+ float: left;
+}
+body.rtl #wikieditor-toolbar-tool-link-int-target-label {
+ float: right;
+}
+body.rtl .wikieditor-toolbar-floated-field-wrapper {
+ float: right;
+ margin-right: 0;
+ margin-left: 2em;
+}
+body.rtl #wikieditor-toolbar-link-int-target-status-exists,
+body.rtl #wikieditor-toolbar-link-int-target-status-notexists,
+body.rtl #wikieditor-toolbar-link-int-target-status-invalid,
+body.rtl #wikieditor-toolbar-link-int-target-status-external {
+ padding-left: 0;
+ padding-right: 30px;
+ background-position: 100% 50%;
+}
+body.rtl #wikieditor-toolbar-link-int-target-status-external {
+ /* @embed */
+ background-image: url(images/dialogs/insert-link-external-rtl.png);
+}
+body.rtl #wikieditor-toolbar-reference-dialog label {
+ float: right;
+}
+/* Template Editor Dialogs */
+.wikiEditor-template-dialog-fields label {
+ text-transform: capitalize;
+ float: left;
+ width: 25%;
+ line-height: 2.25em;
+}
+.wikiEditor-template-dialog-fields textarea {
+ float: right;
+ width: 70%;
+ line-height: 1.5em;
+ height: 1.5em;
+}
+.wikiEditor-template-dialog-fields .wikiEditor-template-dialog-field-wrapper {
+ padding: 0.75em 0.33em;
+ border-bottom: dashed 1px silver;
+ clear: both;
+}
+.wikiEditor-template-dialog-fields .wikiEditor-template-dialog-field-wrapper:first-child {
+ padding-top: 0;
+}
+.wikiEditor-template-dialog-fields .wikiEditor-template-dialog-field-wrapper:last-child {
+ border-bottom: none;
+}
+/* Self Clearing Floats */
+.wikieditor-toolbar-table-dimension-fields:after,
+.wikieditor-toolbar-dialog-wrapper:after {
+ visibility: hidden;
+ display: block;
+ font-size: 0;
+ content: " ";
+ clear: both;
+ height: 0;
+}
+.wikieditor-toolbar-table-dimension-fields,
+.wikieditor-toolbar-dialog-wrapper {
+ display: inline-table;
+}
+/* Hides from IE-mac \*/
+* html .wikieditor-toolbar-table-dimension-fields,
+* html .wikieditor-toolbar-dialog-wrapper {
+ height: 1%;
+}
+.wikieditor-toolbar-table-dimension-fields,
+.wikieditor-toolbar-dialog-wrapper {
+ display: block;
+}
+/* End hide from IE-mac */
+.wikiEditor-toolbar-dialog .ui-dialog-buttonpane {
+ border-top: 1px solid #cccccc !important;
+}
+.wikiEditor-toolbar-dialog .ui-dialog-content {
+ padding-bottom: 1em !important;
+}
+/* Edit dialog */
+.wikiEditor-dialog-editoptions {
+ margin-top: 15px;
+}
+/* Publish dialog */
+.wikiEditor-publish-dialog-copywarn {
+ margin-top: 0.5em;
+}
+.wikiEditor-publish-dialog-summary {
+ margin-top: 1.5em;
+}
+.wikiEditor-publish-dialog-options {
+ margin-top: 1.5em;
+}
diff --git a/extensions/WikiEditor/modules/jquery.wikiEditor.dialogs.config.js b/extensions/WikiEditor/modules/jquery.wikiEditor.dialogs.config.js
new file mode 100644
index 00000000..4ab6ce46
--- /dev/null
+++ b/extensions/WikiEditor/modules/jquery.wikiEditor.dialogs.config.js
@@ -0,0 +1,1150 @@
+/**
+ * Configuration of Dialog module for wikiEditor
+ */
+( function( $ ) { $.wikiEditor.modules.dialogs.config = {
+
+replaceIcons: function( $textarea ) {
+ $textarea
+ .wikiEditor( 'removeFromToolbar', { 'section': 'main', 'group': 'insert', 'tool': 'xlink' } )
+ .wikiEditor( 'removeFromToolbar', { 'section': 'main', 'group': 'insert', 'tool': 'ilink' } )
+ .wikiEditor( 'removeFromToolbar', { 'section': 'main', 'group': 'insert', 'tool': 'reference' } )
+ .wikiEditor( 'removeFromToolbar', { 'section': 'advanced', 'group': 'insert', 'tool': 'table' } )
+ .wikiEditor( 'addToToolbar', {
+ 'section': 'main',
+ 'group': 'insert',
+ 'tools': {
+ 'link': {
+ 'labelMsg': 'wikieditor-toolbar-tool-link',
+ 'type': 'button',
+ 'icon': 'insert-link.png',
+ 'offset': [2, -1654],
+ 'action': {
+ 'type': 'dialog',
+ 'module': 'insert-link'
+ }
+ },
+ 'reference': {
+ 'labelMsg': 'wikieditor-toolbar-tool-reference',
+ 'filters': [ 'body.ns-subject' ],
+ 'type': 'button',
+ 'icon': 'insert-reference.png',
+ 'offset': [2, -1798],
+ 'action': {
+ 'type': 'dialog',
+ 'module': 'insert-reference'
+ }
+ }
+ }
+ } )
+ .wikiEditor( 'addToToolbar', {
+ 'section': 'advanced',
+ 'group': 'insert',
+ 'tools': {
+ 'table': {
+ 'labelMsg': 'wikieditor-toolbar-tool-table',
+ 'type': 'button',
+ 'icon': 'insert-table.png',
+ 'offset': [2, -1942],
+ 'action': {
+ 'type': 'dialog',
+ 'module': 'insert-table'
+ }
+ }
+ }
+ } )
+ .wikiEditor( 'addToToolbar', {
+ 'section': 'advanced',
+ 'groups': {
+ 'search': {
+ 'tools': {
+ 'replace': {
+ 'labelMsg': 'wikieditor-toolbar-tool-replace',
+ 'type': 'button',
+ 'icon': 'search-replace.png',
+ 'offset': [-70, -214],
+ 'action': {
+ 'type': 'dialog',
+ 'module': 'search-and-replace'
+ }
+ }
+ }
+ }
+ }
+ } );
+},
+
+getDefaultConfig: function () {
+ return { 'dialogs': {
+ 'insert-link': {
+ titleMsg: 'wikieditor-toolbar-tool-link-title',
+ id: 'wikieditor-toolbar-link-dialog',
+ html: '\
+ <fieldset>\
+ <div class="wikieditor-toolbar-field-wrapper">\
+ <label for="wikieditor-toolbar-link-int-target" rel="wikieditor-toolbar-tool-link-int-target" id="wikieditor-toolbar-tool-link-int-target-label"></label>\
+ <div id="wikieditor-toolbar-link-int-target-status"></div>\
+ <input type="text" id="wikieditor-toolbar-link-int-target" />\
+ </div>\
+ <div class="wikieditor-toolbar-field-wrapper">\
+ <label for="wikieditor-toolbar-link-int-text" rel="wikieditor-toolbar-tool-link-int-text"></label>\
+ <input type="text" id="wikieditor-toolbar-link-int-text" />\
+ </div>\
+ <div class="wikieditor-toolbar-field-wrapper">\
+ <div class="wikieditor-toolbar-floated-field-wrapper">\
+ <input type="radio" id="wikieditor-toolbar-link-type-int" name="wikieditor-toolbar-link-type" selected />\
+ <label for="wikieditor-toolbar-link-type-int" rel="wikieditor-toolbar-tool-link-int"></label>\
+ </div>\
+ <div class="wikieditor-toolbar-floated-field-wrapper">\
+ <input type="radio" id="wikieditor-toolbar-link-type-ext" name="wikieditor-toolbar-link-type" />\
+ <label for="wikieditor-toolbar-link-type-ext" rel="wikieditor-toolbar-tool-link-ext"></label>\
+ </div>\
+ </div>\
+ </fieldset>',
+ init: function() {
+ function isExternalLink( s ) {
+ // The following things are considered to be external links:
+ // * Starts a URL protocol
+ // * Starts with www.
+ // All of these are potentially valid titles, and the latter two categories match about 6300
+ // titles in enwiki's ns0. Out of 6.9M titles, that's 0.09%
+ if ( typeof arguments.callee.regex == 'undefined' ) {
+ // Cache the regex
+ arguments.callee.regex =
+ new RegExp( "^(" + mw.config.get( 'wgUrlProtocols' ) + "|www\\.)", 'i');
+ }
+ return s.match( arguments.callee.regex );
+ }
+ // Updates the status indicator above the target link
+ function updateWidget( status ) {
+ $( '#wikieditor-toolbar-link-int-target-status' ).children().hide();
+ $( '#wikieditor-toolbar-link-int-target' ).parent()
+ .removeClass(
+ 'status-invalid status-external status-notexists status-exists status-loading'
+ );
+ if ( status ) {
+ $( '#wikieditor-toolbar-link-int-target-status-' + status ).show();
+ $( '#wikieditor-toolbar-link-int-target' ).parent().addClass( 'status-' + status );
+ }
+ if ( status == 'invalid' ) {
+ $( '.ui-dialog:visible .ui-dialog-buttonpane button:first' )
+ .attr( 'disabled', true )
+ .addClass( 'disabled' );
+ } else {
+ $( '.ui-dialog:visible .ui-dialog-buttonpane button:first' )
+ .removeAttr('disabled')
+ .removeClass('disabled');
+ }
+ }
+ // Updates the UI to show if the page title being inputed by the user exists or not
+ // accepts parameter internal for bypassing external link detection
+ function updateExistence( internal ) {
+ // ensure the internal parameter is a boolean
+ if ( internal != true ) internal = false;
+ // Abort previous request
+ var request = $( '#wikieditor-toolbar-link-int-target-status' ).data( 'request' );
+ if ( request ) {
+ request.abort();
+ }
+ var target = $( '#wikieditor-toolbar-link-int-target' ).val();
+ var cache = $( '#wikieditor-toolbar-link-int-target-status' ).data( 'existencecache' );
+ if ( cache[target] ) {
+ updateWidget( cache[target] );
+ return;
+ }
+ if ( target.replace( /^\s+$/,'' ) == '' ) {
+ // Hide the widget when the textbox is empty
+ updateWidget( false );
+ return;
+ }
+ // If the forced internal paremter was not true, check if the target is an external link
+ if ( !internal && isExternalLink( target ) ) {
+ updateWidget( 'external' );
+ return;
+ }
+ if ( target.indexOf( '|' ) != -1 ) {
+ // Title contains | , which means it's invalid
+ // but confuses the API. Show invalid and bypass API
+ updateWidget( 'invalid' );
+ return;
+ }
+ // Show loading spinner while waiting for the API to respond
+ updateWidget( 'loading' );
+ // Call the API to check page status, saving the request object so it can be aborted if
+ // necessary
+ $( '#wikieditor-toolbar-link-int-target-status' ).data(
+ 'request',
+ $.ajax( {
+ url: mw.util.wikiScript( 'api' ),
+ dataType: 'json',
+ data: {
+ 'action': 'query',
+ 'indexpageids': '',
+ 'titles': target,
+ 'converttitles': '',
+ 'format': 'json'
+ },
+ success: function( data ) {
+ var status;
+ if ( !data || typeof data.query == 'undefined' ) {
+ // This happens in some weird cases
+ status = false;
+ } else {
+ var page = data.query.pages[data.query.pageids[0]];
+ status = 'exists';
+ if ( typeof page.missing != 'undefined' )
+ status = 'notexists';
+ else if ( typeof page.invalid != 'undefined' )
+ status = 'invalid';
+ }
+ // Cache the status of the link target if the force internal parameter was not
+ // passed
+ if ( !internal ) cache[target] = status;
+ updateWidget( status );
+ }
+ } )
+ );
+ }
+ $( '#wikieditor-toolbar-link-type-int, #wikieditor-toolbar-link-type-ext' ).click( function() {
+ if( $( '#wikieditor-toolbar-link-type-ext' ).is( ':checked' ) ) {
+ // Abort previous request
+ var request = $( '#wikieditor-toolbar-link-int-target-status' ).data( 'request' );
+ if ( request ) {
+ request.abort();
+ }
+ updateWidget( 'external' );
+ }
+ if( $( '#wikieditor-toolbar-link-type-int' ).is( ':checked' ) )
+ updateExistence( true );
+ });
+ // Set labels of tabs based on rel values
+ $(this).find( '[rel]' ).each( function() {
+ $(this).text( mediaWiki.msg( $(this).attr( 'rel' ) ) );
+ });
+ // Set tabindexes on form fields
+ $.wikiEditor.modules.dialogs.fn.setTabindexes( $(this).find( 'input' ).not( '[tabindex]' ) );
+ // Setup the tooltips in the textboxes
+ $( '#wikieditor-toolbar-link-int-target' )
+ .data( 'tooltip', mediaWiki.msg( 'wikieditor-toolbar-tool-link-int-target-tooltip' ) );
+ $( '#wikieditor-toolbar-link-int-text' )
+ .data( 'tooltip', mediaWiki.msg( 'wikieditor-toolbar-tool-link-int-text-tooltip' ) );
+ $( '#wikieditor-toolbar-link-int-target, #wikieditor-toolbar-link-int-text' )
+ .each( function() {
+ var tooltip = mediaWiki.msg( $( this ).attr( 'id' ) + '-tooltip' );
+ if ( $( this ).val() == '' )
+ $( this )
+ .addClass( 'wikieditor-toolbar-dialog-hint' )
+ .val( $( this ).data( 'tooltip' ) )
+ .data( 'tooltip-mode', true );
+ } )
+ .focus( function() {
+ if( $( this ).val() == $( this ).data( 'tooltip' ) ) {
+ $( this )
+ .val( '' )
+ .removeClass( 'wikieditor-toolbar-dialog-hint' )
+ .data( 'tooltip-mode', false );
+ }
+ })
+ .bind( 'change', function() {
+ if ( $( this ).val() != $( this ).data( 'tooltip' ) ) {
+ $( this )
+ .removeClass( 'wikieditor-toolbar-dialog-hint' )
+ .data( 'tooltip-mode', false );
+ }
+ })
+ .bind( 'blur', function() {
+ if ( $( this ).val() == '' ) {
+ $( this )
+ .addClass( 'wikieditor-toolbar-dialog-hint' )
+ .val( $( this ).data( 'tooltip' ) )
+ .data( 'tooltip-mode', true );
+ }
+ });
+
+ // Automatically copy the value of the internal link page title field to the link text field unless the
+ // user has changed the link text field - this is a convenience thing since most link texts are going to
+ // be the the same as the page title - Also change the internal/external radio button accordingly
+ $( '#wikieditor-toolbar-link-int-target' ).bind( 'change keydown paste cut', function() {
+ // $(this).val() is the old value, before the keypress - Defer this until $(this).val() has
+ // been updated
+ setTimeout( function() {
+ if ( isExternalLink( $( '#wikieditor-toolbar-link-int-target' ).val() ) ) {
+ $( '#wikieditor-toolbar-link-type-ext' ).attr( 'checked', 'checked' );
+ updateWidget( 'external' );
+ } else {
+ $( '#wikieditor-toolbar-link-type-int' ).attr( 'checked', 'checked' );
+ updateExistence();
+ }
+ if ( $( '#wikieditor-toolbar-link-int-text' ).data( 'untouched' ) )
+ if ( $( '#wikieditor-toolbar-link-int-target' ).val() ==
+ $( '#wikieditor-toolbar-link-int-target' ).data( 'tooltip' ) ) {
+ $( '#wikieditor-toolbar-link-int-text' )
+ .addClass( 'wikieditor-toolbar-dialog-hint' )
+ .val( $( '#wikieditor-toolbar-link-int-text' ).data( 'tooltip' ) )
+ .change();
+ } else {
+ $( '#wikieditor-toolbar-link-int-text' )
+ .val( $( '#wikieditor-toolbar-link-int-target' ).val() )
+ .change();
+ }
+ }, 0 );
+ });
+ $( '#wikieditor-toolbar-link-int-text' ).bind( 'change keydown paste cut', function() {
+ var oldVal = $(this).val();
+ var that = this;
+ setTimeout( function() {
+ if ( $(that).val() != oldVal )
+ $(that).data( 'untouched', false );
+ }, 0 );
+ });
+ // Add images to the page existence widget, which will be shown mutually exclusively to communicate if
+ // the page exists, does not exist or the title is invalid (like if it contains a | character)
+ var existsMsg = mediaWiki.msg( 'wikieditor-toolbar-tool-link-int-target-status-exists' );
+ var notexistsMsg = mediaWiki.msg( 'wikieditor-toolbar-tool-link-int-target-status-notexists' );
+ var invalidMsg = mediaWiki.msg( 'wikieditor-toolbar-tool-link-int-target-status-invalid' );
+ var externalMsg = mediaWiki.msg( 'wikieditor-toolbar-tool-link-int-target-status-external' );
+ var loadingMsg = mediaWiki.msg( 'wikieditor-toolbar-tool-link-int-target-status-loading' );
+ $( '#wikieditor-toolbar-link-int-target-status' )
+ .append( $( '<div />' )
+ .attr( 'id', 'wikieditor-toolbar-link-int-target-status-exists' )
+ .append( existsMsg )
+ )
+ .append( $( '<div />' )
+ .attr( 'id', 'wikieditor-toolbar-link-int-target-status-notexists' )
+ .append( notexistsMsg )
+ )
+ .append( $( '<div />' )
+ .attr( 'id', 'wikieditor-toolbar-link-int-target-status-invalid' )
+ .append( invalidMsg )
+ )
+ .append( $( '<div />' )
+ .attr( 'id', 'wikieditor-toolbar-link-int-target-status-external' )
+ .append( externalMsg )
+ )
+ .append( $( '<div />' )
+ .attr( 'id', 'wikieditor-toolbar-link-int-target-status-loading' )
+ .append( $( '<img />' ).attr( {
+ 'src': $.wikiEditor.imgPath + 'dialogs/' + 'loading-small.gif',
+ 'alt': loadingMsg,
+ 'title': loadingMsg
+ } ) )
+ )
+ .data( 'existencecache', {} )
+ .children().hide();
+
+ $( '#wikieditor-toolbar-link-int-target' )
+ .bind( 'keyup paste cut', function() {
+ // Cancel the running timer if applicable
+ if ( typeof $(this).data( 'timerID' ) != 'undefined' ) {
+ clearTimeout( $(this).data( 'timerID' ) );
+ }
+ // Delay fetch for a while
+ // FIXME: Make 120 configurable elsewhere
+ var timerID = setTimeout( updateExistence, 120 );
+ $(this).data( 'timerID', timerID );
+ } )
+ .change( function() {
+ // Cancel the running timer if applicable
+ if ( typeof $(this).data( 'timerID' ) != 'undefined' ) {
+ clearTimeout( $(this).data( 'timerID' ) );
+ }
+ // Fetch right now
+ updateExistence();
+ } );
+
+ // Title suggestions
+ $( '#wikieditor-toolbar-link-int-target' ).data( 'suggcache', {} ).suggestions( {
+ fetch: function( query ) {
+ var that = this;
+ var title = $(this).val();
+
+ if ( isExternalLink( title ) || title.indexOf( '|' ) != -1 || title == '') {
+ $(this).suggestions( 'suggestions', [] );
+ return;
+ }
+
+ var cache = $(this).data( 'suggcache' );
+ if ( typeof cache[title] != 'undefined' ) {
+ $(this).suggestions( 'suggestions', cache[title] );
+ return;
+ }
+
+ var request = $.ajax( {
+ url: mw.util.wikiScript( 'api' ),
+ data: {
+ 'action': 'opensearch',
+ 'search': title,
+ 'namespace': 0,
+ 'suggest': '',
+ 'format': 'json'
+ },
+ dataType: 'json',
+ success: function( data ) {
+ cache[title] = data[1];
+ $(that).suggestions( 'suggestions', data[1] );
+ }
+ });
+ $(this).data( 'request', request );
+ },
+ cancel: function() {
+ var request = $(this).data( 'request' );
+ if ( request )
+ request.abort();
+ }
+ });
+ },
+ dialog: {
+ width: 500,
+ dialogClass: 'wikiEditor-toolbar-dialog',
+ buttons: {
+ 'wikieditor-toolbar-tool-link-insert': function() {
+ function escapeInternalText( s ) {
+ // FIXME: Should this escape [[ too? Seems to work without that
+ return s.replace( /(]{2,})/g, '<nowiki>$1</nowiki>' );
+ }
+ function escapeExternalTarget( s ) {
+ return s.replace( / /g, '%20' )
+ .replace( /\[/g, '%5B' )
+ .replace( /]/g, '%5D' );
+ }
+ function escapeExternalText( s ) {
+ // FIXME: Should this escape [ too? Seems to work without that
+ return s.replace( /(]+)/g, '<nowiki>$1</nowiki>' );
+ }
+ var insertText = '';
+ var whitespace = $( '#wikieditor-toolbar-link-dialog' ).data( 'whitespace' );
+ var target = $( '#wikieditor-toolbar-link-int-target' ).val();
+ var text = $( '#wikieditor-toolbar-link-int-text' ).val();
+ // check if the tooltips were passed as target or text
+ if ( $( '#wikieditor-toolbar-link-int-target' ).data( 'tooltip-mode' ) )
+ target = "";
+ if ( $( '#wikieditor-toolbar-link-int-text' ).data( 'tooltip-mode' ) )
+ text = "";
+ if ( target == '' ) {
+ alert( mediaWiki.msg( 'wikieditor-toolbar-tool-link-empty' ) );
+ return;
+ }
+ if ( $.trim( text ) == '' ) {
+ // [[Foo| ]] creates an invisible link
+ // Instead, generate [[Foo|]]
+ text = '';
+ }
+ if ( $( '#wikieditor-toolbar-link-type-int' ).is( ':checked' ) ) {
+ // FIXME: Exactly how fragile is this?
+ if ( $( '#wikieditor-toolbar-link-int-target-status-invalid' ).is( ':visible' ) ) {
+ // Refuse to add links to invalid titles
+ alert( mediaWiki.msg( 'wikieditor-toolbar-tool-link-int-invalid' ) );
+ return;
+ }
+
+ if ( target == text || !text.length )
+ insertText = '[[' + target + ']]';
+ else
+ insertText = '[[' + target + '|' + escapeInternalText( text ) + ']]';
+ } else {
+ // Prepend http:// if there is no protocol
+ if ( !target.match( /^[a-z]+:\/\/./ ) )
+ target = 'http://' + target;
+
+ // Detect if this is really an internal link in disguise
+ var match = target.match( $(this).data( 'articlePathRegex' ) );
+ if ( match && !$(this).data( 'ignoreLooksInternal' ) ) {
+ var buttons = { };
+ var that = this;
+ buttons[ mediaWiki.msg( 'wikieditor-toolbar-tool-link-lookslikeinternal-int' ) ] =
+ function() {
+ $( '#wikieditor-toolbar-link-int-target' ).val( match[1] ).change();
+ $(this).dialog( 'close' );
+ };
+ buttons[ mediaWiki.msg( 'wikieditor-toolbar-tool-link-lookslikeinternal-ext' ) ] =
+ function() {
+ $(that).data( 'ignoreLooksInternal', true );
+ $(that).closest( '.ui-dialog' ).find( 'button:first' ).click();
+ $(that).data( 'ignoreLooksInternal', false );
+ $(this).dialog( 'close' );
+ };
+ $.wikiEditor.modules.dialogs.quickDialog(
+ mediaWiki.msg( 'wikieditor-toolbar-tool-link-lookslikeinternal', match[1] ),
+ { buttons: buttons }
+ );
+ return;
+ }
+
+ var escTarget = escapeExternalTarget( target );
+ var escText = escapeExternalText( text );
+
+ if ( escTarget == escText )
+ insertText = escTarget;
+ else if ( text == '' )
+ insertText = '[' + escTarget + ']';
+ else
+ insertText = '[' + escTarget + ' ' + escText + ']';
+ }
+ // Preserve whitespace in selection when replacing
+ if ( whitespace ) insertText = whitespace[0] + insertText + whitespace[1];
+ $(this).dialog( 'close' );
+ $.wikiEditor.modules.toolbar.fn.doAction( $(this).data( 'context' ), {
+ type: 'replace',
+ options: {
+ pre: insertText
+ }
+ }, $(this) );
+
+ // Blank form
+ $( '#wikieditor-toolbar-link-int-target, #wikieditor-toolbar-link-int-text' ).val( '' );
+ $( '#wikieditor-toolbar-link-type-int, #wikieditor-toolbar-link-type-ext' )
+ .attr( 'checked', '' );
+ },
+ 'wikieditor-toolbar-tool-link-cancel': function() {
+ // Clear any saved selection state
+ var context = $(this).data( 'context' );
+ context.fn.restoreCursorAndScrollTop();
+ $(this).dialog( 'close' );
+ }
+ },
+ open: function() {
+ // Obtain the server name without the protocol. wgServer may be protocol-relative
+ var serverName = mw.config.get( 'wgServer' ).replace( /^(https?:)?\/\//, '' );
+ // Cache the articlepath regex
+ $(this).data( 'articlePathRegex', new RegExp(
+ '^https?://' + $.escapeRE( serverName + mw.config.get( 'wgArticlePath' ) )
+ .replace( /\\\$1/g, '(.*)' ) + '$'
+ ) );
+ // Pre-fill the text fields based on the current selection
+ var context = $(this).data( 'context' );
+ // Restore and immediately save selection state, needed for inserting stuff later
+ context.fn.restoreCursorAndScrollTop();
+ context.fn.saveCursorAndScrollTop();
+ var selection = context.$textarea.textSelection( 'getSelection' );
+ $( '#wikieditor-toolbar-link-int-target' ).focus();
+ // Trigger the change event, so the link status indicator is up to date
+ $( '#wikieditor-toolbar-link-int-target' ).change();
+ $( '#wikieditor-toolbar-link-dialog' ).data( 'whitespace', [ '', '' ] );
+ if ( selection != '' ) {
+ var target, text, type;
+ var matches;
+ if ( ( matches = selection.match( /^(\s*)\[\[([^\]\|]+)(\|([^\]\|]*))?\]\](\s*)$/ ) ) ) {
+ // [[foo|bar]] or [[foo]]
+ target = matches[2];
+ text = ( matches[4] ? matches[4] : matches[2] );
+ type = 'int';
+ // Preserve whitespace when replacing
+ $( '#wikieditor-toolbar-link-dialog' ).data( 'whitespace', [ matches[1], matches[5] ] );
+ } else if ( ( matches = selection.match( /^(\s*)\[([^\] ]+)( ([^\]]+))?\](\s*)$/ ) ) ) {
+ // [http://www.example.com foo] or [http://www.example.com]
+ target = matches[2];
+ text = ( matches[4] ? matches[4] : '' );
+ type = 'ext';
+ // Preserve whitespace when replacing
+ $( '#wikieditor-toolbar-link-dialog' ).data( 'whitespace', [ matches[1], matches[5] ] );
+ } else {
+ // Trim any leading and trailing whitespace from the selection,
+ // but preserve it when replacing
+ target = text = $.trim( selection );
+ if ( target.length < selection.length ) {
+ $( '#wikieditor-toolbar-link-dialog' ).data( 'whitespace', [
+ selection.substr( 0, selection.indexOf( target.charAt( 0 ) ) ),
+ selection.substr(
+ selection.lastIndexOf( target.charAt( target.length - 1 ) ) + 1
+ ) ]
+ );
+ }
+ }
+
+ // Change the value by calling val() doesn't trigger the change event, so let's do that
+ // ourselves
+ if ( typeof text != 'undefined' )
+ $( '#wikieditor-toolbar-link-int-text' ).val( text ).change();
+ if ( typeof target != 'undefined' )
+ $( '#wikieditor-toolbar-link-int-target' ).val( target ).change();
+ if ( typeof type != 'undefined' )
+ $( '#wikieditor-toolbar-link-' + type ).attr( 'checked', 'checked' );
+ }
+ $( '#wikieditor-toolbar-link-int-text' ).data( 'untouched',
+ $( '#wikieditor-toolbar-link-int-text' ).val() ==
+ $( '#wikieditor-toolbar-link-int-target' ).val() ||
+ $( '#wikieditor-toolbar-link-int-text' ).hasClass( 'wikieditor-toolbar-dialog-hint' )
+ );
+ $( '#wikieditor-toolbar-link-int-target' ).suggestions();
+
+ //don't overwrite user's text
+ if( selection != '' ){
+ $( '#wikieditor-toolbar-link-int-text' ).data( 'untouched', false );
+ }
+
+ $( '#wikieditor-toolbar-link-int-text, #wikiedit-toolbar-link-int-target' )
+ .each( function() {
+ if ( $(this).val() == '' )
+ $(this).parent().find( 'label' ).show();
+ });
+
+ if ( !( $(this).data( 'dialogkeypressset' ) ) ) {
+ $(this).data( 'dialogkeypressset', true );
+ // Execute the action associated with the first button
+ // when the user presses Enter
+ $(this).closest( '.ui-dialog' ).keypress( function( e ) {
+ if ( ( e.keyCode || e.which ) == 13 ) {
+ var button = $(this).data( 'dialogaction' ) || $(this).find( 'button:first' );
+ button.click();
+ e.preventDefault();
+ }
+ });
+
+ // Make tabbing to a button and pressing
+ // Enter do what people expect
+ $(this).closest( '.ui-dialog' ).find( 'button' ).focus( function() {
+ $(this).closest( '.ui-dialog' ).data( 'dialogaction', this );
+ });
+ }
+ }
+ }
+ },
+ 'insert-reference': {
+ titleMsg: 'wikieditor-toolbar-tool-reference-title',
+ id: 'wikieditor-toolbar-reference-dialog',
+ html: '\
+ <div class="wikieditor-toolbar-dialog-wrapper">\
+ <fieldset><div class="wikieditor-toolbar-table-form">\
+ <div class="wikieditor-toolbar-field-wrapper">\
+ <label for="wikieditor-toolbar-reference-text"\
+ rel="wikieditor-toolbar-tool-reference-text"></label>\
+ <input type="text" id="wikieditor-toolbar-reference-text" />\
+ </div>\
+ </div></fieldset>\
+ </div>',
+ init: function() {
+ // Insert translated strings into labels
+ $( this ).find( '[rel]' ).each( function() {
+ $( this ).text( mediaWiki.msg( $( this ).attr( 'rel' ) ) );
+ } );
+
+ },
+ dialog: {
+ dialogClass: 'wikiEditor-toolbar-dialog',
+ width: 590,
+ buttons: {
+ 'wikieditor-toolbar-tool-reference-insert': function() {
+ var insertText = $( '#wikieditor-toolbar-reference-text' ).val();
+ var whitespace = $( '#wikieditor-toolbar-reference-dialog' ).data( 'whitespace' );
+ var attributes = $( '#wikieditor-toolbar-reference-dialog' ).data( 'attributes' );
+ // Close the dialog
+ $( this ).dialog( 'close' );
+ $.wikiEditor.modules.toolbar.fn.doAction(
+ $( this ).data( 'context' ),
+ {
+ type: 'replace',
+ options: {
+ pre: whitespace[0] + '<ref' + attributes + '>',
+ peri: insertText,
+ post: '</ref>' + whitespace[1]
+ }
+ },
+ $( this )
+ );
+ // Restore form state
+ $( '#wikieditor-toolbar-reference-text' ).val( "" );
+ },
+ 'wikieditor-toolbar-tool-reference-cancel': function() {
+ // Clear any saved selection state
+ var context = $( this ).data( 'context' );
+ context.fn.restoreCursorAndScrollTop();
+ $( this ).dialog( 'close' );
+ }
+ },
+ open: function() {
+ // Pre-fill the text fields based on the current selection
+ var context = $(this).data( 'context' );
+ // Restore and immediately save selection state, needed for inserting stuff later
+ context.fn.restoreCursorAndScrollTop();
+ context.fn.saveCursorAndScrollTop();
+ var selection = context.$textarea.textSelection( 'getSelection' );
+ // set focus
+ $( '#wikieditor-toolbar-reference-text' ).focus();
+ $( '#wikieditor-toolbar-reference-dialog' )
+ .data( 'whitespace', [ '', '' ] )
+ .data( 'attributes', '' );
+ if ( selection != '' ) {
+ var matches, text;
+ if ( ( matches = selection.match( /^(\s*)<ref([^\>]*)>([^\<]*)<\/ref\>(\s*)$/ ) ) ) {
+ text = matches[3];
+ // Preserve whitespace when replacing
+ $( '#wikieditor-toolbar-reference-dialog' )
+ .data( 'whitespace', [ matches[1], matches[4] ] );
+ $( '#wikieditor-toolbar-reference-dialog' ).data( 'attributes', matches[2] );
+ } else {
+ text = selection;
+ }
+ $( '#wikieditor-toolbar-reference-text' ).val( text );
+ }
+ if ( !( $( this ).data( 'dialogkeypressset' ) ) ) {
+ $( this ).data( 'dialogkeypressset', true );
+ // Execute the action associated with the first button
+ // when the user presses Enter
+ $( this ).closest( '.ui-dialog' ).keypress( function( e ) {
+ if ( ( e.keyCode || e.which ) == 13 ) {
+ var button = $( this ).data( 'dialogaction' ) || $( this ).find( 'button:first' );
+ button.click();
+ e.preventDefault();
+ }
+ } );
+ // Make tabbing to a button and pressing
+ // Enter do what people expect
+ $( this ).closest( '.ui-dialog' ).find( 'button' ).focus( function() {
+ $( this ).closest( '.ui-dialog' ).data( 'dialogaction', this );
+ } );
+ }
+ }
+ }
+ },
+ 'insert-table': {
+ titleMsg: 'wikieditor-toolbar-tool-table-title',
+ id: 'wikieditor-toolbar-table-dialog',
+ // FIXME: Localize 'x'?
+ html: '\
+ <div class="wikieditor-toolbar-dialog-wrapper">\
+ <fieldset><div class="wikieditor-toolbar-table-form">\
+ <div class="wikieditor-toolbar-field-wrapper">\
+ <input type="checkbox" id="wikieditor-toolbar-table-dimensions-header" checked />\
+ <label for="wikieditor-toolbar-table-dimensions-header"\
+ rel="wikieditor-toolbar-tool-table-dimensions-header"></label>\
+ </div>\
+ <div class="wikieditor-toolbar-field-wrapper">\
+ <input type="checkbox" id="wikieditor-toolbar-table-wikitable" checked />\
+ <label for="wikieditor-toolbar-table-wikitable" rel="wikieditor-toolbar-tool-table-wikitable"></label>\
+ </div>\
+ <div class="wikieditor-toolbar-field-wrapper">\
+ <input type="checkbox" id="wikieditor-toolbar-table-sortable" />\
+ <label for="wikieditor-toolbar-table-sortable" rel="wikieditor-toolbar-tool-table-sortable"></label>\
+ </div>\
+ <div class="wikieditor-toolbar-table-dimension-fields">\
+ <div class="wikieditor-toolbar-field-wrapper">\
+ <label for="wikieditor-toolbar-table-dimensions-rows"\
+ rel="wikieditor-toolbar-tool-table-dimensions-rows"></label><br />\
+ <input type="text" id="wikieditor-toolbar-table-dimensions-rows" size="4" />\
+ </div>\
+ <div class="wikieditor-toolbar-field-wrapper">\
+ <label for="wikieditor-toolbar-table-dimensions-columns"\
+ rel="wikieditor-toolbar-tool-table-dimensions-columns"></label><br />\
+ <input type="text" id="wikieditor-toolbar-table-dimensions-columns" size="4" />\
+ </div>\
+ </div>\
+ </div></fieldset>\
+ <div class="wikieditor-toolbar-table-preview-wrapper" >\
+ <span rel="wikieditor-toolbar-tool-table-example"></span>\
+ <div class="wikieditor-toolbar-table-preview-content">\
+ <table id="wikieditor-toolbar-table-preview" class="wikieditor-toolbar-table-preview wikitable">\
+ <thead>\
+ <tr class="wikieditor-toolbar-table-preview-header">\
+ <th rel="wikieditor-toolbar-tool-table-example-header"></th>\
+ <th rel="wikieditor-toolbar-tool-table-example-header"></th>\
+ <th rel="wikieditor-toolbar-tool-table-example-header"></th>\
+ </tr>\
+ </thead><tbody>\
+ <tr class="wikieditor-toolbar-table-preview-hidden" style="display: none;">\
+ <td rel="wikieditor-toolbar-tool-table-example-cell-text"></td>\
+ <td rel="wikieditor-toolbar-tool-table-example-cell-text"></td>\
+ <td rel="wikieditor-toolbar-tool-table-example-cell-text"></td>\
+ </tr><tr>\
+ <td rel="wikieditor-toolbar-tool-table-example-cell-text"></td>\
+ <td rel="wikieditor-toolbar-tool-table-example-cell-text"></td>\
+ <td rel="wikieditor-toolbar-tool-table-example-cell-text"></td>\
+ </tr><tr>\
+ <td rel="wikieditor-toolbar-tool-table-example-cell-text"></td>\
+ <td rel="wikieditor-toolbar-tool-table-example-cell-text"></td>\
+ <td rel="wikieditor-toolbar-tool-table-example-cell-text"></td>\
+ </tr><tr>\
+ <td rel="wikieditor-toolbar-tool-table-example-cell-text"></td>\
+ <td rel="wikieditor-toolbar-tool-table-example-cell-text"></td>\
+ <td rel="wikieditor-toolbar-tool-table-example-cell-text"></td>\
+ </tr>\
+ </tbody>\
+ </table>\
+ </div>\
+ </div></div>',
+ init: function() {
+ $(this).find( '[rel]' ).each( function() {
+ $(this).text( mediaWiki.msg( $(this).attr( 'rel' ) ) );
+ });
+ // Set tabindexes on form fields
+ $.wikiEditor.modules.dialogs.fn.setTabindexes( $(this).find( 'input' ).not( '[tabindex]' ) );
+
+ $( '#wikieditor-toolbar-table-dimensions-rows' ).val( 3 );
+ $( '#wikieditor-toolbar-table-dimensions-columns' ).val( 3 );
+ $( '#wikieditor-toolbar-table-wikitable' ).click( function() {
+ $( '.wikieditor-toolbar-table-preview' ).toggleClass( 'wikitable' );
+ });
+
+ // Hack for sortable preview: dynamically adding
+ // sortable class doesn't work, so we use a clone
+ $( '#wikieditor-toolbar-table-preview' )
+ .clone()
+ .attr( 'id', 'wikieditor-toolbar-table-preview2' )
+ .addClass( 'sortable' )
+ .insertAfter( $( '#wikieditor-toolbar-table-preview' ) )
+ .hide();
+
+ mw.loader.using( 'jquery.tablesorter', function() {
+ $( '#wikieditor-toolbar-table-preview2' ).tablesorter();
+ });
+
+ $( '#wikieditor-toolbar-table-sortable' ).click( function() {
+ // Swap the currently shown one clone with the other one
+ $( '#wikieditor-toolbar-table-preview' )
+ .hide()
+ .attr( 'id', 'wikieditor-toolbar-table-preview3' );
+ $( '#wikieditor-toolbar-table-preview2' )
+ .attr( 'id', 'wikieditor-toolbar-table-preview' )
+ .show();
+ $( '#wikieditor-toolbar-table-preview3' ).attr( 'id', 'wikieditor-toolbar-table-preview2' );
+ });
+
+ $( '#wikieditor-toolbar-table-dimensions-header' ).click( function() {
+ // Instead of show/hiding, switch the HTML around
+ // We do this because the sortable tables script styles the first row,
+ // visible or not
+ var headerHTML = $( '.wikieditor-toolbar-table-preview-header' ).html();
+ var hiddenHTML = $( '.wikieditor-toolbar-table-preview-hidden' ).html();
+ $( '.wikieditor-toolbar-table-preview-header' ).html( hiddenHTML );
+ $( '.wikieditor-toolbar-table-preview-hidden' ).html( headerHTML );
+ if ( typeof jQuery.fn.tablesorter == 'function' ) {
+ $( '#wikieditor-toolbar-table-preview, #wikieditor-toolbar-table-preview2' )
+ .filter( '.sortable' )
+ .tablesorter();
+ }
+ });
+ },
+ dialog: {
+ resizable: false,
+ dialogClass: 'wikiEditor-toolbar-dialog',
+ width: 590,
+ buttons: {
+ 'wikieditor-toolbar-tool-table-insert': function() {
+ var rowsVal = $( '#wikieditor-toolbar-table-dimensions-rows' ).val();
+ var colsVal = $( '#wikieditor-toolbar-table-dimensions-columns' ).val();
+ var rows = parseInt( rowsVal, 10 );
+ var cols = parseInt( colsVal, 10 );
+ var header = $( '#wikieditor-toolbar-table-dimensions-header' ).is( ':checked' ) ? 1 : 0;
+ if ( isNaN( rows ) || isNaN( cols ) || rows != rowsVal || cols != colsVal ) {
+ alert( mediaWiki.msg( 'wikieditor-toolbar-tool-table-invalidnumber' ) );
+ return;
+ }
+ if ( rows + header == 0 || cols == 0 ) {
+ alert( mediaWiki.msg( 'wikieditor-toolbar-tool-table-zero' ) );
+ return;
+ }
+ if ( rows * cols > 1000 ) {
+ alert( mediaWiki.msg( 'wikieditor-toolbar-tool-table-toomany', 1000 ) );
+ return;
+ }
+ var headerText = mediaWiki.msg( 'wikieditor-toolbar-tool-table-example-header' );
+ var normalText = mediaWiki.msg( 'wikieditor-toolbar-tool-table-example' );
+ var table = "";
+ for ( var r = 0; r < rows + header; r++ ) {
+ table += "|-\n";
+ for ( var c = 0; c < cols; c++ ) {
+ var isHeader = ( header && r == 0 );
+ var delim = isHeader ? '!' : '|';
+ if ( c > 0 ) {
+ delim += delim;
+ }
+ table += delim + ' ' + ( isHeader ? headerText : normalText ) + ' ';
+ }
+ // Replace trailing space by newline
+ // table[table.length - 1] is read-only
+ table = table.substr( 0, table.length - 1 ) + "\n";
+ }
+ var classes = [];
+ if ( $( '#wikieditor-toolbar-table-wikitable' ).is( ':checked' ) )
+ classes.push( 'wikitable' );
+ if ( $( '#wikieditor-toolbar-table-sortable' ).is( ':checked' ) )
+ classes.push( 'sortable' );
+ var classStr = classes.length > 0 ? ' class="' + classes.join( ' ' ) + '"' : '';
+ $(this).dialog( 'close' );
+ $.wikiEditor.modules.toolbar.fn.doAction(
+ $(this).data( 'context' ),
+ {
+ type: 'replace',
+ options: {
+ pre: '{|' + classStr + "\n",
+ peri: table,
+ post: '|}',
+ ownline: true
+ }
+ },
+ $(this)
+ );
+
+ // Restore form state
+ $( '#wikieditor-toolbar-table-dimensions-rows' ).val( 3 );
+ $( '#wikieditor-toolbar-table-dimensions-columns' ).val( 3 );
+ // Simulate clicks instead of setting values, so the according
+ // actions are performed
+ if ( !$( '#wikieditor-toolbar-table-dimensions-header' ).is( ':checked' ) )
+ $( '#wikieditor-toolbar-table-dimensions-header' ).click();
+ if ( !$( '#wikieditor-toolbar-table-wikitable' ).is( ':checked' ) )
+ $( '#wikieditor-toolbar-table-wikitable' ).click();
+ if ( $( '#wikieditor-toolbar-table-sortable' ).is( ':checked' ) )
+ $( '#wikieditor-toolbar-table-sortable' ).click();
+ },
+ 'wikieditor-toolbar-tool-table-cancel': function() {
+ $(this).dialog( 'close' );
+ }
+ },
+ open: function() {
+ $( '#wikieditor-toolbar-table-dimensions-rows' ).focus();
+ if ( !( $(this).data( 'dialogkeypressset' ) ) ) {
+ $(this).data( 'dialogkeypressset', true );
+ // Execute the action associated with the first button
+ // when the user presses Enter
+ $(this).closest( '.ui-dialog' ).keypress( function( e ) {
+ if ( ( e.keyCode || e.which ) == 13 ) {
+ var button = $(this).data( 'dialogaction' ) || $(this).find( 'button:first' );
+ button.click();
+ e.preventDefault();
+ }
+ });
+
+ // Make tabbing to a button and pressing
+ // Enter do what people expect
+ $(this).closest( '.ui-dialog' ).find( 'button' ).focus( function() {
+ $(this).closest( '.ui-dialog' ).data( 'dialogaction', this );
+ });
+ }
+ }
+ }
+ },
+ 'search-and-replace': {
+ 'browsers': {
+ // Left-to-right languages
+ 'ltr': {
+ 'msie': false,
+ 'firefox': [['>=', 2]],
+ 'opera': false,
+ 'safari': [['>=', 3]],
+ 'chrome': [['>=', 3]]
+ },
+ // Right-to-left languages
+ 'rtl': {
+ 'msie': false,
+ 'firefox': [['>=', 2]],
+ 'opera': false,
+ 'safari': [['>=', 3]],
+ 'chrome': [['>=', 3]]
+ }
+ },
+ titleMsg: 'wikieditor-toolbar-tool-replace-title',
+ id: 'wikieditor-toolbar-replace-dialog',
+ html: '\
+ <div id="wikieditor-toolbar-replace-message">\
+ <div id="wikieditor-toolbar-replace-nomatch" rel="wikieditor-toolbar-tool-replace-nomatch"></div>\
+ <div id="wikieditor-toolbar-replace-success"></div>\
+ <div id="wikieditor-toolbar-replace-emptysearch" rel="wikieditor-toolbar-tool-replace-emptysearch"></div>\
+ <div id="wikieditor-toolbar-replace-invalidregex"></div>\
+ </div>\
+ <fieldset>\
+ <div class="wikieditor-toolbar-field-wrapper">\
+ <label for="wikieditor-toolbar-replace-search" rel="wikieditor-toolbar-tool-replace-search"></label>\
+ <input type="text" id="wikieditor-toolbar-replace-search" style="width: 100%;" />\
+ </div>\
+ <div class="wikieditor-toolbar-field-wrapper">\
+ <label for="wikieditor-toolbar-replace-replace" rel="wikieditor-toolbar-tool-replace-replace"></label>\
+ <input type="text" id="wikieditor-toolbar-replace-replace" style="width: 100%;" />\
+ </div>\
+ <div class="wikieditor-toolbar-field-wrapper">\
+ <input type="checkbox" id="wikieditor-toolbar-replace-case" />\
+ <label for="wikieditor-toolbar-replace-case" rel="wikieditor-toolbar-tool-replace-case"></label>\
+ </div>\
+ <div class="wikieditor-toolbar-field-wrapper">\
+ <input type="checkbox" id="wikieditor-toolbar-replace-regex" />\
+ <label for="wikieditor-toolbar-replace-regex" rel="wikieditor-toolbar-tool-replace-regex"></label>\
+ </div>\
+ </fieldset>',
+ init: function() {
+ $(this).find( '[rel]' ).each( function() {
+ $(this).text( mediaWiki.msg( $(this).attr( 'rel' ) ) );
+ });
+ // Set tabindexes on form fields
+ $.wikiEditor.modules.dialogs.fn.setTabindexes( $(this).find( 'input' ).not( '[tabindex]' ) );
+
+ // TODO: Find a cleaner way to share this function
+ $(this).data( 'replaceCallback', function( mode ) {
+ $( '#wikieditor-toolbar-replace-nomatch, #wikieditor-toolbar-replace-success, #wikieditor-toolbar-replace-emptysearch, #wikieditor-toolbar-replace-invalidregex' ).hide();
+ var searchStr = $( '#wikieditor-toolbar-replace-search' ).val();
+ if ( searchStr == '' ) {
+ $( '#wikieditor-toolbar-replace-emptysearch' ).show();
+ return;
+ }
+ var replaceStr = $( '#wikieditor-toolbar-replace-replace' ).val();
+ var flags = 'm';
+ var matchCase = $( '#wikieditor-toolbar-replace-case' ).is( ':checked' );
+ var isRegex = $( '#wikieditor-toolbar-replace-regex' ).is( ':checked' );
+ if ( !matchCase ) {
+ flags += 'i';
+ }
+ if ( mode == 'replaceAll' ) {
+ flags += 'g';
+ }
+ if ( !isRegex ) {
+ searchStr = $.escapeRE( searchStr );
+ }
+ try {
+ var regex = new RegExp( searchStr, flags );
+ } catch( e ) {
+ $( '#wikieditor-toolbar-replace-invalidregex' )
+ .text( mediaWiki.msg( 'wikieditor-toolbar-tool-replace-invalidregex',
+ e.message ) )
+ .show();
+ return;
+ }
+ var $textarea = $(this).data( 'context' ).$textarea;
+ var text = $textarea.textSelection( 'getContents' );
+ var match = false;
+ var offset, s;
+ if ( mode != 'replaceAll' ) {
+ offset = $(this).data( 'offset' );
+ s = text.substr( offset );
+ match = s.match( regex );
+ }
+ if ( !match ) {
+ // Search hit BOTTOM, continuing at TOP
+ offset = 0;
+ s = text;
+ match = s.match( regex );
+ }
+
+ if ( !match ) {
+ $( '#wikieditor-toolbar-replace-nomatch' ).show();
+ } else if ( mode == 'replaceAll' ) {
+ // Instead of using repetitive .match() calls, we use one .match() call with /g
+ // and indexOf() followed by substr() to find the offsets. This is actually
+ // faster because our indexOf+substr loop is faster than a match loop, and the
+ // /g match is so ridiculously fast that it's negligible.
+ // FIXME: Repetitively calling encapsulateSelection() is probably the best strategy
+ // in Firefox/Webkit, but in IE replacing the entire content once is better.
+ var index;
+ for ( var i = 0; i < match.length; i++ ) {
+ index = s.indexOf( match[i] );
+ if ( index == -1 ) {
+ // This shouldn't happen
+ break;
+ }
+ var matchedText = s.substr( index, match[i].length );
+ s = s.substr( index + match[i].length );
+
+ var start = index + offset;
+ var end = start + match[i].length;
+ // Make regex placeholder substitution ($1) work
+ var replace = isRegex ? matchedText.replace( regex, replaceStr ) : replaceStr;
+ var newEnd = start + replace.length;
+ $textarea
+ .textSelection( 'setSelection', { 'start': start, 'end': end } )
+ .textSelection( 'encapsulateSelection', {
+ 'peri': replace,
+ 'replace': true } )
+ .textSelection( 'setSelection', { 'start': start, 'end': newEnd } );
+ offset = newEnd;
+ }
+ $( '#wikieditor-toolbar-replace-success' )
+ .text( mediaWiki.msg( 'wikieditor-toolbar-tool-replace-success', match.length ) )
+ .show();
+ $(this).data( 'offset', 0 );
+ } else {
+ // Make regex placeholder substitution ($1) work
+ var replace = isRegex ? match[0].replace( regex, replaceStr ): replaceStr;
+ var start = match.index + offset;
+ var end = start + match[0].length;
+ var newEnd = start + replace.length;
+ var context = $( this ).data( 'context' );
+ $textarea.textSelection( 'setSelection', { 'start': start,
+ 'end': end } );
+ if ( mode == 'replace' ) {
+ $textarea
+ .textSelection( 'encapsulateSelection', {
+ 'peri': replace,
+ 'replace': true } )
+ .textSelection( 'setSelection', {
+ 'start': start,
+ 'end': newEnd } );
+ }
+ $textarea.textSelection( 'scrollToCaretPosition' );
+ $textarea.textSelection( 'setSelection', { 'start': start,
+ 'end': mode == 'replace' ? newEnd : end } );
+ $( this ).data( 'offset', mode == 'replace' ? newEnd : end );
+ var textbox = typeof context.$iframe != 'undefined' ?
+ context.$iframe[0].contentWindow : $textarea[0];
+ textbox.focus();
+ }
+ });
+ },
+ dialog: {
+ width: 500,
+ dialogClass: 'wikiEditor-toolbar-dialog',
+ buttons: {
+ 'wikieditor-toolbar-tool-replace-button-findnext': function( e ) {
+ $(this).closest( '.ui-dialog' ).data( 'dialogaction', e.target );
+ $(this).data( 'replaceCallback' ).call( this, 'find' );
+ },
+ 'wikieditor-toolbar-tool-replace-button-replacenext': function( e ) {
+ $(this).closest( '.ui-dialog' ).data( 'dialogaction', e.target );
+ $(this).data( 'replaceCallback' ).call( this, 'replace' );
+ },
+ 'wikieditor-toolbar-tool-replace-button-replaceall': function( e ) {
+ $(this).closest( '.ui-dialog' ).data( 'dialogaction', e.target );
+ $(this).data( 'replaceCallback' ).call( this, 'replaceAll' );
+ },
+ 'wikieditor-toolbar-tool-replace-close': function() {
+ $(this).dialog( 'close' );
+ }
+ },
+ open: function() {
+ $(this).data( 'offset', 0 );
+ $( '#wikieditor-toolbar-replace-search' ).focus();
+ $( '#wikieditor-toolbar-replace-nomatch, #wikieditor-toolbar-replace-success, #wikieditor-toolbar-replace-emptysearch, #wikieditor-toolbar-replace-invalidregex' ).hide();
+ if ( !( $(this).data( 'onetimeonlystuff' ) ) ) {
+ $(this).data( 'onetimeonlystuff', true );
+ // Execute the action associated with the first button
+ // when the user presses Enter
+ $(this).closest( '.ui-dialog' ).keypress( function( e ) {
+ if ( ( e.keyCode || e.which ) == 13 ) {
+ var button = $(this).data( 'dialogaction' ) || $(this).find( 'button:first' );
+ button.click();
+ e.preventDefault();
+ }
+ });
+ // Make tabbing to a button and pressing
+ // Enter do what people expect
+ $(this).closest( '.ui-dialog' ).find( 'button' ).focus( function() {
+ $(this).closest( '.ui-dialog' ).data( 'dialogaction', this );
+ });
+ }
+ var dialog = $(this).closest( '.ui-dialog' );
+ var that = this;
+ var context = $(this).data( 'context' );
+ var textbox = typeof context.$iframe != 'undefined' ?
+ context.$iframe[0].contentWindow.document : context.$textarea;
+
+ $( textbox )
+ .bind( 'keypress.srdialog', function( e ) {
+ if ( ( e.keyCode || e.which ) == 13 ) {
+ // Enter
+ var button = dialog.data( 'dialogaction' ) || dialog.find( 'button:first' );
+ button.click();
+ e.preventDefault();
+ } else if ( ( e.keyCode || e.which ) == 27 ) {
+ // Escape
+ $(that).dialog( 'close' );
+ }
+ });
+ },
+ close: function() {
+ var context = $(this).data( 'context' );
+ var textbox = typeof context.$iframe != 'undefined' ?
+ context.$iframe[0].contentWindow.document : context.$textarea;
+ $( textbox ).unbind( 'keypress.srdialog' );
+ $(this).closest( '.ui-dialog' ).data( 'dialogaction', false );
+ }
+ }
+ }
+ } };
+}
+
+}; } ) ( jQuery );
diff --git a/extensions/WikiEditor/modules/jquery.wikiEditor.dialogs.css b/extensions/WikiEditor/modules/jquery.wikiEditor.dialogs.css
new file mode 100644
index 00000000..d1b68ab3
--- /dev/null
+++ b/extensions/WikiEditor/modules/jquery.wikiEditor.dialogs.css
@@ -0,0 +1,56 @@
+/*
+ * CSS for WikiEditor Dialogs jQuery plugin
+ */
+
+.wikiEditor-toolbar-dialog table {
+ margin-top: 0.75em;
+}
+.wikiEditor-toolbar-dialog table td {
+ padding: 0.5em;
+ height: 3em;
+ overflow: visible;
+}
+/* Put suggestions (default z-index 99) on top of dialogs (z-index 1002) */
+div.suggestions {
+ z-index: 1099;
+}
+.wikiEditor-toolbar-dialog table td {
+ padding: 0 !important;
+}
+.wikiEditor-toolbar-dialog .ui-dialog-content fieldset {
+ border: none !important;
+ margin: 0 !important;
+ padding: 0 !important;
+}
+.wikiEditor-toolbar-dialog .ui-widget-header {
+ border-bottom:1px solid #6bc8f3 !important;
+}
+.wikiEditor-toolbar-dialog .ui-dialog-content input[type=text] {
+ -moz-box-sizing: border-box;
+ -ms-box-sizing: border-box;
+ -webkit-box-sizing: border-box;
+ -khtml-box-sizing: border-box;
+}
+.wikiEditor-toolbar-dialog .ui-dialog-content input[type="radio"],
+.wikiEditor-toolbar-dialog .ui-dialog-content input[type="checkbox"] {
+ margin-left: 0;
+}
+.wikiEditor-toolbar-dialog .ui-dialog-titlebar-close {
+ padding: 0;
+}
+body.ltr .wikiEditor-toolbar-dialog .ui-dialog-titlebar-close {
+ right: 0.9em;
+}
+.wikieditor-toolbar-field-wrapper {
+ padding: 0 0 25px 0;
+}
+.wikieditor-toolbar-floated-field-wrapper {
+ float: left;
+ margin-right: 2em;
+}
+.wikieditor-toolbar-dialog-hint {
+ color: #999999;
+}
+.wikiEditor-toolbar-dialog {
+ border: none;
+} \ No newline at end of file
diff --git a/extensions/WikiEditor/modules/jquery.wikiEditor.dialogs.js b/extensions/WikiEditor/modules/jquery.wikiEditor.dialogs.js
new file mode 100644
index 00000000..9b291466
--- /dev/null
+++ b/extensions/WikiEditor/modules/jquery.wikiEditor.dialogs.js
@@ -0,0 +1,217 @@
+/**
+ * Dialog Module for wikiEditor
+ */
+( function( $ ) { $.wikiEditor.modules.dialogs = {
+
+/**
+ * Compatability map
+ */
+'browsers': {
+ // Left-to-right languages
+ 'ltr': {
+ 'msie': [['>=', 7]],
+ // jQuery UI appears to be broken in FF 2.0 - 2.0.0.4
+ '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]]
+ },
+ // Right-to-left languages
+ 'rtl': {
+ 'msie': [['>=', 7]],
+ // jQuery UI appears to be broken in FF 2.0 - 2.0.0.4
+ '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 accessible functions
+ */
+api: {
+ addDialog: function( context, data ) {
+ $.wikiEditor.modules.dialogs.fn.create( context, data )
+ },
+ openDialog: function( context, module ) {
+ 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, module );
+ $dialog = $( '#' + mod.id );
+ }
+
+ // Workaround for bug in jQuery UI: close button in top right retains focus
+ $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' );
+ }
+ }
+},
+/**
+ * Internally used functions
+ */
+fn: {
+ /**
+ * Creates a dialog module within a wikiEditor
+ *
+ * @param {Object} context Context object of editor to create module in
+ * @param {Object} config Configuration object to create module from
+ */
+ create: function( context, config ) {
+ // Defer building of modules, but do check whether they need the iframe rightaway
+ for ( var mod in config ) {
+ var module = config[mod];
+ // Only create the dialog if it's supported, isn't filtered and doesn't exist yet
+ 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 the dialog already exists, but for another textarea, simply remove it
+ var $existingDialog = $( '#' + module.id );
+ if ( $existingDialog.length > 0 && $existingDialog.data( 'context' ).$textarea != context.$textarea ) {
+ $existingDialog.remove();
+ }
+ // Re-select from the DOM, we might have removed the dialog just now
+ $existingDialog = $( '#' + module.id );
+ if ( !filtered && $.wikiEditor.isSupported( module ) && $existingDialog.size() === 0 ) {
+ $.wikiEditor.modules.dialogs.modules[mod] = module;
+ // If this dialog requires the iframe, set it up
+ if ( typeof context.$iframe == 'undefined' && $.wikiEditor.isRequired( module, 'iframe' ) ) {
+ context.fn.setupIframe();
+ }
+ context.$textarea.trigger( 'wikiEditor-dialogs-setup-' + mod );
+ // If this dialog requires immediate creation, create it now
+ if ( typeof module.immediateCreate !== 'undefined' && module.immediateCreate ) {
+ $.wikiEditor.modules.dialogs.fn.reallyCreate( context, module, mod );
+ }
+ }
+ }
+ },
+ /**
+ * Build the actual dialog. This done on-demand rather than in create()
+ * @param {Object} context Context object of editor dialog belongs to
+ * @param {Object} module Dialog module object
+ * @param {String} name Dialog name (key in $.wikiEditor.modules.dialogs.modules)
+ */
+ reallyCreate: function( context, module, name ) {
+ var configuration = module.dialog;
+ // Add some stuff to configuration
+ configuration.bgiframe = true;
+ configuration.autoOpen = false;
+ configuration.modal = true;
+ configuration.title = $.wikiEditor.autoMsg( module, 'title' );
+ // Transform messages in keys
+ // Stupid JS won't let us do stuff like
+ // foo = { mediaWiki.msg( 'bar' ): baz }
+ configuration.newButtons = {};
+ for ( var msg in configuration.buttons )
+ configuration.newButtons[mediaWiki.msg( msg )] = configuration.buttons[msg];
+ configuration.buttons = configuration.newButtons;
+ // Create the dialog <div>
+ var dialogDiv = $( '<div />' )
+ .attr( 'id', module.id )
+ .html( module.html )
+ .data( 'context', context )
+ .appendTo( $( 'body' ) )
+ .each( module.init )
+ .dialog( configuration );
+ // Set tabindexes on buttons added by .dialog()
+ $.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();
+ } );
+
+ // Let the outside world know we set up this dialog
+ context.$textarea.trigger( 'wikiEditor-dialogs-loaded-' + name );
+ },
+ /**
+ * Resize a dialog so its contents fit
+ *
+ * Usage: dialog.each( resize ); or dialog.bind( 'blah', resize );
+ * NOTE: This function assumes $.ui.dialog has already been loaded
+ */
+ resize: function() {
+ var wrapper = $(this).closest( '.ui-dialog' );
+ var oldWidth = wrapper.width();
+ // Make sure elements don't wrapped so we get an accurate idea of whether they really fit. Also temporarily show
+ // hidden elements. Work around jQuery bug where <div style="display:inline;" /> inside a dialog is both
+ // :visible and :hidden
+ var oldHidden = $(this).find( '*' ).not( ':visible' );
+ // Save the style attributes of the hidden elements to restore them later. Calling hide() after show() messes up
+ // for elements hidden with a class
+ 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).width, 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' ) );
+ });
+ },
+ /**
+ * Set the right tabindexes on elements in a dialog
+ * @param $elements Elements to set tabindexes on. If they already have tabindexes, this function can behave a bit weird
+ */
+ setTabindexes: function( $elements ) {
+ // Get the highest tab index
+ var tabIndex = $( document ).lastTabIndex() + 1;
+ $elements.each( function() {
+ $(this).attr( 'tabindex', tabIndex++ );
+ } );
+ }
+},
+// This stuff is just hanging here, perhaps we could come up with a better home for this stuff
+modules: {},
+quickDialog: function( body, settings ) {
+ $( '<div />' )
+ .text( body )
+ .appendTo( $( 'body' ) )
+ .dialog( $.extend( {
+ bgiframe: true,
+ modal: true
+ }, settings ) )
+ .dialog( 'open' );
+}
+
+}; } ) ( jQuery );
diff --git a/extensions/WikiEditor/modules/jquery.wikiEditor.highlight.js b/extensions/WikiEditor/modules/jquery.wikiEditor.highlight.js
new file mode 100644
index 00000000..c804b3aa
--- /dev/null
+++ b/extensions/WikiEditor/modules/jquery.wikiEditor.highlight.js
@@ -0,0 +1,357 @@
+/* Highlight module for wikiEditor */
+
+( function( $ ) { $.wikiEditor.modules.highlight = {
+
+/**
+ * Core Requirements
+ */
+'req': [ 'iframe' ],
+/**
+ * Configuration
+ */
+'cfg': {
+ 'styleVersion': 3
+},
+/**
+ * Internally used event handlers
+ */
+'evt': {
+ 'delayedChange': function( context, event ) {
+ if ( event.data.scope == 'realchange' ) {
+ $.wikiEditor.modules.highlight.fn.scan( context );
+ $.wikiEditor.modules.highlight.fn.mark( context, event.data.scope );
+ }
+ },
+ 'ready': function( context, event ) {
+ $.wikiEditor.modules.highlight.fn.scan( context );
+ $.wikiEditor.modules.highlight.fn.mark( context, 'ready' );
+ }
+},
+/**
+ * Internally used functions
+ */
+'fn': {
+ /**
+ * Creates a highlight module within a wikiEditor
+ *
+ * @param config Configuration object to create module from
+ */
+ 'create': function( context, config ) {
+ context.modules.highlight.markersStr = '';
+ },
+ /**
+ * Scans text division for tokens
+ *
+ * @param division
+ */
+ 'scan': function( context, division ) {
+ // Remove all existing tokens
+ var tokenArray = context.modules.highlight.tokenArray = [];
+ // Scan text for new tokens
+ var text = context.fn.getContents();
+ // Perform a scan for each module which provides any expressions to scan for
+ // FIXME: This traverses the entire string once for every regex. Investigate
+ // whether |-concatenating regexes then traversing once is faster.
+ for ( var module in context.modules ) {
+ if ( module in $.wikiEditor.modules && 'exp' in $.wikiEditor.modules[module] ) {
+ for ( var exp in $.wikiEditor.modules[module].exp ) {
+ // Prepare configuration
+ var regex = $.wikiEditor.modules[module].exp[exp].regex;
+ var label = $.wikiEditor.modules[module].exp[exp].label;
+ var markAfter = $.wikiEditor.modules[module].exp[exp].markAfter || false;
+ // Search for tokens
+ var offset = 0, left, right, match;
+ while ( ( match = text.substr( offset ).match( regex ) ) != null ) {
+ right = ( left = offset + match.index ) + match[0].length;
+ tokenArray[tokenArray.length] = {
+ 'offset': markAfter ? right : left,
+ 'label': label,
+ 'tokenStart': left,
+ 'match': match
+ };
+ // Move to the right of this match
+ offset = right;
+ }
+ }
+ }
+ }
+ // Sort by start
+ tokenArray.sort( function( a, b ) { return a.tokenStart - b.tokenStart; } );
+ // Let the world know, a scan just happened!
+ context.fn.trigger( 'scan' );
+ },
+ /**
+ * Marks up text with HTML
+ *
+ * @param division
+ * @param tokens
+ */
+ // FIXME: What do division and tokens do?
+ // TODO: Document the scan() and mark() APIs somewhere
+ 'mark': function( context, division, tokens ) {
+ // Reset markers
+ var markers = [];
+
+ // Recycle markers that will be skipped in this run
+ if ( context.modules.highlight.markers && division != '' ) {
+ for ( var i = 0; i < context.modules.highlight.markers.length; i++ ) {
+ if ( context.modules.highlight.markers[i].skipDivision == division ) {
+ markers.push( context.modules.highlight.markers[i] );
+ }
+ }
+ }
+ context.modules.highlight.markers = markers;
+
+ // Get all markers
+ context.fn.trigger( 'mark' );
+ markers.sort( function( a, b ) { return a.start - b.start || a.end - b.end; } );
+
+ // Serialize the markers array to a string and compare it with the one stored in the previous run - if they're
+ // equal, there's no markers to change
+ var markersStr = '';
+ for ( var i = 0; i < markers.length; i++ ) {
+ markersStr += markers[i].start + ',' + markers[i].end + ',' + markers[i].type + ',';
+ }
+ if ( context.modules.highlight.markersStr == markersStr ) {
+ // No change, bail out
+ return;
+ }
+ context.modules.highlight.markersStr = markersStr;
+
+ // Traverse the iframe DOM, inserting markers where they're needed - store visited markers here so we know which
+ // markers should be removed
+ var visited = [], v = 0;
+ for ( var i = 0; i < markers.length; i++ ) {
+ if ( typeof markers[i].skipDivision !== 'undefined' && ( division == markers[i].skipDivision ) ) {
+ continue;
+ }
+
+ // We want to isolate each marker, so we may need to split textNodes if a marker starts or ends halfway one.
+ var start = markers[i].start;
+ var s = context.fn.getOffset( start );
+ if ( !s ) {
+ // This shouldn't happen
+ continue;
+ }
+ var startNode = s.node;
+
+ // Don't wrap leading BRs, produces undesirable results
+ // FIXME: It's also possible that the offset is a bit high because getOffset() has incremented .length to
+ // fake the newline caused by startNode being in a P. In this case, prevent the textnode splitting below
+ // from making startNode an empty textnode, IE barfs on that
+ while ( startNode.nodeName == 'BR' || s.offset == startNode.nodeValue.length ) {
+ start++;
+ s = context.fn.getOffset( start );
+ startNode = s.node;
+ }
+
+ // The next marker starts somewhere in this textNode or at this BR
+ if ( s.offset > 0 && s.node.nodeName == '#text' ) {
+ // Split off the prefix - this leaves the prefix in the current node and puts the rest in a new node
+ // which is our start node
+ var newStartNode = startNode.splitText( s.offset < s.node.nodeValue.length ?
+ s.offset : s.node.nodeValue.length - 1
+ );
+ var oldStartNode = startNode;
+ startNode = newStartNode;
+ // Update offset objects. We don't need purgeOffsets(), simply manipulating the existing offset objects
+ // will suffice
+ // FIXME: This manipulates context.offsets directly, which is ugly, but the performance improvement vs.
+ // purgeOffsets() is worth it - this code doesn't set lastTextNode to newStartNode for offset objects
+ // with lastTextNode == oldStartNode, but that doesn't really matter
+ var subtracted = s.offset;
+ var oldLength = s.length;
+
+ var j, o;
+ // Update offset objects referring to oldStartNode
+ for ( j = start - subtracted; j < start; j++ ) {
+ if ( j in context.offsets ) {
+ o = context.offsets[j];
+ o.node = oldStartNode;
+ o.length = subtracted;
+ }
+ }
+ // Update offset objects referring to newStartNode
+ for ( j = start; j < start - subtracted + oldLength; j++ ) {
+ if ( j in context.offsets ) {
+ o = context.offsets[j];
+ o.node = newStartNode;
+ o.offset -= subtracted;
+ o.length -= subtracted;
+ o.lastTextNode = oldStartNode;
+ }
+ }
+ }
+ var end = markers[i].end;
+ // To avoid ending up at the first char of the next node, we grab the offset for end - 1 and add one to the
+ // offset
+ var e = context.fn.getOffset( end - 1 );
+ if ( !e ) {
+ // This shouldn't happen
+ continue;
+ }
+ var endNode = e.node;
+ if ( e.offset + 1 < e.length - 1 && endNode.nodeName == '#text' ) {
+ // Split off the suffix. This puts the suffix in a new node and leaves the rest in endNode
+ var oldEndNode = endNode;
+ var newEndNode = endNode.splitText( e.offset + 1 );
+ // Update offset objects
+ var subtracted = e.offset + 1;
+ var oldLength = e.length;
+ var j, o;
+ // Update offset objects referring to oldEndNode
+ for ( j = end - subtracted; j < end; j++ ) {
+ if ( j in context.offsets ) {
+ o = context.offsets[j];
+ o.node = oldEndNode;
+ o.length = subtracted;
+ }
+ }
+ // We have to insert this one, as it might not exist: we didn't call getOffset( end )
+ context.offsets[end] = {
+ 'node': newEndNode,
+ 'offset': 0,
+ 'length': oldLength - subtracted,
+ 'lastTextNode': oldEndNode
+ };
+ // Update offset objects referring to newEndNode
+ for ( j = end + 1; j < end - subtracted + oldLength; j++ ) {
+ if ( j in context.offsets ) {
+ o = context.offsets[j];
+ o.node = newEndNode;
+ o.offset -= subtracted;
+ o.length -= subtracted;
+ o.lastTextNode = oldEndNode;
+ }
+ }
+ }
+ // Don't wrap trailing BRs, doing that causes weird issues
+ if ( endNode.nodeName == 'BR' ) {
+ endNode = e.lastTextNode;
+ }
+ // If startNode and endNode have different parents, we need to pull endNode and all textnodes in between
+ // into startNode's parent and replace </p><p> with <br>
+ if ( startNode.parentNode != endNode.parentNode ) {
+ var startP = $( startNode ).closest( 'p' ).get( 0 );
+ var t = new context.fn.rawTraverser( startNode, startP, context.$content.get( 0 ), false );
+ var afterStart = startNode.nextSibling;
+ var lastP = startP;
+ var nextT = t.next();
+ while ( nextT && t.node != endNode ) {
+ t = nextT;
+ nextT = t.next();
+ // If t.node has a different parent, merge t.node.parentNode with startNode.parentNode
+ if ( t.node.parentNode != startNode.parentNode ) {
+ var oldParent = t.node.parentNode;
+ if ( afterStart ) {
+ if ( lastP != t.inP ) {
+ // We're entering a new <p>, insert a <br>
+ startNode.parentNode.insertBefore(
+ startNode.ownerDocument.createElement( 'br' ),
+ afterStart
+ );
+ }
+ // A <p> with just a <br> in it is an empty line, so let's not bother with unwrapping it
+ if ( !( oldParent.childNodes.length == 1 && oldParent.firstChild.nodeName == 'BR' ) ) {
+ // Move all children of oldParent into startNode's parent
+ while ( oldParent.firstChild ) {
+ startNode.parentNode.insertBefore( oldParent.firstChild, afterStart );
+ }
+ }
+ } else {
+ if ( lastP != t.inP ) {
+ // We're entering a new <p>, insert a <br>
+ startNode.parentNode.appendChild(
+ startNode.ownerDocument.createElement( 'br' )
+ );
+ }
+ // A <p> with just a <br> in it is an empty line, so let's not bother with unwrapping it
+ if ( !( oldParent.childNodes.length == 1 && oldParent.firstChild.nodeName == 'BR' ) ) {
+ // Move all children of oldParent into startNode's parent
+ while ( oldParent.firstChild ) {
+ startNode.parentNode.appendChild( oldParent.firstChild );
+ }
+ }
+ }
+ // Remove oldParent, which is now empty
+ oldParent.parentNode.removeChild( oldParent );
+ }
+ lastP = t.inP;
+ }
+ // Moving nodes around like this invalidates offset objects
+ // TODO: Update offset objects ourselves for performance. Requires rewriting this code block to be
+ // offset-based rather than traverser-based
+ }
+ // Now wrap everything between startNode and endNode (may be equal).
+ var ca1 = startNode, ca2 = endNode;
+ if ( ca1 && ca2 && ca1.parentNode ) {
+ var anchor = markers[i].getAnchor( ca1, ca2 );
+ if ( !anchor ) {
+ var commonAncestor = ca1.parentNode;
+ if ( markers[i].anchor == 'wrap') {
+ // We have to store things like .parentNode and .nextSibling because appendChild() changes these
+ var newNode = ca1.ownerDocument.createElement( 'span' );
+ var nextNode = ca2.nextSibling;
+ // Append all nodes between ca1 and ca2 (inclusive) to newNode
+ var n = ca1;
+ while ( n != nextNode ) {
+ var ns = n.nextSibling;
+ newNode.appendChild( n );
+ n = ns;
+ }
+ // Insert newNode in the right place
+ if ( nextNode ) {
+ commonAncestor.insertBefore( newNode, nextNode );
+ } else {
+ commonAncestor.appendChild( newNode );
+ }
+ anchor = newNode;
+ } else if ( markers[i].anchor == 'tag' ) {
+ anchor = commonAncestor;
+ }
+ $( anchor ).data( 'marker', markers[i] ).addClass( 'wikiEditor-highlight' );
+ // Allow the module adding this marker to manipulate it
+ markers[i].afterWrap( anchor, markers[i] );
+
+ } else {
+ // Update the marker object
+ $( anchor ).data( 'marker', markers[i] );
+ if ( typeof markers[i].onSkip == 'function' ) {
+ markers[i].onSkip( anchor );
+ }
+ }
+ visited[v++] = anchor;
+ }
+ }
+ // Remove markers that were previously inserted but weren't passed to this function - visited[] contains the
+ // visited elements in order and find() and each() preserve order
+ var j = 0;
+ context.$content.find( '.wikiEditor-highlight' ).each( function() {
+ if ( visited[j] == this ) {
+ // This marker is legit, leave it in
+ j++;
+ return true;
+ }
+ // Remove this marker
+ var marker = $(this).data( 'marker' );
+ if ( marker && typeof marker.skipDivision != 'undefined' && ( division == marker.skipDivision ) ) {
+ // Don't remove these either
+ return true;
+ }
+ if ( marker && typeof marker.beforeUnwrap == 'function' )
+ marker.beforeUnwrap( this );
+ if ( ( marker && marker.anchor == 'tag' ) || $(this).is( 'p' ) ) {
+ // Remove all classes
+ $(this).removeAttr( 'class' );
+ } else {
+ // Assume anchor == 'wrap'
+ $(this).replaceWith( this.childNodes );
+ }
+ context.fn.purgeOffsets();
+ });
+
+ }
+}
+
+}; })( jQuery );
+
diff --git a/extensions/WikiEditor/modules/jquery.wikiEditor.html b/extensions/WikiEditor/modules/jquery.wikiEditor.html
new file mode 100644
index 00000000..f11521a3
--- /dev/null
+++ b/extensions/WikiEditor/modules/jquery.wikiEditor.html
@@ -0,0 +1,135 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html>
+<head>
+ <title>WikiEditor</title>
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+ <!--[if gte IE 8]>
+ <style>
+ /* IE8 ONLY - This is how we are fixing the double-height of BR tags when they are alone in a P tag */
+ p > br {
+ display: none;
+ }
+ p > br + br {
+ display: block;
+ }
+ </style>
+ <![endif]-->
+ <style>
+ body {
+ margin: 0;
+ padding: 0;
+ width: 100%;
+ height: 100%;
+ font-family: monospace;
+ font-size: 9.5pt;
+ line-height: 1.5em;
+ overflow-x: auto; /* Use horizontal scroller if needed; for Firefox 2, not needed in Firefox 3 */
+ white-space: pre-wrap; /* css-3 */
+ white-space: -moz-pre-wrap !important; /* Mozilla, since 1999 */
+ white-space: -pre-wrap; /* Opera 4-6 */
+ white-space: -o-pre-wrap; /* Opera 7 */
+ word-wrap: break-word; /* Internet Explorer 5.5+ */
+ }
+ body.pasting {
+ white-space: normal;
+ }
+ p {
+ margin: 0;
+ padding: 0;
+ }
+ /* General WikiEditor stuff */
+ .wikiEditor-nodisplay {
+ display: none !important;
+ }
+ .wikiEditor-tab {
+ padding-left: 4em;
+ }
+ /* WikiEditor Templates */
+ .wikiEditor-templates .wikiEditor-template {
+ color: silver;
+ }
+ /* WikiEditor TemplateEditor */
+ .wikiEditor-templateEditor .wikiEditor-template-text-shrunken{
+ height: 1px !important;
+ width: 1px !important;
+ overflow: hidden;
+ float: right;
+ }
+ .wikiEditor-templateEditor .wikiEditor-template-text-visible{
+ padding: 0.5em 4px;
+ padding-top: 1.25em;
+ margin-top: -0.95em;
+ background: #F3F3F3 url(images/templateEditor/text-base.png) repeat-x scroll center top;
+ display: block;
+ width: 100%;
+ border-bottom: solid 1px #cccccc;
+ }
+ .wikiEditor-templateEditor .wikiEditor-template {
+ display: inline-block;
+ font-size: 12px;
+ }
+ .wikiEditor-templateEditor .wikiEditor-template-name {
+ cursor: pointer;
+ vertical-align: -2px;
+ display: inline-block;
+ height: 16px;
+ margin-bottom: -1px;
+ margin-right: 2px;
+ overflow: hidden;
+ background: url(images/templateEditor/name-base.png) 0 0 repeat-x #e8e8e8;
+ color: #000000;
+ font-family: monospace;
+ text-decoration: none;
+ padding-left: 0.33em;
+ line-height: 16px;
+ }
+ .wikiEditor-templateEditor .wikiEditor-template-expand {
+ cursor: pointer;
+ vertical-align: -2px;
+ display: inline-block;
+ margin-left: 2px;
+ height: 16px;
+ margin-bottom: -1px;
+ line-height: 16px;
+ overflow: hidden;
+ width: 13px;
+ background-position: 50%;
+ }
+ .wikiEditor-templateEditor .wikiEditor-template-dialog {
+ cursor: pointer;
+ vertical-align: -18%;
+ display: inline-block;
+ height: 16px;
+ overflow: hidden;
+ width: 22px;
+ background-position: 50%;
+ }
+ .wikiEditor-templateEditor .wikiEditor-template-name:hover {
+ text-decoration: underline;
+ }
+ .wikiEditor-templateEditor .wikiEditor-template-expanded .wikiEditor-template-expand {
+ background-image: url(images/templateEditor/collapse.png);
+ }
+ .wikiEditor-templateEditor .wikiEditor-template-expanded .wikiEditor-template-dialog {
+ background-image: url(images/templateEditor/dialog-expanded.png);
+ }
+ .wikiEditor-templateEditor .wikiEditor-template-collapsed .wikiEditor-template-expand {
+ background-image: url(images/templateEditor/expand.png);
+ }
+ .wikiEditor-templateEditor .wikiEditor-template-collapsed .wikiEditor-template-dialog {
+ background-image: url(images/templateEditor/dialog-collapsed.png);
+ }
+ .wikiEditor-templateEditor .wikiEditor-template-expanded {
+ display: block;
+ }
+ .wikiEditor-templateEditor .wikiEditor-template .wikiEditor-template-text {
+
+ }
+ .wikiEditor-templateEditor .wikiEditor-template-end, .wikiEditor-template-start {
+ color: blue;
+ cursor: pointer;
+ }
+ </style>
+</head>
+<body></body>
+</html>
diff --git a/extensions/WikiEditor/modules/jquery.wikiEditor.iframe.js b/extensions/WikiEditor/modules/jquery.wikiEditor.iframe.js
new file mode 100644
index 00000000..5a0cc153
--- /dev/null
+++ b/extensions/WikiEditor/modules/jquery.wikiEditor.iframe.js
@@ -0,0 +1,1387 @@
+/* IFrame extension for wikiEditor */
+
+( function( $ ) { $.wikiEditor.extensions.iframe = function( context ) {
+
+/*
+ * Event Handlers
+ *
+ * These act as filters returning false if the event should be ignored or returning true if it should be passed
+ * on to all modules. This is also where we can attach some extra information to the events.
+ */
+context.evt = $.extend( context.evt, {
+ /**
+ * Filters change events, which occur when the user interacts with the contents of the iframe. The goal of this
+ * function is to both classify the scope of changes as 'division' or 'character' and to prevent further
+ * processing of events which did not actually change the content of the iframe.
+ */
+ 'keydown': function( event ) {
+ switch ( event.which ) {
+ case 90: // z
+ case 89: // y
+ if ( event.which == 89 && !$.browser.msie ) {
+ // only handle y events for IE
+ return true;
+ } else if ( ( event.ctrlKey || event.metaKey ) && context.history.length ) {
+ // HistoryPosition is a negative number between -1 and -context.history.length, in other words
+ // it's the number of steps backwards from the latest state.
+ var newPosition;
+ if ( event.shiftKey || event.which == 89 ) {
+ // Redo
+ newPosition = context.historyPosition + 1;
+ } else {
+ // Undo
+ newPosition = context.historyPosition - 1;
+ }
+ // Only act if we are switching to a valid state
+ if ( newPosition >= ( context.history.length * -1 ) && newPosition < 0 ) {
+ // Make sure we run the history storing code before we make this change
+ context.fn.updateHistory( context.oldDelayedHTML != context.$content.html() );
+ context.oldDelayedHistoryPosition = context.historyPosition;
+ context.historyPosition = newPosition;
+ // Change state
+ // FIXME: Destroys event handlers, will be a problem with template folding
+ context.$content.html(
+ context.history[context.history.length + context.historyPosition].html
+ );
+ context.fn.purgeOffsets();
+ if( context.history[context.history.length + context.historyPosition].sel ) {
+ context.fn.setSelection( {
+ start: context.history[context.history.length + context.historyPosition].sel[0],
+ end: context.history[context.history.length + context.historyPosition].sel[1]
+ } );
+ }
+ }
+ // Prevent the browser from jumping in and doing its stuff
+ return false;
+ }
+ break;
+ // Intercept all tab events to provide consisten behavior across browsers
+ // Webkit browsers insert tab characters by default into the iframe rather than changing input focus
+ case 9: //tab
+ // if any modifier keys are pressed, allow the browser to do it's thing
+ if ( event.ctrlKey || event.altKey || event.shiftKey ) {
+ return true;
+ } else {
+ var $tabindexList = $( '[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: //v
+ if ( event.ctrlKey && $.browser.msie && 'paste' in context.evt ) {
+ //paste, intercepted for IE
+ 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';
+ }
+ // Never let the body be totally empty
+ 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';
+ // Surround by <p> if it does not already have it
+ 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 ) {
+ // Save the cursor position to restore it after all this voodoo
+ var cursorPos = context.fn.getCaretPosition();
+ var oldLength = context.fn.getContents().length;
+ var positionFromEnd = oldLength - cursorPos[1];
+
+ //give everything the wikiEditor class so that we can easily pick out things without that class as pasted
+ context.$content.find( '*' ).addClass( 'wikiEditor' );
+ if ( $.layout.name !== 'webkit' ) {
+ context.$content.addClass( 'pasting' );
+ }
+
+ setTimeout( function() {
+ // Kill stuff we know we don't want
+ context.$content.find( 'script,style,img,input,select,textarea,hr,button,link,meta' ).remove();
+ var nodeToDelete = [];
+ var pastedContent = [];
+ var firstDirtyNode;
+ var $lastDirtyNode;
+ var elementAtCursor;
+ if ( $.browser.msie && !context.offsets ) {
+ elementAtCursor = null;
+ } else {
+ elementAtCursor = context.fn.getOffset( cursorPos[0] );
+ }
+ if ( elementAtCursor == null || elementAtCursor.node == null ) {
+ context.$content.prepend( '<p class = wikiEditor></p>' );
+ firstDirtyNode = context.$content.children()[0];
+ } else {
+ firstDirtyNode = elementAtCursor.node;
+ }
+
+ //this is ugly but seems like the best way to handle the case where we select and replace all editor contents
+ try {
+ firstDirtyNode.parentNode;
+ } catch ( err ) {
+ context.$content.prepend( '<p class = wikiEditor></p>' );
+ firstDirtyNode = context.$content.children()[0];
+ }
+
+ while ( firstDirtyNode != null ) {
+ //we're going to replace the contents of the entire parent node.
+ while ( firstDirtyNode.parentNode && firstDirtyNode.parentNode.nodeName != 'BODY'
+ && ! $( firstDirtyNode ).hasClass( 'wikiEditor' )
+ ) {
+ firstDirtyNode = firstDirtyNode.parentNode;
+ }
+ //go back till we find the first pasted node
+ while ( firstDirtyNode.previousSibling != null
+ && ! $( firstDirtyNode.previousSibling ).hasClass( 'wikiEditor' )
+ ) {
+
+ if ( $( firstDirtyNode.previousSibling ).hasClass( '#comment' ) ) {
+ $( firstDirtyNode ).remove();
+ } else {
+ firstDirtyNode = firstDirtyNode.previousSibling;
+ }
+ }
+
+ if ( firstDirtyNode.previousSibling != null ) {
+ $lastDirtyNode = $( firstDirtyNode.previousSibling );
+ } else {
+ $lastDirtyNode = $( firstDirtyNode );
+ }
+
+ var cc = makeContentCollector( $.browser, null );
+ while ( firstDirtyNode != null ) {
+ cc.collectContent(firstDirtyNode);
+ cc.notifyNextNode(firstDirtyNode.nextSibling);
+
+ nodeToDelete.push( firstDirtyNode );
+
+ firstDirtyNode = firstDirtyNode.nextSibling;
+ if ( $( firstDirtyNode ).hasClass( 'wikiEditor' ) ) {
+ break;
+ }
+ }
+
+ var ccData = cc.finish();
+ pastedContent = ccData.lines;
+ var pastedPretty = '';
+ for ( var i = 0; i < pastedContent.length; i++ ) {
+ //escape html
+ pastedPretty = pastedContent[i].replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/\r?\n/g, '\\n');
+ //replace leading white spaces with &nbsp;
+ 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);
+ }
+
+
+ if( !pastedPretty && $.browser.msie && i == 0 ) {
+ continue;
+ }
+ $newElement = $( '<p class="wikiEditor pasted" ></p>' );
+ if ( pastedPretty ) {
+ $newElement.html( pastedPretty );
+ } else {
+ $newElement.html( '<br class="wikiEditor">' );
+ }
+ $newElement.insertAfter( $lastDirtyNode );
+
+ $lastDirtyNode = $newElement;
+
+ }
+
+ //now delete all the original nodes that we prettified already
+ while ( nodeToDelete.length > 0 ) {
+ $deleteNode = $( nodeToDelete.pop() );
+ $deleteNode.remove();
+ }
+
+ //anything without wikiEditor class was pasted.
+ $selection = context.$content.find( ':not(.wikiEditor)' );
+ if ( $selection.length == 0 ) {
+ break;
+ } else {
+ firstDirtyNode = $selection.eq( 0 )[0];
+ }
+ }
+ context.$content.find( '.wikiEditor' ).removeClass( 'wikiEditor' );
+
+ //now place the cursor at the end of pasted content
+ var newLength = context.fn.getContents().length;
+ var newPos = newLength - positionFromEnd;
+
+ context.fn.purgeOffsets();
+ context.fn.setSelection( { start: newPos, end: newPos } );
+
+ context.fn.scrollToCaretPosition();
+ }, 0 );
+ return true;
+ },
+ 'ready': function( event ) {
+ // Initialize our history queue
+ if ( context.$content ) {
+ context.history.push( { 'html': context.$content.html(), 'sel': context.fn.getCaretPosition() } );
+ } else {
+ context.history.push( { 'html': '', 'sel': context.fn.getCaretPosition() } );
+ }
+ return true;
+ }
+} );
+
+/**
+ * Internally used functions
+ */
+context.fn = $.extend( context.fn, {
+ '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 ) {
+ // This function is slow for large inputs, so aggressively cache input/output pairs
+ if ( html in context.htmlToTextMap ) {
+ return context.htmlToTextMap[html];
+ }
+ var origHTML = html;
+
+ // We use this elaborate trickery for cross-browser compatibility
+ // IE does overzealous whitespace collapsing for $( '<pre />' ).html( html );
+ // We also do <br> and easy cases for <p> conversion here, complicated cases are handled later
+ html = html
+ .replace( /\r?\n/g, "" ) // IE7 inserts newlines before block elements
+ .replace( /&nbsp;/g, " " ) // We inserted these to prevent IE from collapsing spaces
+ .replace( /\<br[^\>]*\>\<\/p\>/gi, '</p>' ) // Remove trailing <br> from <p>
+ .replace( /\<\/p\>\s*\<p[^\>]*\>/gi, "\n" ) // Easy case for <p> conversion
+ .replace( /\<br[^\>]*\>/gi, "\n" ) // <br> conversion
+ .replace( /\<\/p\>(\n*)\<p[^\>]*\>/gi, "$1\n" )
+ // Un-nest <p> tags
+ .replace( /\<p[^\>]*\><p[^\>]*\>/gi, '<p>' )
+ .replace( /\<\/p\><\/p\>/gi, '</p>' );
+ // Save leading and trailing whitespace now and restore it later. IE eats it all, and even Firefox
+ // won't leave everything alone
+ 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(); } );
+ // Convert tabs, <p>s and <br>s back
+ $pre.find( '.wikiEditor-tab' ).each( function() { $( this ).text( "\t" ); } );
+ $pre.find( 'br' ).each( function() { $( this ).replaceWith( "\n" ); } );
+ // Converting <p>s is wrong if there's nothing before them, so check that.
+ // .find( '* + p' ) isn't good enough because textnodes aren't considered
+ $pre.find( 'p' ).each( function() {
+ var text = $( this ).text();
+ // If this <p> is preceded by some text, add a \n at the beginning, and if
+ // it's followed by a textnode, add a \n at the end
+ // We need the traverser because there can be other weird stuff in between
+
+ // Check for preceding 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;
+ }
+
+ // Check for following 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 ) {
+ // IE aggressively collapses whitespace in .text() after having done DOM manipulation,
+ // but for some crazy reason this does work. Also convert \r back to \n
+ retval = $( '<pre>' + $pre.html() + '</pre>' ).text().replace( /\r/g, '\n' );
+ } else {
+ retval = $pre.text();
+ }
+ return context.htmlToTextMap[origHTML] = leading + retval + trailing;
+ },
+ /**
+ * Get the first element before the selection that's in a certain class
+ * @param classname Class to match. Defaults to '', meaning any class
+ * @param strict If true, the element the selection starts in cannot match (default: false)
+ * @return jQuery object or null if unknown
+ */
+ 'beforeSelection': function( classname, strict ) {
+ if ( typeof classname == 'undefined' ) {
+ classname = '';
+ }
+ var e = null, offset = null;
+ if ( $.browser.msie && !context.$iframe[0].contentWindow.document.body ) {
+ return null;
+ }
+ if ( context.$iframe[0].contentWindow.getSelection ) {
+ // Firefox and Opera
+ var selection = context.$iframe[0].contentWindow.getSelection();
+ // On load, webkit seems to not have a valid selection
+ if ( selection.baseNode !== null ) {
+ // Start at the selection's start and traverse the DOM backwards
+ // This is done by traversing an element's children first, then the element itself, then its parent
+ e = selection.getRangeAt( 0 ).startContainer;
+ offset = selection.getRangeAt( 0 ).startOffset;
+ } else {
+ return null;
+ }
+
+ // When the cursor is on an empty line, Opera gives us a bogus range object with
+ // startContainer=endContainer=body and startOffset=endOffset=1
+ 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 ) {
+ // IE
+ // Because there's nothing like range.startContainer in IE, we need to do a DOM traversal
+ // to find the element the start of the selection is in
+ var range = context.$iframe[0].contentWindow.document.selection.createRange();
+ // Set range2 to the text before the selection
+ var range2 = context.$iframe[0].contentWindow.document.body.createTextRange();
+ // For some reason this call throws errors in certain cases, e.g. when the selection is
+ // not in the iframe
+ 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' ) {
+ // The selection is not in a textnode, but between two non-text nodes
+ // (usually inside the <body> between two <br>s). Go to the rightmost
+ // child of the node just before the selection
+ 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;
+ }
+
+ // We'd normally use if( $( e ).hasClass( class ) in the while loop, but running the jQuery
+ // constructor thousands of times is very inefficient
+ 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 $( [] );
+ },
+ /**
+ * Object used by traverser(). Don't use this unless you know what you're doing
+ */
+ '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 ) {
+ // We're back at the ancestor, stop here
+ p = null;
+ }
+ if ( p && p.nodeName == "P" ) {
+ nextInP = null;
+ }
+ }
+ p = p ? p.nextSibling : null;
+ if ( p && p.nodeName == "P" ) {
+ nextInP = p;
+ }
+ do {
+ // Filter nodes with the wikiEditor-noinclude class
+ // Don't use $( p ).hasClass( 'wikiEditor-noinclude' ) because
+ // $() is slow in a tight loop
+ 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 );
+ // Instead of calling the rawTraverser constructor, inline it. This avoids function call overhead
+ 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 ) {
+ // We're back at the ancestor, stop here
+ p = null;
+ }
+ if ( p && p.nodeName == "P" ) {
+ prevInP = null;
+ }
+ }
+ p = p ? p.previousSibling : null;
+ if ( p && p.nodeName == "P" ) {
+ prevInP = p;
+ }
+ do {
+ // Filter nodes with the wikiEditor-noinclude class
+ // Don't use $( p ).hasClass( 'wikiEditor-noinclude' ) because
+ // $() is slow in a tight loop
+ 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 );
+ // Instead of calling the rawTraverser constructor, inline it. This avoids function call overhead
+ return p ? { 'node': p, 'inP': prevInP, 'ancestor': this.ancestor,
+ 'skipNoinclude': this.skipNoinclude, 'next': this.next, 'prev': this.prev } : null;
+ };
+ },
+ /**
+ * Get an object used to traverse the leaf nodes in the iframe DOM. This traversal skips leaf nodes
+ * inside an element with the wikiEditor-noinclude class. This basically wraps rawTraverser
+ *
+ * @param start Node to start at
+ * @return Traverser object, use .next() or .prev() to get a traverser object referring to the
+ * previous/next node
+ */
+ 'traverser': function( start ) {
+ // Find the leftmost leaf node in the tree
+ var startNode = start.jquery ? start.get( 0 ) : start;
+ var node = startNode;
+ var inP = node.nodeName == "P" ? node : null;
+ do {
+ // Filter nodes with the wikiEditor-noinclude class
+ // Don't use $( p ).hasClass( 'wikiEditor-noinclude' ) because
+ // $() is slow in a tight loop
+ 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];
+ }
+ // Our offset is not pre-cached. Find the highest offset below it and interpolate
+ // We need to traverse the entire object because for() doesn't traverse in order
+ // We don't do in-order traversal because the object is sparse
+ 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 ) ) {
+ // Weird edge case: either offset is too large or the document is empty
+ 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 ) {
+ // <p>Foo</p> looks like "Foo\n", make it quack like it too
+ // Basically we're faking the \n character much like we're treating <br>s
+ 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;
+ }
+ },
+ 'saveCursorAndScrollTop': function() {
+ // Stub out textarea behavior
+ return;
+ },
+ 'restoreCursorAndScrollTop': function() {
+ // Stub out textarea behavior
+ return;
+ },
+ 'saveSelection': function() {
+ if ( $.client.profile().name === 'msie' ) {
+ context.$iframe[0].contentWindow.focus();
+ context.savedSelection = context.$iframe[0].contentWindow.document.selection.createRange();
+ }
+ },
+ 'restoreSelection': function() {
+ if ( $.client.profile().name === 'msie' && context.savedSelection !== null ) {
+ context.$iframe[0].contentWindow.focus();
+ context.savedSelection.select();
+ context.savedSelection = null;
+ }
+ },
+ /**
+ * Update the history queue
+ *
+ * @param htmlChange pass true or false to inidicate if there was a text change that should potentially
+ * be given a new history state.
+ */
+ 'updateHistory': function( htmlChange ) {
+ var newHTML = context.$content.html();
+ var newSel = context.fn.getCaretPosition();
+ // Was text changed? Was it because of a REDO or UNDO action?
+ if (
+ context.history.length == 0 ||
+ ( htmlChange && context.oldDelayedHistoryPosition == context.historyPosition )
+ ) {
+ context.oldDelayedSel = newSel;
+ // Do we need to trim extras from our history?
+ // FIXME: this should really be happing on change, not on the delay
+ if ( context.historyPosition < -1 ) {
+ //clear out the extras
+ context.history.splice( context.history.length + context.historyPosition + 1 );
+ context.historyPosition = -1;
+ }
+ context.history.push( { 'html': newHTML, 'sel': newSel } );
+ // If the history has grown longer than 10 items, remove the earliest one
+ while ( context.history.length > 10 ) {
+ context.history.shift();
+ }
+ } else if ( context.oldDelayedSel != newSel ) {
+ // If only the selection was changed, update it
+ context.oldDelayedSel = newSel;
+ context.history[context.history.length + context.historyPosition].sel = newSel;
+ }
+ // synch our old delayed history position until the next undo/redo action
+ context.oldDelayedHistoryPosition = context.historyPosition;
+ },
+ /**
+ * Sets up the iframe in place of the textarea to allow more advanced operations
+ */
+ 'setupIframe': function() {
+ context.$iframe = $( '<iframe></iframe>' )
+ .attr( {
+ 'frameBorder': 0,
+ 'border': 0,
+ 'tabindex': 1,
+ 'src': mw.config.get( 'wgExtensionAssetsPath' ) + '/WikiEditor/modules/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() {
+ // Internet Explorer will reload the iframe once we turn on design mode, so we need to only turn it
+ // on during the first run, and then bail
+ if ( !this.isSecondRun ) {
+ // Turn the document's design mode on
+ context.$iframe[0].contentWindow.document.designMode = 'on';
+ // Let the rest of this function happen next time around
+ if ( $.browser.msie ) {
+ this.isSecondRun = true;
+ return;
+ }
+ }
+ // Get a reference to the content area of the iframe
+ context.$content = $( context.$iframe[0].contentWindow.document.body );
+ // Add classes to the body to influence the styles based on what's enabled
+ for ( module in context.modules ) {
+ context.$content.addClass( 'wikiEditor-' + module );
+ }
+ // If we just do "context.$content.text( context.$textarea.val() )", Internet Explorer will strip
+ // out the whitespace charcters, specifically "\n" - so we must manually encode text and append it
+ // TODO: Refactor this into a textToHtml() function
+ var html = context.$textarea.val()
+ // We're gonna use &esc; as an escape sequence
+ .replace( /&esc;/g, '&esc;esc;' )
+ // Escape existing uses of <p>, </p>, &nbsp; and <span class="wikiEditor-tab"></span>
+ .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;' );
+ // We must do some extra processing on IE to avoid dirty diffs, specifically IE will collapse
+ // leading spaces - browser sniffing is not ideal, but executing this code on a non-broken browser
+ // doesn't cause harm
+ if ( $.browser.msie ) {
+ html = html.replace( /\t/g, '<span class="wikiEditor-tab"></span>' );
+ if ( $.browser.versionNumber <= 7 ) {
+ // Replace all spaces matching &nbsp; - IE <= 7 needs this because of its overzealous
+ // whitespace collapsing
+ html = html.replace( / /g, "&nbsp;" );
+ } else {
+ // IE8 is happy if we just convert the first leading space to &nbsp;
+ html = html.replace( /(^|\n) /g, "$1&nbsp;" );
+ }
+ }
+ // Use a dummy div to escape all entities
+ // This'll also escape <br>, <span> and &nbsp; , so we unescape those after
+ // We also need to unescape the doubly-escaped things mentioned above
+ html = $( '<div />' ).text( '<p>' + html.replace( /\r?\n/g, '</p><p>' ) + '</p>' ).html()
+ .replace( /&amp;nbsp;/g, '&nbsp;' )
+ // Allow <p> tags to survive encoding
+ .replace( /&lt;p&gt;/g, '<p>' )
+ .replace( /&lt;\/p&gt;/g, '</p>' )
+ // And <span class="wikiEditor-tab"></span> too
+ .replace(
+ /&lt;span( |&nbsp;)class=("|&quot;)wikiEditor-tab("|&quot;)&gt;&lt;\/span&gt;/g,
+ '<span class="wikiEditor-tab"></span>'
+ )
+ // Empty <p> tags need <br> tags in them
+ .replace( /<p><\/p>/g, '<p><br></p>' )
+ // Unescape &esc; stuff
+ .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 );
+
+ // Reflect direction of parent frame into child
+ if ( $( 'body' ).is( '.rtl' ) ) {
+ context.$content.addClass( 'rtl' ).attr( 'dir', 'rtl' );
+ }
+ // Activate the iframe, encoding the content of the textarea and copying it to the content of iframe
+ context.$textarea.attr( 'disabled', true );
+ context.$textarea.hide();
+ context.$iframe.show();
+ // Let modules know we're ready to start working with the content
+ context.fn.trigger( 'ready' );
+ // Only save HTML now: ready handlers may have modified it
+ context.oldHTML = context.oldDelayedHTML = context.$content.html();
+ //remove our temporary loading
+ /* Disaling our loading div for now
+ $( '.wikiEditor-ui-loading' ).fadeOut( 'fast', function() {
+ $( this ).remove();
+ } );
+ */
+ // Setup event handling on the iframe
+ $( 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 );
+ } );
+ } );
+ // Attach a submit handler to the form so that when the form is submitted the content of the iframe gets
+ // decoded and copied over to the textarea
+ context.$textarea.closest( 'form' ).submit( function() {
+ context.$textarea.attr( 'disabled', false );
+ context.$textarea.val( context.$textarea.textSelection( 'getContents' ) );
+ } );
+ /* FIXME: This was taken from EditWarning.js - maybe we could do a jquery plugin for this? */
+ // Attach our own handler for onbeforeunload which respects the current one
+ context.fallbackWindowOnBeforeUnload = window.onbeforeunload;
+ window.onbeforeunload = function() {
+ context.$textarea.val( context.$textarea.textSelection( 'getContents' ) );
+ if ( context.fallbackWindowOnBeforeUnload ) {
+ return context.fallbackWindowOnBeforeUnload();
+ }
+ };
+ },
+
+ /*
+ * Compatibility with the $.textSelection jQuery plug-in. When the iframe is in use, these functions provide
+ * equivilant functionality to the otherwise textarea-based functionality.
+ */
+
+ 'getElementAtCursor': function() {
+ if ( context.$iframe[0].contentWindow.getSelection ) {
+ // Firefox and Opera
+ var selection = context.$iframe[0].contentWindow.getSelection();
+ if ( selection.rangeCount == 0 ) {
+ // We don't know where the cursor is
+ return $( [] );
+ }
+ var sc = selection.getRangeAt( 0 ).startContainer;
+ if ( sc.nodeName == "#text" ) sc = sc.parentNode;
+ return $( sc );
+ } else if ( context.$iframe[0].contentWindow.document.selection ) { // should come last; Opera!
+ // IE
+ var selection = context.$iframe[0].contentWindow.document.selection.createRange();
+ return $( selection.parentElement() );
+ }
+ },
+
+ /**
+ * Gets the complete contents of the iframe (in plain text, not HTML)
+ */
+ 'getContents': function() {
+ // For <p></p>, .html() returns <p>&nbsp;</p> in IE
+ // This seems to convince IE while not affecting display
+ if ( !context.$content ) {
+ return '';
+ }
+ var html;
+ if ( $.browser.msie ) {
+ // Don't manipulate the iframe DOM itself, causes cursor jumping issues
+ 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 );
+ },
+ /**
+ * Gets the currently selected text in the content
+ * DO NOT CALL THIS DIRECTLY, use $.textSelection( 'functionname', options ) instead
+ */
+ 'getSelection': function() {
+ var retval;
+ if ( context.$iframe[0].contentWindow.getSelection ) {
+ // Firefox and Opera
+ retval = context.$iframe[0].contentWindow.getSelection();
+ if ( $.browser.opera ) {
+ // Opera strips newlines in getSelection(), so we need something more sophisticated
+ 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 ) { // should come last; Opera!
+ // IE
+ retval = context.$iframe[0].contentWindow.document.selection.createRange();
+ }
+ if ( typeof retval.text != 'undefined' ) {
+ // In IE8, retval.text is stripped of newlines, so we need to process retval.htmlText
+ // to get a reliable answer. IE7 does get this right though
+ // Run this fix for all IE versions anyway, it doesn't hurt
+ retval = context.fn.htmlToText( retval.htmlText );
+ } else if ( typeof retval.toString != 'undefined' ) {
+ retval = retval.toString();
+ }
+ return retval;
+ },
+ /**
+ * Inserts text at the begining and end of a text selection, optionally inserting text at the caret when
+ * selection is empty.
+ * DO NOT CALL THIS DIRECTLY, use $.textSelection( 'functionname', options ) instead
+ */
+ '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+$/, '' ) ) {
+ // Probably a successive button press
+ // strip any extra white space from selText
+ selText = selText.replace( /\s+$/, '' );
+ // set the collapseToEnd flag to ensure our selection is collapsed to the end before any insertion is done
+ collapseToEnd = true;
+ // set selectAfter to true since we know we'll be populating with our default text
+ selectAfter = true;
+ } else if ( options.replace ) {
+ selText = options.peri;
+ } else if ( selText.charAt( selText.length - 1 ) == ' ' ) {
+ // Exclude ending space char
+ // FIXME: Why?
+ selText = selText.substring( 0, selText.length - 1 );
+ post += ' ';
+ }
+ if ( options.splitlines ) {
+ selTextArr = selText.split( /\n/ );
+ }
+
+ if ( context.$iframe[0].contentWindow.getSelection ) {
+ // Firefox and Opera
+ var range = context.$iframe[0].contentWindow.getSelection().getRangeAt( 0 );
+ // if our test above indicated that this was a sucessive button press, we need to collapse the
+ // selection to the end to avoid replacing text
+ if ( collapseToEnd ) {
+ // Make sure we're not collapsing ourselves into a BR tag
+ if ( range.endContainer.nodeName == 'BR' ) {
+ range.setEndBefore( range.endContainer );
+ }
+ range.collapse( false );
+ }
+ if ( options.ownline ) {
+ // We need to figure out if the cursor is at the start or end of a line
+ var atStart = false, atEnd = false;
+ var body = context.$content.get( 0 );
+ if ( range.startOffset == 0 ) {
+ // Start of a line
+ // FIXME: Not necessarily the case with syntax highlighting or
+ // template collapsing
+ atStart = true;
+ } else if ( range.startContainer == body ) {
+ // Look up the node just before the start of the selection
+ // If it's a <BR>, we're at the start of a line that starts with a
+ // block element; if not, we're at the end of a line
+ 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();
+ // Insert the contents one line at a time - insertNode() inserts at the beginning, so this has to happen
+ // in reverse order
+ // Track the first and last inserted node, and if we need to also track where the text we need to select
+ // afterwards starts and ends
+ 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 ) {
+ // IE
+ context.$iframe[0].contentWindow.focus();
+ var range = context.$iframe[0].contentWindow.document.selection.createRange();
+ if ( options.ownline && range.moveStart ) {
+ // Check if we're at the start of a line
+ // If not, prepend a newline
+ var range2 = context.$iframe[0].contentWindow.document.selection.createRange();
+ range2.collapse();
+ range2.moveStart( 'character', -1 );
+ // FIXME: Which check is correct?
+ if ( range2.text != "\r" && range2.text != "\n" && range2.text != "" ) {
+ pre = "\n" + pre;
+ }
+
+ // Check if we're at the end of a line
+ // If not, append a newline
+ 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 our test above indicated that this was a sucessive button press, we need to collapse the
+ // selection to the end to avoid replacing text
+ if ( collapseToEnd ) {
+ range.collapse( false );
+ }
+ // TODO: Clean this up. Duplicate code due to the pre-existing browser specific structure of this
+ // function
+ 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;
+ }
+ // TODO: Maybe find a more elegant way of doing this like the Firefox code above?
+ 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 );
+ }
+ // Trigger the encapsulateSelection event (this might need to get named something else/done differently)
+ $( context.$iframe[0].contentWindow.document ).trigger(
+ 'encapsulateSelection', [ pre, options.peri, post, options.ownline, options.replace ]
+ );
+ return context.$textarea;
+ },
+ /**
+ * Gets the position (in resolution of bytes not nessecarily characters) in a textarea
+ * DO NOT CALL THIS DIRECTLY, use $.textSelection( 'functionname', options ) instead
+ */
+ '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 ) {
+ // We don't know where the cursor is
+ 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' ) {
+ // Grab the node just before the start of the selection
+ 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;
+ }
+
+ // Make sure sc and ec are leaf nodes
+ while ( sc.firstChild ) {
+ sc = sc.firstChild;
+ }
+ while ( ec.firstChild ) {
+ ec = ec.firstChild;
+ }
+ // Make sure the offsets are regenerated if necessary
+ context.fn.getOffset( 0 );
+ var o;
+ for ( o in context.offsets ) {
+ if ( startPos === null && context.offsets[o].node == sc ) {
+ // For some wicked reason o is a string, even though
+ // we put it in as an integer. Use ~~ to coerce it too an int
+ 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 ) {
+ // IE
+ // FIXME: This is mostly copypasted from the textSelection plugin
+ var d = context.$iframe[0].contentWindow.document;
+ var postFinished = false;
+ var periFinished = false;
+ var postFinished = false;
+ var preText, rawPreText, periText;
+ var rawPeriText, postText, rawPostText;
+ // Depending on the document state, and if the cursor has ever been manually placed within the document
+ // the following call such as setEndPoint can result in nasty errors. These cases are always cases
+ // in which the start and end points can safely be assumed to be 0, so we will just try our best to do
+ // the full process but fall back to 0.
+ try {
+ // Create range containing text in the selection
+ var periRange = d.selection.createRange().duplicate();
+ // Create range containing text before the selection
+ var preRange = d.body.createTextRange();
+ // Move the end where we need it
+ preRange.setEndPoint( "EndToStart", periRange );
+ // Create range containing text after the selection
+ var postRange = d.body.createTextRange();
+ // Move the start where we need it
+ postRange.setEndPoint( "StartToEnd", periRange );
+ // Load the text values we need to compare
+ preText = rawPreText = preRange.text;
+ periText = rawPeriText = periRange.text;
+ postText = rawPostText = postRange.text;
+ /*
+ * Check each range for trimmed newlines by shrinking the range by 1
+ * character and seeing if the text property has changed. If it has
+ * not changed then we know that IE has trimmed a \r\n from the end.
+ */
+ 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 ];
+ },
+ /**
+ * Sets the selection of the content
+ * DO NOT CALL THIS DIRECTLY, use $.textSelection( 'functionname', options ) instead
+ *
+ * @param start Character offset of selection start
+ * @param end Character offset of selection end
+ * @param startContainer Element in iframe to start selection in. If not set, start is a character offset
+ * @param endContainer Element in iframe to end selection in. If not set, end is a character offset
+ */
+ '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 ) {
+ // Firefox and Opera
+ 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;
+ // Don't try to set the selection past the end of a node, causes errors
+ // Just put the selection at the end of the node in this case
+ 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 ) {
+ // The requested offset isn't in the offsets array
+ // Give up
+ 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 ) {
+ // IE
+ 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 );
+
+ // IE does newline emulation for <p>s: <p>foo</p><p>bar</p> becomes foo\nbar just fine
+ // but <p>foo</p><br><br><p>bar</p> becomes foo\n\n\n\nbar , one \n too many
+ // Correct for this
+ var matches, counted = 0;
+ // while ( matches = range.htmlText.match( regex ) && matches.length <= counted ) doesn't work
+ // because the assignment side effect hasn't happened yet when the second term is evaluated
+ 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;
+ },
+ /**
+ * Scroll a textarea to the current cursor position. You can set the cursor position with setSelection()
+ * DO NOT CALL THIS DIRECTLY, use $.textSelection( 'functionname', options ) instead
+ */
+ 'scrollToCaretPosition': function( options ) {
+ context.fn.scrollToTop( context.fn.getElementAtCursor(), true );
+ },
+ /**
+ * Scroll an element to the top of the iframe
+ * DO NOT CALL THIS DIRECTLY, use $.textSelection( 'functionname', options ) instead
+ *
+ * @param $element jQuery object containing an element in the iframe
+ * @param force If true, scroll the element even if it's already visible
+ */
+ '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' );
+ }
+} );
+
+/* Setup the IFrame */
+context.fn.setupIframe();
+
+} } )( jQuery );
diff --git a/extensions/WikiEditor/modules/jquery.wikiEditor.js b/extensions/WikiEditor/modules/jquery.wikiEditor.js
new file mode 100644
index 00000000..1f3d1fa5
--- /dev/null
+++ b/extensions/WikiEditor/modules/jquery.wikiEditor.js
@@ -0,0 +1,559 @@
+/**
+ * This plugin provides a way to build a wiki-text editing user interface around a textarea.
+ *
+ * @example To intialize without any modules:
+ * $( 'div#edittoolbar' ).wikiEditor();
+ *
+ * @example To initialize with one or more modules, or to add modules after it's already been initialized:
+ * $( 'textarea#wpTextbox1' ).wikiEditor( 'addModule', 'toolbar', { ... config ... } );
+ *
+ */
+( function( $ ) {
+
+/**
+ * Global static object for wikiEditor that provides generally useful functionality to all modules and contexts.
+ */
+$.wikiEditor = {
+ /**
+ * For each module that is loaded, static code shared by all instances is loaded into this object organized by
+ * module name. The existance of a module in this object only indicates the module is available. To check if a
+ * module is in use by a specific context check the context.modules object.
+ */
+ 'modules': {},
+ /**
+ * A context can be extended, such as adding iframe support, on a per-wikiEditor instance basis.
+ */
+ 'extensions': {},
+ /**
+ * In some cases like with the iframe's HTML file, it's convienent to have a lookup table of all instances of the
+ * WikiEditor. Each context contains an instance field which contains a key that corrosponds to a reference to the
+ * textarea which the WikiEditor was build around. This way, by passing a simple integer you can provide a way back
+ * to a specific context.
+ */
+ 'instances': [],
+ /**
+ * For each browser name, an array of conditions that must be met are supplied in [operaton, value]-form where
+ * operation is a string containing a JavaScript compatible binary operator and value is either a number to be
+ * compared with $.browser.versionNumber or a string to be compared with $.browser.version. If a browser is not
+ * specifically mentioned, we just assume things will work.
+ */
+ 'browsers': {
+ // Left-to-right languages
+ 'ltr': {
+ // The toolbar layout is broken in IE6
+ 'msie': [['>=', 7]],
+ // Layout issues in FF < 2
+ 'firefox': [['>=', 2]],
+ // Text selection bugs galore - this may be a different situation with the new iframe-based solution
+ 'opera': [['>=', 9.6]],
+ // jQuery minimums
+ 'safari': [['>=', 3]],
+ 'chrome': [['>=', 3]],
+ 'netscape': [['>=', 9]],
+ 'blackberry': false,
+ 'ipod': false,
+ 'iphone': false
+ },
+ // Right-to-left languages
+ 'rtl': {
+ // The toolbar layout is broken in IE 7 in RTL mode, and IE6 in any mode
+ 'msie': [['>=', 8]],
+ // Layout issues in FF < 2
+ 'firefox': [['>=', 2]],
+ // Text selection bugs galore - this may be a different situation with the new iframe-based solution
+ 'opera': [['>=', 9.6]],
+ // jQuery minimums
+ 'safari': [['>=', 3]],
+ 'chrome': [['>=', 3]],
+ 'netscape': [['>=', 9]],
+ 'blackberry': false,
+ 'ipod': false,
+ 'iphone': false
+ }
+ },
+ /**
+ * Path to images - this is a bit messy, and it would need to change if this code (and images) gets moved into the
+ * core - or anywhere for that matter...
+ */
+ 'imgPath' : mw.config.get( 'wgExtensionAssetsPath' ) + '/WikiEditor/modules/images/',
+ /**
+ * Checks the current browser against the browsers object to determine if the browser has been black-listed or not.
+ * Because these rules are often very complex, the object contains configurable operators and can check against
+ * either the browser version number or string. This process also involves checking if the current browser is amung
+ * those which we have configured as compatible or not. If the browser was not configured as comptible we just go on
+ * assuming things will work - the argument here is to prevent the need to update the code when a new browser comes
+ * to market. The assumption here is that any new browser will be built on an existing engine or be otherwise so
+ * similar to another existing browser that things actually do work as expected. The merrits of this argument, which
+ * is essentially to blacklist rather than whitelist are debateable, but at this point we've decided it's the more
+ * "open-web" way to go.
+ * @param module Module object, defaults to $.wikiEditor
+ */
+ 'isSupported': function( module ) {
+ // Fallback to the wikiEditor browser map if no special map is provided in the module
+ var mod = module && 'browsers' in module ? module : $.wikiEditor;
+ // Check for and make use of cached value and early opportunities to bail
+ if ( typeof mod.supported !== 'undefined' ) {
+ // Cache hit
+ return mod.supported;
+ }
+ // Run a browser support test and then cache and return the result
+ return mod.supported = $.client.test( mod.browsers );
+ },
+ /**
+ * Checks if a module has a specific requirement
+ * @param module Module object
+ * @param requirement String identifying requirement
+ */
+ 'isRequired': function( module, requirement ) {
+ if ( typeof module['req'] !== 'undefined' ) {
+ for ( var req in module['req'] ) {
+ if ( module['req'][req] == requirement ) {
+ return true;
+ }
+ }
+ }
+ return false;
+ },
+ /**
+ * Provides a way to extract messages from objects. Wraps the mediaWiki.msg() function, which
+ * may eventually become a wrapper for some kind of core MW functionality.
+ *
+ * @param object Object to extract messages from
+ * @param property String of name of property which contains the message. This should be the base name of the
+ * property, which means that in the case of the object { this: 'that', fooMsg: 'bar' }, passing property as 'this'
+ * would return the raw text 'that', while passing property as 'foo' would return the internationalized message
+ * with the key 'bar'.
+ */
+ 'autoMsg': function( object, property ) {
+ // Accept array of possible properties, of which the first one found will be used
+ 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 ) {
+ var p = object[property + 'Msg'];
+ if ( $.isArray( p ) && p.length >= 2 ) {
+ return mediaWiki.msg.apply( mediaWiki.msg, p );
+ } else {
+ return mediaWiki.msg( p );
+ }
+ } else {
+ return '';
+ }
+ },
+ /**
+ * Provides a way to extract a property of an object in a certain language, falling back on the property keyed as
+ * 'default' or 'default-rtl'. If such key doesn't exist, the object itself is considered the actual value, which
+ * should ideally be the case so that you may use a string or object of any number of strings keyed by language
+ * with a default.
+ *
+ * @param object Object to extract property from
+ * @param lang Language code, defaults to wgUserLanguage
+ */
+ 'autoLang': function( object, lang ) {
+ var defaultKey = $( 'body' ).hasClass( 'rtl' ) ? 'default-rtl' : 'default';
+ return object[lang || mw.config.get( 'wgUserLanguage' )] || object[defaultKey] || object['default'] || object;
+ },
+ /**
+ * Provides a way to extract the path of an icon in a certain language, automatically appending a version number for
+ * caching purposes and prepending an image path when icon paths are relative.
+ *
+ * @param icon Icon object from e.g. toolbar config
+ * @param path Default icon path, defaults to $.wikiEditor.imgPath
+ * @param lang Language code, defaults to wgUserLanguage
+ */
+ 'autoIcon': function( icon, path, lang ) {
+ var src = $.wikiEditor.autoLang( icon, lang );
+ path = path || $.wikiEditor.imgPath;
+ // Prepend path if src is not absolute
+ if ( src.substr( 0, 7 ) != 'http://' && src.substr( 0, 8 ) != 'https://' && src[0] != '/' ) {
+ src = path + src;
+ }
+ return src + '?' + mw.loader.version( 'jquery.wikiEditor' );
+ },
+ /**
+ * Get the sprite offset for a language if available, icon for a language if available, or the default offset or icon,
+ * in that order of preference.
+ * @param icon Icon object, see autoIcon()
+ * @param offset Offset object
+ * @param path Icon path, see autoIcon()
+ * @param lang Language code, defaults to wgUserLanguage
+ */
+ 'autoIconOrOffset': function( icon, offset, path, lang ) {
+ lang = lang || mw.config.get( '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 );
+ }
+ }
+};
+
+/**
+ * jQuery plugin that provides a way to initialize a wikiEditor instance on a textarea.
+ */
+$.fn.wikiEditor = function() {
+
+// Skip any further work when running in browsers that are unsupported
+if ( !$.wikiEditor.isSupported() ) {
+ return $(this);
+}
+
+/* Initialization */
+
+// The wikiEditor context is stored in the element's data, so when this function gets called again we can pick up right
+// where we left off
+var context = $(this).data( 'wikiEditor-context' );
+// On first call, we need to set things up, but on all following calls we can skip right to the API handling
+if ( !context || typeof context == 'undefined' ) {
+
+ // Star filling the context with useful data - any jQuery selections, as usual should be named with a preceding $
+ context = {
+ // Reference to the textarea element which the wikiEditor is being built around
+ '$textarea': $(this),
+ // Container for any number of mutually exclusive views that are accessible by tabs
+ 'views': {},
+ // Container for any number of module-specific data - only including data for modules in use on this context
+ 'modules': {},
+ // General place to shouve bits of data into
+ 'data': {},
+ // Unique numeric ID of this instance used both for looking up and differentiating instances of wikiEditor
+ 'instance': $.wikiEditor.instances.push( $(this) ) - 1,
+ // Array mapping elements in the textarea to character offsets
+ 'offsets': null,
+ // Cache for context.fn.htmlToText()
+ 'htmlToTextMap': {},
+ // The previous HTML of the iframe, stored to detect whether something really changed.
+ 'oldHTML': null,
+ // Same for delayedChange()
+ 'oldDelayedHTML': null,
+ // The previous selection of the iframe, stored to detect whether the selection has changed
+ 'oldDelayedSel': null,
+ // Saved selection state for IE
+ 'savedSelection': null,
+ // Stack of states in { html: [string] } form
+ 'history': [],
+ // Current history state position - this is number of steps backwards, so it's always -1 or less
+ 'historyPosition': -1,
+ /// The previous historyPosition, stored to detect if change events were due to an undo or redo action
+ 'oldDelayedHistoryPosition': -1,
+ // List of extensions active on this context
+ 'extensions': []
+ };
+
+ /*
+ * Externally Accessible API
+ *
+ * These are available using calls to $(selection).wikiEditor( call, data ) where selection is a jQuery selection
+ * of the textarea that the wikiEditor instance was built around.
+ */
+
+ context.api = {
+ /**
+ * Activates a module on a specific context with optional configuration data.
+ *
+ * @param data Either a string of the name of a module to add without any additional configuration parameters,
+ * or an object with members keyed with module names and valued with configuration objects.
+ */
+ 'addModule': function( context, data ) {
+ var modules = {};
+ if ( typeof data == 'string' ) {
+ modules[data] = {};
+ } else if ( typeof data == 'object' ) {
+ modules = data;
+ }
+ for ( var module in modules ) {
+ // Check for the existance of an available / supported module with a matching name and a create function
+ if ( typeof module == 'string' && typeof $.wikiEditor.modules[module] !== 'undefined' &&
+ $.wikiEditor.isSupported( $.wikiEditor.modules[module] ) )
+ {
+ // Extend the context's core API with this module's own API calls
+ if ( 'api' in $.wikiEditor.modules[module] ) {
+ for ( var call in $.wikiEditor.modules[module].api ) {
+ // Modules may not overwrite existing API functions - first come, first serve
+ if ( !( call in context.api ) ) {
+ context.api[call] = $.wikiEditor.modules[module].api[call];
+ }
+ }
+ }
+ // Activate the module on this context
+ if ( 'fn' in $.wikiEditor.modules[module] && 'create' in $.wikiEditor.modules[module].fn ) {
+ // Add a place for the module to put it's own stuff
+ context.modules[module] = {};
+ // Tell the module to create itself on the context
+ $.wikiEditor.modules[module].fn.create( context, modules[module] );
+ }
+ }
+ }
+ }
+ };
+
+ /*
+ * Event Handlers
+ *
+ * These act as filters returning false if the event should be ignored or returning true if it should be passed
+ * on to all modules. This is also where we can attach some extra information to the events.
+ */
+
+ context.evt = {
+ /* Empty until extensions add some; see jquery.wikiEditor.iframe.js for examples. */
+ };
+
+ /* Internal Functions */
+
+ context.fn = {
+ /**
+ * Executes core event filters as well as event handlers provided by modules.
+ */
+ 'trigger': function( name, event ) {
+ // Event is an optional argument, but from here on out, at least the type field should be dependable
+ if ( typeof event == 'undefined' ) {
+ event = { 'type': 'custom' };
+ }
+ // Ensure there's a place for extra information to live
+ if ( typeof event.data == 'undefined' ) {
+ event.data = {};
+ }
+
+ // Allow filtering to occur
+ if ( name in context.evt ) {
+ if ( !context.evt[name]( event ) ) {
+ return false;
+ }
+ }
+ var returnFromModules = null; //they return null by default
+ // Pass the event around to all modules activated on this context
+
+ 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 1 returns false, the end result is false
+ if( returnFromModules == null ) {
+ returnFromModules = ret;
+ } else {
+ returnFromModules = returnFromModules && ret;
+ }
+ }
+ }
+ }
+ if ( returnFromModules != null ) {
+ return returnFromModules;
+ } else {
+ return true;
+ }
+ },
+ /**
+ * Adds a button to the UI
+ */
+ 'addButton': function( options ) {
+ // Ensure that buttons and tabs are visible
+ context.$controls.show();
+ context.$buttons.show();
+ return $( '<button />' )
+ .text( $.wikiEditor.autoMsg( options, 'caption' ) )
+ .click( options.action )
+ .appendTo( context.$buttons );
+ },
+ /**
+ * Adds a view to the UI, which is accessed using a set of tabs. Views are mutually exclusive and by default a
+ * wikitext view will be present. Only when more than one view exists will the tabs will be visible.
+ */
+ 'addView': function( options ) {
+ // Adds a tab
+ function addTab( options ) {
+ // Ensure that buttons and tabs are visible
+ context.$controls.show();
+ context.$tabs.show();
+ // Return the newly appended tab
+ return $( '<div></div>' )
+ .attr( 'rel', 'wikiEditor-ui-view-' + options.name )
+ .addClass( context.view == options.name ? 'current' : null )
+ .append( $( '<a></a>' )
+ .attr( 'href', '#' )
+ .mousedown( function() {
+ // No dragging!
+ 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 );
+ }
+ // Automatically add the previously not-needed wikitext tab
+ if ( !context.$tabs.children().size() ) {
+ addTab( { 'name': 'wikitext', 'titleMsg': 'wikieditor-wikitext-tab' } );
+ }
+ // Add the tab for the view we were actually asked to add
+ addTab( options );
+ // Return newly appended view
+ return $( '<div></div>' )
+ .addClass( 'wikiEditor-ui-view wikiEditor-ui-view-' + options.name )
+ .hide()
+ .appendTo( context.$ui );
+ },
+ /**
+ * Save scrollTop and cursor position for IE
+ */
+ 'saveCursorAndScrollTop': function() {
+ if ( $.client.profile().name === 'msie' ) {
+ var IHateIE = {
+ 'scrollTop' : context.$textarea.scrollTop(),
+ 'pos': context.$textarea.textSelection( 'getCaretPosition', { startAndEnd: true } )
+ };
+ context.$textarea.data( 'IHateIE', IHateIE );
+ }
+ },
+ /**
+ * Restore scrollTo and cursor position for IE
+ */
+ 'restoreCursorAndScrollTop': function() {
+ if ( $.client.profile().name === 'msie' ) {
+ var IHateIE = context.$textarea.data( 'IHateIE' );
+ if ( IHateIE ) {
+ context.$textarea.scrollTop( IHateIE.scrollTop );
+ context.$textarea.textSelection( 'setSelection', { start: IHateIE.pos[0], end: IHateIE.pos[1] } );
+ context.$textarea.data( 'IHateIE', null );
+ }
+ }
+ },
+ /**
+ * Save text selection for IE
+ */
+ 'saveSelection': function() {
+ if ( $.client.profile().name === 'msie' ) {
+ context.$textarea.focus();
+ context.savedSelection = document.selection.createRange();
+ }
+ },
+ /**
+ * Restore text selection for IE
+ */
+ 'restoreSelection': function() {
+ if ( $.client.profile().name === 'msie' && context.savedSelection !== null ) {
+ context.$textarea.focus();
+ context.savedSelection.select();
+ context.savedSelection = null;
+ }
+ }
+ };
+
+ /*
+ * Base UI Construction
+ *
+ * The UI is built from several containers, the outer-most being a div classed as "wikiEditor-ui". These containers
+ * provide a certain amount of "free" layout, but in some situations procedural layout is needed, which is performed
+ * as a response to the "resize" event.
+ */
+
+ // Assemble a temporary div to place over the wikiEditor while it's being constructed
+ /* Disabling our loading div for now
+ var $loader = $( '<div></div>' )
+ .addClass( 'wikiEditor-ui-loading' )
+ .append( $( '<span>' + mediaWiki.msg( 'wikieditor-loading' ) + '</span>' )
+ .css( 'marginTop', context.$textarea.height() / 2 ) );
+ */
+ // Encapsulate the textarea with some containers for layout
+ context.$textarea
+ /* Disabling our loading div for now
+ .after( $loader )
+ .add( $loader )
+ */
+ .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' ) );
+ // Get references to some of the newly created containers
+ context.$ui = context.$textarea.parent().parent().parent().parent().parent();
+ context.$wikitext = context.$textarea.parent().parent().parent().parent();
+ // Add in tab and button containers
+ 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>' ) );
+ // Get references to some of the newly created containers
+ 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' );
+ // Clear all floating after the UI
+ context.$ui.after( $( '<div style="clear:both;"></div>' ) );
+ // Attach a right container
+ context.$wikitext.append( $( '<div></div>' ).addClass( 'wikiEditor-ui-right' ) );
+ // Attach a top container to the left pane
+ context.$wikitext.find( '.wikiEditor-ui-left' ).prepend( $( '<div></div>' ).addClass( 'wikiEditor-ui-top' ) );
+ // Setup the intial view
+ context.view = 'wikitext';
+ // Trigger the "resize" event anytime the window is resized
+ $( window ).resize( function( event ) { context.fn.trigger( 'resize', event ); } );
+}
+
+/* API Execution */
+
+// Since javascript gives arguments as an object, we need to convert them so they can be used more easily
+var args = $.makeArray( arguments );
+
+// Dynamically setup core extensions for modules that are required
+if ( args[0] == 'addModule' && typeof args[1] != 'undefined' ) {
+ var modules = args[1];
+ if ( typeof modules != "object" ) {
+ modules = {};
+ modules[args[1]] = '';
+ }
+ for ( var module in modules ) {
+ // Only allow modules which are supported (and thus actually being turned on) affect the decision to extend
+ if ( module in $.wikiEditor.modules && $.wikiEditor.isSupported( $.wikiEditor.modules[module] ) ) {
+ // Activate all required core extensions on context
+ for ( var e in $.wikiEditor.extensions ) {
+ if (
+ $.wikiEditor.isRequired( $.wikiEditor.modules[module], e ) &&
+ $.inArray( e, context.extensions ) === -1
+ ) {
+ context.extensions[context.extensions.length] = e;
+ $.wikiEditor.extensions[e]( context );
+ }
+ }
+ break;
+ }
+ }
+}
+
+// There would need to be some arguments if the API is being called
+if ( args.length > 0 ) {
+ // Handle API calls
+ var call = args.shift();
+ if ( call in context.api ) {
+ context.api[call]( context, typeof args[0] == 'undefined' ? {} : args[0] );
+ }
+}
+
+// Store the context for next time, and support chaining
+return $(this).data( 'wikiEditor-context', context );
+
+}; } )( jQuery );
diff --git a/extensions/WikiEditor/modules/jquery.wikiEditor.preview.css b/extensions/WikiEditor/modules/jquery.wikiEditor.preview.css
new file mode 100644
index 00000000..7eb455b5
--- /dev/null
+++ b/extensions/WikiEditor/modules/jquery.wikiEditor.preview.css
@@ -0,0 +1,26 @@
+/*
+ * CSS for WikiEditor Preview jQuery plugin
+ */
+
+.wikiEditor-preview-loading {
+ padding: 1em;
+ background-color: white;
+}
+.wikiEditor-preview-loading span {
+ color: #666666;
+}
+.wikiEditor-preview-spinner {
+ padding-right: 1em;
+}
+.wikiEditor-preview-contents {
+ padding: 1em;
+ background-color: white;
+}
+/* FIXME: This only works for the first wikiEditor on the page! */
+#wikiEditor-0-preview-dialog .wikiEditor-ui-loading {
+ overflow: hidden;
+ border: none;
+}
+.ui-dialog .ui-dialog-buttonpane {
+ margin: 0 !important;
+} \ No newline at end of file
diff --git a/extensions/WikiEditor/modules/jquery.wikiEditor.preview.js b/extensions/WikiEditor/modules/jquery.wikiEditor.preview.js
new file mode 100644
index 00000000..634672a8
--- /dev/null
+++ b/extensions/WikiEditor/modules/jquery.wikiEditor.preview.js
@@ -0,0 +1,164 @@
+/* Preview module for wikiEditor */
+( function( $ ) { $.wikiEditor.modules.preview = {
+
+/**
+ * Compatability map
+ */
+'browsers': {
+ // Left-to-right languages
+ 'ltr': {
+ 'msie': [['>=', 7]],
+ 'firefox': [['>=', 3]],
+ 'opera': [['>=', 9.6]],
+ 'safari': [['>=', 4]]
+ },
+ // Right-to-left languages
+ 'rtl': {
+ 'msie': [['>=', 8]],
+ 'firefox': [['>=', 3]],
+ 'opera': [['>=', 9.6]],
+ 'safari': [['>=', 4]]
+ }
+},
+/**
+ * Internally used functions
+ */
+fn: {
+ /**
+ * Creates a preview module within a wikiEditor
+ * @param context Context object of editor to create module in
+ * @param config Configuration object to create module from
+ */
+ create: function( context, config ) {
+ if ( 'initialized' in context.modules.preview ) {
+ return;
+ }
+ context.modules.preview = {
+ 'initialized': true,
+ 'previewText': null,
+ 'changesText': null
+ };
+ context.modules.preview.$preview = context.fn.addView( {
+ 'name': 'preview',
+ 'titleMsg': 'wikieditor-preview-tab',
+ 'init': function( context ) {
+ // Gets the latest copy of the wikitext
+ var wikitext = context.$textarea.textSelection( 'getContents' );
+ // Aborts when nothing has changed since the last preview
+ if ( context.modules.preview.previewText == wikitext ) {
+ return;
+ }
+ context.modules.preview.$preview.find( '.wikiEditor-preview-contents' ).empty();
+ context.modules.preview.$preview.find( '.wikiEditor-preview-loading' ).show();
+ $.post(
+ mw.util.wikiScript( 'api' ),
+ {
+ 'action': 'parse',
+ 'title': mw.config.get( 'wgPageName' ),
+ 'text': wikitext,
+ 'prop': 'text',
+ 'pst': '',
+ 'format': 'json'
+ },
+ function( data ) {
+ if (
+ typeof data.parse == 'undefined' ||
+ typeof data.parse.text == 'undefined' ||
+ typeof data.parse.text['*'] == 'undefined'
+ ) {
+ return;
+ }
+ context.modules.preview.previewText = wikitext;
+ context.modules.preview.$preview.find( '.wikiEditor-preview-loading' ).hide();
+ context.modules.preview.$preview.find( '.wikiEditor-preview-contents' )
+ .html( data.parse.text['*'] )
+ .find( 'a:not([href^=#])' ).click( function() { return false; } );
+ },
+ 'json'
+ );
+ }
+ } );
+
+ context.$changesTab = context.fn.addView( {
+ 'name': 'changes',
+ 'titleMsg': 'wikieditor-preview-changes-tab',
+ 'init': function( context ) {
+ // Gets the latest copy of the wikitext
+ var wikitext = context.$textarea.textSelection( 'getContents' );
+ // Aborts when nothing has changed since the last time
+ if ( context.modules.preview.changesText == wikitext ) {
+ return;
+ }
+ context.$changesTab.find( 'table.diff tbody' ).empty();
+ context.$changesTab.find( '.wikiEditor-preview-loading' ).show();
+
+ // Call the API. First PST the input, then diff it
+ var postdata = {
+ 'action': 'parse',
+ 'onlypst': '',
+ 'text': wikitext,
+ 'format': 'json'
+ };
+
+ $.post( mw.util.wikiScript( 'api' ), postdata, function( data ) {
+ try {
+ var postdata2 = {
+ 'action': 'query',
+ 'indexpageids': '',
+ 'prop': 'revisions',
+ 'titles': mw.config.get( 'wgPageName' ),
+ 'rvdifftotext': data.parse.text['*'],
+ 'rvprop': '',
+ 'format': 'json'
+ };
+ var section = $( '[name=wpSection]' ).val();
+ if ( section != '' )
+ postdata2['rvsection'] = section;
+
+ $.post( mw.util.wikiScript( 'api' ), postdata2, function( data ) {
+ // Add diff CSS
+ mw.loader.load( 'mediawiki.action.history.diff' );
+ try {
+ var diff = data.query.pages[data.query.pageids[0]]
+ .revisions[0].diff['*'];
+ context.$changesTab.find( 'table.diff tbody' )
+ .html( diff );
+ context.$changesTab
+ .find( '.wikiEditor-preview-loading' ).hide();
+ context.modules.preview.changesText = wikitext;
+ } catch ( e ) { } // "blah is undefined" error, ignore
+ }, 'json'
+ );
+ } catch( e ) { } // "blah is undefined" error, ignore
+ }, 'json' );
+ }
+ } );
+
+ var loadingMsg = mediaWiki.msg( 'wikieditor-preview-loading' );
+ context.modules.preview.$preview
+ .add( context.$changesTab )
+ .append( $( '<div />' )
+ .addClass( 'wikiEditor-preview-loading' )
+ .append( $( '<img />' )
+ .addClass( 'wikiEditor-preview-spinner' )
+ .attr( {
+ 'src': $.wikiEditor.imgPath + 'dialogs/loading.gif',
+ 'valign': 'absmiddle',
+ 'alt': loadingMsg,
+ 'title': loadingMsg
+ } )
+ )
+ .append(
+ $( '<span></span>' ).text( loadingMsg )
+ )
+ )
+ .append( $( '<div />' )
+ .addClass( 'wikiEditor-preview-contents' )
+ );
+ context.$changesTab.find( '.wikiEditor-preview-contents' )
+ .html( '<table class="diff"><col class="diff-marker" /><col class="diff-content" />' +
+ '<col class="diff-marker" /><col class="diff-content" /><tbody /></table>' );
+ }
+}
+
+}; } )( jQuery );
diff --git a/extensions/WikiEditor/modules/jquery.wikiEditor.previewDialog.css b/extensions/WikiEditor/modules/jquery.wikiEditor.previewDialog.css
new file mode 100644
index 00000000..7a27f594
--- /dev/null
+++ b/extensions/WikiEditor/modules/jquery.wikiEditor.previewDialog.css
@@ -0,0 +1,35 @@
+/*
+ * CSS for WikiEditor Preview Dialog jQuery plugin
+ */
+
+/* FIXME: This only works for the first wikiEditor on the page! */
+#wikiEditor-0-preview-dialog .wikiEditor-ui-loading {
+ background: #f3f3f3;
+ z-index: 10;
+ position: absolute;
+ left: 0;
+ text-align: center;
+ height: 100%;
+ width: 100%;
+ overflow: hidden;
+ border: none;
+}
+/* FIXME: This only works for the first wikiEditor on the page! */
+#wikiEditor-0-preview-dialog .wikiEditor-ui-loading span {
+ display: block;
+ height: 24px;
+ width: 24px;
+ /* @embed */
+ background: url(images/toolbar/loading.gif) 0 0 no-repeat;
+ text-indent: -9999px;
+ margin: 50px auto;
+}
+.ui-dialog .ui-dialog-buttonpane {
+ margin: 0 !important;
+}
+.wikiEditor-preview-dialog-contents {
+ font-size: 0.9em !important;
+}
+.wikiEditor-preview-dialog-contents #firstHeading {
+ font-size: 2.1em;
+} \ No newline at end of file
diff --git a/extensions/WikiEditor/modules/jquery.wikiEditor.previewDialog.js b/extensions/WikiEditor/modules/jquery.wikiEditor.previewDialog.js
new file mode 100644
index 00000000..72003055
--- /dev/null
+++ b/extensions/WikiEditor/modules/jquery.wikiEditor.previewDialog.js
@@ -0,0 +1,131 @@
+/* Publish module for wikiEditor */
+( function( $ ) { $.wikiEditor.modules.previewDialog = {
+
+/**
+ * Compatability map
+ */
+'browsers': {
+ // Left-to-right languages
+ 'ltr': {
+ 'msie': [['>=', 7]],
+ 'firefox': [['>=', 3]],
+ 'opera': [['>=', 9.6]],
+ 'safari': [['>=', 4]]
+ },
+ // Right-to-left languages
+ 'rtl': {
+ 'msie': [['>=', 8]],
+ 'firefox': [['>=', 3]],
+ 'opera': [['>=', 9.6]],
+ 'safari': [['>=', 4]]
+ }
+},
+/**
+ * Internally used functions
+ */
+fn: {
+ /**
+ * Creates a publish module within a wikiEditor
+ * @param context Context object of editor to create module in
+ * @param config Configuration object to create module from
+ */
+ create: function( context, config ) {
+ // Build the dialog behind the Publish button
+ var dialogID = 'wikiEditor-' + context.instance + '-preview-dialog';
+ $.wikiEditor.modules.dialogs.fn.create(
+ context,
+ {
+ preview: {
+ id: dialogID,
+ titleMsg: 'wikieditor-preview-tab',
+ html: '\
+ <div class="wikiEditor-ui-loading"><span></span></div>\
+ <div class="wikiEditor-preview-dialog-contents"></div>\
+ ',
+ init: function() {
+ },
+ dialog: {
+ buttons: {
+ 'wikieditor-publish-dialog-publish': function() {
+ var minorChecked = $( '#wikiEditor-' + context.instance +
+ '-dialog-minor' ).is( ':checked' ) ?
+ 'checked' : '';
+ var watchChecked = $( '#wikiEditor-' + context.instance +
+ '-dialog-watch' ).is( ':checked' ) ?
+ 'checked' : '';
+ $( '#wpMinoredit' ).attr( 'checked', minorChecked );
+ $( '#wpWatchthis' ).attr( 'checked', watchChecked );
+ $( '#wpSummary' ).val( $( '#wikiEditor-' + context.instance +
+ '-dialog-summary' ).val() );
+ $( '#editform' ).submit();
+ },
+ 'wikieditor-publish-dialog-goback': function() {
+ $(this).dialog( 'close' );
+ }
+ },
+ resizable: false,
+ height: $( 'body' ).height() - 100,
+ width: $( 'body' ).width() - 300,
+ position: ['center', 'top'],
+ open: function() {
+ // Gets the latest copy of the wikitext
+ var wikitext = context.fn.getContents();
+ var $dialog = $( '#' + dialogID );
+ $dialog
+ .css( 'position', 'relative' )
+ .css( 'height', $( 'body' ).height() - 200 )
+ .parent()
+ .css( 'top', '25px' );
+ // $dialog.dialog( 'option', 'width', $( 'body' ).width() - 300 );
+ // Aborts when nothing has changed since the last preview
+ if ( context.modules.preview.previewText == wikitext ) {
+ return;
+ }
+
+ $dialog.find( '.wikiEditor-preview-dialog-contents' ).empty();
+ $dialog.find( '.wikiEditor-ui-loading' ).show();
+ $.post(
+ mw.util.wikiScript( 'api' ),
+ {
+ 'action': 'parse',
+ 'title': mw.config.get( 'wgPageName' ),
+ 'text': wikitext,
+ 'prop': 'text',
+ 'pst': '',
+ 'format': 'json'
+ },
+ function( data ) {
+ if (
+ typeof data.parse == 'undefined' ||
+ typeof data.parse.text == 'undefined' ||
+ typeof data.parse.text['*'] == 'undefined'
+ ) {
+ return;
+ }
+ context.modules.preview.previewText = wikitext;
+ $dialog.find( '.wikiEditor-ui-loading' ).hide();
+ $dialog.find( '.wikiEditor-preview-dialog-contents' )
+ .html( '<h1 class="firstHeading" id="firstHeading">' +
+ mw.config.get( 'wgTitle' ) + '</h1>' +
+ data.parse.text['*'] )
+ .find( 'a:not([href^=#])' ).click( function() { return false; } );
+ },
+ 'json'
+ );
+ }
+ },
+ resizeme: false
+ }
+ }
+ );
+ context.fn.addButton( {
+ 'captionMsg': 'wikieditor-preview-tab',
+ 'action': function() {
+ context.$textarea.wikiEditor( 'openDialog', 'preview');
+ return false;
+ }
+ } );
+ }
+}
+
+}; } )( jQuery );
diff --git a/extensions/WikiEditor/modules/jquery.wikiEditor.publish.js b/extensions/WikiEditor/modules/jquery.wikiEditor.publish.js
new file mode 100644
index 00000000..5a758e43
--- /dev/null
+++ b/extensions/WikiEditor/modules/jquery.wikiEditor.publish.js
@@ -0,0 +1,146 @@
+/* Publish module for wikiEditor */
+( function( $ ) { $.wikiEditor.modules.publish = {
+
+/**
+ * Compatability map
+ */
+'browsers': {
+ // Left-to-right languages
+ 'ltr': {
+ 'msie': [['>=', 7]],
+ 'firefox': [['>=', 3]],
+ 'opera': [['>=', 9.6]],
+ 'safari': [['>=', 4]]
+ },
+ // Right-to-left languages
+ 'rtl': {
+ 'msie': [['>=', 8]],
+ 'firefox': [['>=', 3]],
+ 'opera': [['>=', 9.6]],
+ 'safari': [['>=', 4]]
+ }
+},
+/**
+ * Internally used functions
+ */
+fn: {
+ /**
+ * Creates a publish module within a wikiEditor
+ * @param context Context object of editor to create module in
+ * @param config Configuration object to create module from
+ */
+ create: function( context, config ) {
+ // Build the dialog behind the Publish button
+ var dialogID = 'wikiEditor-' + context.instance + '-dialog';
+ $.wikiEditor.modules.dialogs.fn.create(
+ context,
+ {
+ previewsave: {
+ id: dialogID,
+ titleMsg: 'wikieditor-publish-dialog-title',
+ html: '\
+ <div class="wikiEditor-publish-dialog-copywarn"></div>\
+ <div class="wikiEditor-publish-dialog-editoptions">\
+ <form id="wikieditor-' + context.instance + '-publish-dialog-form">\
+ <div class="wikiEditor-publish-dialog-summary">\
+ <label for="wikiEditor-' + context.instance + '-dialog-summary"\
+ rel="wikieditor-publish-dialog-summary"></label>\
+ <br />\
+ <input type="text" id="wikiEditor-' + context.instance + '-dialog-summary"\
+ style="width: 100%;" />\
+ </div>\
+ <div class="wikiEditor-publish-dialog-options">\
+ <input type="checkbox"\
+ id="wikiEditor-' + context.instance + '-dialog-minor" />\
+ <label for="wikiEditor-' + context.instance + '-dialog-minor"\
+ rel="wikieditor-publish-dialog-minor"></label>\
+ <input type="checkbox"\
+ id="wikiEditor-' + context.instance + '-dialog-watch" />\
+ <label for="wikiEditor-' + context.instance + '-dialog-watch"\
+ rel="wikieditor-publish-dialog-watch"></label>\
+ </div>\
+ </form>\
+ </div>',
+ init: function() {
+ $(this).find( '[rel]' ).each( function() {
+ $(this).text( mediaWiki.msg( $(this).attr( 'rel' ) ) );
+ });
+
+ /* REALLY DIRTY HACK! */
+ // Reformat the copyright warning stuff
+ var copyWarnHTML = $( '#editpage-copywarn p' ).html();
+ // TODO: internationalize by splitting on other characters that end statements
+ var copyWarnStatements = copyWarnHTML.split( '. ' );
+ var newCopyWarnHTML = '<ul>';
+ for ( var i = 0; i < copyWarnStatements.length; i++ ) {
+ if ( copyWarnStatements[i] != '' ) {
+ var copyWarnStatement = $.trim( copyWarnStatements[i] ).replace( /\.*$/, '' );
+ newCopyWarnHTML += '<li>' + copyWarnStatement + '.</li>';
+ }
+ }
+ newCopyWarnHTML += '</ul>';
+ // No list if there's only one element
+ $(this).find( '.wikiEditor-publish-dialog-copywarn' ).html(
+ copyWarnStatements.length > 1 ? newCopyWarnHTML : copyWarnHTML
+ );
+ /* END OF REALLY DIRTY HACK */
+
+ if ( $( '#wpMinoredit' ).size() == 0 )
+ $( '#wikiEditor-' + context.instance + '-dialog-minor' ).hide();
+ else if ( $( '#wpMinoredit' ).is( ':checked' ) )
+ $( '#wikiEditor-' + context.instance + '-dialog-minor' )
+ .attr( 'checked', 'checked' );
+ if ( $( '#wpWatchthis' ).size() == 0 )
+ $( '#wikiEditor-' + context.instance + '-dialog-watch' ).hide();
+ else if ( $( '#wpWatchthis' ).is( ':checked' ) )
+ $( '#wikiEditor-' + context.instance + '-dialog-watch' )
+ .attr( 'checked', 'checked' );
+
+ $(this).find( 'form' ).submit( function( e ) {
+ $(this).closest( '.ui-dialog' ).find( 'button:first' ).click();
+ e.preventDefault();
+ });
+ },
+ dialog: {
+ buttons: {
+ 'wikieditor-publish-dialog-publish': function() {
+ var minorChecked = $( '#wikiEditor-' + context.instance +
+ '-dialog-minor' ).is( ':checked' ) ?
+ 'checked' : '';
+ var watchChecked = $( '#wikiEditor-' + context.instance +
+ '-dialog-watch' ).is( ':checked' ) ?
+ 'checked' : '';
+ $( '#wpMinoredit' ).attr( 'checked', minorChecked );
+ $( '#wpWatchthis' ).attr( 'checked', watchChecked );
+ $( '#wpSummary' ).val( $( '#wikiEditor-' + context.instance +
+ '-dialog-summary' ).val() );
+ $( '#editform' ).submit();
+ },
+ 'wikieditor-publish-dialog-goback': function() {
+ $(this).dialog( 'close' );
+ }
+ },
+ open: function() {
+ $( '#wikiEditor-' + context.instance + '-dialog-summary' ).focus();
+ },
+ width: 500
+ },
+ resizeme: false
+ }
+ }
+ );
+ context.fn.addButton( {
+ 'captionMsg': 'wikieditor-publish-button-publish',
+ 'action': function() {
+ $( '#' + dialogID ).dialog( 'open' );
+ return false;
+ }
+ } );
+ context.fn.addButton( {
+ 'captionMsg': 'wikieditor-publish-button-cancel',
+ 'action': function() { }
+ } );
+ }
+}
+
+}; } )( jQuery );
diff --git a/extensions/WikiEditor/modules/jquery.wikiEditor.templateEditor.js b/extensions/WikiEditor/modules/jquery.wikiEditor.templateEditor.js
new file mode 100644
index 00000000..794bddc7
--- /dev/null
+++ b/extensions/WikiEditor/modules/jquery.wikiEditor.templateEditor.js
@@ -0,0 +1,865 @@
+/* TemplateEditor module for wikiEditor */
+( function( $ ) { $.wikiEditor.modules.templateEditor = {
+/**
+ * Name mappings, dirty hack which will be removed once "TemplateInfo" extension is more fully supported
+ */
+'nameMappings': { //keep these all lowercase to navigate web of redirects
+ "infobox skyscraper": "building_name",
+ "infobox settlement": "official_name"
+},
+
+
+/**
+ * Compatability map
+ */
+'browsers': {
+ // Left-to-right languages
+ 'ltr': {
+ 'msie': [['>=', 8]],
+ 'firefox': [['>=', 3]],
+ 'opera': [['>=', 10]],
+ 'safari': [['>=', 4]]
+ },
+ // Right-to-left languages
+ 'rtl': {
+ 'msie': false,
+ 'firefox': [['>=', 3]],
+ 'opera': [['>=', 10]],
+ 'safari': [['>=', 4]]
+ }
+},
+/**
+ * Core Requirements
+ */
+'req': [ 'iframe' ],
+/**
+ * Event handlers
+ */
+evt: {
+
+ mark: function( context, event ) {
+ // The markers returned by this function are skipped on realchange, so don't regenerate them in that case
+ if ( context.modules.highlight.currentScope == 'realchange' ) {
+ return;
+ }
+
+ // Get references to the markers and tokens from the current context
+ var markers = context.modules.highlight.markers;
+ var tokenArray = context.modules.highlight.tokenArray;
+ // Collect matching level 0 template call boundaries from the tokenArray
+ var level = 0;
+ var tokenIndex = 0;
+ while ( tokenIndex < tokenArray.length ){
+ while ( tokenIndex < tokenArray.length && tokenArray[tokenIndex].label != 'TEMPLATE_BEGIN' ) {
+ tokenIndex++;
+ }
+ //open template
+ if ( tokenIndex < tokenArray.length ) {
+ var beginIndex = tokenIndex;
+ var endIndex = -1; //no match found
+ var openTemplates = 1;
+ var templatesMatched = false;
+ while ( tokenIndex < tokenArray.length - 1 && endIndex == -1 ) {
+ tokenIndex++;
+ if ( tokenArray[tokenIndex].label == 'TEMPLATE_BEGIN' ) {
+ openTemplates++;
+ } else if ( tokenArray[tokenIndex].label == 'TEMPLATE_END' ) {
+ openTemplates--;
+ if ( openTemplates == 0 ) {
+ endIndex = tokenIndex;
+ } //we can stop looping
+ }
+ }//while finding template ending
+ if ( endIndex != -1 ) {
+ markers.push( {
+ start: tokenArray[beginIndex].offset,
+ end: tokenArray[endIndex].offset,
+ type: 'template',
+ anchor: 'wrap',
+ afterWrap: function( node ) {
+ // Generate model
+ var model = $.wikiEditor.modules.templateEditor.fn.updateModel( $( node ) );
+ if ( model.isCollapsible() ) {
+ $.wikiEditor.modules.templateEditor.fn.wrapTemplate( $( node ) );
+ $.wikiEditor.modules.templateEditor.fn.bindTemplateEvents( $( node ) );
+ } else {
+ $( node ).addClass( 'wikiEditor-template-text' );
+ }
+ },
+ beforeUnwrap: function( node ) {
+ if ( $( node ).parent().hasClass( 'wikiEditor-template' ) ) {
+ $.wikiEditor.modules.templateEditor.fn.unwrapTemplate( $( node ) );
+ }
+ },
+ onSkip: function( node ) {
+ if ( $( node ).html() == $( node ).data( 'oldHTML' ) ) {
+ // No change
+ return;
+ }
+
+ // Text changed, regenerate model
+ var model = $.wikiEditor.modules.templateEditor.fn.updateModel( $( node ) );
+
+ // Update template name if needed
+ if ( $( node ).parent().hasClass( 'wikiEditor-template' ) ) {
+ var $label = $( node ).parent().find( '.wikiEditor-template-label' );
+ var displayName = $.wikiEditor.modules.templateEditor.fn.getTemplateDisplayName( model );
+ if ( $label.text() != displayName ) {
+ $label.text( displayName );
+ }
+ }
+
+ // Wrap or unwrap the template if needed
+ if ( $( node ).parent().hasClass( 'wikiEditor-template' ) &&
+ !model.isCollapsible() ) {
+ $.wikiEditor.modules.templateEditor.fn.unwrapTemplate( $( node ) );
+ } else if ( !$( node ).parent().hasClass( 'wikiEditor-template' ) &&
+ model.isCollapsible() ) {
+ $.wikiEditor.modules.templateEditor.fn.wrapTemplate( $( node ) );
+ $.wikiEditor.modules.templateEditor.fn.bindTemplateEvents( $( node ) );
+ }
+ },
+ getAnchor: function( ca1, ca2 ) {
+ return $( ca1.parentNode ).is( 'span.wikiEditor-template-text' ) ?
+ ca1.parentNode : null;
+ },
+ context: context,
+ skipDivision: 'realchange'
+ } );
+ } else { //else this was an unmatched opening
+ tokenArray[beginIndex].label = 'TEMPLATE_FALSE_BEGIN';
+ tokenIndex = beginIndex;
+ }
+ }//if opentemplates
+ }
+ }, //mark
+
+ keydown: function( context, event ) {
+ // Reset our ignoreKeypress variable if it's set to true
+ if ( context.$iframe.data( 'ignoreKeypress' ) ) {
+ context.$iframe.data( 'ignoreKeypress', false );
+ }
+ var $evtElem = event.jQueryNode;
+ if ( $evtElem.hasClass( 'wikiEditor-template-label' ) ) {
+ // Allow anything if the command or control key are depressed
+ if ( event.ctrlKey || event.metaKey ) return true;
+ switch ( event.which ) {
+ case 13: // Enter
+ $evtElem.click();
+ event.preventDefault();
+ return false;
+ case 32: // Space
+ $evtElem.parent().siblings( '.wikiEditor-template-expand' ).click();
+ event.preventDefault();
+ return false;
+ case 37:// Left
+ case 38:// Up
+ case 39:// Right
+ case 40: //Down
+ return true;
+ default:
+ // Set the ignroreKeypress variable so we don't allow typing if the key is held
+ context.$iframe.data( 'ignoreKeypress', true );
+ // Can't type in a template name
+ event.preventDefault();
+ return false;
+ }
+ } else if ( $evtElem.hasClass( 'wikiEditor-template-text' ) ) {
+ switch ( event.which ) {
+ case 13: // Enter
+ // Ensure that the user can't break this by holding in the enter key
+ context.$iframe.data( 'ignoreKeypress', true );
+ // FIXME: May be a more elegant way to do this, but this works too
+ context.fn.encapsulateSelection( { 'pre': '\n', 'peri': '', 'post': '' } );
+ event.preventDefault();
+ return false;
+ default: return true;
+ }
+ }
+ },
+ keyup: function( context, event ) {
+ // Rest our ignoreKeypress variable if it's set to true
+ if ( context.$iframe.data( 'ignoreKeypress' ) ) {
+ context.$iframe.data( 'ignoreKeypress', false );
+ }
+ return true;
+ },
+ keypress: function( context, event ) {
+ // If this event is from a keydown event which we want to block, ignore it
+ return ( context.$iframe.data( 'ignoreKeypress' ) ? false : true );
+ }
+},
+/**
+ * Regular expressions that produce tokens
+ */
+exp: [
+ { 'regex': /{{/, 'label': "TEMPLATE_BEGIN" },
+ { 'regex': /}}/, 'label': "TEMPLATE_END", 'markAfter': true }
+],
+/**
+ * Configuration
+ */
+cfg: {
+},
+/**
+ * Internally used functions
+ */
+fn: {
+ /**
+ * Creates template form module within wikieditor
+ * @param context Context object of editor to create module in
+ * @param config Configuration object to create module from
+ */
+ create: function( context, config ) {
+ // Initialize module within the context
+ context.modules.templateEditor = {};
+ },
+ /**
+ * Turns a simple template wrapper (really just a <span>) into a complex one
+ * @param $wrapper Wrapping <span>
+ */
+ wrapTemplate: function( $wrapper ) {
+ var model = $wrapper.data( 'model' );
+ var context = $wrapper.data( 'marker' ).context;
+ var $template = $wrapper
+ .wrap( '<span class="wikiEditor-template"></span>' )
+ .addClass( 'wikiEditor-template-text wikiEditor-template-text-shrunken' )
+ .parent()
+ .addClass( 'wikiEditor-template-collapsed' )
+ .prepend(
+ '<span class="wikiEditor-template-expand wikiEditor-noinclude"></span>' +
+ '<span class="wikiEditor-template-name wikiEditor-noinclude">' +
+ '<span class="wikiEditor-template-label wikiEditor-noinclude">' +
+ $.wikiEditor.modules.templateEditor.fn.getTemplateDisplayName( model ) + '</span>' +
+ '<span class="wikiEditor-template-dialog wikiEditor-noinclude"></span>' +
+ '</span>'
+ );
+ },
+ /**
+ * Turn a complex template wrapper back into a simple one
+ * @param $wrapper Wrapping <span>
+ */
+ unwrapTemplate: function( $wrapper ) {
+ $wrapper.parent().replaceWith( $wrapper );
+ },
+ /**
+ * Bind events to a template
+ * @param $wrapper Original wrapper for the template to bind events to
+ */
+ bindTemplateEvents: function( $wrapper ) {
+ var $template = $wrapper.parent( '.wikiEditor-template' );
+
+ if ( typeof ( opera ) == "undefined" ) {
+ $template.parent().attr('contentEditable', 'false');
+ }
+
+ $template.click( function(event) {event.preventDefault(); return false;} );
+
+ $template.find( '.wikiEditor-template-name' )
+ .click( function( event ) {
+ $.wikiEditor.modules.templateEditor.fn.createDialog( $wrapper );
+ event.stopPropagation();
+ return false;
+ } )
+ .mousedown( function( event ) { event.stopPropagation(); return false; } );
+ $template.find( '.wikiEditor-template-expand' )
+ .click( function( event ) {
+ $.wikiEditor.modules.templateEditor.fn.toggleWikiTextEditor( $wrapper );
+ event.stopPropagation();
+ return false;
+ } )
+ .mousedown( function( event ) { event.stopPropagation(); return false; } );
+ },
+ /**
+ * Toggle the visisbilty of the wikitext for a given template
+ * @param $wrapper The origianl wrapper we want expand/collapse
+ */
+ toggleWikiTextEditor: function( $wrapper ) {
+ var context = $wrapper.data( 'marker' ).context;
+ var $template = $wrapper.parent( '.wikiEditor-template' );
+ context.fn.purgeOffsets();
+ $template
+ .toggleClass( 'wikiEditor-template-expanded' )
+ .toggleClass( 'wikiEditor-template-collapsed' ) ;
+
+ var $templateText = $template.find( '.wikiEditor-template-text' );
+ $templateText.toggleClass( 'wikiEditor-template-text-shrunken' );
+ $templateText.toggleClass( 'wikiEditor-template-text-visible' );
+ if( $templateText.hasClass('wikiEditor-template-text-shrunken') ){
+ //we just closed the template
+
+ // Update the model if we need to
+ if ( $templateText.html() != $templateText.data( 'oldHTML' ) ) {
+ var templateModel = $.wikiEditor.modules.templateEditor.fn.updateModel( $templateText );
+
+ //this is the only place the template name can be changed; keep the template name in sync
+ var $tLabel = $template.find( '.wikiEditor-template-label' );
+ $tLabel.text( $.wikiEditor.modules.templateEditor.fn.getTemplateDisplayName( templateModel ) );
+ }
+
+ }
+ },
+ /**
+ * Create a dialog for editing a given template and open it
+ * @param $wrapper The origianl wrapper for which to create the dialog
+ */
+ createDialog: function( $wrapper ) {
+ var context = $wrapper.data( 'marker' ).context;
+ var $template = $wrapper.parent( '.wikiEditor-template' );
+ var dialog = {
+ 'titleMsg': 'wikieditor-template-editor-dialog-title',
+ 'id': 'wikiEditor-template-dialog',
+ 'html': '\
+ <fieldset>\
+ <div class="wikiEditor-template-dialog-title" />\
+ <div class="wikiEditor-template-dialog-fields" />\
+ </fieldset>',
+ init: function() {
+ $(this).find( '[rel]' ).each( function() {
+ $(this).text( mediaWiki.msg( $(this).attr( 'rel' ) ) );
+ } );
+ },
+ immediateCreate: true,
+ dialog: {
+ width: 600,
+ height: 400,
+ dialogClass: 'wikiEditor-toolbar-dialog',
+ buttons: {
+ 'wikieditor-template-editor-dialog-submit': function() {
+ // More user feedback
+ var $templateDiv = $( this ).data( 'templateDiv' );
+ context.fn.highlightLine( $templateDiv );
+
+ var $templateText = $templateDiv.children( '.wikiEditor-template-text' );
+ var templateModel = $templateText.data( 'model' );
+ $( this ).find( '.wikiEditor-template-dialog-field-wrapper textarea' ).each( function() {
+ // Update the value
+ templateModel.setValue( $( this ).data( 'name' ), $( this ).val() );
+ });
+ //keep text consistent
+ $.wikiEditor.modules.templateEditor.fn.updateModel( $templateText, templateModel );
+
+ $( this ).dialog( 'close' );
+ },
+ 'wikieditor-template-editor-dialog-cancel': function() {
+ $(this).dialog( 'close' );
+ }
+ },
+ open: function() {
+ var $templateDiv = $( this ).data( 'templateDiv' );
+ var $templateText = $templateDiv.children( '.wikiEditor-template-text' );
+ var templateModel = $templateText.data( 'model' );
+ // Update the model if we need to
+ if ( $templateText.html() != $templateText.data( 'oldHTML' ) ) {
+ templateModel = $.wikiEditor.modules.templateEditor.fn.updateModel( $templateText );
+ }
+
+ // Build the table
+ // TODO: Be smart and recycle existing table
+ var params = templateModel.getAllInitialParams();
+ var $fields = $( this ).find( '.wikiEditor-template-dialog-fields' );
+ // Do some bookkeeping so we can recycle existing rows
+ var $rows = $fields.find( '.wikiEditor-template-dialog-field-wrapper' );
+ for ( var paramIndex in params ) {
+ var param = params[paramIndex];
+ if ( typeof param.name == 'undefined' ) {
+ // param is the template name, skip it
+ continue;
+ }
+ var paramText = typeof param == 'string' ?
+ param.name.replace( /[\_\-]/g, ' ' ) :
+ param.name;
+ var paramVal = templateModel.getValue( param.name );
+ if ( $rows.length > 0 ) {
+ // We have another row to recycle
+ var $row = $rows.eq( 0 );
+ $row.children( 'label' ).text( paramText );
+ $row.children( 'textarea' )
+ .data( 'name', param.name )
+ .val( paramVal )
+ .each( function() {
+ $(this).css( 'height', $(this).val().length > 24 ? '4.5em' : '1.5em' );
+ } );
+ $rows = $rows.not( $row );
+ } else {
+ // Create a new row
+ var $paramRow = $( '<div />' )
+ .addClass( 'wikiEditor-template-dialog-field-wrapper' );
+ $( '<label />' )
+ .text( paramText )
+ .appendTo( $paramRow );
+ $( '<textarea />' )
+ .data( 'name', param.name )
+ .val( paramVal )
+ .each( function() {
+ $(this).css( 'height', $(this).val().length > 24 ? '4.5em' : '1.5em' );
+ } )
+ .data( 'expanded', false )
+ .bind( 'cut paste keypress click change', function( e ) {
+ // If this was fired by a tab keypress, let it go
+ if ( e.keyCode == '9' ) return true;
+ var $this = $( this );
+ setTimeout( function() {
+ var expanded = $this.data( 'expanded' );
+ if ( $this.val().indexOf( '\n' ) != -1 || $this.val().length > 24 ) {
+ if ( !expanded ) {
+ $this.animate( { 'height': '4.5em' }, 'fast' );
+ $this.data( 'expanded', true );
+ }
+ } else {
+ if ( expanded ) {
+ $this.animate( { 'height': '1.5em' }, 'fast' );
+ $this.data( 'expanded', false );
+ }
+ }
+ }, 0 );
+ } )
+ .appendTo( $paramRow );
+ $paramRow
+ .append( '<div style="clear:both"></div>' )
+ .appendTo( $fields );
+ }
+ }
+
+ // Remove any leftover rows
+ $rows.remove();
+ $fields.find( 'label' ).autoEllipsis();
+ // Ensure our close button doesn't recieve the ui-state-focus class
+ $( this ).parent( '.ui-dialog' ).find( '.ui-dialog-titlebar-close' )
+ .removeClass( 'ui-state-focus' );
+
+ // Set tabindexes on form fields if needed
+ // First unset the tabindexes on the buttons and existing form fields
+ // so the order doesn't get messed up
+ var $needTabindex = $( this ).closest( '.ui-dialog' ).find( 'button, textarea' );
+ if ( $needTabindex.not( '[tabindex]' ).length ) {
+ // Only do this if there actually are elements missing a tabindex
+ $needTabindex.removeAttr( 'tabindex' );
+ $.wikiEditor.modules.dialogs.fn.setTabindexes( $needTabindex );
+ }
+ }
+ }
+ };
+ // Lazy-create the dialog at this time
+ context.$textarea.wikiEditor( 'addDialog', { 'templateEditor': dialog } );
+ $( '#' + dialog.id )
+ .data( 'templateDiv', $template )
+ .dialog( 'open' );
+ },
+ /**
+ * Update a template's model and HTML
+ * @param $templateText Wrapper <span> containing the template text
+ * @param model Template model to use, will be generated if not set
+ * @return model object
+ */
+ updateModel: function( $templateText, model ) {
+ var context = $templateText.data( 'marker' ).context;
+ var text;
+ if ( typeof model == 'undefined' ) {
+ text = context.fn.htmlToText( $templateText.html() );
+ } else {
+ text = model.getText();
+ }
+ // To keep stuff simple but not break it, we need to do encode newlines as <br>s
+ $templateText.text( text );
+ $templateText.html( $templateText.html().replace( /\n/g, '<br />' ) );
+ $templateText.data( 'oldHTML', $templateText.html() );
+ if ( typeof model == 'undefined' ) {
+ model = new $.wikiEditor.modules.templateEditor.fn.model( text );
+ $templateText.data( 'model', model );
+ }
+ return model;
+ },
+
+ /**
+ * Gets template display name
+ */
+ getTemplateDisplayName: function ( model ) {
+ var tName = model.getName();
+ if( model.getValue( 'name' ) != '' ) {
+ return tName + ': ' + model.getValue( 'name' );
+ } else if( model.getValue( 'Name' ) != '' ) {
+ return tName + ': ' + model.getValue( 'Name' );
+ } else if( tName.toLowerCase() in $.wikiEditor.modules.templateEditor.nameMappings ) {
+ return tName + ': ' + model.getValue( $.wikiEditor.modules.templateEditor.nameMappings[tName.toLowerCase()] );
+ }
+ return tName;
+ },
+
+ /**
+ * Builds a template model from given wikitext representation, allowing object-oriented manipulation of the contents
+ * of the template while preserving whitespace and formatting.
+ *
+ * @param wikitext String of wikitext content
+ */
+ model: function( wikitext ) {
+
+ /* Private members */
+
+ var collapsible = true;
+
+ /* Private Functions */
+
+ /**
+ * Builds a Param object.
+ *
+ * @param name
+ * @param value
+ * @param number
+ * @param nameIndex
+ * @param equalsIndex
+ * @param valueIndex
+ */
+ function Param( name, value, number, nameIndex, equalsIndex, valueIndex ) {
+ this.name = name;
+ this.value = value;
+ this.number = number;
+ this.nameIndex = nameIndex;
+ this.equalsIndex = equalsIndex;
+ this.valueIndex = valueIndex;
+ }
+ /**
+ * Builds a Range object.
+ *
+ * @param begin
+ * @param end
+ */
+ function Range( begin, end ) {
+ this.begin = begin;
+ this.end = end;
+ }
+ /**
+ * Set 'original' to true if you want the original value irrespective of whether the model's been changed
+ *
+ * @param name
+ * @param value
+ * @param original
+ */
+ function getSetValue( name, value, original ) {
+ var valueRange;
+ var rangeIndex;
+ var retVal;
+ if ( isNaN( name ) ) {
+ // It's a string!
+ if ( typeof paramsByName[name] == 'undefined' ) {
+ // Does not exist
+ return "";
+ }
+ rangeIndex = paramsByName[name];
+ } else {
+ // It's a number!
+ rangeIndex = parseInt( name );
+ }
+ if ( typeof params[rangeIndex] == 'undefined' ) {
+ // Does not exist
+ return "";
+ }
+ valueRange = ranges[params[rangeIndex].valueIndex];
+ if ( typeof valueRange.newVal == 'undefined' || original ) {
+ // Value unchanged, return original wikitext
+ retVal = wikitext.substring( valueRange.begin, valueRange.end );
+ } else {
+ // New value exists, return new value
+ retVal = valueRange.newVal;
+ }
+ if ( value != null ) {
+ ranges[params[rangeIndex].valueIndex].newVal = value;
+ }
+ return retVal;
+ };
+
+ /* Public Functions */
+
+ /**
+ * Get template name
+ */
+ this.getName = function() {
+ if( typeof ranges[templateNameIndex].newVal == 'undefined' ) {
+ return wikitext.substring( ranges[templateNameIndex].begin, ranges[templateNameIndex].end );
+ } else {
+ return ranges[templateNameIndex].newVal;
+ }
+ };
+ /**
+ * Set template name (if we want to support this)
+ *
+ * @param name
+ */
+ this.setName = function( name ) {
+ ranges[templateNameIndex].newVal = name;
+ };
+ /**
+ * Set value for a given param name / number
+ *
+ * @param name
+ * @param value
+ */
+ this.setValue = function( name, value ) {
+ return getSetValue( name, value, false );
+ };
+ /**
+ * Get value for a given param name / number
+ *
+ * @param name
+ */
+ this.getValue = function( name ) {
+ return getSetValue( name, null, false );
+ };
+ /**
+ * Get original value of a param
+ *
+ * @param name
+ */
+ this.getOriginalValue = function( name ) {
+ return getSetValue( name, null, true );
+ };
+ /**
+ * Get a list of all param names (numbers for the anonymous ones)
+ */
+ this.getAllParamNames = function() {
+ return paramsByName;
+ };
+ /**
+ * Get the initial params
+ */
+ this.getAllInitialParams = function(){
+ return params;
+ };
+ /**
+ * Get original template text
+ */
+ this.getOriginalText = function() {
+ return wikitext;
+ };
+ /**
+ * Get modified template text
+ */
+ this.getText = function() {
+ newText = "";
+ for ( i = 0 ; i < ranges.length; i++ ) {
+ if( typeof ranges[i].newVal == 'undefined' ) {
+ newText += wikitext.substring( ranges[i].begin, ranges[i].end );
+ } else {
+ newText += ranges[i].newVal;
+ }
+ }
+ return newText;
+ };
+
+ this.isCollapsible = function() {
+ return collapsible;
+ };
+
+ /**
+ * Update ranges if there's been a change in one or more 'segments' of the template.
+ * Removes adjustment function so adjustment is only made once ever.
+ */
+
+ this.updateRanges = function() {
+ var adjustment = 0;
+ for (var i = 0 ; i < ranges.length; i++ ) {
+ ranges[i].begin += adjustment;
+ if( typeof ranges[i].adjust != 'undefined' ) {
+ adjustment += ranges[i].adjust();
+ // NOTE: adjust should be a function that has the information necessary to calculate the length of
+ // this 'segment'
+ delete ranges[i].adjust;
+ }
+ ranges[i].end += adjustment;
+ }
+ };
+
+ // Whitespace* {{ whitespace* nonwhitespace:
+ if ( wikitext.match( /\s*{{\s*[^\s|]*:/ ) ) {
+ collapsible = false; // is a parser function
+ }
+ /*
+ * Take all template-specific characters that are not particular to the template we're looking at, namely {|=},
+ * and convert them into something harmless, in this case 'X'
+ */
+ // Get rid of first {{ with whitespace
+ var sanatizedStr = wikitext.replace( /{{/, " " );
+ // Replace end
+ endBraces = sanatizedStr.match( /}}\s*$/ );
+ if ( endBraces ) {
+ sanatizedStr = sanatizedStr.substring( 0, endBraces.index ) + " " +
+ sanatizedStr.substring( endBraces.index + 2 );
+ }
+
+
+ //treat HTML comments like whitespace
+ while ( sanatizedStr.indexOf( '<!' ) != -1 ) {
+ startIndex = sanatizedStr.indexOf( '<!' );
+ endIndex = sanatizedStr.indexOf('-->') + 3;
+ if( endIndex < 3 ){
+ break;
+ }
+ sanatizedSegment = sanatizedStr.substring( startIndex,endIndex ).replace( /\S/g , ' ' );
+ sanatizedStr =
+ sanatizedStr.substring( 0, startIndex ) + sanatizedSegment + sanatizedStr.substring( endIndex );
+ }
+
+ // Match the open braces we just found with equivalent closing braces note, works for any level of braces
+ while ( sanatizedStr.indexOf( '{{' ) != -1 ) {
+ startIndex = sanatizedStr.indexOf( '{{' ) + 1;
+ openBraces = 2;
+ endIndex = startIndex;
+ while ( (openBraces > 0) && (endIndex < sanatizedStr.length) ) {
+ var brace = sanatizedStr[++endIndex];
+ openBraces += brace == '}' ? -1 : brace == '{' ? 1 : 0;
+ }
+ sanatizedSegment = sanatizedStr.substring( startIndex,endIndex ).replace( /[{}|=]/g , 'X' );
+ sanatizedStr =
+ sanatizedStr.substring( 0, startIndex ) + sanatizedSegment + sanatizedStr.substring( endIndex );
+ }
+ //links, images, etc, which also can nest
+ while ( sanatizedStr.indexOf( '[[' ) != -1 ) {
+ startIndex = sanatizedStr.indexOf( '[[' ) + 1;
+ openBraces = 2;
+ endIndex = startIndex;
+ while ( (openBraces > 0) && (endIndex < sanatizedStr.length) ) {
+ var brace = sanatizedStr[++endIndex];
+ openBraces += brace == ']' ? -1 : brace == '[' ? 1 : 0;
+ }
+ sanatizedSegment = sanatizedStr.substring( startIndex,endIndex ).replace( /[\[\]|=]/g , 'X' );
+ sanatizedStr =
+ sanatizedStr.substring( 0, startIndex ) + sanatizedSegment + sanatizedStr.substring( endIndex );
+ }
+
+ /*
+ * Parse 1 param at a time
+ */
+ var ranges = [];
+ var params = [];
+ var templateNameIndex = 0;
+ var doneParsing = false;
+ oldDivider = 0;
+ divider = sanatizedStr.indexOf( '|', oldDivider );
+ if ( divider == -1 ) {
+ divider = sanatizedStr.length;
+ doneParsing = true;
+ collapsible = false; //zero params
+ }
+ nameMatch = sanatizedStr.substring( 0, divider ).match( /[^\s]/ );
+ if ( nameMatch != null ) {
+ ranges.push( new Range( 0 ,nameMatch.index ) ); //whitespace and squiggles upto the name
+ nameEndMatch = sanatizedStr.substring( 0 , divider ).match( /[^\s]\s*$/ ); //last nonwhitespace character
+ templateNameIndex = ranges.push( new Range( nameMatch.index,
+ nameEndMatch.index + 1 ) );
+ templateNameIndex--; //push returns 1 less than the array
+ ranges[templateNameIndex].old = wikitext.substring( ranges[templateNameIndex].begin,
+ ranges[templateNameIndex].end );
+ } else {
+ ranges.push(new Range(0,0));
+ ranges[templateNameIndex].old = "";
+ }
+ params.push( ranges[templateNameIndex].old ); //put something in params (0)
+ /*
+ * Start looping over params
+ */
+ var currentParamNumber = 0;
+ var valueEndIndex = ranges[templateNameIndex].end;
+ var paramsByName = [];
+ while ( !doneParsing ) {
+ currentParamNumber++;
+ oldDivider = divider;
+ divider = sanatizedStr.indexOf( '|', oldDivider + 1 );
+ if ( divider == -1 ) {
+ divider = sanatizedStr.length;
+ doneParsing = true;
+ }
+ currentField = sanatizedStr.substring( oldDivider+1, divider );
+ if ( currentField.indexOf( '=' ) == -1 ) {
+ // anonymous field, gets a number
+
+ //default values, since we'll allow empty values
+ valueBeginIndex = oldDivider + 1;
+ valueEndIndex = oldDivider + 1;
+
+ valueBegin = currentField.match( /\S+/ ); //first nonwhitespace character
+ if( valueBegin != null ){
+ valueBeginIndex = valueBegin.index + oldDivider+1;
+ valueEnd = currentField.match( /[^\s]\s*$/ ); //last nonwhitespace character
+ if( valueEnd == null ){ //ie
+ continue;
+ }
+ valueEndIndex = valueEnd.index + oldDivider + 2;
+ }
+ ranges.push( new Range( ranges[ranges.length-1].end,
+ valueBeginIndex ) ); //all the chars upto now
+ nameIndex = ranges.push( new Range( valueBeginIndex, valueBeginIndex ) ) - 1;
+ equalsIndex = ranges.push( new Range( valueBeginIndex, valueBeginIndex ) ) - 1;
+ valueIndex = ranges.push( new Range( valueBeginIndex, valueEndIndex ) ) - 1;
+ params.push( new Param(
+ currentParamNumber,
+ wikitext.substring( ranges[valueIndex].begin, ranges[valueIndex].end ),
+ currentParamNumber,
+ nameIndex,
+ equalsIndex,
+ valueIndex
+ ) );
+ paramsByName[currentParamNumber] = currentParamNumber;
+ } else {
+ // There's an equals, could be comment or a value pair
+ currentName = currentField.substring( 0, currentField.indexOf( '=' ) );
+ // Still offset by oldDivider - first nonwhitespace character
+ nameBegin = currentName.match( /\S+/ );
+ if ( nameBegin == null ) {
+ // This is a comment inside a template call / parser abuse. let's not encourage it
+ currentParamNumber--;
+ continue;
+ }
+ nameBeginIndex = nameBegin.index + oldDivider + 1;
+ // Last nonwhitespace and non } character
+ nameEnd = currentName.match( /[^\s]\s*$/ );
+ if( nameEnd == null ){ //ie
+ continue;
+ }
+ nameEndIndex = nameEnd.index + oldDivider + 2;
+ // All the chars upto now
+ ranges.push( new Range( ranges[ranges.length-1].end, nameBeginIndex ) );
+ nameIndex = ranges.push( new Range( nameBeginIndex, nameEndIndex ) ) - 1;
+ currentValue = currentField.substring( currentField.indexOf( '=' ) + 1);
+ oldDivider += currentField.indexOf( '=' ) + 1;
+
+ //default values, since we'll allow empty values
+ valueBeginIndex = oldDivider + 1;
+ valueEndIndex = oldDivider + 1;
+
+ // First nonwhitespace character
+ valueBegin = currentValue.match( /\S+/ );
+ if( valueBegin != null ){
+ valueBeginIndex = valueBegin.index + oldDivider + 1;
+ // Last nonwhitespace and non } character
+ valueEnd = currentValue.match( /[^\s]\s*$/ );
+ if( valueEnd == null ){ //ie
+ continue;
+ }
+ valueEndIndex = valueEnd.index + oldDivider + 2;
+ }
+ // All the chars upto now
+ equalsIndex = ranges.push( new Range( ranges[ranges.length-1].end, valueBeginIndex) ) - 1;
+ valueIndex = ranges.push( new Range( valueBeginIndex, valueEndIndex ) ) - 1;
+ params.push( new Param(
+ wikitext.substring( nameBeginIndex, nameEndIndex ),
+ wikitext.substring( valueBeginIndex, valueEndIndex ),
+ currentParamNumber,
+ nameIndex,
+ equalsIndex,
+ valueIndex
+ ) );
+ paramsByName[wikitext.substring( nameBeginIndex, nameEndIndex )] = currentParamNumber;
+ }
+ }
+ // The rest of the string
+ ranges.push( new Range( valueEndIndex, wikitext.length ) );
+
+ // Save vars
+ this.ranges = ranges;
+ this.wikitext = wikitext;
+ this.params = params;
+ this.paramsByName = paramsByName;
+ this.templateNameIndex = templateNameIndex;
+ } //model
+}
+}; } )( jQuery );
diff --git a/extensions/WikiEditor/modules/jquery.wikiEditor.templates.js b/extensions/WikiEditor/modules/jquery.wikiEditor.templates.js
new file mode 100644
index 00000000..b303e3fa
--- /dev/null
+++ b/extensions/WikiEditor/modules/jquery.wikiEditor.templates.js
@@ -0,0 +1,69 @@
+/* Templates Module for wikiEditor */
+( function( $ ) { $.wikiEditor.modules.templates = {
+
+/**
+ * Core Requirements
+ */
+'req': [ 'iframe' ],
+/**
+ * Object Templates
+ */
+'tpl': {
+ 'marker': {
+ 'type': 'template',
+ 'anchor': 'wrap',
+ 'skipDivision': 'realchange',
+ 'afterWrap': function( node ) {
+ $( node ).addClass( 'wikiEditor-template' );
+ },
+ 'getAnchor': function( ca1, ca2 ) {
+ return $( ca1.parentNode ).is( '.wikiEditor-template' ) ? ca1.parentNode : null;
+ }
+ }
+},
+/**
+ * Event handlers
+ */
+'evt': {
+ 'mark': function( context, event ) {
+ // The markers returned by this function are skipped on realchange, so don't regenerate them in that case
+ if ( context.modules.highlight.currentScope == 'realchange' ) {
+ return;
+ }
+ // Get references to the markers and tokens from the current context
+ var markers = context.modules.highlight.markers;
+ var tokens = context.modules.highlight.tokenArray;
+ // Use depth-tracking to extract top-level templates from tokens
+ var depth = 0, bias, start;
+ for ( var i in tokens ) {
+ depth += ( bias = tokens[i].label == 'TEMPLATE_BEGIN' ? 1 : ( tokens[i].label == 'TEMPLATE_END' ? -1 : 0 ) );
+ if ( bias > 0 && depth == 1 ) {
+ // Top-level opening - use offset as start
+ start = tokens[i].offset;
+ } else if ( bias < 0 && depth == 0 ) {
+ // Top-level closing - use offset as end
+ markers[markers.length] = $.extend(
+ { 'context': context, 'start': start, 'end': tokens[i].offset },
+ $.wikiEditor.modules.templates.tpl.marker
+ );
+ }
+ if ( depth < 0 ) {
+ depth = 0;
+ }
+ }
+ }
+},
+'exp': [
+ { 'regex': /{{/, 'label': "TEMPLATE_BEGIN" },
+ { 'regex': /}}/, 'label': "TEMPLATE_END", 'markAfter': true }
+],
+/**
+ * Internally used functions
+ */
+'fn': {
+ 'create': function( context, config ) {
+ // Do some stuff here...
+ }
+}
+
+}; } ) ( jQuery );
diff --git a/extensions/WikiEditor/modules/jquery.wikiEditor.toc.css b/extensions/WikiEditor/modules/jquery.wikiEditor.toc.css
new file mode 100644
index 00000000..bb1e8775
--- /dev/null
+++ b/extensions/WikiEditor/modules/jquery.wikiEditor.toc.css
@@ -0,0 +1,177 @@
+/*
+ * CSS for WikiEditor Table of Contents jQuery plugin
+ */
+
+.wikiEditor-ui-toc {
+ /* height and width are set dynamically */
+ /*float: right;*/
+ padding: 0;
+ overflow: auto;
+ overflow-x: hidden;
+}
+.wikiEditor-ui-toc {
+ border-left: solid silver 1px;
+}
+.wikiEditor-ui-toc ul {
+ padding: 0;
+ margin: 0;
+ list-style: none;
+ /* IE needs to be told in great detail how to act, or it misbehaves */
+ list-style-image: none;
+ list-style-position: outside;
+ list-style-type: none;
+ width: 100%;
+}
+.tab-toc {
+ /* Should match the toolbar */
+ /* @embed */
+ background-image: url(images/toolbar/base.png);
+ background-position: left top;
+ background-repeat: repeat-x;
+ height: 26px;
+ padding: 3px 0;
+ line-height: 26px;
+ padding-left: 1em;
+ border-bottom: solid 1px silver;
+ white-space: nowrap;
+ overflow: hidden;
+}
+.tab-toc a {
+ outline: none;
+}
+.wikiEditor-ui-toc li {
+ padding: 0;
+ margin: 0;
+}
+.wikiEditor-ui-toc ul ul {
+ padding: 0;
+ margin: 0;
+ margin-bottom: 0 !important;
+ margin-top: 0 !important;
+ list-style: none;
+ background-image: none;
+}
+.wikiEditor-ui-toc ul li div {
+ display: block;
+ font-size: 0.9em;
+ cursor: pointer;
+ color: #0645ad;
+}
+.wikiEditor-ui-toc ul li div {
+ padding: 0.125em;
+ padding-left: 1em;
+}
+.wikiEditor-ui-toc ul ul li div {
+ padding-left: 2em;
+}
+.wikiEditor-ui-toc ul ul ul li div {
+ padding-left: 3em;
+}
+.wikiEditor-ui-toc ul ul ul ul li div {
+ padding-left: 4em;
+}
+.wikiEditor-ui-toc ul ul ul ul ul li div {
+ padding-left: 5em;
+}
+.wikiEditor-ui-toc ul ul ul ul ul ul li div {
+ padding-left: 6em;
+}
+.wikiEditor-ui-toc ul li div.current {
+ background-color: #FAFAFA;
+ color: #333333;
+}
+.wikiEditor-ui-toc ul li div.section-0 {
+ font-size: 1em;
+ padding-top: 0.5em;
+ padding-bottom: 0.5em;
+ border-bottom: solid 1px #DDDDDD;
+}
+/* Collapsing changes */
+.wikiEditor-ui-toc {
+ overflow-y: hidden;
+ position: relative;
+}
+.wikiEditor-ui-toc ul {
+ overflow-y: auto;
+ overflow-x: hidden;
+ height: 100%;
+ margin-bottom: 0 !important;
+
+}
+.wikiEditor-ui-toc ul ul {
+ float: none;
+ height: auto;
+}
+#wikiEditor-ui-toc-collapse {
+ height: 100%;
+ width: 18px;
+ position: absolute;
+ top: 0;
+ left: 0;
+}
+.wikiEditor-ui-toc-collapse-open {
+ /* @embed */
+ background: #f3f3f3 url(images/toc/close.png) 4px 50% no-repeat;
+ border-left: 1px solid #DDDDDD;
+}
+.wikiEditor-ui-toc-collapse-closed {
+ /* @embed */
+ background: #f3f3f3 url(images/toc/open.png) 4px 50% no-repeat;
+}
+/* Resizing Changes */
+.wikiEditor-ui-toc-resize-vertical,
+.ui-resizable-w {
+ width: 4px;
+ position: absolute;
+ top: 0;
+ left: 0;
+ height: 100%;
+ cursor: ew-resize;
+}
+.wikiEditor-ui .wikiEditor-ui-right {
+ overflow: visible;
+}
+.wikiEditor-ui-right .ui-resizable-w {
+ left: 0px !important;
+ z-index: 0;
+}
+.wikiEditor-ui-right .wikiEditor-ui-toc-resize-grip {
+ width: 5px;
+ height: 12px;
+ padding: 3px;
+ position: absolute;
+ top: 7px;
+ left: -12px !important;
+ cursor: ew-resize;
+ /* @embed */
+ background: url(images/toc/grip.png) 50% 50% no-repeat;
+ z-index: 0;
+}
+.wikiEditor-ui-toolbar .tab-toc {
+ float: right;
+ margin: 3px 16px 3px 3px;
+ line-height: 26px;
+}
+.wikiEditor-ui-toc-expandControl {
+ position: absolute;
+ z-index: 2;
+ top: 0px;
+ right: 10px;
+ height: 26px;
+ padding: 3px 0;
+ line-height: 26px;
+ padding-right: 1em;
+ white-space: nowrap;
+ overflow: hidden;
+}
+.wikiEditor-ui-text textarea {
+ resize: none;
+}
+.wikiEditor-ui-text textarea:focus {
+ outline: none;
+}
+/* Self Clearing for the wikiText view */
+.wikiEditor-ui-view-wikiText {
+ overflow: auto;
+ width: 100%;
+} \ No newline at end of file
diff --git a/extensions/WikiEditor/modules/jquery.wikiEditor.toc.js b/extensions/WikiEditor/modules/jquery.wikiEditor.toc.js
new file mode 100644
index 00000000..a01335e2
--- /dev/null
+++ b/extensions/WikiEditor/modules/jquery.wikiEditor.toc.js
@@ -0,0 +1,667 @@
+/* TOC Module for wikiEditor */
+( function( $ ) { $.wikiEditor.modules.toc = {
+
+/**
+ * Compatability map
+ */
+'browsers': {
+ // Left-to-right languages
+ 'ltr': {
+ 'msie': [['>=', 7]],
+ 'firefox': [['>=', 3]],
+ 'opera': [['>=', 10]],
+ 'safari': [['>=', 4]],
+ 'chrome': [['>=', 4]]
+ },
+ // Right-to-left languages
+ 'rtl': {
+ 'msie': [['>=', 8]],
+ 'firefox': [['>=', 3]],
+ 'opera': [['>=', 10]],
+ 'safari': [['>=', 4]],
+ 'chrome': [['>=', 4]]
+ }
+},
+/**
+ * Core Requirements
+ */
+'req': [ 'iframe' ],
+/**
+ * Configuration
+ */
+cfg: {
+ // Default width of table of contents
+ defaultWidth: '166px',
+ // Minimum width to allow resizing to before collapsing the table of contents - used when resizing and collapsing
+ minimumWidth: '70px',
+ // Minimum width of the wikiText area
+ textMinimumWidth: '450px',
+ // The style property to be used for positioning the flexible module in regular mode
+ flexProperty: 'marginRight',
+ // Boolean var indicating text direction
+ rtl: false
+},
+/**
+ * API accessible functions
+ */
+api: {
+ //
+},
+/**
+ * Event handlers
+ */
+evt: {
+ change: function( context, event ) {
+ $.wikiEditor.modules.toc.fn.update( context );
+ },
+ ready: function( context, event ) {
+ // Add the TOC to the document
+ $.wikiEditor.modules.toc.fn.build( context );
+ if ( !context.$content ) {
+ return;
+ }
+ context.$content.parent()
+ .blur( function() {
+ var context = event.data.context;
+ $.wikiEditor.modules.toc.fn.unhighlight( context );
+ });
+ $.wikiEditor.modules.toc.fn.improveUI();
+ $.wikiEditor.modules.toc.evt.resize( context );
+ },
+ resize: function( context, event ) {
+ var availableWidth = context.$wikitext.width() - parseFloat( $.wikiEditor.modules.toc.cfg.textMinimumWidth ),
+ totalMinWidth = parseFloat( $.wikiEditor.modules.toc.cfg.minimumWidth ) +
+ parseFloat( $.wikiEditor.modules.toc.cfg.textMinimumWidth );
+ context.$ui.find( '.wikiEditor-ui-right' )
+ .resizable( 'option', 'maxWidth', availableWidth );
+ if ( context.modules.toc.$toc.data( 'positionMode' ) != 'disabled' &&
+ context.$wikitext.width() < totalMinWidth ) {
+ $.wikiEditor.modules.toc.fn.disable( context );
+ } else if ( context.modules.toc.$toc.data( 'positionMode' ) == 'disabled' &&
+ context.$wikitext.width() > totalMinWidth ) {
+ $.wikiEditor.modules.toc.fn.enable( context );
+ } else if ( context.modules.toc.$toc.data( 'positionMode' ) == 'regular' &&
+ context.$ui.find( '.wikiEditor-ui-right' ).width() > availableWidth ) {
+ //switch mode
+ $.wikiEditor.modules.toc.fn.switchLayout( context );
+ } else if ( context.modules.toc.$toc.data( 'positionMode' ) == 'goofy' &&
+ context.modules.toc.$toc.data( 'previousWidth' ) < context.$wikitext.width() ) {
+ //switch mode
+ $.wikiEditor.modules.toc.fn.switchLayout( context );
+ }
+ if ( context.modules.toc.$toc.data( 'positionMode' ) == 'goofy' ) {
+ context.modules.toc.$toc.find( 'div' ).autoEllipsis(
+ { 'position': 'right', 'tooltip': true, 'restoreText': true }
+ );
+ }
+ // reset the height of the TOC
+ if ( !context.modules.toc.$toc.data( 'collapsed' ) ){
+ context.modules.toc.$toc.height(
+ context.$ui.find( '.wikiEditor-ui-left' ).height() -
+ context.$ui.find( '.tab-toc' ).outerHeight()
+ );
+ }
+
+ // store the width of the view for comparison on next resize
+ context.modules.toc.$toc.data( 'previousWidth', context.$wikitext.width() );
+ },
+ mark: function( context, event ) {
+ var hash = '';
+ var markers = context.modules.highlight.markers;
+ var tokenArray = context.modules.highlight.tokenArray;
+ var outline = context.data.outline = [];
+ var h = 0;
+ for ( var i = 0; i < tokenArray.length; i++ ) {
+ if ( tokenArray[i].label != 'TOC_HEADER' ) {
+ continue;
+ }
+ h++;
+ markers.push( {
+ index: h,
+ start: tokenArray[i].tokenStart,
+ end: tokenArray[i].offset,
+ type: 'toc',
+ anchor: 'tag',
+ afterWrap: function( node ) {
+ var marker = $( node ).data( 'marker' );
+ $( node ).addClass( 'wikiEditor-toc-header' )
+ .addClass( 'wikiEditor-toc-section-' + marker.index )
+ .data( 'section', marker.index );
+ },
+ beforeUnwrap: function( node ) {
+ $( node ).removeClass( 'wikiEditor-toc-header' )
+ .removeClass( 'wikiEditor-toc-section-' + $( node ).data( 'section' ) );
+ },
+ onSkip: function( node ) {
+ var marker = $( node ).data( 'marker' );
+ if ( $( node ).data( 'section' ) != marker.index ) {
+ $( node )
+ .removeClass( 'wikiEditor-toc-section-' + $( node ).data( 'section' ) )
+ .addClass( 'wikiEditor-toc-section-' + marker.index )
+ .data( 'section', marker.index );
+ }
+ },
+ getAnchor: function( ca1, ca2 ) {
+ return $( ca1.parentNode ).is( '.wikiEditor-toc-header' ) ?
+ ca1.parentNode : null;
+ }
+ } );
+ hash += tokenArray[i].match[2] + '\n';
+ outline.push ( {
+ 'text': tokenArray[i].match[2],
+ 'level': tokenArray[i].match[1].length,
+ 'index': h
+ } );
+ }
+ // Only update the TOC if it's been changed - we do this by comparing a hash of the headings this time to last
+ if ( typeof context.modules.toc.lastHash == 'undefined' || context.modules.toc.lastHash !== hash ) {
+ $.wikiEditor.modules.toc.fn.build( context );
+ $.wikiEditor.modules.toc.fn.update( context );
+ // Remember the changed version
+ context.modules.toc.lastHash = hash;
+ }
+ }
+},
+exp: [
+ { 'regex': /^(={1,6})([^\r\n]+?)\1\s*$/m, 'label': 'TOC_HEADER', 'markAfter': true }
+],
+/**
+ * Internally used functions
+ */
+fn: {
+ /**
+ * Creates a table of contents module within a wikiEditor
+ *
+ * @param {Object} context Context object of editor to create module in
+ * @param {Object} config Configuration object to create module from
+ */
+ create: function( context, config ) {
+ if ( '$toc' in context.modules.toc ) {
+ return;
+ }
+ $.wikiEditor.modules.toc.cfg.rtl = $( 'body' ).is( '.rtl' );
+ $.wikiEditor.modules.toc.cfg.flexProperty = $.wikiEditor.modules.toc.cfg.rtl ? 'marginLeft' : 'marginRight';
+ var height = context.$ui.find( '.wikiEditor-ui-left' ).height();
+ context.modules.toc.$toc = $( '<div />' )
+ .addClass( 'wikiEditor-ui-toc' )
+ .data( 'context', context )
+ .data( 'positionMode', 'regular' )
+ .data( 'collapsed', false );
+ context.$ui.find( '.wikiEditor-ui-right' )
+ .append( context.modules.toc.$toc );
+ context.modules.toc.$toc.height(
+ context.$ui.find( '.wikiEditor-ui-left' ).height()
+ );
+ $.wikiEditor.modules.toc.fn.redraw( context, $.wikiEditor.modules.toc.cfg.defaultWidth );
+ },
+ redraw: function( context, fixedWidth ) {
+ var fixedWidth = parseFloat( fixedWidth );
+ if( context.modules.toc.$toc.data( 'positionMode' ) == 'regular' ) {
+ context.$ui.find( '.wikiEditor-ui-right' )
+ .css( 'width', fixedWidth + 'px' );
+ context.$ui.find( '.wikiEditor-ui-left' )
+ .css( $.wikiEditor.modules.toc.cfg.flexProperty, ( -1 * fixedWidth ) + 'px' )
+ .children()
+ .css( $.wikiEditor.modules.toc.cfg.flexProperty, fixedWidth + 'px' );
+ } else if( context.modules.toc.$toc.data( 'positionMode' ) == 'goofy' ) {
+ context.$ui.find( '.wikiEditor-ui-left' )
+ .css( 'width', fixedWidth );
+ context.$ui.find( '.wikiEditor-ui-right' )
+ .css( $.wikiEditor.modules.toc.cfg.rtl ? 'right': 'left', fixedWidth );
+ context.$wikitext.css( 'height', context.$ui.find( '.wikiEditor-ui-right' ).height() );
+ }
+ },
+ switchLayout: function( context ) {
+ var width,
+ height = context.$ui.find( '.wikiEditor-ui-right' ).height();
+ if( context.modules.toc.$toc.data( 'positionMode' ) == 'regular'
+ && !context.modules.toc.$toc.data( 'collapsed' ) ) {
+ // store position mode
+ context.modules.toc.$toc.data( 'positionMode', 'goofy' );
+ // store the width of the TOC, to ensure we dont allow it to be larger than this when switching back
+ context.modules.toc.$toc.data( 'positionModeChangeAt',
+ context.$ui.find( '.wikiEditor-ui-right' ).width() );
+ width = $.wikiEditor.modules.toc.cfg.textMinimumWidth;
+ // set our styles for goofy mode
+ context.$ui.find( '.wikiEditor-ui-left' )
+ .css( $.wikiEditor.modules.toc.cfg.flexProperty, '')
+ .css( { 'position': 'absolute', 'float': 'none',
+ 'left': $.wikiEditor.modules.toc.cfg.rtl ? 'auto': 0,
+ 'right' : $.wikiEditor.modules.toc.cfg.rtl ? 0 : 'auto' } )
+ .children()
+ .css( $.wikiEditor.modules.toc.cfg.flexProperty, '' );
+ context.$ui.find( '.wikiEditor-ui-right' )
+ .css( { 'width': 'auto', 'position': 'absolute', 'float': 'none',
+ 'right': $.wikiEditor.modules.toc.cfg.rtl ? 'auto': 0,
+ 'left' : $.wikiEditor.modules.toc.cfg.rtl ? 0 : 'auto' } );
+ context.$wikitext
+ .css( 'position', 'relative' );
+ } else if ( context.modules.toc.$toc.data( 'positionMode' ) == 'goofy' ) {
+ // store position mode
+ context.modules.toc.$toc.data( 'positionMode', 'regular' );
+ // set width
+ width = context.$wikitext.width() - context.$ui.find( '.wikiEditor-ui-left' ).width();
+ if ( width > context.modules.toc.$toc.data( 'positionModeChangeAt' ) ) {
+ width = context.modules.toc.$toc.data( 'positionModeChangeAt' );
+ }
+ // set our styles for regular mode
+ context.$wikitext
+ .css( { 'position': '', 'height': '' } );
+ context.$ui.find( '.wikiEditor-ui-right' )
+ .css( $.wikiEditor.modules.toc.cfg.flexProperty, '' )
+ .css( { 'position': '', 'left': '', 'right': '', 'float': '', 'top': '', 'height': '' } );
+ context.$ui.find( '.wikiEditor-ui-left' )
+ .css( { 'width': '', 'position': '', 'left': '', 'float': '', 'right': '' } );
+ }
+ $.wikiEditor.modules.toc.fn.redraw( context, width );
+ },
+ disable: function( context ) {
+ if ( context.modules.toc.$toc.data( 'collapsed' ) ) {
+ context.$ui.find( '.wikiEditor-ui-toc-expandControl' ).hide();
+ } else {
+ if( context.modules.toc.$toc.data( 'positionMode' ) == 'goofy' ) {
+ $.wikiEditor.modules.toc.fn.switchLayout( context );
+ }
+ context.$ui.find( '.wikiEditor-ui-right' ).hide();
+ context.$ui.find( '.wikiEditor-ui-left' )
+ .css( $.wikiEditor.modules.toc.cfg.flexProperty, '' )
+ .children()
+ .css( $.wikiEditor.modules.toc.cfg.flexProperty, '' );
+ }
+ context.modules.toc.$toc.data( 'positionMode', 'disabled' );
+ },
+ enable: function( context ) {
+ context.modules.toc.$toc.data( 'positionMode', 'regular' );
+ if ( context.modules.toc.$toc.data( 'collapsed' ) ) {
+ context.$ui.find( '.wikiEditor-ui-toc-expandControl' ).show();
+ } else {
+ context.$ui.find( '.wikiEditor-ui-right' ).show();
+ $.wikiEditor.modules.toc.fn.redraw( context, $.wikiEditor.modules.toc.cfg.minimumWidth );
+ context.modules.toc.$toc.find( 'div' ).autoEllipsis(
+ { 'position': 'right', 'tooltip': true, 'restoreText': true }
+ );
+ }
+ },
+ unhighlight: function( context ) {
+ // FIXME: For some reason, IE calls this function twice, the first time with context undefined
+ // Investigate this when you have time please! In the meantime, the user interaction is working just
+ // fine because the second call is valid
+ if ( context ) {
+ context.modules.toc.$toc.find( 'div' ).removeClass( 'current' );
+ }
+ },
+ /**
+ * Highlight the section the cursor is currently within
+ *
+ * @param {Object} context
+ */
+ update: function( context ) {
+ //temporarily commenting this out because it is causing all kinds of cursor
+ //and text jumping issues in IE. WIll get back to this --pdhanda
+ /*
+ var div = context.fn.beforeSelection( 'wikiEditor-toc-header' );
+ if ( div === null ) {
+ // beforeSelection couldn't figure it out, keep the old highlight state
+ return;
+ }
+
+ $.wikiEditor.modules.toc.fn.unhighlight( context );
+ var section = div.data( 'section' ) || 0;
+ if ( context.data.outline.length > 0 ) {
+ var sectionLink = context.modules.toc.$toc.find( 'div.section-' + section );
+ sectionLink.addClass( 'current' );
+
+ // Scroll the highlighted link into view if necessary
+ var relTop = sectionLink.offset().top - context.modules.toc.$toc.offset().top;
+
+ var scrollTop = context.modules.toc.$toc.scrollTop();
+ var divHeight = context.modules.toc.$toc.height();
+ var sectionHeight = sectionLink.height();
+ if ( relTop < 0 )
+ // Scroll up
+ context.modules.toc.$toc.scrollTop( scrollTop + relTop );
+ else if ( relTop + sectionHeight > divHeight )
+ // Scroll down
+ context.modules.toc.$toc.scrollTop( scrollTop + relTop + sectionHeight - divHeight );
+ }
+ */
+ },
+
+ /**
+ * Collapse the contents module
+ *
+ * @param {Object} event Event object with context as data
+ */
+ collapse: function( event ) {
+ var $this = $( this ),
+ context = $this.data( 'context' );
+ if( context.modules.toc.$toc.data( 'positionMode' ) == 'goofy' ) {
+ $.wikiEditor.modules.toc.fn.switchLayout( context );
+ }
+ var pT = $this.parent().position().top - 1;
+ context.modules.toc.$toc.data( 'collapsed', true );
+ var leftParam = {}, leftChildParam = {};
+ leftParam[ $.wikiEditor.modules.toc.cfg.flexProperty ] = '-1px';
+ leftChildParam[ $.wikiEditor.modules.toc.cfg.flexProperty ] = '1px';
+ context.$ui.find( '.wikiEditor-ui-left' )
+ .animate( leftParam, 'fast', function() {
+ $( this ).css( $.wikiEditor.modules.toc.cfg.flexProperty, 0 );
+ } )
+ .children()
+ .animate( leftChildParam, 'fast', function() {
+ $( this ).css( $.wikiEditor.modules.toc.cfg.flexProperty, 0 );
+ } );
+ context.$ui.find( '.wikiEditor-ui-right' )
+ .css( {
+ 'marginTop' : '1px',
+ 'position' : 'absolute',
+ 'left' : $.wikiEditor.modules.toc.cfg.rtl ? 0 : 'auto',
+ 'right' : $.wikiEditor.modules.toc.cfg.rtl ? 'auto' : 0,
+ 'top' : pT } )
+ .fadeOut( 'fast', function() {
+ $( this ).hide()
+ .css( { 'marginTop': '0', 'width': '1px' } );
+ context.$ui.find( '.wikiEditor-ui-toc-expandControl' ).fadeIn( 'fast' );
+ // Let the UI know things have moved around
+ context.fn.trigger( 'tocCollapse' );
+ context.fn.trigger( 'resize' );
+ } );
+
+ $.cookie( 'wikiEditor-' + context.instance + '-toc-width', 0 );
+ return false;
+ },
+
+ /**
+ * Expand the contents module
+ *
+ * @param {Object} event Event object with context as data
+ */
+ expand: function( event ) {
+ var $this = $( this ),
+ context = $this.data( 'context' ),
+ openWidth = parseFloat( context.modules.toc.$toc.data( 'openWidth' ) ),
+ availableSpace = context.$wikitext.width() - parseFloat( $.wikiEditor.modules.toc.cfg.textMinimumWidth );
+ if ( availableSpace < $.wikiEditor.modules.toc.cfg.textMinmumWidth ) return false;
+ context.modules.toc.$toc.data( 'collapsed', false );
+ // check if we've got enough room to open to our stored width
+ if ( availableSpace < openWidth ) openWidth = availableSpace;
+ context.$ui.find( '.wikiEditor-ui-toc-expandControl' ).hide();
+ var leftParam = {}, leftChildParam = {};
+ leftParam[ $.wikiEditor.modules.toc.cfg.flexProperty ] = parseFloat( openWidth ) * -1;
+ leftChildParam[ $.wikiEditor.modules.toc.cfg.flexProperty ] = openWidth;
+ context.$ui.find( '.wikiEditor-ui-left' )
+ .animate( leftParam, 'fast' )
+ .children()
+ .animate( leftChildParam, 'fast' );
+ context.$ui.find( '.wikiEditor-ui-right' )
+ .show()
+ .css( 'marginTop', '1px' )
+ .animate( { 'width' : openWidth }, 'fast', function() {
+ context.$content.trigger( 'mouseup' );
+ $( this ).css( {
+ 'marginTop' : '0',
+ 'position' : 'relative',
+ 'right' : 'auto',
+ 'left' : 'auto',
+ 'top': 'auto' } );
+ context.fn.trigger( 'tocExpand' );
+ context.fn.trigger( 'resize' );
+ } );
+ $.cookie( 'wikiEditor-' + context.instance + '-toc-width',
+ context.modules.toc.$toc.data( 'openWidth' ) );
+ return false;
+ },
+ /**
+ * Builds table of contents
+ *
+ * @param {Object} context
+ */
+ build: function( context ) {
+ /**
+ * Builds a structured outline from flat outline
+ *
+ * @param {Object} outline Array of objects with level fields
+ */
+ function buildStructure( outline, offset, level ) {
+ if ( offset == undefined ) offset = 0;
+ if ( level == undefined ) level = 1;
+ var sections = [];
+ for ( var i = offset; i < outline.length; i++ ) {
+ if ( outline[i].nLevel == level ) {
+ var sub = buildStructure( outline, i + 1, level + 1 );
+ if ( sub.length ) {
+ outline[i].sections = sub;
+ }
+ sections[sections.length] = outline[i];
+ } else if ( outline[i].nLevel < level ) {
+ break;
+ }
+ }
+ return sections;
+ }
+ /**
+ * Builds unordered list HTML object from structured outline
+ *
+ * @param {Object} structure Structured outline
+ */
+ function buildList( structure ) {
+ var list = $( '<ul />' );
+ for ( var i = 0; i < structure.length; i++ ) {
+ var div = $( '<div />' )
+ .addClass( 'section-' + structure[i].index )
+ .data( 'index', structure[i].index )
+ .mousedown( function() {
+ // No dragging!
+ return false;
+ } )
+ .click( function( event ) {
+ var wrapper = context.$content.find(
+ '.wikiEditor-toc-section-' + $( this ).data( 'index' ) );
+ if ( wrapper.size() == 0 )
+ wrapper = context.$content;
+ context.fn.scrollToTop( wrapper, true );
+ context.$textarea.textSelection( 'setSelection', {
+ 'start': 0,
+ 'startContainer': wrapper
+ } );
+ // Bring user's eyes to the point we've now jumped to
+ context.fn.highlightLine( $( wrapper ) );
+ // Highlight the clicked link
+ //remove highlighting of toc after a second. Temporary hack till the highlight works --pdhanda
+ //$.wikiEditor.modules.toc.fn.unhighlight( context );
+ $( this ).addClass( 'current' );
+ //$( this ).removeClass( 'current' );
+ setTimeout( function() { $.wikiEditor.modules.toc.fn.unhighlight( context ) }, 1000 );
+
+ if ( typeof $.trackAction != 'undefined' )
+ $.trackAction( 'ntoc.heading' );
+ event.preventDefault();
+ } )
+ .text( structure[i].text );
+ if ( structure[i].text == '' )
+ div.html( '&nbsp;' );
+ var item = $( '<li />' ).append( div );
+ if ( structure[i].sections !== undefined ) {
+ item.append( buildList( structure[i].sections ) );
+ }
+ list.append( item );
+ }
+ return list;
+ }
+ /**
+ * Builds controls for collapsing and expanding the TOC
+ *
+ */
+ function buildCollapseControls( ) {
+ var $collapseControl = $( '<div />' ), $expandControl = $( '<div />' );
+ $collapseControl
+ .addClass( 'tab' )
+ .addClass( 'tab-toc' )
+ .append( '<a href="#" />' )
+ .mousedown( function( e ) {
+ // No dragging!
+ e.preventDefault();
+ return false;
+ } )
+ .bind( 'click.wikiEditor-toc', function( e ) {
+ context.modules.toc.$toc.trigger( 'collapse.wikiEditor-toc' );
+ // No dragging!
+ e.preventDefault();
+ return false;
+ } )
+ .find( 'a' )
+ .text( mediaWiki.msg( 'wikieditor-toc-hide' ) );
+ $expandControl
+ .addClass( 'wikiEditor-ui-toc-expandControl' )
+ .append( '<a href="#" />' )
+ .mousedown( function( e ) {
+ // No dragging!
+ e.preventDefault();
+ return false;
+ } )
+ .bind( 'click.wikiEditor-toc', function( e ) {
+ context.modules.toc.$toc.trigger( 'expand.wikiEditor-toc' );
+ // No dragging!
+ e.preventDefault();
+ return false;
+ } )
+ .hide()
+ .find( 'a' )
+ .text( mediaWiki.msg( 'wikieditor-toc-show' ) );
+ $collapseControl.insertBefore( context.modules.toc.$toc );
+ context.$ui.find( '.wikiEditor-ui-left .wikiEditor-ui-top' ).append( $expandControl );
+ }
+ /**
+ * Initializes resizing controls on the TOC and sets the width of
+ * the TOC based on it's previous state
+ *
+ */
+ function buildResizeControls( ) {
+ context.$ui
+ .data( 'resizableDone', true )
+ .find( '.wikiEditor-ui-right' )
+ .data( 'wikiEditor-ui-left', context.$ui.find( '.wikiEditor-ui-left' ) )
+ .resizable( { handles: 'w,e', preventPositionLeftChange: true,
+ minWidth: parseFloat( $.wikiEditor.modules.toc.cfg.minimumWidth ),
+ start: function( e, ui ) {
+ var $this = $( this );
+ // Toss a transparent cover over our iframe
+ $( '<div />' )
+ .addClass( 'wikiEditor-ui-resize-mask' )
+ .css( {
+ 'position': 'absolute',
+ 'z-index': 2,
+ 'left': 0,
+ 'top': 0,
+ 'bottom': 0,
+ 'right': 0
+ } )
+ .appendTo( context.$ui.find( '.wikiEditor-ui-left' ) );
+ $this.resizable( 'option', 'maxWidth', $this.parent().width() -
+ parseFloat( $.wikiEditor.modules.toc.cfg.textMinimumWidth ) );
+ if(context.modules.toc.$toc.data( 'positionMode' ) == 'goofy' ) {
+ $.wikiEditor.modules.toc.fn.switchLayout( context );
+ }
+ },
+ resize: function( e, ui ) {
+ // for some odd reason, ui.size.width seems a step ahead of what the *actual* width of
+ // the resizable is
+ $( this ).css( { 'width': ui.size.width, 'top': 'auto', 'height': 'auto' } )
+ .data( 'wikiEditor-ui-left' )
+ .css( $.wikiEditor.modules.toc.cfg.flexProperty, ( -1 * ui.size.width ) )
+ .children().css( $.wikiEditor.modules.toc.cfg.flexProperty, ui.size.width );
+ // Let the UI know things have moved around
+ context.fn.trigger( 'resize' );
+ },
+ stop: function ( e, ui ) {
+ context.$ui.find( '.wikiEditor-ui-resize-mask' ).remove();
+ context.$content.trigger( 'mouseup' );
+ if( ui.size.width <= parseFloat( $.wikiEditor.modules.toc.cfg.minimumWidth ) ) {
+ context.modules.toc.$toc.trigger( 'collapse.wikiEditor-toc' );
+ } else {
+ context.modules.toc.$toc.find( 'div' ).autoEllipsis(
+ { 'position': 'right', 'tooltip': true, 'restoreText': true }
+ );
+ context.modules.toc.$toc.data( 'openWidth', ui.size.width );
+ $.cookie( 'wikiEditor-' + context.instance + '-toc-width', ui.size.width );
+ }
+ // Let the UI know things have moved around
+ context.fn.trigger( 'resize' );
+ }
+ });
+ // Convert our east resize handle into a secondary west resize handle
+ var handle = $.wikiEditor.modules.toc.cfg.rtl ? 'w' : 'e';
+ context.$ui.find( '.ui-resizable-' + handle )
+ .removeClass( 'ui-resizable-' + handle )
+ .addClass( 'ui-resizable-' + ( handle == 'w' ? 'e' : 'w' ) )
+ .addClass( 'wikiEditor-ui-toc-resize-grip' );
+ // Bind collapse and expand event handlers to the TOC
+ context.modules.toc.$toc
+ .bind( 'collapse.wikiEditor-toc', $.wikiEditor.modules.toc.fn.collapse )
+ .bind( 'expand.wikiEditor-toc', $.wikiEditor.modules.toc.fn.expand );
+ context.modules.toc.$toc.data( 'openWidth', $.wikiEditor.modules.toc.cfg.defaultWidth );
+ // If the toc-width cookie is set, reset the widths based upon that
+ if ( $.cookie( 'wikiEditor-' + context.instance + '-toc-width' ) == 0 ) {
+ context.modules.toc.$toc.trigger( 'collapse.wikiEditor-toc', { data: context } );
+ } else if ( $.cookie( 'wikiEditor-' + context.instance + '-toc-width' ) > 0 ) {
+ var initialWidth = $.cookie( 'wikiEditor-' + context.instance + '-toc-width' );
+ if( initialWidth < parseFloat( $.wikiEditor.modules.toc.cfg.minimumWidth ) )
+ initialWidth = parseFloat( $.wikiEditor.modules.toc.cfg.minimumWidth ) + 1;
+ context.modules.toc.$toc.data( 'openWidth', initialWidth + 'px' );
+ $.wikiEditor.modules.toc.fn.redraw( context, initialWidth );
+ }
+ }
+
+ // Normalize heading levels for list creation
+ // This is based on Linker::generateTOC(), so it should behave like the
+ // TOC on rendered articles does - which is considdered to be correct
+ // at this point in time.
+ if ( context.data.outline ) {
+ var outline = context.data.outline;
+ var lastLevel = 0;
+ var nLevel = 0;
+ for ( var i = 0; i < outline.length; i++ ) {
+ if ( outline[i].level > lastLevel ) {
+ nLevel++;
+ }
+ else if ( outline[i].level < lastLevel ) {
+ nLevel -= Math.max( 1, lastLevel - outline[i].level );
+ }
+ if ( nLevel <= 0 ) {
+ nLevel = 1;
+ }
+ outline[i].nLevel = nLevel;
+ lastLevel = outline[i].level;
+ }
+ // Recursively build the structure and add special item for
+ // section 0, if needed
+ var structure = buildStructure( outline );
+ if ( $( 'input[name=wpSection]' ).val() == '' ) {
+ structure.unshift( { 'text': mw.config.get( 'wgPageName' ).replace( /_/g, ' ' ), 'level': 1, 'index': 0 } );
+ }
+ context.modules.toc.$toc.html( buildList( structure ) );
+
+ if ( !context.$ui.data( 'resizableDone' ) ) {
+ buildResizeControls();
+ buildCollapseControls();
+ }
+ context.modules.toc.$toc.find( 'div' ).autoEllipsis(
+ { 'position': 'right', 'tooltip': true, 'restoreText': true }
+ );
+ }
+ },
+ improveUI: function() {
+ /*
+ * Extending resizable to allow west resizing without altering the left position attribute
+ */
+ $.ui.plugin.add( "resizable", "preventPositionLeftChange", {
+ resize: function( event, ui ) {
+ $( this ).data( "resizable" ).position.left = 0;
+ }
+ } );
+ }
+}
+
+};
+
+} ) ( jQuery );
diff --git a/extensions/WikiEditor/modules/jquery.wikiEditor.toolbar.config.js b/extensions/WikiEditor/modules/jquery.wikiEditor.toolbar.config.js
new file mode 100644
index 00000000..ab92d173
--- /dev/null
+++ b/extensions/WikiEditor/modules/jquery.wikiEditor.toolbar.config.js
@@ -0,0 +1,1091 @@
+/**
+ * Configuration of Toolbar module for wikiEditor
+ */
+( function( $ ) { $.wikiEditor.modules.toolbar.config = {
+
+getDefaultConfig: function() {
+ var fileNamespace = mw.config.get( 'wgFormattedNamespaces' )[6];
+ return { 'toolbar': {
+ // Main section
+ 'main': {
+ 'type': 'toolbar',
+ 'groups': {
+ 'format': {
+ 'tools': {
+ 'bold': {
+ 'labelMsg': 'wikieditor-toolbar-tool-bold',
+ 'type': 'button',
+ 'offset': {
+ 'default': [2, -574],
+ 'en': [2, -142],
+ 'cs': [2, -142],
+ 'de': [2, -214],
+ 'fr': [2, -286],
+ 'es': [2, -358],
+ 'he': [2, -142],
+ 'hu': [2, -214],
+ 'it': [2, -286],
+ 'nl': [2, -502],
+ 'pt': [2, -358],
+ 'pt-br': [2, -358],
+ 'pl': [2, -142],
+ 'ml': [2, -142]
+ },
+ 'icon': {
+ 'default': 'format-bold.png',
+ 'en': 'format-bold-B.png',
+ 'cs': 'format-bold-B.png',
+ 'de': 'format-bold-F.png',
+ 'fr': 'format-bold-G.png',
+ 'es': 'format-bold-N.png',
+ 'he': 'format-bold-B.png',
+ 'hu': 'format-bold-F.png',
+ 'it': 'format-bold-G.png',
+ 'ka': 'format-bold-ka.png',
+ 'nl': 'format-bold-V.png',
+ 'pt': 'format-bold-N.png',
+ 'pt-br': 'format-bold-N.png',
+ 'pl': 'format-bold-B.png',
+ 'ru': 'format-bold-ru.png',
+ 'ml': 'format-bold-B.png'
+ },
+ 'action': {
+ 'type': 'encapsulate',
+ 'options': {
+ 'pre': "'''",
+ 'periMsg': 'wikieditor-toolbar-tool-bold-example',
+ 'post': "'''"
+ }
+ }
+ },
+ 'italic': {
+ 'section': 'main',
+ 'group': 'format',
+ 'id': 'italic',
+ 'labelMsg': 'wikieditor-toolbar-tool-italic',
+ 'type': 'button',
+ 'offset': {
+ 'default': [2, -718],
+ 'en': [2, -862],
+ 'cs': [2, -862],
+ 'de': [2, -934],
+ 'fr': [2, -862],
+ 'es': [2, -790],
+ 'he': [2, -862],
+ 'it': [2, -790],
+ 'nl': [2, -790],
+ 'pt': [2, -862],
+ 'pt-br': [2, -862],
+ 'pl': [2, -862],
+ 'ru': [2, -934],
+ 'ml': [2, -862]
+ },
+ 'icon': {
+ 'default': 'format-italic.png',
+ 'en': 'format-italic-I.png',
+ 'cs': 'format-italic-I.png',
+ 'de': 'format-italic-K.png',
+ 'fr': 'format-italic-I.png',
+ 'es': 'format-italic-C.png',
+ 'he': 'format-italic-I.png',
+ 'hu': 'format-italic-D.png',
+ 'it': 'format-italic-C.png',
+ 'ka': 'format-italic-ka.png',
+ 'nl': 'format-italic-C.png',
+ 'pt': 'format-italic-I.png',
+ 'pt-br': 'format-italic-I.png',
+ 'pl': 'format-italic-I.png',
+ 'ru': 'format-italic-K.png',
+ 'ml': 'format-italic-I.png'
+ },
+ 'action': {
+ 'type': 'encapsulate',
+ 'options': {
+ 'pre': "''",
+ 'periMsg': 'wikieditor-toolbar-tool-italic-example',
+ 'post': "''"
+ }
+ }
+ }
+ }
+ },
+ 'insert': {
+ 'tools': {
+ 'xlink': {
+ 'labelMsg': 'wikieditor-toolbar-tool-xlink',
+ 'type': 'button',
+ 'icon': 'insert-xlink.png',
+ 'offset': [-70, 2],
+ 'action': {
+ 'type': 'encapsulate',
+ 'options': {
+ 'pre': "[",
+ 'periMsg': 'wikieditor-toolbar-tool-xlink-example',
+ 'post': "]"
+ }
+ }
+ },
+ 'ilink': {
+ 'labelMsg': 'wikieditor-toolbar-tool-ilink',
+ 'type': 'button',
+ 'icon': 'insert-ilink.png',
+ 'offset': [2, -1582],
+ 'action': {
+ 'type': 'encapsulate',
+ 'options': {
+ 'pre': "[[",
+ 'periMsg': 'wikieditor-toolbar-tool-ilink-example',
+ 'post': "]]"
+ }
+ }
+ },
+ 'file': {
+ 'labelMsg': 'wikieditor-toolbar-tool-file',
+ 'type': 'button',
+ 'icon': 'insert-file.png',
+ 'offset': [2, -1438],
+ 'action': {
+ 'type': 'encapsulate',
+ 'options': {
+ 'pre': '[[' + fileNamespace + ':',
+ 'periMsg': 'wikieditor-toolbar-tool-file-example',
+ 'post': "]]"
+ }
+ }
+ },
+ 'reference': {
+ 'labelMsg': 'wikieditor-toolbar-tool-reference',
+ 'filters': [ 'body.ns-subject' ],
+ 'type': 'button',
+ 'offset': [2, -1798],
+ 'icon': 'insert-reference.png',
+ 'action': {
+ 'type': 'encapsulate',
+ 'options': {
+ 'pre': "<ref>",
+ 'periMsg': 'wikieditor-toolbar-tool-reference-example',
+ 'post': "</ref>"
+ }
+ }
+ },
+ 'signature': {
+ 'labelMsg': 'wikieditor-toolbar-tool-signature',
+ 'type': 'button',
+ 'offset': [2, -1872],
+ 'icon': 'insert-signature.png',
+ 'action': {
+ 'type': 'encapsulate',
+ 'options': {
+ 'pre': "--~~~~"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ // Format section
+ 'advanced': {
+ 'labelMsg': 'wikieditor-toolbar-section-advanced',
+ 'type': 'toolbar',
+ 'groups': {
+ 'heading': {
+ 'tools': {
+ 'heading': {
+ 'labelMsg': 'wikieditor-toolbar-tool-heading',
+ 'type': 'select',
+ 'list': {
+ 'heading-2' : {
+ 'labelMsg': 'wikieditor-toolbar-tool-heading-2',
+ 'action': {
+ 'type': 'encapsulate',
+ 'options': {
+ 'pre': '== ',
+ 'periMsg': 'wikieditor-toolbar-tool-heading-example',
+ 'post': ' ==',
+ 'regex': /^(\s*)(={1,6})(.*?)\2(\s*)$/,
+ 'regexReplace': "\$1==\$3==\$4",
+ 'ownline': true
+ }
+ }
+ },
+ 'heading-3' : {
+ 'labelMsg': 'wikieditor-toolbar-tool-heading-3',
+ 'action': {
+ 'type': 'encapsulate',
+ 'options': {
+ 'pre': '=== ',
+ 'periMsg': 'wikieditor-toolbar-tool-heading-example',
+ 'post': ' ===',
+ 'regex': /^(\s*)(={1,6})(.*?)\2(\s*)$/,
+ 'regexReplace': "\$1===\$3===\$4",
+ 'ownline': true
+ }
+ }
+ },
+ 'heading-4' : {
+ 'labelMsg': 'wikieditor-toolbar-tool-heading-4',
+ 'action': {
+ 'type': 'encapsulate',
+ 'options': {
+ 'pre': '==== ',
+ 'periMsg': 'wikieditor-toolbar-tool-heading-example',
+ 'post': ' ====',
+ 'regex': /^(\s*)(={1,6})(.*?)\2(\s*)$/,
+ 'regexReplace': "\$1====\$3====\$4",
+ 'ownline': true
+ }
+ }
+ },
+ 'heading-5' : {
+ 'labelMsg': 'wikieditor-toolbar-tool-heading-5',
+ 'action': {
+ 'type': 'encapsulate',
+ 'options': {
+ 'pre': '===== ',
+ 'periMsg': 'wikieditor-toolbar-tool-heading-example',
+ 'post': ' =====',
+ 'regex': /^(\s*)(={1,6})(.*?)\2(\s*)$/,
+ 'regexReplace': "\$1=====\$3=====\$4",
+ 'ownline': true
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ 'format': {
+ 'labelMsg': 'wikieditor-toolbar-group-format',
+ 'tools': {
+ 'ulist': {
+ 'labelMsg': 'wikieditor-toolbar-tool-ulist',
+ 'type': 'button',
+ 'icon': {
+ 'default': 'format-ulist.png',
+ 'default-rtl': 'format-ulist-rtl.png'
+ },
+ 'offset': {
+ 'default': [2, -1366],
+ 'default-rtl': [-70, -286]
+ },
+ 'action': {
+ 'type': 'encapsulate',
+ 'options': {
+ 'pre': "* ",
+ 'periMsg': 'wikieditor-toolbar-tool-ulist-example',
+ 'post': "",
+ 'ownline': true,
+ 'splitlines': true
+ }
+ }
+ },
+ 'olist': {
+ 'labelMsg': 'wikieditor-toolbar-tool-olist',
+ 'type': 'button',
+ 'icon': {
+ 'default': 'format-olist.png',
+ 'default-rtl': 'format-olist-rtl.png'
+ },
+ 'offset': {
+ 'default': [2, -1078],
+ 'default-rtl': [-70, -358]
+ },
+ 'action': {
+ 'type': 'encapsulate',
+ 'options': {
+ 'pre': "# ",
+ 'periMsg': 'wikieditor-toolbar-tool-olist-example',
+ 'post': "",
+ 'ownline': true,
+ 'splitlines': true
+ }
+ }
+ },
+ 'indent': {
+ 'labelMsg': 'wikieditor-toolbar-tool-indent',
+ 'type': 'button',
+ 'icon': {
+ 'default': 'format-indent.png',
+ 'default-rtl': 'format-indent-rtl.png'
+ },
+ 'offset': {
+ 'default': [2, -646],
+ 'default-rtl': [-70, -430]
+ },
+ 'action': {
+ 'type': 'encapsulate',
+ 'options': {
+ 'pre': ":",
+ 'periMsg': 'wikieditor-toolbar-tool-indent-example',
+ 'post': "",
+ 'ownline': true,
+ 'splitlines': true
+ }
+ }
+ },
+ 'nowiki': {
+ 'labelMsg': 'wikieditor-toolbar-tool-nowiki',
+ 'type': 'button',
+ 'icon': 'insert-nowiki.png',
+ 'offset': [-70, -70],
+ 'action': {
+ 'type': 'encapsulate',
+ 'options': {
+ 'pre': "<nowiki>",
+ 'periMsg': 'wikieditor-toolbar-tool-nowiki-example',
+ 'post': "</nowiki>"
+ }
+ }
+ },
+ 'newline': {
+ 'labelMsg': 'wikieditor-toolbar-tool-newline',
+ 'type': 'button',
+ 'icon': 'insert-newline.png',
+ 'offset': [2, -1726],
+ 'action': {
+ 'type': 'encapsulate',
+ 'options': {
+ 'pre': "<br />\n"
+ }
+ }
+ }
+ }
+ },
+ 'size': {
+ 'tools': {
+ 'big': {
+ 'labelMsg': 'wikieditor-toolbar-tool-big',
+ 'type': 'button',
+ 'icon': 'format-big.png',
+ 'offset': [2, 2],
+ 'action': {
+ 'type': 'encapsulate',
+ 'options': {
+ 'pre': "<big>",
+ 'periMsg': 'wikieditor-toolbar-tool-big-example',
+ 'post': "</big>"
+ }
+ }
+ },
+ 'small': {
+ 'labelMsg': 'wikieditor-toolbar-tool-small',
+ 'type': 'button',
+ 'icon': 'format-small.png',
+ 'offset': [2, -1150],
+ 'action': {
+ 'type': 'encapsulate',
+ 'options': {
+ 'pre': "<small>",
+ 'periMsg': 'wikieditor-toolbar-tool-small-example',
+ 'post': "</small>"
+ }
+ }
+ },
+ 'superscript': {
+ 'labelMsg': 'wikieditor-toolbar-tool-superscript',
+ 'type': 'button',
+ 'icon': 'format-superscript.png',
+ 'offset': [2, -1294],
+ 'action': {
+ 'type': 'encapsulate',
+ 'options': {
+ 'pre': "<sup>",
+ 'periMsg': 'wikieditor-toolbar-tool-superscript-example',
+ 'post': "</sup>"
+ }
+ }
+ },
+ 'subscript': {
+ 'labelMsg': 'wikieditor-toolbar-tool-subscript',
+ 'type': 'button',
+ 'icon': 'format-subscript.png',
+ 'offset': [2, -1222],
+ 'action': {
+ 'type': 'encapsulate',
+ 'options': {
+ 'pre': "<sub>",
+ 'periMsg': 'wikieditor-toolbar-tool-subscript-example',
+ 'post': "</sub>"
+ }
+ }
+ }
+ }
+ },
+ 'insert': {
+ 'labelMsg': 'wikieditor-toolbar-group-insert',
+ 'tools': {
+ 'gallery': {
+ 'labelMsg': 'wikieditor-toolbar-tool-gallery',
+ 'type': 'button',
+ 'icon': 'insert-gallery.png',
+ 'offset': [2, -1510],
+ 'action': {
+ 'type': 'encapsulate',
+ 'options': {
+ 'pre': "<gallery>\n",
+ 'periMsg': [
+ 'wikieditor-toolbar-tool-gallery-example', fileNamespace
+ ],
+ 'post': "\n</gallery>",
+ 'ownline': true
+ }
+ }
+ },
+ 'table': {
+ 'labelMsg': 'wikieditor-toolbar-tool-table',
+ 'type': 'button',
+ 'icon': 'insert-table.png',
+ 'offset': [2, -1942],
+ 'filters': [ '#wpTextbox1:not(.toolbar-dialogs)' ],
+ 'action': {
+ 'type': 'encapsulate',
+ 'options': {
+ 'pre': "{| class=\"wikitable\" border=\"1\"\n|",
+ 'periMsg': 'wikieditor-toolbar-tool-table-example-old',
+ 'post': "\n|}",
+ 'ownline': true
+ }
+ }
+ },
+ 'redirect': {
+ 'labelMsg': 'wikieditor-toolbar-tool-redirect',
+ 'type': 'button',
+ 'icon': {
+ 'default': 'insert-redirect.png',
+ 'default-rtl': 'insert-redirect-rtl.png'
+ },
+ 'offset': {
+ 'default': [-70, -142],
+ 'default-rtl': [-70, -502]
+ },
+ 'action': {
+ 'type': 'encapsulate',
+ 'options': {
+ 'pre': "#REDIRECT [[",
+ 'periMsg': 'wikieditor-toolbar-tool-redirect-example',
+ 'post': "]]",
+ 'ownline': true
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ 'characters': {
+ 'labelMsg': 'wikieditor-toolbar-section-characters',
+ 'type': 'booklet',
+ 'deferLoad': true,
+ 'pages': {
+ 'latin': {
+ 'labelMsg': 'wikieditor-toolbar-characters-page-latin',
+ 'layout': 'characters',
+ 'characters': [
+ "\u00c1", "\u00e1", "\u00c0", "\u00e0", "\u00c2", "\u00e2", "\u00c4", "\u00e4", "\u00c3",
+ "\u00e3", "\u01cd", "\u01ce", "\u0100", "\u0101", "\u0102", "\u0103", "\u0104", "\u0105",
+ "\u00c5", "\u00e5", "\u0106", "\u0107", "\u0108", "\u0109", "\u00c7", "\u00e7", "\u010c",
+ "\u010d", "\u010a", "\u010b", "\u0110", "\u0111", "\u010e", "\u010f", "\u00c9", "\u00e9",
+ "\u00c8", "\u00e8", "\u00ca", "\u00ea", "\u00cb", "\u00eb", "\u011a", "\u011b", "\u0112",
+ "\u0113", "\u0114", "\u0115", "\u0116", "\u0117", "\u0118", "\u0119", "\u011c", "\u011d",
+ "\u0122", "\u0123", "\u011e", "\u011f", "\u0120", "\u0121", "\u0124", "\u0125", "\u0126",
+ "\u0127", "\u00cd", "\u00ed", "\u00cc", "\u00ec", "\u00ce", "\u00ee", "\u00cf", "\u00ef",
+ "\u0128", "\u0129", "\u01cf", "\u01d0", "\u012a", "\u012b", "\u012c", "\u012d", "\u0130",
+ "\u0131", "\u012e", "\u012f", "\u0134", "\u0135", "\u0136", "\u0137", "\u0139", "\u013a",
+ "\u013b", "\u013c", "\u013d", "\u013e", "\u0141", "\u0142", "\u013f", "\u0140", "\u0143",
+ "\u0144", "\u00d1", "\u00f1", "\u0145", "\u0146", "\u0147", "\u0148", "\u00d3", "\u00f3",
+ "\u00d2", "\u00f2", "\u00d4", "\u00f4", "\u00d6", "\u00f6", "\u00d5", "\u00f5", "\u01d1",
+ "\u01d2", "\u014c", "\u014d", "\u014e", "\u014f", "\u01ea", "\u01eb", "\u0150", "\u0151",
+ "\u0154", "\u0155", "\u0156", "\u0157", "\u0158", "\u0159", "\u015a", "\u015b", "\u015c",
+ "\u015d", "\u015e", "\u015f", "\u0160", "\u0161", "\u0162", "\u0163", "\u0164", "\u0165",
+ "\u00da", "\u00fa", "\u00d9", "\u00f9", "\u00db", "\u00fb", "\u00dc", "\u00fc", "\u0168",
+ "\u0169", "\u016e", "\u016f", "\u01d3", "\u01d4", "\u016a", "\u016b", "\u01d6", "\u01d8",
+ "\u01da", "\u01dc", "\u016c", "\u016d", "\u0172", "\u0173", "\u0170", "\u0171", "\u0174",
+ "\u0175", "\u00dd", "\u00fd", "\u0176", "\u0177", "\u0178", "\u00ff", "\u0232", "\u0233",
+ "\u0179", "\u017a", "\u017d", "\u017e", "\u017b", "\u017c", "\u00c6", "\u00e6", "\u01e2",
+ "\u01e3", "\u00d8", "\u00f8", "\u0152", "\u0153", "\u00df", "\u00f0", "\u00de", "\u00fe",
+ "\u018f", "\u0259"
+ ]
+ },
+ 'latinextended': {
+ 'labelMsg': 'wikieditor-toolbar-characters-page-latinextended',
+ 'layout': 'characters',
+ 'characters': [
+ "\u1e00", "\u1e01", "\u1e9a", "\u1ea0", "\u1ea1", "\u1ea2", "\u1ea3", "\u1ea4", "\u1ea5",
+ "\u1ea6", "\u1ea7", "\u1ea8", "\u1ea9", "\u1eaa", "\u1eab", "\u1eac", "\u1ead", "\u1eae",
+ "\u1eaf", "\u1eb0", "\u1eb1", "\u1eb2", "\u1eb3", "\u1eb4", "\u1eb5", "\u1eb6", "\u1eb7",
+ "\u1e02", "\u1e03", "\u1e04", "\u1e05", "\u1e06", "\u1e07", "\u1e08", "\u1e09", "\u1e0a",
+ "\u1e0b", "\u1e0c", "\u1e0d", "\u1e0e", "\u1e0f", "\u1e10", "\u1e11", "\u1e12", "\u1e13",
+ "\u1e14", "\u1e15", "\u1e16", "\u1e17", "\u1e18", "\u1e19", "\u1e1a", "\u1e1b", "\u1e1c",
+ "\u1e1d", "\u1eb8", "\u1eb9", "\u1eba", "\u1ebb", "\u1ebc", "\u1ebd", "\u1ebe", "\u1ebf",
+ "\u1ec0", "\u1ec1", "\u1ec2", "\u1ec3", "\u1ec4", "\u1ec5", "\u1ec6", "\u1ec7", "\u1e1e",
+ "\u1e1f", "\u1e20", "\u1e21", "\u1e22", "\u1e23", "\u1e24", "\u1e25", "\u1e26", "\u1e27",
+ "\u1e28", "\u1e29", "\u1e2a", "\u1e2b", "\u1e96", "\u1e2c", "\u1e2d", "\u1e2e", "\u1e2f",
+ "\u1ec8", "\u1ec9", "\u1eca", "\u1ecb", "\u1e30", "\u1e31", "\u1e32", "\u1e33", "\u1e34",
+ "\u1e35", "\u1e36", "\u1e37", "\u1e38", "\u1e39", "\u1e3a", "\u1e3b", "\u1e3c", "\u1e3d",
+ "\u1efa", "\u1efb", "\u1e3e", "\u1e3f", "\u1e40", "\u1e41", "\u1e42", "\u1e43", "\u1e44",
+ "\u1e45", "\u1e46", "\u1e47", "\u1e48", "\u1e49", "\u1e4a", "\u1e4b", "\u1e4c", "\u1e4d",
+ "\u1e4e", "\u1e4f", "\u1e50", "\u1e51", "\u1e52", "\u1e53", "\u1ecc", "\u1ecd", "\u1ece",
+ "\u1ecf", "\u1ed0", "\u1ed1", "\u1ed2", "\u1ed3", "\u1ed4", "\u1ed5", "\u1ed6", "\u1ed7",
+ "\u1ed8", "\u1ed9", "\u1eda", "\u1edb", "\u1edc", "\u1edd", "\u1ede", "\u1edf", "\u1ee0",
+ "\u1ee1", "\u1ee2", "\u1ee3", "\u1e54", "\u1e55", "\u1e56", "\u1e57", "\u1e58", "\u1e59",
+ "\u1e5a", "\u1e5b", "\u1e5c", "\u1e5d", "\u1e5e", "\u1e5f", "\u1e60", "\u1e61", "\u1e9b",
+ "\u1e62", "\u1e63", "\u1e64", "\u1e65", "\u1e66", "\u1e67", "\u1e68", "\u1e69", "\u1e9c",
+ "\u1e9d", "\u1e6a", "\u1e6b", "\u1e6c", "\u1e6d", "\u1e6e", "\u1e6f", "\u1e70", "\u1e71",
+ "\u1e97", "\u1e72", "\u1e73", "\u1e74", "\u1e75", "\u1e76", "\u1e77", "\u1e78", "\u1e79",
+ "\u1e7a", "\u1e7b", "\u1ee4", "\u1ee5", "\u1ee6", "\u1ee7", "\u1ee8", "\u1ee9", "\u1eea",
+ "\u1eeb", "\u1eec", "\u1eed", "\u1eee", "\u1eef", "\u1ef0", "\u1ef1", "\u1e7c", "\u1e7d",
+ "\u1e7e", "\u1e7f", "\u1efc", "\u1efd", "\u1e80", "\u1e81", "\u1e82", "\u1e83", "\u1e84",
+ "\u1e85", "\u1e86", "\u1e87", "\u1e88", "\u1e89", "\u1e98", "\u1e8a", "\u1e8b", "\u1e8c",
+ "\u1e8d", "\u1e8e", "\u1e8f", "\u1e99", "\u1ef2", "\u1ef3", "\u1ef4", "\u1ef5", "\u1ef6",
+ "\u1ef7", "\u1ef8", "\u1ef9", "\u1efe", "\u1eff", "\u1e90", "\u1e91", "\u1e92", "\u1e93",
+ "\u1e94", "\u1e95", "\u1e9e", "\u1e9f"
+ ]
+ },
+ 'ipa': {
+ 'labelMsg': 'wikieditor-toolbar-characters-page-ipa',
+ 'layout': 'characters',
+ 'characters': [
+ "p", "t\u032a", "t", "\u0288", "c", "k", "q", "\u02a1", "\u0294", "b","d\u032a", "d", "\u0256",
+ "\u025f", "\u0261", "\u0262", "\u0253", "\u0257", "\u0284", "\u0260", "\u029b", "t\u0361s",
+ "t\u0361\u0283", "t\u0361\u0255", "d\u0361z", "d\u0361\u0292", "d\u0361\u0291", "\u0278", "f",
+ "\u03b8", "s", "\u0283", "\u0285", "\u0286", "\u0282", "\u0255", "\u00e7", "\u0267", "x",
+ "\u03c7", "\u0127", "\u029c", "h", "\u03b2", "v", "\u028d", "\u00f0", "z", "\u0292", "\u0293",
+ "\u0290", "\u0291", "\u029d", "\u0263", "\u0281", "\u0295", "\u0296", "\u02a2", "\u0266",
+ "\u026c", "\u026e", "m", "m\u0329", "\u0271", "\u0271\u0329", "\u0271\u030d", "n\u032a",
+ "n\u032a\u030d", "n", "n\u0329", "\u0273", "\u0273\u0329", "\u0272", "\u0272\u0329", "\u014b",
+ "\u014b\u030d", "\u014b\u0329", "\u0274", "\u0274\u0329", "\u0299", "\u0299\u0329", "r",
+ "r\u0329", "\u0280", "\u0280\u0329", "\u027e", "\u027d", "\u027f", "\u027a", "l\u032a",
+ "l\u032a\u0329", "l", "l\u0329", "\u026b", "\u026b\u0329", "\u026d", "\u026d\u0329", "\u028e",
+ "\u028e\u0329", "\u029f", "\u029f\u0329", "w", "\u0265", "\u028b", "\u0279", "\u027b", "j",
+ "\u0270", "\u0298", "\u01c2", "\u01c0", "!", "\u01c1", "\u02b0", "\u02b1", "\u02b7", "\u02b8",
+ "\u02b2", "\u02b3", "\u207f", "\u02e1", "\u02b4", "\u02b5", "\u02e2", "\u02e3", "\u02e0",
+ "\u02b6", "\u02e4", "\u02c1", "\u02c0", "\u02bc", "i", "i\u032f", "\u0129", "y", "y\u032f",
+ "\u1ef9", "\u026a", "\u026a\u032f", "\u026a\u0303", "\u028f", "\u028f\u032f", "\u028f\u0303",
+ "\u0268", "\u0268\u032f", "\u0268\u0303", "\u0289", "\u0289\u032f", "\u0289\u0303", "\u026f",
+ "\u026f\u032f", "\u026f\u0303", "u", "u\u032f", "\u0169", "\u028a", "\u028a\u032f",
+ "\u028a\u0303", "e", "e\u032f", "\u1ebd", "\u00f8", "\u00f8\u032f", "\u00f8\u0303", "\u0258",
+ "\u0258\u032f", "\u0258\u0303", "\u0275", "\u0275\u032f", "\u0275\u0303", "\u0264",
+ "\u0264\u032f", "\u0264\u0303", "o", "o\u032f", "\u00f5", "\u025b", "\u025b\u032f",
+ "\u025b\u0303", "\u0153", "\u0153\u032f", "\u0153\u0303", "\u025c", "\u025c\u032f",
+ "\u025c\u0303", "\u0259", "\u0259\u032f", "\u0259\u0303", "\u025e", "\u025e\u032f",
+ "\u025e\u0303", "\u028c", "\u028c\u032f", "\u028c\u0303", "\u0254", "\u0254\u032f",
+ "\u0254\u0303", "\u00e6", "\u00e6\u032f", "\u00e6\u0303", "\u0276", "\u0276\u032f",
+ "\u0276\u0303", "a", "a\u032f", "\u00e3", "\u0250", "\u0250\u032f", "\u0250\u0303", "\u0251",
+ "\u0251\u032f", "\u0251\u0303", "\u0252", "\u0252\u032f", "\u0252\u0303", "\u02c8", "\u02cc",
+ "\u02d0", "\u02d1", "\u02d8", ".", "\u203f", "|", "\u2016"
+ ]
+ },
+ 'symbols': {
+ 'labelMsg': 'wikieditor-toolbar-characters-page-symbols',
+ 'layout': 'characters',
+ 'characters': [
+ "~", "|", "\u00a1", "\u00bf", "\u2020", "\u2021", "\u2194", "\u2191", "\u2193", "\u2022",
+ "\u00b6", "#", "\u00bd", "\u2153", "\u2154", "\u00bc", "\u00be", "\u215b", "\u215c", "\u215d",
+ "\u215e", "\u221e", "\u2018", "\u2019",
+ {
+ 'label': "\u201c\u201d",
+ 'action': {
+ 'type': 'encapsulate', 'options': { 'pre': "\u201c", 'post': "\u201d" }
+ }
+ },
+ {
+ 'label': "\u201e\u201c",
+ 'action': {
+ 'type': 'encapsulate', 'options': { 'pre': "\u201e", 'post': "\u201c" }
+ }
+ },
+ {
+ 'label': "\u201e\u201d",
+ 'action': {
+ 'type': 'encapsulate', 'options': { 'pre': "\u201e", 'post': "\u201d" }
+ }
+ },
+ {
+ 'label': "\u00ab\u00bb",
+ 'action': {
+ 'type': 'encapsulate', 'options': { 'pre': "\u00ab", 'post': "\u00bb" }
+ }
+ },
+ "\u00a4", "\u20b3", "\u0e3f", "\u20b5", "\u00a2", "\u20a1", "\u20a2", "$", "\u20ab", "\u20af",
+ "\u20ac", "\u20a0", "\u20a3", "\u0192", "\u20b4", "\u20ad", "\u20a4", "\u2133", "\u20a5",
+ "\u20a6", "\u2116", "\u20a7", "\u20b0", "\u00a3", "\u17db", "\u20a8", "\u20aa", "\u09f3",
+ "\u20ae", "\u20a9", "\u00a5", "\u2660", "\u2663", "\u2665", "\u2666", "m\u00b2", "m\u00b3",
+ "\u2013", "\u2014", "\u2026", "\u2018", "\u2019", "\u201c", "\u201d", "\u00b0", "\u2032",
+ "\u2033", "\u2248", "\u2260", "\u2264", "\u2265", "\u00b1", "\u2212", "\u00d7", "\u00f7",
+ "\u2190", "\u2192", "\u00b7", "\u00a7"
+ ]
+ },
+ 'greek': {
+ 'labelMsg': 'wikieditor-toolbar-characters-page-greek',
+ 'layout': 'characters',
+ 'language': 'hl',
+ 'characters': [
+ "\u0391", "\u0386", "\u03b1", "\u03ac", "\u0392", "\u03b2", "\u0393", "\u03b3", "\u0394",
+ "\u03b4", "\u0395", "\u0388", "\u03b5", "\u03ad", "\u0396", "\u03b6", "\u0397", "\u0389",
+ "\u03b7", "\u03ae", "\u0398", "\u03b8", "\u0399", "\u038a", "\u03b9", "\u03af", "\u039a",
+ "\u03ba", "\u039b", "\u03bb", "\u039c", "\u03bc", "\u039d", "\u03bd", "\u039e", "\u03be",
+ "\u039f", "\u038c", "\u03bf", "\u03cc", "\u03a0", "\u03c0", "\u03a1", "\u03c1", "\u03a3",
+ "\u03c3", "\u03c2", "\u03a4", "\u03c4", "\u03a5", "\u038e", "\u03c5", "\u03cd", "\u03a6",
+ "\u03c6", "\u03a7", "\u03c7", "\u03a8", "\u03c8", "\u03a9", "\u038f", "\u03c9", "\u03ce"
+ ]
+ },
+ 'cyrillic': {
+ 'labelMsg': 'wikieditor-toolbar-characters-page-cyrillic',
+ 'layout': 'characters',
+ 'characters': [
+ "\u0410", "\u0430", "\u04d8", "\u04d9", "\u0411", "\u0431", "\u0412", "\u0432", "\u0413",
+ "\u0433", "\u0490", "\u0491", "\u0403", "\u0453", "\u0492", "\u0493", "\u0414", "\u0434",
+ "\u0402", "\u0452", "\u0415", "\u0435", "\u0404", "\u0454", "\u0401", "\u0451", "\u0416",
+ "\u0436", "\u0417", "\u0437", "\u0405", "\u0455", "\u0418", "\u0438", "\u0406", "\u0456",
+ "\u0407", "\u0457", "\u04c0", "\u0419", "\u0439", "\u04e2", "\u04e3", "\u0408", "\u0458",
+ "\u041a", "\u043a", "\u040c", "\u045c", "\u049a", "\u049b", "\u041b", "\u043b", "\u0409",
+ "\u0459", "\u041c", "\u043c", "\u041d", "\u043d", "\u040a", "\u045a", "\u04a2", "\u04a3",
+ "\u041e", "\u043e", "\u04e8", "\u04e9", "\u041f", "\u043f", "\u0420", "\u0440", "\u0421",
+ "\u0441", "\u0422", "\u0442", "\u040b", "\u045b", "\u0423", "\u0443", "\u040e", "\u045e",
+ "\u04ee", "\u04ef", "\u04b0", "\u04b1", "\u04ae", "\u04af", "\u0424", "\u0444", "\u0425",
+ "\u0445", "\u04b2", "\u04b3", "\u04ba", "\u04bb", "\u0426", "\u0446", "\u0427", "\u0447",
+ "\u04b6", "\u04b7", "\u040f", "\u045f", "\u0428", "\u0448", "\u0429", "\u0449", "\u042a",
+ "\u044a", "\u042b", "\u044b", "\u042c", "\u044c", "\u042d", "\u044d", "\u042e", "\u044e",
+ "\u042f", "\u044f"
+ ]
+ },
+ // The core 28-letter alphabet, special letters for the Arabic language,
+ // vowels, punctuation, digits.
+ // Names of letters are written as in the Unicode charts.
+ 'arabic': {
+ 'labelMsg': 'wikieditor-toolbar-characters-page-arabic',
+ 'layout': 'characters',
+ 'language': 'ar',
+ 'direction': 'rtl',
+ 'characters': [
+ // core alphabet
+ "\u0627", "\u0628", "\u062a", "\u062b", "\u062c", "\u062d", "\u062e", "\u062f",
+ "\u0630", "\u0631", "\u0632", "\u0633", "\u0634", "\u0635", "\u0636", "\u0637",
+ "\u0638", "\u0639", "\u063a", "\u0641", "\u0642", "\u0643", "\u0644", "\u0645",
+ "\u0646", "\u0647", "\u0648", "\u064a",
+ // special letters for the Arabic language
+ "\u0621", // Hamza
+ "\u0622", "\u0623", "\u0625", "\u0671", // Alef
+ "\u0624", // Waw hamza
+ "\u0626", // Yeh hamza
+ "\u0649", // Alef maksura
+ "\u0629", // Teh marbuta
+ // vowels
+ "\u064E", "\u064F", "\u0650", "\u064B", "\u064C", "\u064D", "\u0651", "\u0652",
+ "\u0670",
+ // punctuation
+ "\u060c", "\u061b", "\u061f", "\u0640",
+ // digits
+ "\u0660", "\u0661", "\u0662", "\u0663", "\u0664", "\u0665", "\u0666", "\u0667",
+ "\u0668", "\u0669", "\u066A", "\u066B", "\u066C", "\u066D"
+ ]
+ },
+ // Characters for languages other than Arabic.
+ 'arabicextended': {
+ 'labelMsg': 'wikieditor-toolbar-characters-page-arabicextended',
+ 'layout': 'characters',
+ 'language': 'ar',
+ 'direction': 'rtl',
+ 'characters': [
+ // Alef
+ "\u0672", "\u0673", "\u0674", "\u0675", "\u0773", "\u0774",
+ // Beh
+ "\u066E", "\u067B", "\u067E", "\u0680", "\u0750", "\u0751", "\u0752", "\u0753",
+ "\u0754", "\u0755", "\u0756",
+ // Teh
+ "\u0679", "\u067A", "\u067C", "\u067D", "\u067F",
+ // Jeem
+ "\u0681", "\u0682", "\u0683", "\u0684", "\u0685", "\u0686", "\u0687", "\u06BF",
+ // Hah
+ "\u0757", "\u0758", "\u076E", "\u076F", "\u0772", "\u077C",
+ // Dal
+ "\u0688", "\u0689", "\u068A", "\u068B", "\u068C", "\u068D", "\u068E", "\u068F",
+ "\u0690", "\u06EE", "\u0759", "\u075A",
+ // Reh
+ "\u0691", "\u0692", "\u0693", "\u0694", "\u0695", "\u0696", "\u0697", "\u0698",
+ "\u0699", "\u06EF", "\u075B", "\u076B", "\u076C", "\u0771",
+ // Seen
+ "\u069A", "\u069B", "\u069C", "\u077D",
+ // Sheen
+ "\u06FA", "\u075C", "\u076D", "\u0770", "\u077E",
+ // Sad
+ "\u069D", "\u069E",
+ // Dad
+ "\u06FB",
+ // Tah
+ "\u069F",
+ // Ain
+ "\u06A0", "\u075D", "\u075E", "\u075F",
+ // Ghain
+ "\u06FC",
+ // Feh
+ "\u06A1", "\u06A2", "\u06A3", "\u06A4", "\u06A5", "\u06A6", "\u0760", "\u0761",
+ // Qaf
+ "\u066F", "\u06A7", "\u06A8",
+ // Kaf
+ "\u063B", "\u063C", "\u06A9", "\u06AA", "\u06AB", "\u06AC", "\u06AD", "\u06AE",
+ "\u06AF", "\u06B0", "\u06B1", "\u06B2", "\u06B3", "\u06B4", "\u0762", "\u0763",
+ "\u0764", "\u077F",
+ // Lam
+ "\u06B5", "\u06B6", "\u06B7", "\u06B8", "\u076A",
+ // Meem
+ "\u0765", "\u0766",
+ // Noon
+ "\u06B9", "\u06BA", "\u06BB", "\u06BC", "\u06BD", "\u0767", "\u0768", "\u0769",
+ // Heh
+ "\u06BE", "\u06C0", "\u06C1", "\u06C2", "\u06C3", "\u06D5", "\u06FF",
+ // Waw
+ "\u0676", "\u0677", "\u06C4", "\u06C5", "\u06C6", "\u06C7", "\u06C8", "\u06C9",
+ "\u06CA", "\u06CB", "\u06CF", "\u0778", "\u0779",
+ // Yeh
+ "\u0620", "\u063D", "\u063E", "\u063F", "\u0678", "\u06CC", "\u06CD", "\u06CE",
+ "\u06D0", "\u06D1", "\u06D2", "\u06D3", "\u0775", "\u0776", "\u0777", "\u077A",
+ "\u077B",
+ // diacritics
+ "\u0656", "\u0657", "\u0658", "\u0659", "\u065A", "\u065B", "\u065C", "\u065D",
+ "\u065E", "\u065F",
+ // special punctuation
+ "\u06D4", "\u06FD", "\u06FE",
+ // special digits
+ "\u06F0", "\u06F1", "\u06F2", "\u06F3", "\u06F4", "\u06F5", "\u06F6", "\u06F7",
+ "\u06F8", "\u06F9",
+ ]
+ },
+ 'hebrew': {
+ 'labelMsg': 'wikieditor-toolbar-characters-page-hebrew',
+ 'layout': 'characters',
+ 'direction': 'rtl',
+ 'characters': [
+ "\u05d0", "\u05d1", "\u05d2", "\u05d3", "\u05d4", "\u05d5", "\u05d6", "\u05d7", "\u05d8",
+ "\u05d9", "\u05db", "\u05da", "\u05dc", "\u05de", "\u05dd", "\u05e0", "\u05df", "\u05e1",
+ "\u05e2", "\u05e4", "\u05e3", "\u05e6", "\u05e5", "\u05e7", "\u05e8", "\u05e9", "\u05ea",
+ "\u05f3", "\u05f4", "\u05f0", "\u05f1", "\u05f2", "\u05be",
+ [ "\u05b0\u25cc", "\u05b0" ], [ "\u05b1\u25cc", "\u05b1" ], [ "\u05b2\u25cc", "\u05b2" ],
+ [ "\u05b3\u25cc", "\u05b3" ], [ "\u05b4\u25cc", "\u05b4" ], [ "\u05b5\u25cc", "\u05b5" ],
+ [ "\u05b6\u25cc", "\u05b6" ], [ "\u05b7\u25cc", "\u05b7" ], [ "\u05b8\u25cc", "\u05b8" ],
+ [ "\u05b9\u25cc", "\u05b9" ], [ "\u05bb\u25cc", "\u05bb" ], [ "\u05bc\u25cc", "\u05bc" ],
+ [ "\u05c1\u25cc", "\u05c1" ], [ "\u05c2\u25cc", "\u05c2" ], [ "\u05c7\u25cc", "\u05c7" ],
+ [ "\u0591\u25cc", "\u0591" ], [ "\u0592\u25cc", "\u0592" ], [ "\u0593\u25cc", "\u0593" ],
+ [ "\u0594\u25cc", "\u0594" ], [ "\u0595\u25cc", "\u0595" ], [ "\u0596\u25cc", "\u0596" ],
+ [ "\u0597\u25cc", "\u0597" ], [ "\u0598\u25cc", "\u0598" ], [ "\u0599\u25cc", "\u0599" ],
+ [ "\u059a\u25cc", "\u059a" ], [ "\u059b\u25cc", "\u059b" ], [ "\u059c\u25cc", "\u059c" ],
+ [ "\u059d\u25cc", "\u059d" ], [ "\u059e\u25cc", "\u059e" ], [ "\u059f\u25cc", "\u059f" ],
+ [ "\u05a0\u25cc", "\u05a0" ], [ "\u05a1\u25cc", "\u05a1" ], [ "\u05a2\u25cc", "\u05a2" ],
+ [ "\u05a3\u25cc", "\u05a3" ], [ "\u05a4\u25cc", "\u05a4" ], [ "\u05a5\u25cc", "\u05a5" ],
+ [ "\u05a6\u25cc", "\u05a6" ], [ "\u05a7\u25cc", "\u05a7" ], [ "\u05a8\u25cc", "\u05a8" ],
+ [ "\u05a9\u25cc", "\u05a9" ], [ "\u05aa\u25cc", "\u05aa" ], [ "\u05ab\u25cc", "\u05ab" ],
+ [ "\u05ac\u25cc", "\u05ac" ], [ "\u05ad\u25cc", "\u05ad" ], [ "\u05ae\u25cc", "\u05ae" ],
+ [ "\u05af\u25cc", "\u05af" ], [ "\u05bf\u25cc", "\u05bf" ], [ "\u05c0\u25cc", "\u05c0" ],
+ [ "\u05c3\u25cc", "\u05c3" ]
+ ]
+ },
+ 'bangla': {
+ 'labelMsg': 'wikieditor-toolbar-characters-page-bangla',
+ 'language': 'bn',
+ 'layout': 'characters',
+ 'characters': [
+ "\u0985", "\u0986", "\u0987", "\u0988", "\u0989", "\u098a", "\u098b", "\u098f", "\u0990",
+ "\u0993", "\u0994", "\u09be", "\u09bf", "\u09c0", "\u09c1", "\u09c2", "\u09c3", "\u09c7",
+ "\u09c8", "\u09cb", "\u09cc", "\u0995", "\u0996", "\u0997", "\u0998", "\u0999", "\u099a",
+ "\u099b", "\u099c", "\u099d", "\u099e", "\u099f", "\u09a0", "\u09a1", "\u09a2", "\u09a3",
+ "\u09a4", "\u09a5", "\u09a6", "\u09a7", "\u09a8", "\u09aa", "\u09ab", "\u09ac", "\u09ad",
+ "\u09ae", "\u09af", "\u09b0", "\u09b2", "\u09b6", "\u09b7", "\u09b8", "\u09b9", "\u09a1\u09bc",
+ "\u09a2\u09bc", "\u09af\u09bc", "\u09ce", "\u0982", "\u0983", "\u0981", "\u09cd", "\u09e7",
+ "\u09e8", "\u09e9", "\u09ea", "\u09eb", "\u09ec", "\u09ed", "\u09ee", "\u09ef", "\u09e6"
+ ]
+ },
+ 'telugu': {
+ 'labelMsg': 'wikieditor-toolbar-characters-page-telugu',
+ 'language': 'te',
+ 'layout': 'characters',
+ 'characters': [
+ "\u0c01", "\u0c02", "\u0c03", "\u0c05", "\u0c06", "\u0c07", "\u0c08", "\u0c09", "\u0c0a",
+ "\u0c0b", "\u0c60", "\u0c0c", "\u0c61", "\u0c0e", "\u0c0f", "\u0c10", "\u0c12", "\u0c13",
+ "\u0c14", "\u0c15", "\u0c16", "\u0c17", "\u0c18", "\u0c19", "\u0c1a", "\u0c1b", "\u0c1c",
+ "\u0c1d", "\u0c1e", "\u0c1f", "\u0c20", "\u0c21", "\u0c22", "\u0c23", "\u0c24", "\u0c25",
+ "\u0c26", "\u0c27", "\u0c28", "\u0c2a", "\u0c2b", "\u0c2c", "\u0c2d", "\u0c2e", "\u0c2f",
+ "\u0c30", "\u0c31", "\u0c32", "\u0c33", "\u0c35", "\u0c36", "\u0c37", "\u0c38", "\u0c39",
+ "\u0c3e", "\u0c3f", "\u0c40", "\u0c41", "\u0c42", "\u0c43", "\u0c44", "\u0c46", "\u0c47",
+ "\u0c48", "\u0c4a", "\u0c4b", "\u0c4c", "\u0c4d", "\u0c62", "\u0c63", "\u0c58", "\u0c59",
+ "\u0c66", "\u0c67", "\u0c68", "\u0c69", "\u0c6a", "\u0c6b", "\u0c6c", "\u0c6d", "\u0c6e",
+ "\u0c6f", "\u0c3d", "\u0c78", "\u0c79", "\u0c7a", "\u0c7b", "\u0c7c", "\u0c7d", "\u0c7e",
+ "\u0c7f"
+ ]
+ },
+ 'sinhala': {
+ 'labelMsg': 'wikieditor-toolbar-characters-page-sinhala',
+ 'language': 'si',
+ 'layout': 'characters',
+ 'characters': [
+ "\u0d85", "\u0d86", "\u0d87", "\u0d88", "\u0d89", "\u0d8a", "\u0d8b", "\u0d8c", "\u0d8d",
+ "\u0d8e", "\u0d8f", "\u0d90", "\u0d91", "\u0d92", "\u0d93", "\u0d94", "\u0d95", "\u0d96",
+ "\u0d9a", "\u0d9b", "\u0d9c", "\u0d9d", "\u0d9e", "\u0d9f", "\u0da0", "\u0da1", "\u0da2",
+ "\u0da3", "\u0da4", "\u0da5", "\u0da6", "\u0da7", "\u0da8", "\u0da9", "\u0daa", "\u0dab",
+ "\u0dac", "\u0dad", "\u0dae", "\u0daf", "\u0db0", "\u0db1", "\u0db3", "\u0db4", "\u0db5",
+ "\u0db6", "\u0db7", "\u0db8", "\u0db9", "\u0dba", "\u0dbb", "\u0dbd", "\u0dc0", "\u0dc1",
+ "\u0dc2", "\u0dc3", "\u0dc4", "\u0dc5", "\u0dc6",
+ [ "\u25cc\u0dcf", "\u0dcf" ], [ "\u25cc\u0dd0", "\u0dd0" ], [ "\u25cc\u0dd1", "\u0dd1" ],
+ [ "\u25cc\u0dd2", "\u0dd2" ], [ "\u25cc\u0dd3", "\u0dd3" ], [ "\u25cc\u0dd4", "\u0dd4" ],
+ [ "\u25cc\u0dd6", "\u0dd6" ], [ "\u25cc\u0dd8", "\u0dd8" ], [ "\u25cc\u0df2", "\u0df2" ],
+ [ "\u25cc\u0ddf", "\u0ddf" ], [ "\u25cc\u0df3", "\u0df3" ], [ "\u25cc\u0dd9", "\u0dd9" ],
+ [ "\u25cc\u0dda", "\u0dda" ], [ "\u25cc\u0ddc", "\u0ddc" ], [ "\u25cc\u0ddd", "\u0ddd" ],
+ [ "\u25cc\u0dde", "\u0dde" ], [ "\u25cc\u0dca", "\u0dca" ]
+ ]
+ },
+ 'gujarati': {
+ 'labelMsg': 'wikieditor-toolbar-characters-page-gujarati',
+ 'language': 'gu',
+ 'layout': 'characters',
+ 'characters': [
+ "\u0ad0", "\u0a85", "\u0a86", "\u0a87", "\u0a88", "\u0a89", "\u0a8a", "\u0a8b", "\u0ae0",
+ "\u0a8c", "\u0ae1", "\u0a8d", "\u0a8f", "\u0a90", "\u0a91", "\u0a93", "\u0a94", "\u0a95",
+ "\u0a96", "\u0a97", "\u0a98", "\u0a99", "\u0a9a", "\u0a9b", "\u0a9c", "\u0a9d", "\u0a9e",
+ "\u0a9f", "\u0aa0", "\u0aa1", "\u0aa2", "\u0aa3", "\u0aa4", "\u0aa5", "\u0aa6", "\u0aa7",
+ "\u0aa8", "\u0aaa", "\u0aab", "\u0aac", "\u0aad", "\u0aae", "\u0aaf", "\u0ab0", "\u0ab2",
+ "\u0ab5", "\u0ab6", "\u0ab7", "\u0ab8", "\u0ab9", "\u0ab3", "\u0abd",
+ [ "\u25cc\u0abe", "\u0abe" ], [ "\u25cc\u0abf", "\u0abf" ], [ "\u25cc\u0ac0", "\u0ac0" ],
+ [ "\u25cc\u0ac1", "\u0ac1" ], [ "\u25cc\u0ac2", "\u0ac2" ], [ "\u25cc\u0ac3", "\u0ac3" ],
+ [ "\u25cc\u0ac4", "\u0ac4" ], [ "\u25cc\u0ae2", "\u0ae2" ], [ "\u25cc\u0ae3", "\u0ae3" ],
+ [ "\u25cc\u0ac5", "\u0ac5" ], [ "\u25cc\u0ac7", "\u0ac7" ], [ "\u25cc\u0ac8", "\u0ac8" ],
+ [ "\u25cc\u0ac9", "\u0ac9" ], [ "\u25cc\u0acb", "\u0acb" ], [ "\u25cc\u0acc", "\u0acc" ],
+ [ "\u25cc\u0acd", "\u0acd" ]
+ ]
+ },
+ 'thai': {
+ 'labelMsg': 'wikieditor-toolbar-characters-page-thai',
+ 'language': 'th',
+ 'layout': 'characters',
+ 'characters': [
+ "\u0e01", "\u0e02", "\u0e03", "\u0e04", "\u0e05", "\u0e06", "\u0e07", "\u0e08", "\u0e09",
+ "\u0e0a", "\u0e0b", "\u0e0c", "\u0e0d", "\u0e0e", "\u0e0f", "\u0e10", "\u0e11", "\u0e12",
+ "\u0e13", "\u0e14", "\u0e15", "\u0e16", "\u0e17", "\u0e18", "\u0e19", "\u0e1a", "\u0e1b",
+ "\u0e1c", "\u0e1d", "\u0e1e", "\u0e1f", "\u0e20", "\u0e21", "\u0e22", "\u0e23", "\u0e24",
+ "\u0e25", "\u0e26", "\u0e27", "\u0e28", "\u0e29", "\u0e2a", "\u0e2b", "\u0e2c", "\u0e2d",
+ "\u0e2e", "\u0e30", "\u0e31", "\u0e32", "\u0e45", "\u0e33", "\u0e34", "\u0e35", "\u0e36",
+ "\u0e37", "\u0e38", "\u0e39", "\u0e40", "\u0e41", "\u0e42", "\u0e43", "\u0e44", "\u0e47",
+ "\u0e48", "\u0e49", "\u0e4a", "\u0e4b", "\u0e4c", "\u0e4d", "\u0e3a", "\u0e4e", "\u0e50",
+ "\u0e51", "\u0e52", "\u0e53", "\u0e54", "\u0e55", "\u0e56", "\u0e57", "\u0e58", "\u0e59",
+ "\u0e3f", "\u0e46", "\u0e2f", "\u0e5a", "\u0e4f", "\u0e5b"
+ ]
+ },
+ 'lao': {
+ 'labelMsg': 'wikieditor-toolbar-characters-page-lao',
+ 'language': 'lo',
+ 'layout': 'characters',
+ 'characters': [
+ "\u0e81", "\u0e82", "\u0e84", "\u0e87", "\u0e88", "\u0eaa", "\u0e8a", "\u0e8d", "\u0e94",
+ "\u0e95", "\u0e96", "\u0e97", "\u0e99", "\u0e9a", "\u0e9b", "\u0e9c", "\u0e9d", "\u0e9e",
+ "\u0e9f", "\u0ea1", "\u0ea2", "\u0ea5", "\u0ea7", "\u0eab", "\u0ead", "\u0eae", "\u0ea3",
+ "\u0edc", "\u0edd", "\u0ebc", "\u0ebd", "\u0eb0", "\u0eb1", "\u0eb2", "\u0eb3", "\u0eb4",
+ "\u0eb5", "\u0eb6", "\u0eb7", "\u0eb8", "\u0eb9", "\u0ebb", "\u0ec0", "\u0ec1", "\u0ec2",
+ "\u0ec3", "\u0ec4", "\u0ec8", "\u0ec9", "\u0eca", "\u0ecb", "\u0ecc", "\u0ecd", "\u0ed0",
+ "\u0ed1", "\u0ed2", "\u0ed3", "\u0ed4", "\u0ed5", "\u0ed6", "\u0ed7", "\u0ed8", "\u0ed9",
+ "\u20ad", "\u0ec6", "\u0eaf"
+ ]
+ },
+ 'khmer': {
+ 'labelMsg': 'wikieditor-toolbar-characters-page-khmer',
+ 'language': 'km',
+ 'layout': 'characters',
+ 'characters': [
+ "\u1780", "\u1781", "\u1782", "\u1783", "\u1784", "\u1785", "\u1786", "\u1787", "\u1788",
+ "\u1789", "\u178a", "\u178b", "\u178c", "\u178d", "\u178e", "\u178f", "\u1790", "\u1791",
+ "\u1792", "\u1793", "\u1794", "\u1795", "\u1796", "\u1797", "\u1798", "\u1799", "\u179a",
+ "\u179b", "\u179c", "\u179f", "\u17a0", "\u17a1", "\u17a2", "\u17a3", "\u17a4", "\u17a5",
+ "\u17a6", "\u17a7", "\u17a8", "\u17a9", "\u17aa", "\u17ab", "\u17ac", "\u17ad", "\u17ae",
+ "\u17af", "\u17b0", "\u17b1", "\u17b2", "\u17b3", "\u17d2", "\u17b4", "\u17b5", "\u17b6",
+ "\u17b7", "\u17b8", "\u17b9", "\u17ba", "\u17bb", "\u17bc", "\u17bd", "\u17be", "\u17bf",
+ "\u17c0", "\u17c1", "\u17c2", "\u17c3", "\u17c4", "\u17c5", "\u17c6", "\u17c7", "\u17c8",
+ "\u17c9", "\u17ca", "\u17cb", "\u17cc", "\u17cd", "\u17ce", "\u17cf", "\u17d0", "\u17d1",
+ "\u17d3", "\u17dd", "\u17dc", "\u17e0", "\u17e1", "\u17e2", "\u17e3", "\u17e4", "\u17e5",
+ "\u17e6", "\u17e7", "\u17e8", "\u17e9", "\u17db", "\u17d4", "\u17d5", "\u17d6", "\u17d7",
+ "\u17d8", "\u17d9", "\u17da", "\u17f0", "\u17f1", "\u17f2", "\u17f3", "\u17f4", "\u17f5",
+ "\u17f6", "\u17f7", "\u17f8", "\u17f9", "\u19e0", "\u19e1", "\u19e2", "\u19e3", "\u19e4",
+ "\u19e5", "\u19e6", "\u19e7", "\u19e8", "\u19e9", "\u19ea", "\u19eb", "\u19ec", "\u19ed",
+ "\u19ee", "\u19ef", "\u19f0", "\u19f1", "\u19f2", "\u19f3", "\u19f4", "\u19f5", "\u19f6",
+ "\u19f7", "\u19f8", "\u19f9", "\u19fa", "\u19fb", "\u19fc", "\u19fd", "\u19fe", "\u19ff"
+ ]
+ }
+ }
+ },
+ 'help': {
+ 'labelMsg': 'wikieditor-toolbar-section-help',
+ 'type': 'booklet',
+ 'deferLoad': true,
+ 'pages': {
+ 'format': {
+ 'labelMsg': 'wikieditor-toolbar-help-page-format',
+ 'layout': 'table',
+ 'headings': [
+ { 'textMsg': 'wikieditor-toolbar-help-heading-description' },
+ { 'textMsg': 'wikieditor-toolbar-help-heading-syntax' },
+ { 'textMsg': 'wikieditor-toolbar-help-heading-result' }
+ ],
+ 'rows': [
+ {
+ 'description': { 'htmlMsg': 'wikieditor-toolbar-help-content-italic-description' },
+ 'syntax': { 'htmlMsg': 'wikieditor-toolbar-help-content-italic-syntax' },
+ 'result': { 'htmlMsg': 'wikieditor-toolbar-help-content-italic-result' }
+ },
+ {
+ 'description': { 'htmlMsg': 'wikieditor-toolbar-help-content-bold-description' },
+ 'syntax': { 'htmlMsg': 'wikieditor-toolbar-help-content-bold-syntax' },
+ 'result': { 'htmlMsg': 'wikieditor-toolbar-help-content-bold-result' }
+ },
+ {
+ 'description': { 'htmlMsg': 'wikieditor-toolbar-help-content-bolditalic-description' },
+ 'syntax': { 'htmlMsg': 'wikieditor-toolbar-help-content-bolditalic-syntax' },
+ 'result': { 'htmlMsg': 'wikieditor-toolbar-help-content-bolditalic-result' }
+ }
+ ]
+ },
+ 'link': {
+ 'labelMsg': 'wikieditor-toolbar-help-page-link',
+ 'layout': 'table',
+ 'headings': [
+ { 'textMsg': 'wikieditor-toolbar-help-heading-description' },
+ { 'textMsg': 'wikieditor-toolbar-help-heading-syntax' },
+ { 'textMsg': 'wikieditor-toolbar-help-heading-result' }
+ ],
+ 'rows': [
+ {
+ 'description': { 'htmlMsg': 'wikieditor-toolbar-help-content-ilink-description' },
+ 'syntax': { 'htmlMsg': 'wikieditor-toolbar-help-content-ilink-syntax' },
+ 'result': { 'htmlMsg': 'wikieditor-toolbar-help-content-ilink-result' }
+ },
+ {
+ 'description': { 'htmlMsg': 'wikieditor-toolbar-help-content-xlink-description' },
+ 'syntax': { 'htmlMsg': 'wikieditor-toolbar-help-content-xlink-syntax' },
+ 'result': { 'htmlMsg': 'wikieditor-toolbar-help-content-xlink-result' }
+ }
+ ]
+ },
+ 'heading': {
+ 'labelMsg': 'wikieditor-toolbar-help-page-heading',
+ 'layout': 'table',
+ 'headings': [
+ { 'textMsg': 'wikieditor-toolbar-help-heading-description' },
+ { 'textMsg': 'wikieditor-toolbar-help-heading-syntax' },
+ { 'textMsg': 'wikieditor-toolbar-help-heading-result' }
+ ],
+ 'rows': [
+ {
+ 'description': { 'htmlMsg': 'wikieditor-toolbar-help-content-heading2-description' },
+ 'syntax': { 'htmlMsg': 'wikieditor-toolbar-help-content-heading2-syntax' },
+ 'result': { 'htmlMsg': 'wikieditor-toolbar-help-content-heading2-result' }
+ },
+ {
+ 'description': { 'htmlMsg': 'wikieditor-toolbar-help-content-heading3-description' },
+ 'syntax': { 'htmlMsg': 'wikieditor-toolbar-help-content-heading3-syntax' },
+ 'result': { 'htmlMsg': 'wikieditor-toolbar-help-content-heading3-result' }
+ },
+ {
+ 'description': { 'htmlMsg': 'wikieditor-toolbar-help-content-heading4-description' },
+ 'syntax': { 'htmlMsg': 'wikieditor-toolbar-help-content-heading4-syntax' },
+ 'result': { 'htmlMsg': 'wikieditor-toolbar-help-content-heading4-result' }
+ },
+ {
+ 'description': { 'htmlMsg': 'wikieditor-toolbar-help-content-heading5-description' },
+ 'syntax': { 'htmlMsg': 'wikieditor-toolbar-help-content-heading5-syntax' },
+ 'result': { 'htmlMsg': 'wikieditor-toolbar-help-content-heading5-result' }
+ }
+ ]
+ },
+ 'list': {
+ 'labelMsg': 'wikieditor-toolbar-help-page-list',
+ 'layout': 'table',
+ 'headings': [
+ { 'textMsg': 'wikieditor-toolbar-help-heading-description' },
+ { 'textMsg': 'wikieditor-toolbar-help-heading-syntax' },
+ { 'textMsg': 'wikieditor-toolbar-help-heading-result' }
+ ],
+ 'rows': [
+ {
+ 'description': { 'htmlMsg': 'wikieditor-toolbar-help-content-ulist-description' },
+ 'syntax': { 'htmlMsg': 'wikieditor-toolbar-help-content-ulist-syntax' },
+ 'result': { 'htmlMsg': 'wikieditor-toolbar-help-content-ulist-result' }
+ },
+ {
+ 'description': { 'htmlMsg': 'wikieditor-toolbar-help-content-olist-description' },
+ 'syntax': { 'htmlMsg': 'wikieditor-toolbar-help-content-olist-syntax' },
+ 'result': { 'htmlMsg': 'wikieditor-toolbar-help-content-olist-result' }
+ }
+ ]
+ },
+ 'file': {
+ 'labelMsg': 'wikieditor-toolbar-help-page-file',
+ 'layout': 'table',
+ 'headings': [
+ { 'textMsg': 'wikieditor-toolbar-help-heading-description' },
+ { 'textMsg': 'wikieditor-toolbar-help-heading-syntax' },
+ { 'textMsg': 'wikieditor-toolbar-help-heading-result' }
+ ],
+ 'rows': [
+ {
+ 'description': { 'htmlMsg': 'wikieditor-toolbar-help-content-file-description' },
+ 'syntax': { 'htmlMsg': [ 'wikieditor-toolbar-help-content-file-syntax', fileNamespace ] },
+ 'result': { 'htmlMsg': [ 'wikieditor-toolbar-help-content-file-result', mw.config.get( 'stylepath' ) ] }
+ }
+ ]
+ },
+ 'reference': {
+ 'labelMsg': 'wikieditor-toolbar-help-page-reference',
+ 'layout': 'table',
+ 'headings': [
+ { 'textMsg': 'wikieditor-toolbar-help-heading-description' },
+ { 'textMsg': 'wikieditor-toolbar-help-heading-syntax' },
+ { 'textMsg': 'wikieditor-toolbar-help-heading-result' }
+ ],
+ 'rows': [
+ {
+ 'description': { 'htmlMsg': 'wikieditor-toolbar-help-content-reference-description' },
+ 'syntax': { 'htmlMsg': 'wikieditor-toolbar-help-content-reference-syntax' },
+ 'result': { 'htmlMsg': 'wikieditor-toolbar-help-content-reference-result' }
+ },
+ {
+ 'description': { 'htmlMsg': 'wikieditor-toolbar-help-content-rereference-description' },
+ 'syntax': { 'htmlMsg': 'wikieditor-toolbar-help-content-rereference-syntax' },
+ 'result': { 'htmlMsg': 'wikieditor-toolbar-help-content-rereference-result' }
+ },
+ {
+ 'description': { 'htmlMsg': 'wikieditor-toolbar-help-content-showreferences-description' },
+ 'syntax': { 'htmlMsg': 'wikieditor-toolbar-help-content-showreferences-syntax' },
+ 'result': { 'htmlMsg': 'wikieditor-toolbar-help-content-showreferences-result' }
+ }
+ ]
+ },
+ 'discussion': {
+ 'labelMsg': 'wikieditor-toolbar-help-page-discussion',
+ 'layout': 'table',
+ 'headings': [
+ { 'textMsg': 'wikieditor-toolbar-help-heading-description' },
+ { 'textMsg': 'wikieditor-toolbar-help-heading-syntax' },
+ { 'textMsg': 'wikieditor-toolbar-help-heading-result' }
+ ],
+ 'rows': [
+ {
+ 'description': {
+ 'htmlMsg': 'wikieditor-toolbar-help-content-signaturetimestamp-description'
+ },
+ 'syntax': { 'htmlMsg': 'wikieditor-toolbar-help-content-signaturetimestamp-syntax' },
+ 'result': { 'htmlMsg': 'wikieditor-toolbar-help-content-signaturetimestamp-result' }
+ },
+ {
+ 'description': { 'htmlMsg': 'wikieditor-toolbar-help-content-signature-description' },
+ 'syntax': { 'htmlMsg': 'wikieditor-toolbar-help-content-signature-syntax' },
+ 'result': { 'htmlMsg': 'wikieditor-toolbar-help-content-signature-result' }
+ },
+ {
+ 'description': { 'htmlMsg': 'wikieditor-toolbar-help-content-indent-description' },
+ 'syntax': { 'htmlMsg': 'wikieditor-toolbar-help-content-indent-syntax' },
+ 'result': { 'htmlMsg': 'wikieditor-toolbar-help-content-indent-result' }
+ }
+ ]
+ }
+ }
+ }
+ } };
+}
+
+}; } ) ( jQuery ); \ No newline at end of file
diff --git a/extensions/WikiEditor/modules/jquery.wikiEditor.toolbar.css b/extensions/WikiEditor/modules/jquery.wikiEditor.toolbar.css
new file mode 100644
index 00000000..aab08ca8
--- /dev/null
+++ b/extensions/WikiEditor/modules/jquery.wikiEditor.toolbar.css
@@ -0,0 +1,303 @@
+/*
+ * CSS for WikiEditor Toolbar jQuery plugin
+ */
+
+.wikiEditor-ui-toolbar {
+ position: relative;
+ width: 100%;
+}
+/* Expandable Sections */
+.wikiEditor-ui-toolbar .sections {
+ float: left;
+ width: 100%;
+ clear: both;
+ height: 0;
+}
+.wikiEditor-ui-toolbar .sections .section {
+ display: none;
+ float: left;
+ width: 100%;
+ border-top: solid 1px #DDDDDD;
+ background-color: #E0EEf7;
+}
+.wikiEditor-ui-toolbar {
+ /* @embed */
+ background-image: url(images/toolbar/base.png);
+ background-position: left top;
+ background-repeat: repeat-x;
+}
+/* Gets overridden when the section div is in class loading - see below */
+.wikiEditor-ui-toolbar .sections div .spinner {
+ display: none;
+}
+.wikiEditor-ui-toolbar .sections .loading .spinner {
+ display: block;
+ float: left;
+ /* @embed */
+ background-image: url(images/toolbar/loading.gif);
+ background-position: left center;
+ background-repeat: no-repeat;
+ padding-left: 32px;
+ margin-left: 0.5em;
+ height: 32px;
+ color: #666666;
+}
+/* Top Level Containers */
+.wikiEditor-ui-toolbar .tabs,
+.wikiEditor-ui-toolbar .section-main {
+ position: relative;
+ float: left;
+ height: 26px;
+}
+/* Groups */
+.wikiEditor-ui-toolbar .group {
+ float: left;
+ height: 26px;
+ padding-right: 6px;
+ border-right: solid 1px #DDDDDD;
+ margin: 3px;
+}
+.wikiEditor-ui-toolbar .group-search {
+ float: right;
+ padding: 0 0 0 6px;
+ border-right: none;
+ border-left: 1px solid #DDDDDD;
+}
+.wikiEditor-ui-toolbar .group-insert {
+ border-right: none;
+}
+/* Sprited Buttons */
+.wikiEditor-toolbar-spritedButton {
+ /* @embed */
+ background: url(images/toolbar/button-sprite.png) 0 0 no-repeat;
+ display: block;
+ float: left;
+ height: 22px;
+ text-indent: -9999px;
+ width: 22px;
+ padding: 2px;
+ cursor: pointer;
+ overflow: hidden;
+}
+/* Tabs */
+.wikiEditor-ui-toolbar .tabs {
+ list-style: none;
+ margin: 3px;
+}
+.wikiEditor-ui-toolbar .tabs span.tab {
+ display: block;
+ float: left;
+ line-height: 26px;
+}
+.wikiEditor-ui-toolbar .tabs span.tab a,
+.wikiEditor-ui-toolbar .tabs span.tab a:visited {
+ display: inline-block;
+ float: left;
+ padding-left: 18px;
+ padding-right: 12px;
+ height: 26px;
+ cursor: pointer;
+ color: #0645ad;
+ /* @embed */
+ background-image: url(images/toolbar/arrow-ltr.png);
+ background-position: left center;
+ background-repeat: no-repeat;
+}
+.wikiEditor-ui-toolbar .tabs span.tab a.current,
+.wikiEditor-ui-toolbar .tabs span.tab a.current:visited {
+ color: #333333;
+ /* @embed */
+ background-image: url(images/toolbar/arrow-down.png);
+}
+.wikiEditor-ui-toolbar .tabs span.tab a.current:hover {
+ text-decoration: none;
+}
+.wikiEditor-ui-toolbar .tabs span.tab a.loading {
+ /* @embed */
+ background-image: url(images/toolbar/loading-small.gif);
+}
+/* Toolbar */
+.wikiEditor-ui-toolbar .group .label {
+ float: left;
+ border: 0px;
+ height: 22px;
+ line-height: 22px;
+ margin: 2px;
+ margin-left: 5px;
+ margin-right: 8px;
+ color: #777777;
+ cursor: default;
+}
+.wikiEditor-ui-toolbar .group img.tool {
+ float: left;
+ border: 0px;
+ height: 22px;
+ width: 22px;
+ padding: 2px;
+ cursor: pointer;
+}
+.wikiEditor-ui-toolbar .group .tool-select {
+ float: left;
+ margin: 2px;
+ height: 22px;
+ cursor: pointer;
+ border: solid 1px silver;
+ padding: 0;
+ margin-right: 0;
+ cursor: pointer;
+ background-color: #ffffff;
+}
+.wikiEditor-ui-toolbar .group .tool-select .label {
+ /* @embed */
+ background-image: url(images/toolbar/arrow-down.png);
+ background-position: center right;
+ background-repeat: no-repeat;
+ padding: 0;
+ margin: 0;
+ padding-left: 4px;
+ padding-right: 22px;
+ margin-right: 4px;
+ cursor: pointer;
+ text-decoration: none;
+ color: #333333;
+}
+.wikiEditor-ui-toolbar .group .tool-select .menu .options {
+ position: absolute;
+ display: none;
+ margin-left: -1px;
+ margin-top: 22px;
+ border: solid 1px silver;
+ background-color: #ffffff;
+}
+.wikiEditor-ui-toolbar .group .tool-select .options .option {
+ display: block;
+ padding: 0.5em;
+ text-decoration: none;
+ color: black;
+ white-space: nowrap;
+}
+.wikiEditor-ui-toolbar .group .tool-select .options .option:hover {
+ background-color: #E0EEf7;
+}
+.wikiEditor-ui-toolbar .group .tool-select .options .option[rel=heading-2] {
+ font-size: 150%;
+ font-weight: normal;
+}
+.wikiEditor-ui-toolbar .group .tool-select .options .option[rel=heading-3] {
+ font-size: 132%;
+ font-weight: normal;
+}
+.wikiEditor-ui-toolbar .group .tool-select .options .option[rel=heading-4] {
+ font-size: 116%;
+ font-weight: normal;
+}
+.wikiEditor-ui-toolbar .group .tool-select .options .option[rel=heading-5] {
+ font-size: 100%;
+ font-weight: bold;
+}
+/* Booklet */
+.wikiEditor-ui-toolbar .booklet .index {
+ float: left;
+ width: 20%;
+ height: 125px;
+ overflow: auto;
+}
+.wikiEditor-ui-toolbar .booklet .index div {
+ padding: 4px;
+ padding-left: 6px;
+ cursor: pointer;
+ color: #0645ad;
+}
+.wikiEditor-ui-toolbar .booklet .index .current {
+ background-color: #FAFAFA;
+ color: #333333;
+ cursor: default;
+}
+.wikiEditor-ui-toolbar .booklet .pages {
+ float: right;
+ width: 80%;
+ height: 125px;
+ overflow: auto;
+ background-color: #FAFAFA;
+}
+/* Help Pages */
+.wikiEditor-ui-toolbar .page-table table {
+ padding-left: 5px;
+ padding-right: 5px;
+ background: none;
+}
+.wikiEditor-ui-toolbar .page-table th {
+ color: #999999;
+}
+.wikiEditor-ui-toolbar .page-table td {
+ color: black;
+ border-top: solid 1px #EEEEEE;
+}
+.wikiEditor-ui-toolbar .page-table th,
+.wikiEditor-ui-toolbar .page-table td {
+ text-align: left;
+ padding: 5px;
+ margin: 0px;
+}
+.wikiEditor-ui-toolbar .section-help .page-table td.cell-syntax,
+.wikiEditor-ui-toolbar .section-help .page-table td.syntax {
+ font-family: monospace, "Courier New";
+}
+.wikiEditor-ui-toolbar .section-help .page-table td.syntax,
+.wikiEditor-ui-toolbar .section-help .page-table td.cell-syntax,
+.wikiEditor-ui-toolbar .section-help .page-table td.cell-result,
+.wikiEditor-ui-toolbar .section-help .page-table td.result {
+ width: 40%;
+}
+.wikiEditor-ui-toolbar .section-help .page-table td.description,
+.wikiEditor-ui-toolbar .section-help .page-table td.description {
+ width: 20%;
+}
+/* Characters Pages */
+.wikiEditor-ui-toolbar .page-characters div span {
+ border: solid 1px #DDDDDD;
+ padding: 5px;
+ padding-left: 8px;
+ padding-right: 8px;
+ margin-left: 5px;
+ margin-top: 5px;
+ height: 1em;
+ float: left;
+ display: block;
+ color: black;
+ text-decoration: none;
+ cursor: pointer;
+ font-family: monospace, "Courier New";
+ font-size: 1.25em;
+}
+.wikiEditor-ui-toolbar .page-characters div[dir=rtl] span {
+ direction: rtl;
+}
+.wikiEditor-ui-toolbar .page-characters div span:hover {
+ background-color: white;
+ text-decoration: none;
+ border-color: #a8d7f9;
+}
+.ui-widget table td.wikieditor-toolbar-table-preview-wrapper span {
+ padding: 4px 6px 0px;
+ display: block;
+}
+.ui-widget table .wikieditor-toolbar-table-preview-frame {
+ width: 340px;
+ background: #fff;
+ padding: 10px;
+ overflow: hidden;
+ display: block;
+ position: relative;
+}
+.ui-widget table .wikieditor-toolbar-table-preview-content {
+ width: 375px;
+ display: block;
+}
+.ui-widget table .wikieditor-toolbar-table-preview {
+ width: 340px;
+}
+.ui-widget table td.wikieditor-toolbar-table-preview-wrapper {
+ background: #e5e5e5;
+ padding: 10px;
+}
diff --git a/extensions/WikiEditor/modules/jquery.wikiEditor.toolbar.js b/extensions/WikiEditor/modules/jquery.wikiEditor.toolbar.js
new file mode 100644
index 00000000..7a72c923
--- /dev/null
+++ b/extensions/WikiEditor/modules/jquery.wikiEditor.toolbar.js
@@ -0,0 +1,775 @@
+/**
+ * Toolbar module for wikiEditor
+ */
+( function( $ ) { $.wikiEditor.modules.toolbar = {
+
+/**
+ * API accessible functions
+ */
+api : {
+ addToToolbar : function( context, data ) {
+
+ var smooth = true, type, i;
+
+ for ( type in data ) {
+ switch ( type ) {
+ case 'sections':
+ var $sections = context.modules.toolbar.$toolbar.find( 'div.sections' ),
+ $tabs = context.modules.toolbar.$toolbar.find( 'div.tabs' );
+ for ( var section in data[type] ) {
+ if ( section == 'main' ) {
+ // Section
+ context.modules.toolbar.$toolbar.prepend(
+ $.wikiEditor.modules.toolbar.fn.buildSection(
+ context, section, data[type][section]
+ )
+ );
+ continue;
+ }
+ // Section
+ $sections.append(
+ $.wikiEditor.modules.toolbar.fn.buildSection( context, section, data[type][section] )
+ );
+ // Tab
+ $tabs.append(
+ $.wikiEditor.modules.toolbar.fn.buildTab( context, section, data[type][section] )
+ );
+ }
+ break;
+ case 'groups':
+ if ( ! ( 'section' in data ) ) {
+ continue;
+ }
+ var $section = context.modules.toolbar.$toolbar.find( 'div[rel="' + data.section + '"].section' );
+ for ( var group in data[type] ) {
+ // Group
+ $section.append(
+ $.wikiEditor.modules.toolbar.fn.buildGroup( context, group, data[type][group] )
+ );
+ }
+ smooth = false;
+ 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 ( var tool in data[type] ) {
+ // Tool
+ $group.append( $.wikiEditor.modules.toolbar.fn.buildTool( context, tool, data[type][tool] ) );
+ }
+ if ( $group.children().length ) {
+ $group.show();
+ }
+ smooth = false;
+ 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 ( var page in data[type] ) {
+ // Page
+ $pages.append( $.wikiEditor.modules.toolbar.fn.buildPage( context, page, data[type][page] ) );
+ // Index
+ $index.append(
+ $.wikiEditor.modules.toolbar.fn.buildBookmark( context, page, data[type][page] )
+ );
+ }
+ $.wikiEditor.modules.toolbar.fn.updateBookletSelection( context, page, $pages, $index );
+ smooth = false;
+ 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 ( i = 0; i < data.rows.length; i++ ) {
+ // Row
+ $table.append( $.wikiEditor.modules.toolbar.fn.buildRow( context, data.rows[i] ) );
+ }
+ smooth = false;
+ break;
+ case 'characters':
+ if ( ! ( 'section' in data && 'page' in data ) ) {
+ continue;
+ }
+ var $characters = context.modules.toolbar.$toolbar.find(
+ 'div[rel="' + data.section + '"].section ' + 'div[rel="' + data.page + '"].page div'
+ );
+ var actions = $characters.data( 'actions' );
+ for ( i = 0; i < data.characters.length; i++ ) {
+ // Character
+ $characters
+ .append(
+ $( $.wikiEditor.modules.toolbar.fn.buildCharacter( data.characters[i], actions ) )
+ .mousedown( function( e ) {
+ context.fn.saveCursorAndScrollTop();
+ // No dragging!
+ 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;
+ } )
+ );
+ }
+ smooth = false;
+ break;
+ default: break;
+ }
+ }
+
+ // Fix div.section size after adding things; if smooth is true uses a smooth
+ // animation, otherwise just change height (breaking any ongoing animation)
+ var $divSections = context.modules.toolbar.$toolbar.find( 'div.sections' );
+ var $visibleSection = $divSections.find( '.section:visible' );
+ if ( $visibleSection.size() ) {
+ if ( smooth ) {
+ $divSections.animate( { 'height': $visibleSection.outerHeight() }, 'fast' );
+ } else {
+ $divSections.height( $visibleSection.outerHeight() );
+ }
+ }
+ },
+ removeFromToolbar : function( context, data ) {
+ if ( typeof data.section == 'string' ) {
+ // Section
+ var tab = 'div.tabs span[rel="' + data.section + '"].tab';
+ var target = 'div[rel="' + data.section + '"].section';
+ var group = null;
+ if ( typeof data.group == 'string' ) {
+ // Toolbar group
+ target += ' div[rel="' + data.group + '"].group';
+ if ( typeof data.tool == 'string' ) {
+ // Save for later checking if empty
+ group = target;
+ // Tool
+ target += ' span[rel="' + data.tool + '"].tool';
+ }
+ } else if ( typeof data.page == 'string' ) {
+ // Booklet page
+ var index = target + ' div.index div[rel="' + data.page + '"]';
+ target += ' div.pages div[rel="' + data.page + '"].page';
+ if ( typeof data.character == 'string' ) {
+ // Character
+ target += ' span[rel="' + data.character + '"]';
+ } else if ( typeof data.row == 'number' ) {
+ // Table row
+ target += ' table tr:not(:has(th)):eq(' + data.row + ')';
+ } else {
+ // Just a page, remove the index too!
+ 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 {
+ // Just a section, remove the tab too!
+ context.modules.toolbar.$toolbar.find( tab ).remove();
+ }
+ context.modules.toolbar.$toolbar.find( target ).remove();
+ // Hide empty groups
+ if ( group ) {
+ var $group = context.modules.toolbar.$toolbar.find( group );
+ if ( $group.children().length === 0 ) {
+ $group.hide();
+ }
+ }
+ }
+ }
+},
+/**
+ * Event handlers
+ */
+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 );
+ }
+},
+/**
+ * Internally used functions
+ */
+fn: {
+ /**
+ * Creates a toolbar module within a wikiEditor
+ *
+ * @param {Object} context Context object of editor to create module in
+ * @param {Object} config Configuration object to create module from
+ */
+ 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 );
+ },
+ /**
+ * Performs an operation based on parameters
+ *
+ * @param {Object} context
+ * @param {Object} action
+ * @param {Object} source
+ */
+ doAction : function( context, action, source ) {
+ // Verify that this has been called from a source that's within the toolbar
+ // 'trackAction' defined in click tracking
+ if ( $.trackAction !== undefined && source.closest( '.wikiEditor-ui-toolbar' ).size() ) {
+ // Build a unique id for this action by tracking the parent rel attributes up to the toolbar level
+ 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 ( 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 ( var tool in group.tools ) {
+ tool = $.wikiEditor.modules.toolbar.fn.buildTool( context, tool, group.tools[tool] );
+ if ( tool ) {
+ // Consider a group with only hidden tools empty as well
+ // .is( ':visible' ) always returns false because tool is not attached to the DOM yet
+ 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 ( var i = 0; i < tool.filters.length; i++ ) {
+ if ( $( tool.filters[i] ).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.saveCursorAndScrollTop();
+ // No dragging!
+ e.preventDefault();
+ return false;
+ } )
+ .click( function( e ) {
+ $.wikiEditor.modules.toolbar.fn.doAction(
+ $(this).data( 'context' ), $(this).data( 'action' ), $(this)
+ );
+ e.preventDefault();
+ return false;
+ } );
+ }
+ return $button;
+ case 'select':
+ var $select = $( '<div/>' )
+ .attr( { 'rel' : id, 'class' : 'tool tool-select' } );
+ var $options = $( '<div/>' ).addClass( 'options' );
+ if ( 'list' in tool ) {
+ for ( var 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.saveCursorAndScrollTop();
+ // No dragging!
+ e.preventDefault();
+ return false;
+ } )
+ .click( function( e ) {
+ $.wikiEditor.modules.toolbar.fn.doAction(
+ $(this).data( 'context' ), $(this).data( 'action' ), $(this)
+ );
+ // Hide the dropdown
+ // Sanity check: if this somehow gets called while the dropdown
+ // is hidden, don't show it
+ 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 ) {
+ // No dragging!
+ 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 ) {
+ // No dragging!
+ 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: '/' }
+ );
+ // Click tracking
+ if ( $.trackAction !== undefined){
+ $.trackAction(section + '.' + $(this).attr('rel'));
+ }
+ // No dragging!
+ event.preventDefault();
+ return false;
+ } );
+ },
+ buildPage : function( context, id, page ) {
+ var html;
+ var $page = $( '<div/>' ).attr( {
+ 'class' : 'page page-' + id,
+ 'rel' : id
+ } );
+ switch ( page.layout ) {
+ case 'table':
+ $page.addClass( 'page-table' );
+ 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 ( var i = 0; i < page.rows.length; i++ ) {
+ html += $.wikiEditor.modules.toolbar.fn.buildRow( context, page.rows[i] );
+ }
+ }
+ $page.html( html );
+ break;
+ case 'characters':
+ $page.addClass( 'page-characters' );
+ var $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 ) {
+ 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.saveCursorAndScrollTop();
+ // No dragging!
+ 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 ( var i = 0; i< headings.length; i++ ) {
+ html += '<th>' + $.wikiEditor.autoMsg( headings[i], ['html', 'text'] ) + '</th>';
+ }
+ return html;
+ },
+ buildRow : function( context, row ) {
+ var html = '<tr>';
+ for ( var 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' );
+ // Re-save cookie
+ if ( selected !== null ) {
+ $.cookie( 'wikiEditor-' + context.instance + '-toolbar-section', selected, { expires: 30, path: '/' } );
+ }
+ var $link =
+ $( '<a/>' )
+ .addClass( selected == id ? 'current' : null )
+ .attr( 'href', '#' )
+ .text( $.wikiEditor.autoMsg( section, 'label' ) )
+ .data( 'context', context )
+ .mouseup( function( e ) {
+ $(this).blur();
+ } )
+ .mousedown( function( e ) {
+ // No dragging!
+ 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';
+ var $previousSections = $section.parent().find( '.section-visible' );
+ $previousSections.css( 'position', 'absolute' );
+ $previousSections.removeClass( 'section-visible' );
+ $previousSections.fadeOut( 'fast', function() { $(this).css( 'position', 'static' ); } );
+ $(this).parent().parent().find( 'a' ).removeClass( 'current' );
+ $sections.css( 'overflow', 'hidden' );
+ var animate = function( $that ) {
+ $sections
+ .css( 'display', 'block' )
+ .animate( { 'height': $section.outerHeight() }, $section.outerHeight() * 2, function() {
+ $that.css( 'overflow', 'visible' ).css( 'height', 'auto' );
+ context.fn.trigger( 'resize' );
+ } );
+ };
+ if ( show ) {
+ $section.addClass( 'section-visible' );
+ $section.fadeIn( 'fast' );
+ if ( $section.hasClass( 'loading' ) ) {
+ // Loading of this section was deferred, load it now
+ var $that = $(this);
+ $that.addClass( 'current loading' );
+ setTimeout( function() {
+ $section.trigger( 'loadSection' );
+ animate( $that );
+ $that.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' );
+ } );
+ }
+ // Click tracking
+ if ( $.trackAction !== undefined ) {
+ $.trackAction( $section.attr('rel') + '.' + ( show ? 'show': 'hide' ) );
+ }
+ // Save the currently visible section
+ $.cookie(
+ 'wikiEditor-' + $(this).data( 'context' ).instance + '-toolbar-section',
+ show ? $section.attr( 'rel' ) : null,
+ { expires: 30, path: '/' }
+ );
+ e.preventDefault();
+ return false;
+ });
+ return $( '<span/>' )
+ .attr({
+ 'class' : 'tab tab-' + id,
+ 'rel' : id
+ })
+ .append( $link );
+ },
+ 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 ( section.deferLoad !== undefined && section.deferLoad && id !== 'main' && !show ) {
+ // This class shows the spinner and serves as a marker for the click handler in buildTab()
+ $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 );
+ }
+
+ // Show or hide 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 ( var 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 ( var 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 );
+ // Re-save cookie
+ if ( selected != null ) {
+ $.cookie( cookie, selected, { expires: 30, path: '/' } );
+ }
+ var $selectedIndex = $index.find( '*[rel="' + selected + '"]' );
+ if ( $selectedIndex.size() === 0 ) {
+ $selectedIndex = $index.children().eq( 0 );
+ selected = $selectedIndex.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 ( var 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() {
+ // HACK: Opera doesn't seem to want to redraw after these bits
+ // are added to the DOM, so we can just FORCE it!
+ 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 );