r73173 MediaWiki - Code Review archive

Repository:MediaWiki
Revision:r73172‎ | r73173 | r73174 >
Date:23:13, 16 September 2010
Author:tparscal
Status:ok
Tags:
Comment:
Added in conentCollector for paste-filtering support
Modified paths:
  • /trunk/extensions/WikiEditor/WikiEditor.hooks.php (modified) (history)
  • /trunk/extensions/WikiEditor/modules/contentCollector.js (added) (history)

Diff [purge]

Index: trunk/extensions/WikiEditor/WikiEditor.hooks.php
@@ -12,13 +12,19 @@
1313
1414 static $modules = array(
1515
 16+ /* Third-party modules */
 17+
 18+ 'contentCollector' => array(
 19+ 'scripts' => 'extensions/WikiEditor/modules/contentCollector.js',
 20+ ),
 21+
1622 /* WikiEditor jQuery plugin Resources */
1723
1824 'jquery.wikiEditor' => array(
1925 'scripts' => 'extensions/WikiEditor/modules/jquery.wikiEditor.js',
2026 'styles' => 'extensions/WikiEditor/modules/jquery.wikiEditor.css',
2127 'dependencies' => array(
22 - 'jquery.client', 'jquery.textSelection', 'jquery.delayedBind'
 28+ 'jquery.client', 'jquery.textSelection', 'jquery.delayedBind', 'contentCollector',
2329 ),
2430 'messages' => array(
2531 'wikieditor-wikitext-tab',
Index: trunk/extensions/WikiEditor/modules/contentCollector.js
@@ -0,0 +1,439 @@
 2+// THIS FILE HAS BEEN MODIFIED for use with the mediawiki wikiEditor
 3+// It no longer requires etherpad.collab.ace.easysync2.Changeset
 4+// THIS FILE WAS ORIGINALLY AN APPJET MODULE: etherpad.collab.ace.contentcollector
 5+
 6+/**
 7+ * Copyright 2009 Google Inc.
 8+ *
 9+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 10+ * use this file except in compliance with the License. You may obtain a copy of
 11+ * the License at
 12+ *
 13+ * http://www.apache.org/licenses/LICENSE-2.0
 14+ *
 15+ * Unless required by applicable law or agreed to in writing, software
 16+ * distributed under the License is distributed on an "AS-IS" BASIS, WITHOUT
 17+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 18+ * License for the specific language governing permissions and limitations under
 19+ * the License.
 20+ */
 21+
 22+var _MAX_LIST_LEVEL = 8;
 23+
 24+function sanitizeUnicode(s) {
 25+ return s.replace(/[\uffff\ufffe\ufeff\ufdd0-\ufdef\ud800-\udfff]/g, '?');
 26+}
 27+
 28+function makeContentCollector( browser, domInterface ) {
 29+ browser = browser || {};
 30+
 31+ var dom = domInterface || {
 32+ isNodeText : function(n) {
 33+ return (n.nodeType == 3);
 34+ },
 35+ nodeTagName : function(n) {
 36+ return n.tagName;
 37+ },
 38+ nodeValue : function(n) {
 39+ try {
 40+ return n.nodeValue;
 41+ } catch ( err ) {
 42+ return '';
 43+ }
 44+ },
 45+ nodeName : function(n) {
 46+ return n.nodeName;
 47+ },
 48+ nodeNumChildren : function(n) {
 49+ return n.childNodes.length;
 50+ },
 51+ nodeChild : function(n, i) {
 52+ return n.childNodes.item(i);
 53+ },
 54+ nodeProp : function(n, p) {
 55+ return n[p];
 56+ },
 57+ nodeAttr : function(n, a) {
 58+ return n.getAttribute(a);
 59+ },
 60+ optNodeInnerHTML : function(n) {
 61+ return n.innerHTML;
 62+ }
 63+ };
 64+
 65+ var _blockElems = {
 66+ "div" : 1,
 67+ "p" : 1,
 68+ "pre" : 1,
 69+ "li" : 1
 70+ };
 71+ function isBlockElement(n) {
 72+ return !!_blockElems[(dom.nodeTagName(n) || "").toLowerCase()];
 73+ }
 74+ function textify(str) {
 75+ return sanitizeUnicode(str.replace(/[\n\r ]/g, ' ').replace(/\xa0/g,
 76+ ' ').replace(/\t/g, ' '));
 77+ }
 78+ function getAssoc(node, name) {
 79+ return dom.nodeProp(node, "_magicdom_" + name);
 80+ }
 81+
 82+ var lines = (function() {
 83+ var textArray = [];
 84+ var self = {
 85+ length : function() {
 86+ return textArray.length;
 87+ },
 88+ atColumnZero : function() {
 89+ return textArray[textArray.length - 1] === "";
 90+ },
 91+ startNew : function() {
 92+ textArray.push("");
 93+ self.flush(true);
 94+ },
 95+ textOfLine : function(i) {
 96+ return textArray[i];
 97+ },
 98+ appendText : function(txt, attrString) {
 99+ textArray[textArray.length - 1] += txt;
 100+ // dmesg(txt+" / "+attrString);
 101+ },
 102+ textLines : function() {
 103+ return textArray.slice();
 104+ },
 105+ // call flush only when you're done
 106+ flush : function(withNewline) {
 107+
 108+ }
 109+ };
 110+ self.startNew();
 111+ return self;
 112+ }());
 113+ var cc = {};
 114+ function _ensureColumnZero(state) {
 115+ if (!lines.atColumnZero()) {
 116+ _startNewLine(state);
 117+ }
 118+ }
 119+ var selection, startPoint, endPoint;
 120+ var selStart = [ -1, -1 ], selEnd = [ -1, -1 ];
 121+ var blockElems = {
 122+ "div" : 1,
 123+ "p" : 1,
 124+ "pre" : 1
 125+ };
 126+ function _isEmpty(node, state) {
 127+ // consider clean blank lines pasted in IE to be empty
 128+ if (dom.nodeNumChildren(node) == 0)
 129+ return true;
 130+ if (dom.nodeNumChildren(node) == 1 && getAssoc(node, "shouldBeEmpty")
 131+ && dom.optNodeInnerHTML(node) == " "
 132+ && !getAssoc(node, "unpasted")) {
 133+ if (state) {
 134+ var child = dom.nodeChild(node, 0);
 135+ _reachPoint(child, 0, state);
 136+ _reachPoint(child, 1, state);
 137+ }
 138+ return true;
 139+ }
 140+ return false;
 141+ }
 142+ function _pointHere(charsAfter, state) {
 143+ var ln = lines.length() - 1;
 144+ var chr = lines.textOfLine(ln).length;
 145+ if (chr == 0 && state.listType && state.listType != 'none') {
 146+ chr += 1; // listMarker
 147+ }
 148+ chr += charsAfter;
 149+ return [ ln, chr ];
 150+ }
 151+ function _reachBlockPoint(nd, idx, state) {
 152+ if (!dom.isNodeText(nd))
 153+ _reachPoint(nd, idx, state);
 154+ }
 155+ function _reachPoint(nd, idx, state) {
 156+ if (startPoint && nd == startPoint.node && startPoint.index == idx) {
 157+ selStart = _pointHere(0, state);
 158+ }
 159+ if (endPoint && nd == endPoint.node && endPoint.index == idx) {
 160+ selEnd = _pointHere(0, state);
 161+ }
 162+ }
 163+ function _incrementFlag(state, flagName) {
 164+ state.flags[flagName] = (state.flags[flagName] || 0) + 1;
 165+ }
 166+ function _decrementFlag(state, flagName) {
 167+ state.flags[flagName]--;
 168+ }
 169+ function _enterList(state, listType) {
 170+ var oldListType = state.listType;
 171+ state.listLevel = (state.listLevel || 0) + 1;
 172+ if (listType != 'none') {
 173+ state.listNesting = (state.listNesting || 0) + 1;
 174+ }
 175+ state.listType = listType;
 176+ return oldListType;
 177+ }
 178+ function _exitList(state, oldListType) {
 179+ state.listLevel--;
 180+ if (state.listType != 'none') {
 181+ state.listNesting--;
 182+ }
 183+ state.listType = oldListType;
 184+ }
 185+ function _produceListMarker(state) {
 186+
 187+ }
 188+ function _startNewLine(state) {
 189+ if (state) {
 190+ var atBeginningOfLine = lines.textOfLine(lines.length() - 1).length == 0;
 191+ if (atBeginningOfLine && state.listType && state.listType != 'none') {
 192+ _produceListMarker(state);
 193+ }
 194+ }
 195+ lines.startNew();
 196+ }
 197+ cc.notifySelection = function(sel) {
 198+ if (sel) {
 199+ selection = sel;
 200+ startPoint = selection.startPoint;
 201+ endPoint = selection.endPoint;
 202+ }
 203+ };
 204+ cc.collectContent = function(node, state) {
 205+ if (!state) {
 206+ state = {
 207+ flags : {/* name -> nesting counter */}
 208+ };
 209+ }
 210+ var isBlock = isBlockElement(node);
 211+ var isEmpty = _isEmpty(node, state);
 212+ if (isBlock)
 213+ _ensureColumnZero(state);
 214+ var startLine = lines.length() - 1;
 215+ _reachBlockPoint(node, 0, state);
 216+ if (dom.isNodeText(node)) {
 217+ var txt = dom.nodeValue(node);
 218+ var rest = '';
 219+ var x = 0; // offset into original text
 220+ if (txt.length == 0) {
 221+ if (startPoint && node == startPoint.node) {
 222+ selStart = _pointHere(0, state);
 223+ }
 224+ if (endPoint && node == endPoint.node) {
 225+ selEnd = _pointHere(0, state);
 226+ }
 227+ }
 228+ while (txt.length > 0) {
 229+ var consumed = 0;
 230+ if (!browser.firefox || state.flags.preMode) {
 231+ var firstLine = txt.split('\n', 1)[0];
 232+ consumed = firstLine.length + 1;
 233+ rest = txt.substring(consumed);
 234+ txt = firstLine;
 235+ } else { /* will only run this loop body once */
 236+ }
 237+ if (startPoint && node == startPoint.node
 238+ && startPoint.index - x <= txt.length) {
 239+ selStart = _pointHere(startPoint.index - x, state);
 240+ }
 241+ if (endPoint && node == endPoint.node
 242+ && endPoint.index - x <= txt.length) {
 243+ selEnd = _pointHere(endPoint.index - x, state);
 244+ }
 245+ var txt2 = txt;
 246+ if ((!state.flags.preMode) && /^[\r\n]*$/.exec(txt)) {
 247+ // prevents textnodes containing just "\n" from being
 248+ // significant
 249+ // in safari when pasting text, now that we convert them to
 250+ // spaces instead of removing them, because in other cases
 251+ // removing "\n" from pasted HTML will collapse words
 252+ // together.
 253+ txt2 = "";
 254+ }
 255+ var atBeginningOfLine = lines.textOfLine(lines.length() - 1).length == 0;
 256+ if (atBeginningOfLine) {
 257+ // newlines in the source mustn't become spaces at beginning
 258+ // of line box
 259+ txt2 = txt2.replace(/^\n*/, '');
 260+ }
 261+ if (atBeginningOfLine && state.listType
 262+ && state.listType != 'none') {
 263+ _produceListMarker(state);
 264+ }
 265+ lines.appendText(textify(txt2));
 266+
 267+ x += consumed;
 268+ txt = rest;
 269+ if (txt.length > 0) {
 270+ _startNewLine(state);
 271+ }
 272+ }
 273+
 274+ } else {
 275+ var cls = dom.nodeProp(node, "className");
 276+ var tname = (dom.nodeTagName(node) || "").toLowerCase();
 277+ if (tname == "br") {
 278+ _startNewLine(state);
 279+ } else if (tname == "script" || tname == "style") {
 280+ // ignore
 281+ } else if (!isEmpty) {
 282+ var styl = dom.nodeAttr(node, "style");
 283+
 284+ var isPre = (tname == "pre");
 285+ if ((!isPre) && browser.safari) {
 286+ isPre = (styl && /\bwhite-space:\s*pre\b/i.exec(styl));
 287+ }
 288+ if (isPre)
 289+ _incrementFlag(state, 'preMode');
 290+ var oldListTypeOrNull = null;
 291+
 292+ var nc = dom.nodeNumChildren(node);
 293+ for ( var i = 0; i < nc; i++) {
 294+ var c = dom.nodeChild(node, i);
 295+ //very specific IE case where it inserts <span lang="en"> which we want to ginore.
 296+ //to reproduce copy content from wordpad andpaste into the middle of a line in IE
 297+ if ( browser.msie && cls.indexOf('wikiEditor') >= 0 && dom.nodeName(c) == 'SPAN' && dom.nodeAttr(c, 'lang') == "" ) {
 298+ continue;
 299+ }
 300+ cc.collectContent(c, state);
 301+ }
 302+
 303+ if (isPre)
 304+ _decrementFlag(state, 'preMode');
 305+
 306+ if (oldListTypeOrNull) {
 307+ _exitList(state, oldListTypeOrNull);
 308+ }
 309+ }
 310+ }
 311+ if (!browser.msie) {
 312+ _reachBlockPoint(node, 1, state);
 313+ }
 314+ if (isBlock) {
 315+ if (lines.length() - 1 == startLine) {
 316+ _startNewLine(state);
 317+ } else {
 318+ _ensureColumnZero(state);
 319+ }
 320+ }
 321+
 322+ if (browser.msie) {
 323+ // in IE, a point immediately after a DIV appears on the next line
 324+ //_reachBlockPoint(node, 1, state);
 325+ }
 326+ };
 327+ // can pass a falsy value for end of doc
 328+ cc.notifyNextNode = function(node) {
 329+ // an "empty block" won't end a line; this addresses an issue in IE with
 330+ // typing into a blank line at the end of the document. typed text
 331+ // goes into the body, and the empty line div still looks clean.
 332+ // it is incorporated as dirty by the rule that a dirty region has
 333+ // to end a line.
 334+ if ((!node) || (isBlockElement(node) && !_isEmpty(node))) {
 335+ _ensureColumnZero(null);
 336+ }
 337+ };
 338+ // each returns [line, char] or [-1,-1]
 339+ var getSelectionStart = function() {
 340+ return selStart;
 341+ };
 342+ var getSelectionEnd = function() {
 343+ return selEnd;
 344+ };
 345+
 346+ // returns array of strings for lines found, last entry will be "" if
 347+ // last line is complete (i.e. if a following span should be on a new line).
 348+ // can be called at any point
 349+ cc.getLines = function() {
 350+ return lines.textLines();
 351+ };
 352+
 353+ // cc.applyHints = function(hints) {
 354+ // if (hints.pastedLines) {
 355+ //
 356+ // }
 357+ // }
 358+
 359+ cc.finish = function() {
 360+ lines.flush();
 361+ var lineStrings = cc.getLines();
 362+
 363+ if ( lineStrings.length > 0 && !lineStrings[lineStrings.length - 1] ) {
 364+ lineStrings.length--;
 365+ }
 366+
 367+ var ss = getSelectionStart();
 368+ var se = getSelectionEnd();
 369+
 370+ function fixLongLines() {
 371+ // design mode does not deal with with really long lines!
 372+ var lineLimit = 2000; // chars
 373+ var buffer = 10; // chars allowed over before wrapping
 374+ var linesWrapped = 0;
 375+ var numLinesAfter = 0;
 376+ for ( var i = lineStrings.length - 1; i >= 0; i--) {
 377+ var oldString = lineStrings[i];
 378+ if (oldString.length > lineLimit + buffer) {
 379+ var newStrings = [];
 380+ while (oldString.length > lineLimit) {
 381+ // var semiloc = oldString.lastIndexOf(';',
 382+ // lineLimit-1);
 383+ // var lengthToTake = (semiloc >= 0 ? (semiloc+1) :
 384+ // lineLimit);
 385+ lengthToTake = lineLimit;
 386+ newStrings.push(oldString.substring(0, lengthToTake));
 387+ oldString = oldString.substring(lengthToTake);
 388+
 389+ }
 390+ if (oldString.length > 0) {
 391+ newStrings.push(oldString);
 392+ }
 393+ function fixLineNumber(lineChar) {
 394+ if (lineChar[0] < 0)
 395+ return;
 396+ var n = lineChar[0];
 397+ var c = lineChar[1];
 398+ if (n > i) {
 399+ n += (newStrings.length - 1);
 400+ } else if (n == i) {
 401+ var a = 0;
 402+ while (c > newStrings[a].length) {
 403+ c -= newStrings[a].length;
 404+ a++;
 405+ }
 406+ n += a;
 407+ }
 408+ lineChar[0] = n;
 409+ lineChar[1] = c;
 410+ }
 411+ fixLineNumber(ss);
 412+ fixLineNumber(se);
 413+ linesWrapped++;
 414+ numLinesAfter += newStrings.length;
 415+
 416+ newStrings.unshift(i, 1);
 417+ lineStrings.splice.apply(lineStrings, newStrings);
 418+
 419+ }
 420+ }
 421+ return {
 422+ linesWrapped : linesWrapped,
 423+ numLinesAfter : numLinesAfter
 424+ };
 425+ }
 426+ var wrapData = fixLongLines();
 427+
 428+ return {
 429+ selStart : ss,
 430+ selEnd : se,
 431+ linesWrapped : wrapData.linesWrapped,
 432+ numLinesAfter : wrapData.numLinesAfter,
 433+ lines : lineStrings
 434+ };
 435+ }
 436+
 437+ return cc;
 438+}
 439+
 440+
Property changes on: trunk/extensions/WikiEditor/modules/contentCollector.js
___________________________________________________________________
Added: svn:eol-style
1441 + native

Status & tagging log