r110067 MediaWiki - Code Review archive

Repository:MediaWiki
Revision:r110066‎ | r110067 | r110068 >
Date:18:41, 26 January 2012
Author:ialex
Status:ok
Tags:
Comment:
svn:eol-style native
Modified paths:
  • /trunk/extensions/EducationProgram/includes/EPPageObject.php (modified) (history)
  • /trunk/extensions/EducationProgram/includes/EPRevision.php (modified) (history)
  • /trunk/extensions/EducationProgram/includes/EPRevisionPager.php (modified) (history)
  • /trunk/extensions/EducationProgram/includes/EPRevisions.php (modified) (history)
  • /trunk/extensions/EducationProgram/specials/SpecialCourseHistory.php (modified) (history)
  • /trunk/extensions/EducationProgram/specials/SpecialEPHistory.php (modified) (history)
  • /trunk/extensions/EducationProgram/specials/SpecialInstitutionHistory.php (modified) (history)
  • /trunk/extensions/MwEmbedSupport/MwEmbedModules/MwEmbedSupport/jquery.loadingSpinner/spinner.js (modified) (history)
  • /trunk/extensions/VisualEditor/contentEditable/diff_match_patch.js (modified) (history)
  • /trunk/extensions/VisualEditor/contentEditable/index.php (modified) (history)
  • /trunk/extensions/VisualEditor/contentEditable/main.js (modified) (history)
  • /trunk/extensions/VisualEditor/contentEditable/rangy/rangy-cssclassapplier.js (modified) (history)
  • /trunk/extensions/VisualEditor/contentEditable/rangy/rangy-selectionsaverestore.js (modified) (history)
  • /trunk/extensions/VisualEditor/contentEditable/rangy/rangy-serializer.js (modified) (history)
  • /trunk/extensions/VisualEditor/contentEditable/views/es.ContentView.js (modified) (history)
  • /trunk/extensions/VisualEditor/contentEditable/views/es.DocumentView.js (modified) (history)
  • /trunk/extensions/VisualEditor/contentEditable/views/es.DocumentViewLeafNode.js (modified) (history)
  • /trunk/extensions/VisualEditor/contentEditable/views/es.ParagraphView.js (modified) (history)
  • /trunk/extensions/VisualEditor/contentEditable/views/es.SurfaceView.js (modified) (history)

Diff [purge]

Property changes on: trunk/extensions/VisualEditor/contentEditable/main.js
___________________________________________________________________
Added: svn:eol-style
11 + native
Index: trunk/extensions/VisualEditor/contentEditable/rangy/rangy-selectionsaverestore.js
@@ -1,195 +1,195 @@
2 -/**
3 - * @license Selection save and restore module for Rangy.
4 - * Saves and restores user selections using marker invisible elements in the DOM.
5 - *
6 - * Part of Rangy, a cross-browser JavaScript range and selection library
7 - * http://code.google.com/p/rangy/
8 - *
9 - * Depends on Rangy core.
10 - *
11 - * Copyright 2011, Tim Down
12 - * Licensed under the MIT license.
13 - * Version: 1.2.2
14 - * Build date: 13 November 2011
15 - */
16 -rangy.createModule("SaveRestore", function(api, module) {
17 - api.requireModules( ["DomUtil", "DomRange", "WrappedRange"] );
18 -
19 - var dom = api.dom;
20 -
21 - var markerTextChar = "\ufeff";
22 -
23 - function gEBI(id, doc) {
24 - return (doc || document).getElementById(id);
25 - }
26 -
27 - function insertRangeBoundaryMarker(range, atStart) {
28 - var markerId = "selectionBoundary_" + (+new Date()) + "_" + ("" + Math.random()).slice(2);
29 - var markerEl;
30 - var doc = dom.getDocument(range.startContainer);
31 -
32 - // Clone the Range and collapse to the appropriate boundary point
33 - var boundaryRange = range.cloneRange();
34 - boundaryRange.collapse(atStart);
35 -
36 - // Create the marker element containing a single invisible character using DOM methods and insert it
37 - markerEl = doc.createElement("span");
38 - markerEl.id = markerId;
39 - markerEl.style.lineHeight = "0";
40 - markerEl.style.display = "none";
41 - markerEl.className = "rangySelectionBoundary";
42 - markerEl.appendChild(doc.createTextNode(markerTextChar));
43 -
44 - boundaryRange.insertNode(markerEl);
45 - boundaryRange.detach();
46 - return markerEl;
47 - }
48 -
49 - function setRangeBoundary(doc, range, markerId, atStart) {
50 - var markerEl = gEBI(markerId, doc);
51 - if (markerEl) {
52 - range[atStart ? "setStartBefore" : "setEndBefore"](markerEl);
53 - markerEl.parentNode.removeChild(markerEl);
54 - } else {
55 - module.warn("Marker element has been removed. Cannot restore selection.");
56 - }
57 - }
58 -
59 - function compareRanges(r1, r2) {
60 - return r2.compareBoundaryPoints(r1.START_TO_START, r1);
61 - }
62 -
63 - function saveSelection(win) {
64 - win = win || window;
65 - var doc = win.document;
66 - if (!api.isSelectionValid(win)) {
67 - module.warn("Cannot save selection. This usually happens when the selection is collapsed and the selection document has lost focus.");
68 - return;
69 - }
70 - var sel = api.getSelection(win);
71 - var ranges = sel.getAllRanges();
72 - var rangeInfos = [], startEl, endEl, range;
73 -
74 - // Order the ranges by position within the DOM, latest first
75 - ranges.sort(compareRanges);
76 -
77 - for (var i = 0, len = ranges.length; i < len; ++i) {
78 - range = ranges[i];
79 - if (range.collapsed) {
80 - endEl = insertRangeBoundaryMarker(range, false);
81 - rangeInfos.push({
82 - markerId: endEl.id,
83 - collapsed: true
84 - });
85 - } else {
86 - endEl = insertRangeBoundaryMarker(range, false);
87 - startEl = insertRangeBoundaryMarker(range, true);
88 -
89 - rangeInfos[i] = {
90 - startMarkerId: startEl.id,
91 - endMarkerId: endEl.id,
92 - collapsed: false,
93 - backwards: ranges.length == 1 && sel.isBackwards()
94 - };
95 - }
96 - }
97 -
98 - // Now that all the markers are in place and DOM manipulation over, adjust each range's boundaries to lie
99 - // between its markers
100 - for (i = len - 1; i >= 0; --i) {
101 - range = ranges[i];
102 - if (range.collapsed) {
103 - range.collapseBefore(gEBI(rangeInfos[i].markerId, doc));
104 - } else {
105 - range.setEndBefore(gEBI(rangeInfos[i].endMarkerId, doc));
106 - range.setStartAfter(gEBI(rangeInfos[i].startMarkerId, doc));
107 - }
108 - }
109 -
110 - // Ensure current selection is unaffected
111 - sel.setRanges(ranges);
112 - return {
113 - win: win,
114 - doc: doc,
115 - rangeInfos: rangeInfos,
116 - restored: false
117 - };
118 - }
119 -
120 - function restoreSelection(savedSelection, preserveDirection) {
121 - if (!savedSelection.restored) {
122 - var rangeInfos = savedSelection.rangeInfos;
123 - var sel = api.getSelection(savedSelection.win);
124 - var ranges = [];
125 -
126 - // Ranges are in reverse order of appearance in the DOM. We want to restore earliest first to avoid
127 - // normalization affecting previously restored ranges.
128 - for (var len = rangeInfos.length, i = len - 1, rangeInfo, range; i >= 0; --i) {
129 - rangeInfo = rangeInfos[i];
130 - range = api.createRange(savedSelection.doc);
131 - if (rangeInfo.collapsed) {
132 - var markerEl = gEBI(rangeInfo.markerId, savedSelection.doc);
133 - if (markerEl) {
134 - markerEl.style.display = "inline";
135 - var previousNode = markerEl.previousSibling;
136 -
137 - // Workaround for issue 17
138 - if (previousNode && previousNode.nodeType == 3) {
139 - markerEl.parentNode.removeChild(markerEl);
140 - range.collapseToPoint(previousNode, previousNode.length);
141 - } else {
142 - range.collapseBefore(markerEl);
143 - markerEl.parentNode.removeChild(markerEl);
144 - }
145 - } else {
146 - module.warn("Marker element has been removed. Cannot restore selection.");
147 - }
148 - } else {
149 - setRangeBoundary(savedSelection.doc, range, rangeInfo.startMarkerId, true);
150 - setRangeBoundary(savedSelection.doc, range, rangeInfo.endMarkerId, false);
151 - }
152 -
153 - // Normalizing range boundaries is only viable if the selection contains only one range. For example,
154 - // if the selection contained two ranges that were both contained within the same single text node,
155 - // both would alter the same text node when restoring and break the other range.
156 - if (len == 1) {
157 - range.normalizeBoundaries();
158 - }
159 - ranges[i] = range;
160 - }
161 - if (len == 1 && preserveDirection && api.features.selectionHasExtend && rangeInfos[0].backwards) {
162 - sel.removeAllRanges();
163 - sel.addRange(ranges[0], true);
164 - } else {
165 - sel.setRanges(ranges);
166 - }
167 -
168 - savedSelection.restored = true;
169 - }
170 - }
171 -
172 - function removeMarkerElement(doc, markerId) {
173 - var markerEl = gEBI(markerId, doc);
174 - if (markerEl) {
175 - markerEl.parentNode.removeChild(markerEl);
176 - }
177 - }
178 -
179 - function removeMarkers(savedSelection) {
180 - var rangeInfos = savedSelection.rangeInfos;
181 - for (var i = 0, len = rangeInfos.length, rangeInfo; i < len; ++i) {
182 - rangeInfo = rangeInfos[i];
183 - if (rangeInfo.collapsed) {
184 - removeMarkerElement(savedSelection.doc, rangeInfo.markerId);
185 - } else {
186 - removeMarkerElement(savedSelection.doc, rangeInfo.startMarkerId);
187 - removeMarkerElement(savedSelection.doc, rangeInfo.endMarkerId);
188 - }
189 - }
190 - }
191 -
192 - api.saveSelection = saveSelection;
193 - api.restoreSelection = restoreSelection;
194 - api.removeMarkerElement = removeMarkerElement;
195 - api.removeMarkers = removeMarkers;
196 -});
 2+/**
 3+ * @license Selection save and restore module for Rangy.
 4+ * Saves and restores user selections using marker invisible elements in the DOM.
 5+ *
 6+ * Part of Rangy, a cross-browser JavaScript range and selection library
 7+ * http://code.google.com/p/rangy/
 8+ *
 9+ * Depends on Rangy core.
 10+ *
 11+ * Copyright 2011, Tim Down
 12+ * Licensed under the MIT license.
 13+ * Version: 1.2.2
 14+ * Build date: 13 November 2011
 15+ */
 16+rangy.createModule("SaveRestore", function(api, module) {
 17+ api.requireModules( ["DomUtil", "DomRange", "WrappedRange"] );
 18+
 19+ var dom = api.dom;
 20+
 21+ var markerTextChar = "\ufeff";
 22+
 23+ function gEBI(id, doc) {
 24+ return (doc || document).getElementById(id);
 25+ }
 26+
 27+ function insertRangeBoundaryMarker(range, atStart) {
 28+ var markerId = "selectionBoundary_" + (+new Date()) + "_" + ("" + Math.random()).slice(2);
 29+ var markerEl;
 30+ var doc = dom.getDocument(range.startContainer);
 31+
 32+ // Clone the Range and collapse to the appropriate boundary point
 33+ var boundaryRange = range.cloneRange();
 34+ boundaryRange.collapse(atStart);
 35+
 36+ // Create the marker element containing a single invisible character using DOM methods and insert it
 37+ markerEl = doc.createElement("span");
 38+ markerEl.id = markerId;
 39+ markerEl.style.lineHeight = "0";
 40+ markerEl.style.display = "none";
 41+ markerEl.className = "rangySelectionBoundary";
 42+ markerEl.appendChild(doc.createTextNode(markerTextChar));
 43+
 44+ boundaryRange.insertNode(markerEl);
 45+ boundaryRange.detach();
 46+ return markerEl;
 47+ }
 48+
 49+ function setRangeBoundary(doc, range, markerId, atStart) {
 50+ var markerEl = gEBI(markerId, doc);
 51+ if (markerEl) {
 52+ range[atStart ? "setStartBefore" : "setEndBefore"](markerEl);
 53+ markerEl.parentNode.removeChild(markerEl);
 54+ } else {
 55+ module.warn("Marker element has been removed. Cannot restore selection.");
 56+ }
 57+ }
 58+
 59+ function compareRanges(r1, r2) {
 60+ return r2.compareBoundaryPoints(r1.START_TO_START, r1);
 61+ }
 62+
 63+ function saveSelection(win) {
 64+ win = win || window;
 65+ var doc = win.document;
 66+ if (!api.isSelectionValid(win)) {
 67+ module.warn("Cannot save selection. This usually happens when the selection is collapsed and the selection document has lost focus.");
 68+ return;
 69+ }
 70+ var sel = api.getSelection(win);
 71+ var ranges = sel.getAllRanges();
 72+ var rangeInfos = [], startEl, endEl, range;
 73+
 74+ // Order the ranges by position within the DOM, latest first
 75+ ranges.sort(compareRanges);
 76+
 77+ for (var i = 0, len = ranges.length; i < len; ++i) {
 78+ range = ranges[i];
 79+ if (range.collapsed) {
 80+ endEl = insertRangeBoundaryMarker(range, false);
 81+ rangeInfos.push({
 82+ markerId: endEl.id,
 83+ collapsed: true
 84+ });
 85+ } else {
 86+ endEl = insertRangeBoundaryMarker(range, false);
 87+ startEl = insertRangeBoundaryMarker(range, true);
 88+
 89+ rangeInfos[i] = {
 90+ startMarkerId: startEl.id,
 91+ endMarkerId: endEl.id,
 92+ collapsed: false,
 93+ backwards: ranges.length == 1 && sel.isBackwards()
 94+ };
 95+ }
 96+ }
 97+
 98+ // Now that all the markers are in place and DOM manipulation over, adjust each range's boundaries to lie
 99+ // between its markers
 100+ for (i = len - 1; i >= 0; --i) {
 101+ range = ranges[i];
 102+ if (range.collapsed) {
 103+ range.collapseBefore(gEBI(rangeInfos[i].markerId, doc));
 104+ } else {
 105+ range.setEndBefore(gEBI(rangeInfos[i].endMarkerId, doc));
 106+ range.setStartAfter(gEBI(rangeInfos[i].startMarkerId, doc));
 107+ }
 108+ }
 109+
 110+ // Ensure current selection is unaffected
 111+ sel.setRanges(ranges);
 112+ return {
 113+ win: win,
 114+ doc: doc,
 115+ rangeInfos: rangeInfos,
 116+ restored: false
 117+ };
 118+ }
 119+
 120+ function restoreSelection(savedSelection, preserveDirection) {
 121+ if (!savedSelection.restored) {
 122+ var rangeInfos = savedSelection.rangeInfos;
 123+ var sel = api.getSelection(savedSelection.win);
 124+ var ranges = [];
 125+
 126+ // Ranges are in reverse order of appearance in the DOM. We want to restore earliest first to avoid
 127+ // normalization affecting previously restored ranges.
 128+ for (var len = rangeInfos.length, i = len - 1, rangeInfo, range; i >= 0; --i) {
 129+ rangeInfo = rangeInfos[i];
 130+ range = api.createRange(savedSelection.doc);
 131+ if (rangeInfo.collapsed) {
 132+ var markerEl = gEBI(rangeInfo.markerId, savedSelection.doc);
 133+ if (markerEl) {
 134+ markerEl.style.display = "inline";
 135+ var previousNode = markerEl.previousSibling;
 136+
 137+ // Workaround for issue 17
 138+ if (previousNode && previousNode.nodeType == 3) {
 139+ markerEl.parentNode.removeChild(markerEl);
 140+ range.collapseToPoint(previousNode, previousNode.length);
 141+ } else {
 142+ range.collapseBefore(markerEl);
 143+ markerEl.parentNode.removeChild(markerEl);
 144+ }
 145+ } else {
 146+ module.warn("Marker element has been removed. Cannot restore selection.");
 147+ }
 148+ } else {
 149+ setRangeBoundary(savedSelection.doc, range, rangeInfo.startMarkerId, true);
 150+ setRangeBoundary(savedSelection.doc, range, rangeInfo.endMarkerId, false);
 151+ }
 152+
 153+ // Normalizing range boundaries is only viable if the selection contains only one range. For example,
 154+ // if the selection contained two ranges that were both contained within the same single text node,
 155+ // both would alter the same text node when restoring and break the other range.
 156+ if (len == 1) {
 157+ range.normalizeBoundaries();
 158+ }
 159+ ranges[i] = range;
 160+ }
 161+ if (len == 1 && preserveDirection && api.features.selectionHasExtend && rangeInfos[0].backwards) {
 162+ sel.removeAllRanges();
 163+ sel.addRange(ranges[0], true);
 164+ } else {
 165+ sel.setRanges(ranges);
 166+ }
 167+
 168+ savedSelection.restored = true;
 169+ }
 170+ }
 171+
 172+ function removeMarkerElement(doc, markerId) {
 173+ var markerEl = gEBI(markerId, doc);
 174+ if (markerEl) {
 175+ markerEl.parentNode.removeChild(markerEl);
 176+ }
 177+ }
 178+
 179+ function removeMarkers(savedSelection) {
 180+ var rangeInfos = savedSelection.rangeInfos;
 181+ for (var i = 0, len = rangeInfos.length, rangeInfo; i < len; ++i) {
 182+ rangeInfo = rangeInfos[i];
 183+ if (rangeInfo.collapsed) {
 184+ removeMarkerElement(savedSelection.doc, rangeInfo.markerId);
 185+ } else {
 186+ removeMarkerElement(savedSelection.doc, rangeInfo.startMarkerId);
 187+ removeMarkerElement(savedSelection.doc, rangeInfo.endMarkerId);
 188+ }
 189+ }
 190+ }
 191+
 192+ api.saveSelection = saveSelection;
 193+ api.restoreSelection = restoreSelection;
 194+ api.removeMarkerElement = removeMarkerElement;
 195+ api.removeMarkers = removeMarkers;
 196+});
Property changes on: trunk/extensions/VisualEditor/contentEditable/rangy/rangy-selectionsaverestore.js
___________________________________________________________________
Added: svn:eol-style
197197 + native
Index: trunk/extensions/VisualEditor/contentEditable/rangy/rangy-serializer.js
@@ -1,300 +1,300 @@
2 -/**
3 - * @license Serializer module for Rangy.
4 - * Serializes Ranges and Selections. An example use would be to store a user's selection on a particular page in a
5 - * cookie or local storage and restore it on the user's next visit to the same page.
6 - *
7 - * Part of Rangy, a cross-browser JavaScript range and selection library
8 - * http://code.google.com/p/rangy/
9 - *
10 - * Depends on Rangy core.
11 - *
12 - * Copyright 2011, Tim Down
13 - * Licensed under the MIT license.
14 - * Version: 1.2.2
15 - * Build date: 13 November 2011
16 - */
17 -rangy.createModule("Serializer", function(api, module) {
18 - api.requireModules( ["WrappedSelection", "WrappedRange"] );
19 - var UNDEF = "undefined";
20 -
21 - // encodeURIComponent and decodeURIComponent are required for cookie handling
22 - if (typeof encodeURIComponent == UNDEF || typeof decodeURIComponent == UNDEF) {
23 - module.fail("Global object is missing encodeURIComponent and/or decodeURIComponent method");
24 - }
25 -
26 - // Checksum for checking whether range can be serialized
27 - var crc32 = (function() {
28 - function utf8encode(str) {
29 - var utf8CharCodes = [];
30 -
31 - for (var i = 0, len = str.length, c; i < len; ++i) {
32 - c = str.charCodeAt(i);
33 - if (c < 128) {
34 - utf8CharCodes.push(c);
35 - } else if (c < 2048) {
36 - utf8CharCodes.push((c >> 6) | 192, (c & 63) | 128);
37 - } else {
38 - utf8CharCodes.push((c >> 12) | 224, ((c >> 6) & 63) | 128, (c & 63) | 128);
39 - }
40 - }
41 - return utf8CharCodes;
42 - }
43 -
44 - var cachedCrcTable = null;
45 -
46 - function buildCRCTable() {
47 - var table = [];
48 - for (var i = 0, j, crc; i < 256; ++i) {
49 - crc = i;
50 - j = 8;
51 - while (j--) {
52 - if ((crc & 1) == 1) {
53 - crc = (crc >>> 1) ^ 0xEDB88320;
54 - } else {
55 - crc >>>= 1;
56 - }
57 - }
58 - table[i] = crc >>> 0;
59 - }
60 - return table;
61 - }
62 -
63 - function getCrcTable() {
64 - if (!cachedCrcTable) {
65 - cachedCrcTable = buildCRCTable();
66 - }
67 - return cachedCrcTable;
68 - }
69 -
70 - return function(str) {
71 - var utf8CharCodes = utf8encode(str), crc = -1, crcTable = getCrcTable();
72 - for (var i = 0, len = utf8CharCodes.length, y; i < len; ++i) {
73 - y = (crc ^ utf8CharCodes[i]) & 0xFF;
74 - crc = (crc >>> 8) ^ crcTable[y];
75 - }
76 - return (crc ^ -1) >>> 0;
77 - };
78 - })();
79 -
80 - var dom = api.dom;
81 -
82 - function escapeTextForHtml(str) {
83 - return str.replace(/</g, "&lt;").replace(/>/g, "&gt;");
84 - }
85 -
86 - function nodeToInfoString(node, infoParts) {
87 - infoParts = infoParts || [];
88 - var nodeType = node.nodeType, children = node.childNodes, childCount = children.length;
89 - var nodeInfo = [nodeType, node.nodeName, childCount].join(":");
90 - var start = "", end = "";
91 - switch (nodeType) {
92 - case 3: // Text node
93 - start = escapeTextForHtml(node.nodeValue);
94 - break;
95 - case 8: // Comment
96 - start = "<!--" + escapeTextForHtml(node.nodeValue) + "-->";
97 - break;
98 - default:
99 - start = "<" + nodeInfo + ">";
100 - end = "</>";
101 - break;
102 - }
103 - if (start) {
104 - infoParts.push(start);
105 - }
106 - for (var i = 0; i < childCount; ++i) {
107 - nodeToInfoString(children[i], infoParts);
108 - }
109 - if (end) {
110 - infoParts.push(end);
111 - }
112 - return infoParts;
113 - }
114 -
115 - // Creates a string representation of the specified element's contents that is similar to innerHTML but omits all
116 - // attributes and comments and includes child node counts. This is done instead of using innerHTML to work around
117 - // IE <= 8's policy of including element properties in attributes, which ruins things by changing an element's
118 - // innerHTML whenever the user changes an input within the element.
119 - function getElementChecksum(el) {
120 - var info = nodeToInfoString(el).join("");
121 - return crc32(info).toString(16);
122 - }
123 -
124 - function serializePosition(node, offset, rootNode) {
125 - var pathBits = [], n = node;
126 - rootNode = rootNode || dom.getDocument(node).documentElement;
127 - while (n && n != rootNode) {
128 - pathBits.push(dom.getNodeIndex(n, true));
129 - n = n.parentNode;
130 - }
131 - return pathBits.join("/") + ":" + offset;
132 - }
133 -
134 - function deserializePosition(serialized, rootNode, doc) {
135 - if (rootNode) {
136 - doc = doc || dom.getDocument(rootNode);
137 - } else {
138 - doc = doc || document;
139 - rootNode = doc.documentElement;
140 - }
141 - var bits = serialized.split(":");
142 - var node = rootNode;
143 - var nodeIndices = bits[0] ? bits[0].split("/") : [], i = nodeIndices.length, nodeIndex;
144 -
145 - while (i--) {
146 - nodeIndex = parseInt(nodeIndices[i], 10);
147 - if (nodeIndex < node.childNodes.length) {
148 - node = node.childNodes[parseInt(nodeIndices[i], 10)];
149 - } else {
150 - throw module.createError("deserializePosition failed: node " + dom.inspectNode(node) +
151 - " has no child with index " + nodeIndex + ", " + i);
152 - }
153 - }
154 -
155 - return new dom.DomPosition(node, parseInt(bits[1], 10));
156 - }
157 -
158 - function serializeRange(range, omitChecksum, rootNode) {
159 - rootNode = rootNode || api.DomRange.getRangeDocument(range).documentElement;
160 - if (!dom.isAncestorOf(rootNode, range.commonAncestorContainer, true)) {
161 - throw new Error("serializeRange: range is not wholly contained within specified root node");
162 - }
163 - var serialized = serializePosition(range.startContainer, range.startOffset, rootNode) + "," +
164 - serializePosition(range.endContainer, range.endOffset, rootNode);
165 - if (!omitChecksum) {
166 - serialized += "{" + getElementChecksum(rootNode) + "}";
167 - }
168 - return serialized;
169 - }
170 -
171 - function deserializeRange(serialized, rootNode, doc) {
172 - if (rootNode) {
173 - doc = doc || dom.getDocument(rootNode);
174 - } else {
175 - doc = doc || document;
176 - rootNode = doc.documentElement;
177 - }
178 - var result = /^([^,]+),([^,\{]+)({([^}]+)})?$/.exec(serialized);
179 - var checksum = result[4], rootNodeChecksum = getElementChecksum(rootNode);
180 - if (checksum && checksum !== getElementChecksum(rootNode)) {
181 - throw new Error("deserializeRange: checksums of serialized range root node (" + checksum +
182 - ") and target root node (" + rootNodeChecksum + ") do not match");
183 - }
184 - var start = deserializePosition(result[1], rootNode, doc), end = deserializePosition(result[2], rootNode, doc);
185 - var range = api.createRange(doc);
186 - range.setStart(start.node, start.offset);
187 - range.setEnd(end.node, end.offset);
188 - return range;
189 - }
190 -
191 - function canDeserializeRange(serialized, rootNode, doc) {
192 - if (rootNode) {
193 - doc = doc || dom.getDocument(rootNode);
194 - } else {
195 - doc = doc || document;
196 - rootNode = doc.documentElement;
197 - }
198 - var result = /^([^,]+),([^,]+)({([^}]+)})?$/.exec(serialized);
199 - var checksum = result[3];
200 - return !checksum || checksum === getElementChecksum(rootNode);
201 - }
202 -
203 - function serializeSelection(selection, omitChecksum, rootNode) {
204 - selection = selection || api.getSelection();
205 - var ranges = selection.getAllRanges(), serializedRanges = [];
206 - for (var i = 0, len = ranges.length; i < len; ++i) {
207 - serializedRanges[i] = serializeRange(ranges[i], omitChecksum, rootNode);
208 - }
209 - return serializedRanges.join("|");
210 - }
211 -
212 - function deserializeSelection(serialized, rootNode, win) {
213 - if (rootNode) {
214 - win = win || dom.getWindow(rootNode);
215 - } else {
216 - win = win || window;
217 - rootNode = win.document.documentElement;
218 - }
219 - var serializedRanges = serialized.split("|");
220 - var sel = api.getSelection(win);
221 - var ranges = [];
222 -
223 - for (var i = 0, len = serializedRanges.length; i < len; ++i) {
224 - ranges[i] = deserializeRange(serializedRanges[i], rootNode, win.document);
225 - }
226 - sel.setRanges(ranges);
227 -
228 - return sel;
229 - }
230 -
231 - function canDeserializeSelection(serialized, rootNode, win) {
232 - var doc;
233 - if (rootNode) {
234 - doc = win ? win.document : dom.getDocument(rootNode);
235 - } else {
236 - win = win || window;
237 - rootNode = win.document.documentElement;
238 - }
239 - var serializedRanges = serialized.split("|");
240 -
241 - for (var i = 0, len = serializedRanges.length; i < len; ++i) {
242 - if (!canDeserializeRange(serializedRanges[i], rootNode, doc)) {
243 - return false;
244 - }
245 - }
246 -
247 - return true;
248 - }
249 -
250 -
251 - var cookieName = "rangySerializedSelection";
252 -
253 - function getSerializedSelectionFromCookie(cookie) {
254 - var parts = cookie.split(/[;,]/);
255 - for (var i = 0, len = parts.length, nameVal, val; i < len; ++i) {
256 - nameVal = parts[i].split("=");
257 - if (nameVal[0].replace(/^\s+/, "") == cookieName) {
258 - val = nameVal[1];
259 - if (val) {
260 - return decodeURIComponent(val.replace(/\s+$/, ""));
261 - }
262 - }
263 - }
264 - return null;
265 - }
266 -
267 - function restoreSelectionFromCookie(win) {
268 - win = win || window;
269 - var serialized = getSerializedSelectionFromCookie(win.document.cookie);
270 - if (serialized) {
271 - deserializeSelection(serialized, win.doc)
272 - }
273 - }
274 -
275 - function saveSelectionCookie(win, props) {
276 - win = win || window;
277 - props = (typeof props == "object") ? props : {};
278 - var expires = props.expires ? ";expires=" + props.expires.toUTCString() : "";
279 - var path = props.path ? ";path=" + props.path : "";
280 - var domain = props.domain ? ";domain=" + props.domain : "";
281 - var secure = props.secure ? ";secure" : "";
282 - var serialized = serializeSelection(api.getSelection(win));
283 - win.document.cookie = encodeURIComponent(cookieName) + "=" + encodeURIComponent(serialized) + expires + path + domain + secure;
284 - }
285 -
286 - api.serializePosition = serializePosition;
287 - api.deserializePosition = deserializePosition;
288 -
289 - api.serializeRange = serializeRange;
290 - api.deserializeRange = deserializeRange;
291 - api.canDeserializeRange = canDeserializeRange;
292 -
293 - api.serializeSelection = serializeSelection;
294 - api.deserializeSelection = deserializeSelection;
295 - api.canDeserializeSelection = canDeserializeSelection;
296 -
297 - api.restoreSelectionFromCookie = restoreSelectionFromCookie;
298 - api.saveSelectionCookie = saveSelectionCookie;
299 -
300 - api.getElementChecksum = getElementChecksum;
301 -});
 2+/**
 3+ * @license Serializer module for Rangy.
 4+ * Serializes Ranges and Selections. An example use would be to store a user's selection on a particular page in a
 5+ * cookie or local storage and restore it on the user's next visit to the same page.
 6+ *
 7+ * Part of Rangy, a cross-browser JavaScript range and selection library
 8+ * http://code.google.com/p/rangy/
 9+ *
 10+ * Depends on Rangy core.
 11+ *
 12+ * Copyright 2011, Tim Down
 13+ * Licensed under the MIT license.
 14+ * Version: 1.2.2
 15+ * Build date: 13 November 2011
 16+ */
 17+rangy.createModule("Serializer", function(api, module) {
 18+ api.requireModules( ["WrappedSelection", "WrappedRange"] );
 19+ var UNDEF = "undefined";
 20+
 21+ // encodeURIComponent and decodeURIComponent are required for cookie handling
 22+ if (typeof encodeURIComponent == UNDEF || typeof decodeURIComponent == UNDEF) {
 23+ module.fail("Global object is missing encodeURIComponent and/or decodeURIComponent method");
 24+ }
 25+
 26+ // Checksum for checking whether range can be serialized
 27+ var crc32 = (function() {
 28+ function utf8encode(str) {
 29+ var utf8CharCodes = [];
 30+
 31+ for (var i = 0, len = str.length, c; i < len; ++i) {
 32+ c = str.charCodeAt(i);
 33+ if (c < 128) {
 34+ utf8CharCodes.push(c);
 35+ } else if (c < 2048) {
 36+ utf8CharCodes.push((c >> 6) | 192, (c & 63) | 128);
 37+ } else {
 38+ utf8CharCodes.push((c >> 12) | 224, ((c >> 6) & 63) | 128, (c & 63) | 128);
 39+ }
 40+ }
 41+ return utf8CharCodes;
 42+ }
 43+
 44+ var cachedCrcTable = null;
 45+
 46+ function buildCRCTable() {
 47+ var table = [];
 48+ for (var i = 0, j, crc; i < 256; ++i) {
 49+ crc = i;
 50+ j = 8;
 51+ while (j--) {
 52+ if ((crc & 1) == 1) {
 53+ crc = (crc >>> 1) ^ 0xEDB88320;
 54+ } else {
 55+ crc >>>= 1;
 56+ }
 57+ }
 58+ table[i] = crc >>> 0;
 59+ }
 60+ return table;
 61+ }
 62+
 63+ function getCrcTable() {
 64+ if (!cachedCrcTable) {
 65+ cachedCrcTable = buildCRCTable();
 66+ }
 67+ return cachedCrcTable;
 68+ }
 69+
 70+ return function(str) {
 71+ var utf8CharCodes = utf8encode(str), crc = -1, crcTable = getCrcTable();
 72+ for (var i = 0, len = utf8CharCodes.length, y; i < len; ++i) {
 73+ y = (crc ^ utf8CharCodes[i]) & 0xFF;
 74+ crc = (crc >>> 8) ^ crcTable[y];
 75+ }
 76+ return (crc ^ -1) >>> 0;
 77+ };
 78+ })();
 79+
 80+ var dom = api.dom;
 81+
 82+ function escapeTextForHtml(str) {
 83+ return str.replace(/</g, "&lt;").replace(/>/g, "&gt;");
 84+ }
 85+
 86+ function nodeToInfoString(node, infoParts) {
 87+ infoParts = infoParts || [];
 88+ var nodeType = node.nodeType, children = node.childNodes, childCount = children.length;
 89+ var nodeInfo = [nodeType, node.nodeName, childCount].join(":");
 90+ var start = "", end = "";
 91+ switch (nodeType) {
 92+ case 3: // Text node
 93+ start = escapeTextForHtml(node.nodeValue);
 94+ break;
 95+ case 8: // Comment
 96+ start = "<!--" + escapeTextForHtml(node.nodeValue) + "-->";
 97+ break;
 98+ default:
 99+ start = "<" + nodeInfo + ">";
 100+ end = "</>";
 101+ break;
 102+ }
 103+ if (start) {
 104+ infoParts.push(start);
 105+ }
 106+ for (var i = 0; i < childCount; ++i) {
 107+ nodeToInfoString(children[i], infoParts);
 108+ }
 109+ if (end) {
 110+ infoParts.push(end);
 111+ }
 112+ return infoParts;
 113+ }
 114+
 115+ // Creates a string representation of the specified element's contents that is similar to innerHTML but omits all
 116+ // attributes and comments and includes child node counts. This is done instead of using innerHTML to work around
 117+ // IE <= 8's policy of including element properties in attributes, which ruins things by changing an element's
 118+ // innerHTML whenever the user changes an input within the element.
 119+ function getElementChecksum(el) {
 120+ var info = nodeToInfoString(el).join("");
 121+ return crc32(info).toString(16);
 122+ }
 123+
 124+ function serializePosition(node, offset, rootNode) {
 125+ var pathBits = [], n = node;
 126+ rootNode = rootNode || dom.getDocument(node).documentElement;
 127+ while (n && n != rootNode) {
 128+ pathBits.push(dom.getNodeIndex(n, true));
 129+ n = n.parentNode;
 130+ }
 131+ return pathBits.join("/") + ":" + offset;
 132+ }
 133+
 134+ function deserializePosition(serialized, rootNode, doc) {
 135+ if (rootNode) {
 136+ doc = doc || dom.getDocument(rootNode);
 137+ } else {
 138+ doc = doc || document;
 139+ rootNode = doc.documentElement;
 140+ }
 141+ var bits = serialized.split(":");
 142+ var node = rootNode;
 143+ var nodeIndices = bits[0] ? bits[0].split("/") : [], i = nodeIndices.length, nodeIndex;
 144+
 145+ while (i--) {
 146+ nodeIndex = parseInt(nodeIndices[i], 10);
 147+ if (nodeIndex < node.childNodes.length) {
 148+ node = node.childNodes[parseInt(nodeIndices[i], 10)];
 149+ } else {
 150+ throw module.createError("deserializePosition failed: node " + dom.inspectNode(node) +
 151+ " has no child with index " + nodeIndex + ", " + i);
 152+ }
 153+ }
 154+
 155+ return new dom.DomPosition(node, parseInt(bits[1], 10));
 156+ }
 157+
 158+ function serializeRange(range, omitChecksum, rootNode) {
 159+ rootNode = rootNode || api.DomRange.getRangeDocument(range).documentElement;
 160+ if (!dom.isAncestorOf(rootNode, range.commonAncestorContainer, true)) {
 161+ throw new Error("serializeRange: range is not wholly contained within specified root node");
 162+ }
 163+ var serialized = serializePosition(range.startContainer, range.startOffset, rootNode) + "," +
 164+ serializePosition(range.endContainer, range.endOffset, rootNode);
 165+ if (!omitChecksum) {
 166+ serialized += "{" + getElementChecksum(rootNode) + "}";
 167+ }
 168+ return serialized;
 169+ }
 170+
 171+ function deserializeRange(serialized, rootNode, doc) {
 172+ if (rootNode) {
 173+ doc = doc || dom.getDocument(rootNode);
 174+ } else {
 175+ doc = doc || document;
 176+ rootNode = doc.documentElement;
 177+ }
 178+ var result = /^([^,]+),([^,\{]+)({([^}]+)})?$/.exec(serialized);
 179+ var checksum = result[4], rootNodeChecksum = getElementChecksum(rootNode);
 180+ if (checksum && checksum !== getElementChecksum(rootNode)) {
 181+ throw new Error("deserializeRange: checksums of serialized range root node (" + checksum +
 182+ ") and target root node (" + rootNodeChecksum + ") do not match");
 183+ }
 184+ var start = deserializePosition(result[1], rootNode, doc), end = deserializePosition(result[2], rootNode, doc);
 185+ var range = api.createRange(doc);
 186+ range.setStart(start.node, start.offset);
 187+ range.setEnd(end.node, end.offset);
 188+ return range;
 189+ }
 190+
 191+ function canDeserializeRange(serialized, rootNode, doc) {
 192+ if (rootNode) {
 193+ doc = doc || dom.getDocument(rootNode);
 194+ } else {
 195+ doc = doc || document;
 196+ rootNode = doc.documentElement;
 197+ }
 198+ var result = /^([^,]+),([^,]+)({([^}]+)})?$/.exec(serialized);
 199+ var checksum = result[3];
 200+ return !checksum || checksum === getElementChecksum(rootNode);
 201+ }
 202+
 203+ function serializeSelection(selection, omitChecksum, rootNode) {
 204+ selection = selection || api.getSelection();
 205+ var ranges = selection.getAllRanges(), serializedRanges = [];
 206+ for (var i = 0, len = ranges.length; i < len; ++i) {
 207+ serializedRanges[i] = serializeRange(ranges[i], omitChecksum, rootNode);
 208+ }
 209+ return serializedRanges.join("|");
 210+ }
 211+
 212+ function deserializeSelection(serialized, rootNode, win) {
 213+ if (rootNode) {
 214+ win = win || dom.getWindow(rootNode);
 215+ } else {
 216+ win = win || window;
 217+ rootNode = win.document.documentElement;
 218+ }
 219+ var serializedRanges = serialized.split("|");
 220+ var sel = api.getSelection(win);
 221+ var ranges = [];
 222+
 223+ for (var i = 0, len = serializedRanges.length; i < len; ++i) {
 224+ ranges[i] = deserializeRange(serializedRanges[i], rootNode, win.document);
 225+ }
 226+ sel.setRanges(ranges);
 227+
 228+ return sel;
 229+ }
 230+
 231+ function canDeserializeSelection(serialized, rootNode, win) {
 232+ var doc;
 233+ if (rootNode) {
 234+ doc = win ? win.document : dom.getDocument(rootNode);
 235+ } else {
 236+ win = win || window;
 237+ rootNode = win.document.documentElement;
 238+ }
 239+ var serializedRanges = serialized.split("|");
 240+
 241+ for (var i = 0, len = serializedRanges.length; i < len; ++i) {
 242+ if (!canDeserializeRange(serializedRanges[i], rootNode, doc)) {
 243+ return false;
 244+ }
 245+ }
 246+
 247+ return true;
 248+ }
 249+
 250+
 251+ var cookieName = "rangySerializedSelection";
 252+
 253+ function getSerializedSelectionFromCookie(cookie) {
 254+ var parts = cookie.split(/[;,]/);
 255+ for (var i = 0, len = parts.length, nameVal, val; i < len; ++i) {
 256+ nameVal = parts[i].split("=");
 257+ if (nameVal[0].replace(/^\s+/, "") == cookieName) {
 258+ val = nameVal[1];
 259+ if (val) {
 260+ return decodeURIComponent(val.replace(/\s+$/, ""));
 261+ }
 262+ }
 263+ }
 264+ return null;
 265+ }
 266+
 267+ function restoreSelectionFromCookie(win) {
 268+ win = win || window;
 269+ var serialized = getSerializedSelectionFromCookie(win.document.cookie);
 270+ if (serialized) {
 271+ deserializeSelection(serialized, win.doc)
 272+ }
 273+ }
 274+
 275+ function saveSelectionCookie(win, props) {
 276+ win = win || window;
 277+ props = (typeof props == "object") ? props : {};
 278+ var expires = props.expires ? ";expires=" + props.expires.toUTCString() : "";
 279+ var path = props.path ? ";path=" + props.path : "";
 280+ var domain = props.domain ? ";domain=" + props.domain : "";
 281+ var secure = props.secure ? ";secure" : "";
 282+ var serialized = serializeSelection(api.getSelection(win));
 283+ win.document.cookie = encodeURIComponent(cookieName) + "=" + encodeURIComponent(serialized) + expires + path + domain + secure;
 284+ }
 285+
 286+ api.serializePosition = serializePosition;
 287+ api.deserializePosition = deserializePosition;
 288+
 289+ api.serializeRange = serializeRange;
 290+ api.deserializeRange = deserializeRange;
 291+ api.canDeserializeRange = canDeserializeRange;
 292+
 293+ api.serializeSelection = serializeSelection;
 294+ api.deserializeSelection = deserializeSelection;
 295+ api.canDeserializeSelection = canDeserializeSelection;
 296+
 297+ api.restoreSelectionFromCookie = restoreSelectionFromCookie;
 298+ api.saveSelectionCookie = saveSelectionCookie;
 299+
 300+ api.getElementChecksum = getElementChecksum;
 301+});
Property changes on: trunk/extensions/VisualEditor/contentEditable/rangy/rangy-serializer.js
___________________________________________________________________
Added: svn:eol-style
302302 + native
Index: trunk/extensions/VisualEditor/contentEditable/rangy/rangy-cssclassapplier.js
@@ -1,713 +1,713 @@
2 -/**
3 - * @license CSS Class Applier module for Rangy.
4 - * Adds, removes and toggles CSS classes on Ranges and Selections
5 - *
6 - * Part of Rangy, a cross-browser JavaScript range and selection library
7 - * http://code.google.com/p/rangy/
8 - *
9 - * Depends on Rangy core.
10 - *
11 - * Copyright 2011, Tim Down
12 - * Licensed under the MIT license.
13 - * Version: 1.2.2
14 - * Build date: 13 November 2011
15 - */
16 -rangy.createModule("CssClassApplier", function(api, module) {
17 - api.requireModules( ["WrappedSelection", "WrappedRange"] );
18 -
19 - var dom = api.dom;
20 -
21 -
22 -
23 - var defaultTagName = "span";
24 -
25 - function trim(str) {
26 - return str.replace(/^\s\s*/, "").replace(/\s\s*$/, "");
27 - }
28 -
29 - function hasClass(el, cssClass) {
30 - return el.className && new RegExp("(?:^|\\s)" + cssClass + "(?:\\s|$)").test(el.className);
31 - }
32 -
33 - function addClass(el, cssClass) {
34 - if (el.className) {
35 - if (!hasClass(el, cssClass)) {
36 - el.className += " " + cssClass;
37 - }
38 - } else {
39 - el.className = cssClass;
40 - }
41 - }
42 -
43 - var removeClass = (function() {
44 - function replacer(matched, whiteSpaceBefore, whiteSpaceAfter) {
45 - return (whiteSpaceBefore && whiteSpaceAfter) ? " " : "";
46 - }
47 -
48 - return function(el, cssClass) {
49 - if (el.className) {
50 - el.className = el.className.replace(new RegExp("(?:^|\\s)" + cssClass + "(?:\\s|$)"), replacer);
51 - }
52 - };
53 - })();
54 -
55 - function sortClassName(className) {
56 - return className.split(/\s+/).sort().join(" ");
57 - }
58 -
59 - function getSortedClassName(el) {
60 - return sortClassName(el.className);
61 - }
62 -
63 - function haveSameClasses(el1, el2) {
64 - return getSortedClassName(el1) == getSortedClassName(el2);
65 - }
66 -
67 - function replaceWithOwnChildren(el) {
68 -
69 - var parent = el.parentNode;
70 - while (el.hasChildNodes()) {
71 - parent.insertBefore(el.firstChild, el);
72 - }
73 - parent.removeChild(el);
74 - }
75 -
76 - function rangeSelectsAnyText(range, textNode) {
77 - var textRange = range.cloneRange();
78 - textRange.selectNodeContents(textNode);
79 -
80 - var intersectionRange = textRange.intersection(range);
81 - var text = intersectionRange ? intersectionRange.toString() : "";
82 - textRange.detach();
83 -
84 - return text != "";
85 - }
86 -
87 - function getEffectiveTextNodes(range) {
88 - return range.getNodes([3], function(textNode) {
89 - return rangeSelectsAnyText(range, textNode);
90 - });
91 - }
92 -
93 - function elementsHaveSameNonClassAttributes(el1, el2) {
94 - if (el1.attributes.length != el2.attributes.length) return false;
95 - for (var i = 0, len = el1.attributes.length, attr1, attr2, name; i < len; ++i) {
96 - attr1 = el1.attributes[i];
97 - name = attr1.name;
98 - if (name != "class") {
99 - attr2 = el2.attributes.getNamedItem(name);
100 - if (attr1.specified != attr2.specified) return false;
101 - if (attr1.specified && attr1.nodeValue !== attr2.nodeValue) return false;
102 - }
103 - }
104 - return true;
105 - }
106 -
107 - function elementHasNonClassAttributes(el, exceptions) {
108 - for (var i = 0, len = el.attributes.length, attrName; i < len; ++i) {
109 - attrName = el.attributes[i].name;
110 - if ( !(exceptions && dom.arrayContains(exceptions, attrName)) && el.attributes[i].specified && attrName != "class") {
111 - return true;
112 - }
113 - }
114 - return false;
115 - }
116 -
117 - function elementHasProps(el, props) {
118 - for (var p in props) {
119 - if (props.hasOwnProperty(p) && el[p] !== props[p]) {
120 - return false;
121 - }
122 - }
123 - return true;
124 - }
125 -
126 - var getComputedStyleProperty;
127 -
128 - if (typeof window.getComputedStyle != "undefined") {
129 - getComputedStyleProperty = function(el, propName) {
130 - return dom.getWindow(el).getComputedStyle(el, null)[propName];
131 - };
132 - } else if (typeof document.documentElement.currentStyle != "undefined") {
133 - getComputedStyleProperty = function(el, propName) {
134 - return el.currentStyle[propName];
135 - };
136 - } else {
137 - module.fail("No means of obtaining computed style properties found");
138 - }
139 -
140 - var isEditableElement;
141 -
142 - (function() {
143 - var testEl = document.createElement("div");
144 - if (typeof testEl.isContentEditable == "boolean") {
145 - isEditableElement = function(node) {
146 - return node && node.nodeType == 1 && node.isContentEditable;
147 - };
148 - } else {
149 - isEditableElement = function(node) {
150 - if (!node || node.nodeType != 1 || node.contentEditable == "false") {
151 - return false;
152 - }
153 - return node.contentEditable == "true" || isEditableElement(node.parentNode);
154 - };
155 - }
156 - })();
157 -
158 - function isEditingHost(node) {
159 - var parent;
160 - return node && node.nodeType == 1
161 - && (( (parent = node.parentNode) && parent.nodeType == 9 && parent.designMode == "on")
162 - || (isEditableElement(node) && !isEditableElement(node.parentNode)));
163 - }
164 -
165 - function isEditable(node) {
166 - return (isEditableElement(node) || (node.nodeType != 1 && isEditableElement(node.parentNode))) && !isEditingHost(node);
167 - }
168 -
169 - var inlineDisplayRegex = /^inline(-block|-table)?$/i;
170 -
171 - function isNonInlineElement(node) {
172 - return node && node.nodeType == 1 && !inlineDisplayRegex.test(getComputedStyleProperty(node, "display"));
173 - }
174 -
175 - // White space characters as defined by HTML 4 (http://www.w3.org/TR/html401/struct/text.html)
176 - var htmlNonWhiteSpaceRegex = /[^\r\n\t\f \u200B]/;
177 -
178 - function isUnrenderedWhiteSpaceNode(node) {
179 - if (node.data.length == 0) {
180 - return true;
181 - }
182 - if (htmlNonWhiteSpaceRegex.test(node.data)) {
183 - return false;
184 - }
185 - var cssWhiteSpace = getComputedStyleProperty(node.parentNode, "whiteSpace");
186 - switch (cssWhiteSpace) {
187 - case "pre":
188 - case "pre-wrap":
189 - case "-moz-pre-wrap":
190 - return false;
191 - case "pre-line":
192 - if (/[\r\n]/.test(node.data)) {
193 - return false;
194 - }
195 - }
196 -
197 - // We now have a whitespace-only text node that may be rendered depending on its context. If it is adjacent to a
198 - // non-inline element, it will not be rendered. This seems to be a good enough definition.
199 - return isNonInlineElement(node.previousSibling) || isNonInlineElement(node.nextSibling);
200 - }
201 -
202 - function isSplitPoint(node, offset) {
203 - if (dom.isCharacterDataNode(node)) {
204 - if (offset == 0) {
205 - return !!node.previousSibling;
206 - } else if (offset == node.length) {
207 - return !!node.nextSibling;
208 - } else {
209 - return true;
210 - }
211 - }
212 -
213 - return offset > 0 && offset < node.childNodes.length;
214 - }
215 -
216 - function splitNodeAt(node, descendantNode, descendantOffset, rangesToPreserve) {
217 - var newNode;
218 - var splitAtStart = (descendantOffset == 0);
219 -
220 - if (dom.isAncestorOf(descendantNode, node)) {
221 -
222 - return node;
223 - }
224 -
225 - if (dom.isCharacterDataNode(descendantNode)) {
226 - if (descendantOffset == 0) {
227 - descendantOffset = dom.getNodeIndex(descendantNode);
228 - descendantNode = descendantNode.parentNode;
229 - } else if (descendantOffset == descendantNode.length) {
230 - descendantOffset = dom.getNodeIndex(descendantNode) + 1;
231 - descendantNode = descendantNode.parentNode;
232 - } else {
233 - throw module.createError("splitNodeAt should not be called with offset in the middle of a data node ("
234 - + descendantOffset + " in " + descendantNode.data);
235 - }
236 - }
237 -
238 - if (isSplitPoint(descendantNode, descendantOffset)) {
239 - if (!newNode) {
240 - newNode = descendantNode.cloneNode(false);
241 - if (newNode.id) {
242 - newNode.removeAttribute("id");
243 - }
244 - var child;
245 - while ((child = descendantNode.childNodes[descendantOffset])) {
246 - newNode.appendChild(child);
247 - }
248 - dom.insertAfter(newNode, descendantNode);
249 - }
250 - return (descendantNode == node) ? newNode : splitNodeAt(node, newNode.parentNode, dom.getNodeIndex(newNode), rangesToPreserve);
251 - } else if (node != descendantNode) {
252 - newNode = descendantNode.parentNode;
253 -
254 - // Work out a new split point in the parent node
255 - var newNodeIndex = dom.getNodeIndex(descendantNode);
256 -
257 - if (!splitAtStart) {
258 - newNodeIndex++;
259 - }
260 - return splitNodeAt(node, newNode, newNodeIndex, rangesToPreserve);
261 - }
262 - return node;
263 - }
264 -
265 - function areElementsMergeable(el1, el2) {
266 - return el1.tagName == el2.tagName && haveSameClasses(el1, el2) && elementsHaveSameNonClassAttributes(el1, el2);
267 - }
268 -
269 - function createAdjacentMergeableTextNodeGetter(forward) {
270 - var propName = forward ? "nextSibling" : "previousSibling";
271 -
272 - return function(textNode, checkParentElement) {
273 - var el = textNode.parentNode;
274 - var adjacentNode = textNode[propName];
275 - if (adjacentNode) {
276 - // Can merge if the node's previous/next sibling is a text node
277 - if (adjacentNode && adjacentNode.nodeType == 3) {
278 - return adjacentNode;
279 - }
280 - } else if (checkParentElement) {
281 - // Compare text node parent element with its sibling
282 - adjacentNode = el[propName];
283 -
284 - if (adjacentNode && adjacentNode.nodeType == 1 && areElementsMergeable(el, adjacentNode)) {
285 - return adjacentNode[forward ? "firstChild" : "lastChild"];
286 - }
287 - }
288 - return null;
289 - }
290 - }
291 -
292 - var getPreviousMergeableTextNode = createAdjacentMergeableTextNodeGetter(false),
293 - getNextMergeableTextNode = createAdjacentMergeableTextNodeGetter(true);
294 -
295 -
296 - function Merge(firstNode) {
297 - this.isElementMerge = (firstNode.nodeType == 1);
298 - this.firstTextNode = this.isElementMerge ? firstNode.lastChild : firstNode;
299 - this.textNodes = [this.firstTextNode];
300 - }
301 -
302 - Merge.prototype = {
303 - doMerge: function() {
304 - var textBits = [], textNode, parent, text;
305 - for (var i = 0, len = this.textNodes.length; i < len; ++i) {
306 - textNode = this.textNodes[i];
307 - parent = textNode.parentNode;
308 - textBits[i] = textNode.data;
309 - if (i) {
310 - parent.removeChild(textNode);
311 - if (!parent.hasChildNodes()) {
312 - parent.parentNode.removeChild(parent);
313 - }
314 - }
315 - }
316 - this.firstTextNode.data = text = textBits.join("");
317 - return text;
318 - },
319 -
320 - getLength: function() {
321 - var i = this.textNodes.length, len = 0;
322 - while (i--) {
323 - len += this.textNodes[i].length;
324 - }
325 - return len;
326 - },
327 -
328 - toString: function() {
329 - var textBits = [];
330 - for (var i = 0, len = this.textNodes.length; i < len; ++i) {
331 - textBits[i] = "'" + this.textNodes[i].data + "'";
332 - }
333 - return "[Merge(" + textBits.join(",") + ")]";
334 - }
335 - };
336 -
337 - var optionProperties = ["elementTagName", "ignoreWhiteSpace", "applyToEditableOnly"];
338 -
339 - // Allow "class" as a property name in object properties
340 - var mappedPropertyNames = {"class" : "className"};
341 -
342 - function CssClassApplier(cssClass, options, tagNames) {
343 - this.cssClass = cssClass;
344 - var normalize, i, len, propName;
345 -
346 - var elementPropertiesFromOptions = null;
347 -
348 - // Initialize from options object
349 - if (typeof options == "object" && options !== null) {
350 - tagNames = options.tagNames;
351 - elementPropertiesFromOptions = options.elementProperties;
352 -
353 - for (i = 0; propName = optionProperties[i++]; ) {
354 - if (options.hasOwnProperty(propName)) {
355 - this[propName] = options[propName];
356 - }
357 - }
358 - normalize = options.normalize;
359 - } else {
360 - normalize = options;
361 - }
362 -
363 - // Backwards compatibility: the second parameter can also be a Boolean indicating whether normalization
364 - this.normalize = (typeof normalize == "undefined") ? true : normalize;
365 -
366 - // Initialize element properties and attribute exceptions
367 - this.attrExceptions = [];
368 - var el = document.createElement(this.elementTagName);
369 - this.elementProperties = {};
370 - for (var p in elementPropertiesFromOptions) {
371 - if (elementPropertiesFromOptions.hasOwnProperty(p)) {
372 - // Map "class" to "className"
373 - if (mappedPropertyNames.hasOwnProperty(p)) {
374 - p = mappedPropertyNames[p];
375 - }
376 - el[p] = elementPropertiesFromOptions[p];
377 -
378 - // Copy the property back from the dummy element so that later comparisons to check whether elements
379 - // may be removed are checking against the right value. For example, the href property of an element
380 - // returns a fully qualified URL even if it was previously assigned a relative URL.
381 - this.elementProperties[p] = el[p];
382 - this.attrExceptions.push(p);
383 - }
384 - }
385 -
386 - this.elementSortedClassName = this.elementProperties.hasOwnProperty("className") ?
387 - sortClassName(this.elementProperties.className + " " + cssClass) : cssClass;
388 -
389 - // Initialize tag names
390 - this.applyToAnyTagName = false;
391 - var type = typeof tagNames;
392 - if (type == "string") {
393 - if (tagNames == "*") {
394 - this.applyToAnyTagName = true;
395 - } else {
396 - this.tagNames = trim(tagNames.toLowerCase()).split(/\s*,\s*/);
397 - }
398 - } else if (type == "object" && typeof tagNames.length == "number") {
399 - this.tagNames = [];
400 - for (i = 0, len = tagNames.length; i < len; ++i) {
401 - if (tagNames[i] == "*") {
402 - this.applyToAnyTagName = true;
403 - } else {
404 - this.tagNames.push(tagNames[i].toLowerCase());
405 - }
406 - }
407 - } else {
408 - this.tagNames = [this.elementTagName];
409 - }
410 - }
411 -
412 - CssClassApplier.prototype = {
413 - elementTagName: defaultTagName,
414 - elementProperties: {},
415 - ignoreWhiteSpace: true,
416 - applyToEditableOnly: false,
417 -
418 - hasClass: function(node) {
419 - return node.nodeType == 1 && dom.arrayContains(this.tagNames, node.tagName.toLowerCase()) && hasClass(node, this.cssClass);
420 - },
421 -
422 - getSelfOrAncestorWithClass: function(node) {
423 - while (node) {
424 - if (this.hasClass(node, this.cssClass)) {
425 - return node;
426 - }
427 - node = node.parentNode;
428 - }
429 - return null;
430 - },
431 -
432 - isModifiable: function(node) {
433 - return !this.applyToEditableOnly || isEditable(node);
434 - },
435 -
436 - // White space adjacent to an unwrappable node can be ignored for wrapping
437 - isIgnorableWhiteSpaceNode: function(node) {
438 - return this.ignoreWhiteSpace && node && node.nodeType == 3 && isUnrenderedWhiteSpaceNode(node);
439 - },
440 -
441 - // Normalizes nodes after applying a CSS class to a Range.
442 - postApply: function(textNodes, range, isUndo) {
443 -
444 - var firstNode = textNodes[0], lastNode = textNodes[textNodes.length - 1];
445 -
446 - var merges = [], currentMerge;
447 -
448 - var rangeStartNode = firstNode, rangeEndNode = lastNode;
449 - var rangeStartOffset = 0, rangeEndOffset = lastNode.length;
450 -
451 - var textNode, precedingTextNode;
452 -
453 - for (var i = 0, len = textNodes.length; i < len; ++i) {
454 - textNode = textNodes[i];
455 - precedingTextNode = getPreviousMergeableTextNode(textNode, !isUndo);
456 -
457 - if (precedingTextNode) {
458 - if (!currentMerge) {
459 - currentMerge = new Merge(precedingTextNode);
460 - merges.push(currentMerge);
461 - }
462 - currentMerge.textNodes.push(textNode);
463 - if (textNode === firstNode) {
464 - rangeStartNode = currentMerge.firstTextNode;
465 - rangeStartOffset = rangeStartNode.length;
466 - }
467 - if (textNode === lastNode) {
468 - rangeEndNode = currentMerge.firstTextNode;
469 - rangeEndOffset = currentMerge.getLength();
470 - }
471 - } else {
472 - currentMerge = null;
473 - }
474 - }
475 -
476 - // Test whether the first node after the range needs merging
477 - var nextTextNode = getNextMergeableTextNode(lastNode, !isUndo);
478 -
479 - if (nextTextNode) {
480 - if (!currentMerge) {
481 - currentMerge = new Merge(lastNode);
482 - merges.push(currentMerge);
483 - }
484 - currentMerge.textNodes.push(nextTextNode);
485 - }
486 -
487 - // Do the merges
488 - if (merges.length) {
489 -
490 - for (i = 0, len = merges.length; i < len; ++i) {
491 - merges[i].doMerge();
492 - }
493 -
494 -
495 - // Set the range boundaries
496 - range.setStart(rangeStartNode, rangeStartOffset);
497 - range.setEnd(rangeEndNode, rangeEndOffset);
498 - }
499 -
500 - },
501 -
502 - createContainer: function(doc) {
503 - var el = doc.createElement(this.elementTagName);
504 - api.util.extend(el, this.elementProperties);
505 - addClass(el, this.cssClass);
506 - return el;
507 - },
508 -
509 - applyToTextNode: function(textNode) {
510 -
511 -
512 - var parent = textNode.parentNode;
513 - if (parent.childNodes.length == 1 && dom.arrayContains(this.tagNames, parent.tagName.toLowerCase())) {
514 - addClass(parent, this.cssClass);
515 - } else {
516 - var el = this.createContainer(dom.getDocument(textNode));
517 - textNode.parentNode.insertBefore(el, textNode);
518 - el.appendChild(textNode);
519 - }
520 -
521 - },
522 -
523 - isRemovable: function(el) {
524 - return el.tagName.toLowerCase() == this.elementTagName
525 - && getSortedClassName(el) == this.elementSortedClassName
526 - && elementHasProps(el, this.elementProperties)
527 - && !elementHasNonClassAttributes(el, this.attrExceptions)
528 - && this.isModifiable(el);
529 - },
530 -
531 - undoToTextNode: function(textNode, range, ancestorWithClass) {
532 -
533 - if (!range.containsNode(ancestorWithClass)) {
534 - // Split out the portion of the ancestor from which we can remove the CSS class
535 - //var parent = ancestorWithClass.parentNode, index = dom.getNodeIndex(ancestorWithClass);
536 - var ancestorRange = range.cloneRange();
537 - ancestorRange.selectNode(ancestorWithClass);
538 -
539 - if (ancestorRange.isPointInRange(range.endContainer, range.endOffset)/* && isSplitPoint(range.endContainer, range.endOffset)*/) {
540 - splitNodeAt(ancestorWithClass, range.endContainer, range.endOffset, [range]);
541 - range.setEndAfter(ancestorWithClass);
542 - }
543 - if (ancestorRange.isPointInRange(range.startContainer, range.startOffset)/* && isSplitPoint(range.startContainer, range.startOffset)*/) {
544 - ancestorWithClass = splitNodeAt(ancestorWithClass, range.startContainer, range.startOffset, [range]);
545 - }
546 - }
547 -
548 - if (this.isRemovable(ancestorWithClass)) {
549 - replaceWithOwnChildren(ancestorWithClass);
550 - } else {
551 - removeClass(ancestorWithClass, this.cssClass);
552 - }
553 - },
554 -
555 - applyToRange: function(range) {
556 - range.splitBoundaries();
557 - var textNodes = getEffectiveTextNodes(range);
558 -
559 - if (textNodes.length) {
560 - var textNode;
561 -
562 - for (var i = 0, len = textNodes.length; i < len; ++i) {
563 - textNode = textNodes[i];
564 -
565 - if (!this.isIgnorableWhiteSpaceNode(textNode) && !this.getSelfOrAncestorWithClass(textNode)
566 - && this.isModifiable(textNode)) {
567 - this.applyToTextNode(textNode);
568 - }
569 - }
570 - range.setStart(textNodes[0], 0);
571 - textNode = textNodes[textNodes.length - 1];
572 - range.setEnd(textNode, textNode.length);
573 - if (this.normalize) {
574 - this.postApply(textNodes, range, false);
575 - }
576 - }
577 - },
578 -
579 - applyToSelection: function(win) {
580 -
581 - win = win || window;
582 - var sel = api.getSelection(win);
583 -
584 - var range, ranges = sel.getAllRanges();
585 - sel.removeAllRanges();
586 - var i = ranges.length;
587 - while (i--) {
588 - range = ranges[i];
589 - this.applyToRange(range);
590 - sel.addRange(range);
591 - }
592 -
593 - },
594 -
595 - undoToRange: function(range) {
596 -
597 - range.splitBoundaries();
598 - var textNodes = getEffectiveTextNodes(range);
599 - var textNode, ancestorWithClass;
600 - var lastTextNode = textNodes[textNodes.length - 1];
601 -
602 - if (textNodes.length) {
603 - for (var i = 0, len = textNodes.length; i < len; ++i) {
604 - textNode = textNodes[i];
605 - ancestorWithClass = this.getSelfOrAncestorWithClass(textNode);
606 - if (ancestorWithClass && this.isModifiable(textNode)) {
607 - this.undoToTextNode(textNode, range, ancestorWithClass);
608 - }
609 -
610 - // Ensure the range is still valid
611 - range.setStart(textNodes[0], 0);
612 - range.setEnd(lastTextNode, lastTextNode.length);
613 - }
614 -
615 -
616 -
617 - if (this.normalize) {
618 - this.postApply(textNodes, range, true);
619 - }
620 - }
621 - },
622 -
623 - undoToSelection: function(win) {
624 - win = win || window;
625 - var sel = api.getSelection(win);
626 - var ranges = sel.getAllRanges(), range;
627 - sel.removeAllRanges();
628 - for (var i = 0, len = ranges.length; i < len; ++i) {
629 - range = ranges[i];
630 - this.undoToRange(range);
631 - sel.addRange(range);
632 - }
633 - },
634 -
635 - getTextSelectedByRange: function(textNode, range) {
636 - var textRange = range.cloneRange();
637 - textRange.selectNodeContents(textNode);
638 -
639 - var intersectionRange = textRange.intersection(range);
640 - var text = intersectionRange ? intersectionRange.toString() : "";
641 - textRange.detach();
642 -
643 - return text;
644 - },
645 -
646 - isAppliedToRange: function(range) {
647 - if (range.collapsed) {
648 - return !!this.getSelfOrAncestorWithClass(range.commonAncestorContainer);
649 - } else {
650 - var textNodes = range.getNodes( [3] );
651 - for (var i = 0, textNode; textNode = textNodes[i++]; ) {
652 - if (!this.isIgnorableWhiteSpaceNode(textNode) && rangeSelectsAnyText(range, textNode)
653 - && this.isModifiable(textNode) && !this.getSelfOrAncestorWithClass(textNode)) {
654 - return false;
655 - }
656 - }
657 - return true;
658 - }
659 - },
660 -
661 - isAppliedToSelection: function(win) {
662 - win = win || window;
663 - var sel = api.getSelection(win);
664 - var ranges = sel.getAllRanges();
665 - var i = ranges.length;
666 - while (i--) {
667 - if (!this.isAppliedToRange(ranges[i])) {
668 - return false;
669 - }
670 - }
671 -
672 - return true;
673 - },
674 -
675 - toggleRange: function(range) {
676 - if (this.isAppliedToRange(range)) {
677 - this.undoToRange(range);
678 - } else {
679 - this.applyToRange(range);
680 - }
681 - },
682 -
683 - toggleSelection: function(win) {
684 - if (this.isAppliedToSelection(win)) {
685 - this.undoToSelection(win);
686 - } else {
687 - this.applyToSelection(win);
688 - }
689 - },
690 -
691 - detach: function() {}
692 - };
693 -
694 - function createCssClassApplier(cssClass, options, tagNames) {
695 - return new CssClassApplier(cssClass, options, tagNames);
696 - }
697 -
698 - CssClassApplier.util = {
699 - hasClass: hasClass,
700 - addClass: addClass,
701 - removeClass: removeClass,
702 - hasSameClasses: haveSameClasses,
703 - replaceWithOwnChildren: replaceWithOwnChildren,
704 - elementsHaveSameNonClassAttributes: elementsHaveSameNonClassAttributes,
705 - elementHasNonClassAttributes: elementHasNonClassAttributes,
706 - splitNodeAt: splitNodeAt,
707 - isEditableElement: isEditableElement,
708 - isEditingHost: isEditingHost,
709 - isEditable: isEditable
710 - };
711 -
712 - api.CssClassApplier = CssClassApplier;
713 - api.createCssClassApplier = createCssClassApplier;
714 -});
 2+/**
 3+ * @license CSS Class Applier module for Rangy.
 4+ * Adds, removes and toggles CSS classes on Ranges and Selections
 5+ *
 6+ * Part of Rangy, a cross-browser JavaScript range and selection library
 7+ * http://code.google.com/p/rangy/
 8+ *
 9+ * Depends on Rangy core.
 10+ *
 11+ * Copyright 2011, Tim Down
 12+ * Licensed under the MIT license.
 13+ * Version: 1.2.2
 14+ * Build date: 13 November 2011
 15+ */
 16+rangy.createModule("CssClassApplier", function(api, module) {
 17+ api.requireModules( ["WrappedSelection", "WrappedRange"] );
 18+
 19+ var dom = api.dom;
 20+
 21+
 22+
 23+ var defaultTagName = "span";
 24+
 25+ function trim(str) {
 26+ return str.replace(/^\s\s*/, "").replace(/\s\s*$/, "");
 27+ }
 28+
 29+ function hasClass(el, cssClass) {
 30+ return el.className && new RegExp("(?:^|\\s)" + cssClass + "(?:\\s|$)").test(el.className);
 31+ }
 32+
 33+ function addClass(el, cssClass) {
 34+ if (el.className) {
 35+ if (!hasClass(el, cssClass)) {
 36+ el.className += " " + cssClass;
 37+ }
 38+ } else {
 39+ el.className = cssClass;
 40+ }
 41+ }
 42+
 43+ var removeClass = (function() {
 44+ function replacer(matched, whiteSpaceBefore, whiteSpaceAfter) {
 45+ return (whiteSpaceBefore && whiteSpaceAfter) ? " " : "";
 46+ }
 47+
 48+ return function(el, cssClass) {
 49+ if (el.className) {
 50+ el.className = el.className.replace(new RegExp("(?:^|\\s)" + cssClass + "(?:\\s|$)"), replacer);
 51+ }
 52+ };
 53+ })();
 54+
 55+ function sortClassName(className) {
 56+ return className.split(/\s+/).sort().join(" ");
 57+ }
 58+
 59+ function getSortedClassName(el) {
 60+ return sortClassName(el.className);
 61+ }
 62+
 63+ function haveSameClasses(el1, el2) {
 64+ return getSortedClassName(el1) == getSortedClassName(el2);
 65+ }
 66+
 67+ function replaceWithOwnChildren(el) {
 68+
 69+ var parent = el.parentNode;
 70+ while (el.hasChildNodes()) {
 71+ parent.insertBefore(el.firstChild, el);
 72+ }
 73+ parent.removeChild(el);
 74+ }
 75+
 76+ function rangeSelectsAnyText(range, textNode) {
 77+ var textRange = range.cloneRange();
 78+ textRange.selectNodeContents(textNode);
 79+
 80+ var intersectionRange = textRange.intersection(range);
 81+ var text = intersectionRange ? intersectionRange.toString() : "";
 82+ textRange.detach();
 83+
 84+ return text != "";
 85+ }
 86+
 87+ function getEffectiveTextNodes(range) {
 88+ return range.getNodes([3], function(textNode) {
 89+ return rangeSelectsAnyText(range, textNode);
 90+ });
 91+ }
 92+
 93+ function elementsHaveSameNonClassAttributes(el1, el2) {
 94+ if (el1.attributes.length != el2.attributes.length) return false;
 95+ for (var i = 0, len = el1.attributes.length, attr1, attr2, name; i < len; ++i) {
 96+ attr1 = el1.attributes[i];
 97+ name = attr1.name;
 98+ if (name != "class") {
 99+ attr2 = el2.attributes.getNamedItem(name);
 100+ if (attr1.specified != attr2.specified) return false;
 101+ if (attr1.specified && attr1.nodeValue !== attr2.nodeValue) return false;
 102+ }
 103+ }
 104+ return true;
 105+ }
 106+
 107+ function elementHasNonClassAttributes(el, exceptions) {
 108+ for (var i = 0, len = el.attributes.length, attrName; i < len; ++i) {
 109+ attrName = el.attributes[i].name;
 110+ if ( !(exceptions && dom.arrayContains(exceptions, attrName)) && el.attributes[i].specified && attrName != "class") {
 111+ return true;
 112+ }
 113+ }
 114+ return false;
 115+ }
 116+
 117+ function elementHasProps(el, props) {
 118+ for (var p in props) {
 119+ if (props.hasOwnProperty(p) && el[p] !== props[p]) {
 120+ return false;
 121+ }
 122+ }
 123+ return true;
 124+ }
 125+
 126+ var getComputedStyleProperty;
 127+
 128+ if (typeof window.getComputedStyle != "undefined") {
 129+ getComputedStyleProperty = function(el, propName) {
 130+ return dom.getWindow(el).getComputedStyle(el, null)[propName];
 131+ };
 132+ } else if (typeof document.documentElement.currentStyle != "undefined") {
 133+ getComputedStyleProperty = function(el, propName) {
 134+ return el.currentStyle[propName];
 135+ };
 136+ } else {
 137+ module.fail("No means of obtaining computed style properties found");
 138+ }
 139+
 140+ var isEditableElement;
 141+
 142+ (function() {
 143+ var testEl = document.createElement("div");
 144+ if (typeof testEl.isContentEditable == "boolean") {
 145+ isEditableElement = function(node) {
 146+ return node && node.nodeType == 1 && node.isContentEditable;
 147+ };
 148+ } else {
 149+ isEditableElement = function(node) {
 150+ if (!node || node.nodeType != 1 || node.contentEditable == "false") {
 151+ return false;
 152+ }
 153+ return node.contentEditable == "true" || isEditableElement(node.parentNode);
 154+ };
 155+ }
 156+ })();
 157+
 158+ function isEditingHost(node) {
 159+ var parent;
 160+ return node && node.nodeType == 1
 161+ && (( (parent = node.parentNode) && parent.nodeType == 9 && parent.designMode == "on")
 162+ || (isEditableElement(node) && !isEditableElement(node.parentNode)));
 163+ }
 164+
 165+ function isEditable(node) {
 166+ return (isEditableElement(node) || (node.nodeType != 1 && isEditableElement(node.parentNode))) && !isEditingHost(node);
 167+ }
 168+
 169+ var inlineDisplayRegex = /^inline(-block|-table)?$/i;
 170+
 171+ function isNonInlineElement(node) {
 172+ return node && node.nodeType == 1 && !inlineDisplayRegex.test(getComputedStyleProperty(node, "display"));
 173+ }
 174+
 175+ // White space characters as defined by HTML 4 (http://www.w3.org/TR/html401/struct/text.html)
 176+ var htmlNonWhiteSpaceRegex = /[^\r\n\t\f \u200B]/;
 177+
 178+ function isUnrenderedWhiteSpaceNode(node) {
 179+ if (node.data.length == 0) {
 180+ return true;
 181+ }
 182+ if (htmlNonWhiteSpaceRegex.test(node.data)) {
 183+ return false;
 184+ }
 185+ var cssWhiteSpace = getComputedStyleProperty(node.parentNode, "whiteSpace");
 186+ switch (cssWhiteSpace) {
 187+ case "pre":
 188+ case "pre-wrap":
 189+ case "-moz-pre-wrap":
 190+ return false;
 191+ case "pre-line":
 192+ if (/[\r\n]/.test(node.data)) {
 193+ return false;
 194+ }
 195+ }
 196+
 197+ // We now have a whitespace-only text node that may be rendered depending on its context. If it is adjacent to a
 198+ // non-inline element, it will not be rendered. This seems to be a good enough definition.
 199+ return isNonInlineElement(node.previousSibling) || isNonInlineElement(node.nextSibling);
 200+ }
 201+
 202+ function isSplitPoint(node, offset) {
 203+ if (dom.isCharacterDataNode(node)) {
 204+ if (offset == 0) {
 205+ return !!node.previousSibling;
 206+ } else if (offset == node.length) {
 207+ return !!node.nextSibling;
 208+ } else {
 209+ return true;
 210+ }
 211+ }
 212+
 213+ return offset > 0 && offset < node.childNodes.length;
 214+ }
 215+
 216+ function splitNodeAt(node, descendantNode, descendantOffset, rangesToPreserve) {
 217+ var newNode;
 218+ var splitAtStart = (descendantOffset == 0);
 219+
 220+ if (dom.isAncestorOf(descendantNode, node)) {
 221+
 222+ return node;
 223+ }
 224+
 225+ if (dom.isCharacterDataNode(descendantNode)) {
 226+ if (descendantOffset == 0) {
 227+ descendantOffset = dom.getNodeIndex(descendantNode);
 228+ descendantNode = descendantNode.parentNode;
 229+ } else if (descendantOffset == descendantNode.length) {
 230+ descendantOffset = dom.getNodeIndex(descendantNode) + 1;
 231+ descendantNode = descendantNode.parentNode;
 232+ } else {
 233+ throw module.createError("splitNodeAt should not be called with offset in the middle of a data node ("
 234+ + descendantOffset + " in " + descendantNode.data);
 235+ }
 236+ }
 237+
 238+ if (isSplitPoint(descendantNode, descendantOffset)) {
 239+ if (!newNode) {
 240+ newNode = descendantNode.cloneNode(false);
 241+ if (newNode.id) {
 242+ newNode.removeAttribute("id");
 243+ }
 244+ var child;
 245+ while ((child = descendantNode.childNodes[descendantOffset])) {
 246+ newNode.appendChild(child);
 247+ }
 248+ dom.insertAfter(newNode, descendantNode);
 249+ }
 250+ return (descendantNode == node) ? newNode : splitNodeAt(node, newNode.parentNode, dom.getNodeIndex(newNode), rangesToPreserve);
 251+ } else if (node != descendantNode) {
 252+ newNode = descendantNode.parentNode;
 253+
 254+ // Work out a new split point in the parent node
 255+ var newNodeIndex = dom.getNodeIndex(descendantNode);
 256+
 257+ if (!splitAtStart) {
 258+ newNodeIndex++;
 259+ }
 260+ return splitNodeAt(node, newNode, newNodeIndex, rangesToPreserve);
 261+ }
 262+ return node;
 263+ }
 264+
 265+ function areElementsMergeable(el1, el2) {
 266+ return el1.tagName == el2.tagName && haveSameClasses(el1, el2) && elementsHaveSameNonClassAttributes(el1, el2);
 267+ }
 268+
 269+ function createAdjacentMergeableTextNodeGetter(forward) {
 270+ var propName = forward ? "nextSibling" : "previousSibling";
 271+
 272+ return function(textNode, checkParentElement) {
 273+ var el = textNode.parentNode;
 274+ var adjacentNode = textNode[propName];
 275+ if (adjacentNode) {
 276+ // Can merge if the node's previous/next sibling is a text node
 277+ if (adjacentNode && adjacentNode.nodeType == 3) {
 278+ return adjacentNode;
 279+ }
 280+ } else if (checkParentElement) {
 281+ // Compare text node parent element with its sibling
 282+ adjacentNode = el[propName];
 283+
 284+ if (adjacentNode && adjacentNode.nodeType == 1 && areElementsMergeable(el, adjacentNode)) {
 285+ return adjacentNode[forward ? "firstChild" : "lastChild"];
 286+ }
 287+ }
 288+ return null;
 289+ }
 290+ }
 291+
 292+ var getPreviousMergeableTextNode = createAdjacentMergeableTextNodeGetter(false),
 293+ getNextMergeableTextNode = createAdjacentMergeableTextNodeGetter(true);
 294+
 295+
 296+ function Merge(firstNode) {
 297+ this.isElementMerge = (firstNode.nodeType == 1);
 298+ this.firstTextNode = this.isElementMerge ? firstNode.lastChild : firstNode;
 299+ this.textNodes = [this.firstTextNode];
 300+ }
 301+
 302+ Merge.prototype = {
 303+ doMerge: function() {
 304+ var textBits = [], textNode, parent, text;
 305+ for (var i = 0, len = this.textNodes.length; i < len; ++i) {
 306+ textNode = this.textNodes[i];
 307+ parent = textNode.parentNode;
 308+ textBits[i] = textNode.data;
 309+ if (i) {
 310+ parent.removeChild(textNode);
 311+ if (!parent.hasChildNodes()) {
 312+ parent.parentNode.removeChild(parent);
 313+ }
 314+ }
 315+ }
 316+ this.firstTextNode.data = text = textBits.join("");
 317+ return text;
 318+ },
 319+
 320+ getLength: function() {
 321+ var i = this.textNodes.length, len = 0;
 322+ while (i--) {
 323+ len += this.textNodes[i].length;
 324+ }
 325+ return len;
 326+ },
 327+
 328+ toString: function() {
 329+ var textBits = [];
 330+ for (var i = 0, len = this.textNodes.length; i < len; ++i) {
 331+ textBits[i] = "'" + this.textNodes[i].data + "'";
 332+ }
 333+ return "[Merge(" + textBits.join(",") + ")]";
 334+ }
 335+ };
 336+
 337+ var optionProperties = ["elementTagName", "ignoreWhiteSpace", "applyToEditableOnly"];
 338+
 339+ // Allow "class" as a property name in object properties
 340+ var mappedPropertyNames = {"class" : "className"};
 341+
 342+ function CssClassApplier(cssClass, options, tagNames) {
 343+ this.cssClass = cssClass;
 344+ var normalize, i, len, propName;
 345+
 346+ var elementPropertiesFromOptions = null;
 347+
 348+ // Initialize from options object
 349+ if (typeof options == "object" && options !== null) {
 350+ tagNames = options.tagNames;
 351+ elementPropertiesFromOptions = options.elementProperties;
 352+
 353+ for (i = 0; propName = optionProperties[i++]; ) {
 354+ if (options.hasOwnProperty(propName)) {
 355+ this[propName] = options[propName];
 356+ }
 357+ }
 358+ normalize = options.normalize;
 359+ } else {
 360+ normalize = options;
 361+ }
 362+
 363+ // Backwards compatibility: the second parameter can also be a Boolean indicating whether normalization
 364+ this.normalize = (typeof normalize == "undefined") ? true : normalize;
 365+
 366+ // Initialize element properties and attribute exceptions
 367+ this.attrExceptions = [];
 368+ var el = document.createElement(this.elementTagName);
 369+ this.elementProperties = {};
 370+ for (var p in elementPropertiesFromOptions) {
 371+ if (elementPropertiesFromOptions.hasOwnProperty(p)) {
 372+ // Map "class" to "className"
 373+ if (mappedPropertyNames.hasOwnProperty(p)) {
 374+ p = mappedPropertyNames[p];
 375+ }
 376+ el[p] = elementPropertiesFromOptions[p];
 377+
 378+ // Copy the property back from the dummy element so that later comparisons to check whether elements
 379+ // may be removed are checking against the right value. For example, the href property of an element
 380+ // returns a fully qualified URL even if it was previously assigned a relative URL.
 381+ this.elementProperties[p] = el[p];
 382+ this.attrExceptions.push(p);
 383+ }
 384+ }
 385+
 386+ this.elementSortedClassName = this.elementProperties.hasOwnProperty("className") ?
 387+ sortClassName(this.elementProperties.className + " " + cssClass) : cssClass;
 388+
 389+ // Initialize tag names
 390+ this.applyToAnyTagName = false;
 391+ var type = typeof tagNames;
 392+ if (type == "string") {
 393+ if (tagNames == "*") {
 394+ this.applyToAnyTagName = true;
 395+ } else {
 396+ this.tagNames = trim(tagNames.toLowerCase()).split(/\s*,\s*/);
 397+ }
 398+ } else if (type == "object" && typeof tagNames.length == "number") {
 399+ this.tagNames = [];
 400+ for (i = 0, len = tagNames.length; i < len; ++i) {
 401+ if (tagNames[i] == "*") {
 402+ this.applyToAnyTagName = true;
 403+ } else {
 404+ this.tagNames.push(tagNames[i].toLowerCase());
 405+ }
 406+ }
 407+ } else {
 408+ this.tagNames = [this.elementTagName];
 409+ }
 410+ }
 411+
 412+ CssClassApplier.prototype = {
 413+ elementTagName: defaultTagName,
 414+ elementProperties: {},
 415+ ignoreWhiteSpace: true,
 416+ applyToEditableOnly: false,
 417+
 418+ hasClass: function(node) {
 419+ return node.nodeType == 1 && dom.arrayContains(this.tagNames, node.tagName.toLowerCase()) && hasClass(node, this.cssClass);
 420+ },
 421+
 422+ getSelfOrAncestorWithClass: function(node) {
 423+ while (node) {
 424+ if (this.hasClass(node, this.cssClass)) {
 425+ return node;
 426+ }
 427+ node = node.parentNode;
 428+ }
 429+ return null;
 430+ },
 431+
 432+ isModifiable: function(node) {
 433+ return !this.applyToEditableOnly || isEditable(node);
 434+ },
 435+
 436+ // White space adjacent to an unwrappable node can be ignored for wrapping
 437+ isIgnorableWhiteSpaceNode: function(node) {
 438+ return this.ignoreWhiteSpace && node && node.nodeType == 3 && isUnrenderedWhiteSpaceNode(node);
 439+ },
 440+
 441+ // Normalizes nodes after applying a CSS class to a Range.
 442+ postApply: function(textNodes, range, isUndo) {
 443+
 444+ var firstNode = textNodes[0], lastNode = textNodes[textNodes.length - 1];
 445+
 446+ var merges = [], currentMerge;
 447+
 448+ var rangeStartNode = firstNode, rangeEndNode = lastNode;
 449+ var rangeStartOffset = 0, rangeEndOffset = lastNode.length;
 450+
 451+ var textNode, precedingTextNode;
 452+
 453+ for (var i = 0, len = textNodes.length; i < len; ++i) {
 454+ textNode = textNodes[i];
 455+ precedingTextNode = getPreviousMergeableTextNode(textNode, !isUndo);
 456+
 457+ if (precedingTextNode) {
 458+ if (!currentMerge) {
 459+ currentMerge = new Merge(precedingTextNode);
 460+ merges.push(currentMerge);
 461+ }
 462+ currentMerge.textNodes.push(textNode);
 463+ if (textNode === firstNode) {
 464+ rangeStartNode = currentMerge.firstTextNode;
 465+ rangeStartOffset = rangeStartNode.length;
 466+ }
 467+ if (textNode === lastNode) {
 468+ rangeEndNode = currentMerge.firstTextNode;
 469+ rangeEndOffset = currentMerge.getLength();
 470+ }
 471+ } else {
 472+ currentMerge = null;
 473+ }
 474+ }
 475+
 476+ // Test whether the first node after the range needs merging
 477+ var nextTextNode = getNextMergeableTextNode(lastNode, !isUndo);
 478+
 479+ if (nextTextNode) {
 480+ if (!currentMerge) {
 481+ currentMerge = new Merge(lastNode);
 482+ merges.push(currentMerge);
 483+ }
 484+ currentMerge.textNodes.push(nextTextNode);
 485+ }
 486+
 487+ // Do the merges
 488+ if (merges.length) {
 489+
 490+ for (i = 0, len = merges.length; i < len; ++i) {
 491+ merges[i].doMerge();
 492+ }
 493+
 494+
 495+ // Set the range boundaries
 496+ range.setStart(rangeStartNode, rangeStartOffset);
 497+ range.setEnd(rangeEndNode, rangeEndOffset);
 498+ }
 499+
 500+ },
 501+
 502+ createContainer: function(doc) {
 503+ var el = doc.createElement(this.elementTagName);
 504+ api.util.extend(el, this.elementProperties);
 505+ addClass(el, this.cssClass);
 506+ return el;
 507+ },
 508+
 509+ applyToTextNode: function(textNode) {
 510+
 511+
 512+ var parent = textNode.parentNode;
 513+ if (parent.childNodes.length == 1 && dom.arrayContains(this.tagNames, parent.tagName.toLowerCase())) {
 514+ addClass(parent, this.cssClass);
 515+ } else {
 516+ var el = this.createContainer(dom.getDocument(textNode));
 517+ textNode.parentNode.insertBefore(el, textNode);
 518+ el.appendChild(textNode);
 519+ }
 520+
 521+ },
 522+
 523+ isRemovable: function(el) {
 524+ return el.tagName.toLowerCase() == this.elementTagName
 525+ && getSortedClassName(el) == this.elementSortedClassName
 526+ && elementHasProps(el, this.elementProperties)
 527+ && !elementHasNonClassAttributes(el, this.attrExceptions)
 528+ && this.isModifiable(el);
 529+ },
 530+
 531+ undoToTextNode: function(textNode, range, ancestorWithClass) {
 532+
 533+ if (!range.containsNode(ancestorWithClass)) {
 534+ // Split out the portion of the ancestor from which we can remove the CSS class
 535+ //var parent = ancestorWithClass.parentNode, index = dom.getNodeIndex(ancestorWithClass);
 536+ var ancestorRange = range.cloneRange();
 537+ ancestorRange.selectNode(ancestorWithClass);
 538+
 539+ if (ancestorRange.isPointInRange(range.endContainer, range.endOffset)/* && isSplitPoint(range.endContainer, range.endOffset)*/) {
 540+ splitNodeAt(ancestorWithClass, range.endContainer, range.endOffset, [range]);
 541+ range.setEndAfter(ancestorWithClass);
 542+ }
 543+ if (ancestorRange.isPointInRange(range.startContainer, range.startOffset)/* && isSplitPoint(range.startContainer, range.startOffset)*/) {
 544+ ancestorWithClass = splitNodeAt(ancestorWithClass, range.startContainer, range.startOffset, [range]);
 545+ }
 546+ }
 547+
 548+ if (this.isRemovable(ancestorWithClass)) {
 549+ replaceWithOwnChildren(ancestorWithClass);
 550+ } else {
 551+ removeClass(ancestorWithClass, this.cssClass);
 552+ }
 553+ },
 554+
 555+ applyToRange: function(range) {
 556+ range.splitBoundaries();
 557+ var textNodes = getEffectiveTextNodes(range);
 558+
 559+ if (textNodes.length) {
 560+ var textNode;
 561+
 562+ for (var i = 0, len = textNodes.length; i < len; ++i) {
 563+ textNode = textNodes[i];
 564+
 565+ if (!this.isIgnorableWhiteSpaceNode(textNode) && !this.getSelfOrAncestorWithClass(textNode)
 566+ && this.isModifiable(textNode)) {
 567+ this.applyToTextNode(textNode);
 568+ }
 569+ }
 570+ range.setStart(textNodes[0], 0);
 571+ textNode = textNodes[textNodes.length - 1];
 572+ range.setEnd(textNode, textNode.length);
 573+ if (this.normalize) {
 574+ this.postApply(textNodes, range, false);
 575+ }
 576+ }
 577+ },
 578+
 579+ applyToSelection: function(win) {
 580+
 581+ win = win || window;
 582+ var sel = api.getSelection(win);
 583+
 584+ var range, ranges = sel.getAllRanges();
 585+ sel.removeAllRanges();
 586+ var i = ranges.length;
 587+ while (i--) {
 588+ range = ranges[i];
 589+ this.applyToRange(range);
 590+ sel.addRange(range);
 591+ }
 592+
 593+ },
 594+
 595+ undoToRange: function(range) {
 596+
 597+ range.splitBoundaries();
 598+ var textNodes = getEffectiveTextNodes(range);
 599+ var textNode, ancestorWithClass;
 600+ var lastTextNode = textNodes[textNodes.length - 1];
 601+
 602+ if (textNodes.length) {
 603+ for (var i = 0, len = textNodes.length; i < len; ++i) {
 604+ textNode = textNodes[i];
 605+ ancestorWithClass = this.getSelfOrAncestorWithClass(textNode);
 606+ if (ancestorWithClass && this.isModifiable(textNode)) {
 607+ this.undoToTextNode(textNode, range, ancestorWithClass);
 608+ }
 609+
 610+ // Ensure the range is still valid
 611+ range.setStart(textNodes[0], 0);
 612+ range.setEnd(lastTextNode, lastTextNode.length);
 613+ }
 614+
 615+
 616+
 617+ if (this.normalize) {
 618+ this.postApply(textNodes, range, true);
 619+ }
 620+ }
 621+ },
 622+
 623+ undoToSelection: function(win) {
 624+ win = win || window;
 625+ var sel = api.getSelection(win);
 626+ var ranges = sel.getAllRanges(), range;
 627+ sel.removeAllRanges();
 628+ for (var i = 0, len = ranges.length; i < len; ++i) {
 629+ range = ranges[i];
 630+ this.undoToRange(range);
 631+ sel.addRange(range);
 632+ }
 633+ },
 634+
 635+ getTextSelectedByRange: function(textNode, range) {
 636+ var textRange = range.cloneRange();
 637+ textRange.selectNodeContents(textNode);
 638+
 639+ var intersectionRange = textRange.intersection(range);
 640+ var text = intersectionRange ? intersectionRange.toString() : "";
 641+ textRange.detach();
 642+
 643+ return text;
 644+ },
 645+
 646+ isAppliedToRange: function(range) {
 647+ if (range.collapsed) {
 648+ return !!this.getSelfOrAncestorWithClass(range.commonAncestorContainer);
 649+ } else {
 650+ var textNodes = range.getNodes( [3] );
 651+ for (var i = 0, textNode; textNode = textNodes[i++]; ) {
 652+ if (!this.isIgnorableWhiteSpaceNode(textNode) && rangeSelectsAnyText(range, textNode)
 653+ && this.isModifiable(textNode) && !this.getSelfOrAncestorWithClass(textNode)) {
 654+ return false;
 655+ }
 656+ }
 657+ return true;
 658+ }
 659+ },
 660+
 661+ isAppliedToSelection: function(win) {
 662+ win = win || window;
 663+ var sel = api.getSelection(win);
 664+ var ranges = sel.getAllRanges();
 665+ var i = ranges.length;
 666+ while (i--) {
 667+ if (!this.isAppliedToRange(ranges[i])) {
 668+ return false;
 669+ }
 670+ }
 671+
 672+ return true;
 673+ },
 674+
 675+ toggleRange: function(range) {
 676+ if (this.isAppliedToRange(range)) {
 677+ this.undoToRange(range);
 678+ } else {
 679+ this.applyToRange(range);
 680+ }
 681+ },
 682+
 683+ toggleSelection: function(win) {
 684+ if (this.isAppliedToSelection(win)) {
 685+ this.undoToSelection(win);
 686+ } else {
 687+ this.applyToSelection(win);
 688+ }
 689+ },
 690+
 691+ detach: function() {}
 692+ };
 693+
 694+ function createCssClassApplier(cssClass, options, tagNames) {
 695+ return new CssClassApplier(cssClass, options, tagNames);
 696+ }
 697+
 698+ CssClassApplier.util = {
 699+ hasClass: hasClass,
 700+ addClass: addClass,
 701+ removeClass: removeClass,
 702+ hasSameClasses: haveSameClasses,
 703+ replaceWithOwnChildren: replaceWithOwnChildren,
 704+ elementsHaveSameNonClassAttributes: elementsHaveSameNonClassAttributes,
 705+ elementHasNonClassAttributes: elementHasNonClassAttributes,
 706+ splitNodeAt: splitNodeAt,
 707+ isEditableElement: isEditableElement,
 708+ isEditingHost: isEditingHost,
 709+ isEditable: isEditable
 710+ };
 711+
 712+ api.CssClassApplier = CssClassApplier;
 713+ api.createCssClassApplier = createCssClassApplier;
 714+});
Property changes on: trunk/extensions/VisualEditor/contentEditable/rangy/rangy-cssclassapplier.js
___________________________________________________________________
Added: svn:eol-style
715715 + native
Property changes on: trunk/extensions/VisualEditor/contentEditable/index.php
___________________________________________________________________
Added: svn:eol-style
716716 + native
Index: trunk/extensions/VisualEditor/contentEditable/views/es.ParagraphView.js
@@ -1,26 +1,26 @@
2 -/**
3 - * Creates an es.ParagraphView object.
4 - *
5 - * @class
6 - * @constructor
7 - * @extends {es.DocumentViewLeafNode}
8 - * @param {es.ParagraphModel} model Paragraph model to view
9 - */
10 -es.ParagraphView = function( model ) {
11 - // Inheritance
12 - es.DocumentViewLeafNode.call( this, model );
13 -
14 - // DOM Changes
15 - this.$.addClass( 'es-paragraphView' );
16 -};
17 -
18 -/* Registration */
19 -
20 -es.DocumentView.splitRules.paragraph = {
21 - 'self': true,
22 - 'children': null
23 -};
24 -
25 -/* Inheritance */
26 -
27 -es.extendClass( es.ParagraphView, es.DocumentViewLeafNode );
 2+/**
 3+ * Creates an es.ParagraphView object.
 4+ *
 5+ * @class
 6+ * @constructor
 7+ * @extends {es.DocumentViewLeafNode}
 8+ * @param {es.ParagraphModel} model Paragraph model to view
 9+ */
 10+es.ParagraphView = function( model ) {
 11+ // Inheritance
 12+ es.DocumentViewLeafNode.call( this, model );
 13+
 14+ // DOM Changes
 15+ this.$.addClass( 'es-paragraphView' );
 16+};
 17+
 18+/* Registration */
 19+
 20+es.DocumentView.splitRules.paragraph = {
 21+ 'self': true,
 22+ 'children': null
 23+};
 24+
 25+/* Inheritance */
 26+
 27+es.extendClass( es.ParagraphView, es.DocumentViewLeafNode );
Property changes on: trunk/extensions/VisualEditor/contentEditable/views/es.ParagraphView.js
___________________________________________________________________
Added: svn:eol-style
2828 + native
Property changes on: trunk/extensions/VisualEditor/contentEditable/views/es.SurfaceView.js
___________________________________________________________________
Added: svn:eol-style
2929 + native
Index: trunk/extensions/VisualEditor/contentEditable/views/es.ContentView.js
@@ -1,244 +1,244 @@
2 -es.ContentView = function( $container, model ) {
3 - // Inheritance
4 - es.EventEmitter.call( this );
5 -
6 - // Properties
7 - this.$ = $container;
8 - this.model = model;
9 -
10 - if ( model ) {
11 - // Events
12 - var _this = this;
13 - this.model.on( 'update', function( offset ) {
14 - _this.render( offset || 0 );
15 - } );
16 - }
17 -}
18 -
19 -
20 -/* Static Members */
21 -
22 -/**
23 - * List of annotation rendering implementations.
24 - *
25 - * Each supported annotation renderer must have an open and close property, each either a string or
26 - * a function which accepts a data argument.
27 - *
28 - * @static
29 - * @member
30 - */
31 -es.ContentView.annotationRenderers = {
32 - 'object/template': {
33 - 'open': function( data ) {
34 - return '<span class="es-contentView-format-object" contentEditable="false">' + data.html;
35 - },
36 - 'close': '</span>'
37 - },
38 - 'object/hook': {
39 - 'open': function( data ) {
40 - return '<span class="es-contentView-format-object">' + data.html;
41 - },
42 - 'close': '</span>'
43 - },
44 - 'textStyle/bold': {
45 - //'open': '<span class="es-contentView-format-textStyle-bold">',
46 - //'close': '</span>'
47 - 'open': '<b>',
48 - 'close': '</b>'
49 - },
50 - 'textStyle/italic': {
51 - 'open': '<span class="es-contentView-format-textStyle-italic">',
52 - 'close': '</span>'
53 - },
54 - 'textStyle/strong': {
55 - 'open': '<span class="es-contentView-format-textStyle-strong">',
56 - 'close': '</span>'
57 - },
58 - 'textStyle/emphasize': {
59 - 'open': '<span class="es-contentView-format-textStyle-emphasize">',
60 - 'close': '</span>'
61 - },
62 - 'textStyle/big': {
63 - 'open': '<span class="es-contentView-format-textStyle-big">',
64 - 'close': '</span>'
65 - },
66 - 'textStyle/small': {
67 - 'open': '<span class="es-contentView-format-textStyle-small">',
68 - 'close': '</span>'
69 - },
70 - 'textStyle/superScript': {
71 - 'open': '<span class="es-contentView-format-textStyle-superScript">',
72 - 'close': '</span>'
73 - },
74 - 'textStyle/subScript': {
75 - 'open': '<span class="es-contentView-format-textStyle-subScript">',
76 - 'close': '</span>'
77 - },
78 - 'link/external': {
79 - 'open': function( data ) {
80 - return '<span class="es-contentView-format-link" data-href="' + data.href + '">';
81 - },
82 - 'close': '</span>'
83 - },
84 - 'link/internal': {
85 - 'open': function( data ) {
86 - return '<span class="es-contentView-format-link" data-title="wiki/' + data.title + '">';
87 - },
88 - 'close': '</span>'
89 - }
90 -};
91 -
92 -/**
93 - * Mapping of character and HTML entities or renderings.
94 - *
95 - * @static
96 - * @member
97 - */
98 -es.ContentView.htmlCharacters = {
99 - '&': '&amp;',
100 - '<': '&lt;',
101 - '>': '&gt;',
102 - '\'': '&#039;',
103 - '"': '&quot;',
104 - '\n': '<span class="es-contentView-whitespace">&#182;</span>',
105 - '\t': '<span class="es-contentView-whitespace">&#8702;</span>',
106 - ' ': '&nbsp;'
107 -};
108 -
109 -/* Static Methods */
110 -
111 -/**
112 - * Gets a rendered opening or closing of an annotation.
113 - *
114 - * Tag nesting is handled using a stack, which keeps track of what is currently open. A common stack
115 - * argument should be used while rendering content.
116 - *
117 - * @static
118 - * @method
119 - * @param {String} bias Which side of the annotation to render, either "open" or "close"
120 - * @param {Object} annotation Annotation to render
121 - * @param {Array} stack List of currently open annotations
122 - * @returns {String} Rendered annotation
123 - */
124 -es.ContentView.renderAnnotation = function( bias, annotation, stack ) {
125 - var renderers = es.ContentView.annotationRenderers,
126 - type = annotation.type,
127 - out = '';
128 - if ( type in renderers ) {
129 - if ( bias === 'open' ) {
130 - // Add annotation to the top of the stack
131 - stack.push( annotation );
132 - // Open annotation
133 - out += typeof renderers[type].open === 'function' ?
134 - renderers[type].open( annotation.data ) : renderers[type].open;
135 - } else {
136 - if ( stack[stack.length - 1] === annotation ) {
137 - // Remove annotation from top of the stack
138 - stack.pop();
139 - // Close annotation
140 - out += typeof renderers[type].close === 'function' ?
141 - renderers[type].close( annotation.data ) : renderers[type].close;
142 - } else {
143 - // Find the annotation in the stack
144 - var depth = es.inArray( annotation, stack ),
145 - i;
146 - if ( depth === -1 ) {
147 - throw 'Invalid stack error. An element is missing from the stack.';
148 - }
149 - // Close each already opened annotation
150 - for ( i = stack.length - 1; i >= depth + 1; i-- ) {
151 - out += typeof renderers[stack[i].type].close === 'function' ?
152 - renderers[stack[i].type].close( stack[i].data ) :
153 - renderers[stack[i].type].close;
154 - }
155 - // Close the buried annotation
156 - out += typeof renderers[type].close === 'function' ?
157 - renderers[type].close( annotation.data ) : renderers[type].close;
158 - // Re-open each previously opened annotation
159 - for ( i = depth + 1; i < stack.length; i++ ) {
160 - out += typeof renderers[stack[i].type].open === 'function' ?
161 - renderers[stack[i].type].open( stack[i].data ) :
162 - renderers[stack[i].type].open;
163 - }
164 - // Remove the annotation from the middle of the stack
165 - stack.splice( depth, 1 );
166 - }
167 - }
168 - }
169 - return out;
170 -};
171 -
172 -/* Methods */
173 -
174 -es.ContentView.prototype.render = function( offset ) {
175 - this.$.html(this.getHtml(0, this.model.getContentLength()));
176 -};
177 -
178 -/**
179 - * Gets an HTML rendering of a range of data within content model.
180 - *
181 - * @method
182 - * @param {es.Range} range Range of content to render
183 - * @param {String} Rendered HTML of data within content model
184 - */
185 -es.ContentView.prototype.getHtml = function( range, options ) {
186 - if ( range ) {
187 - range.normalize();
188 - } else {
189 - range = { 'start': 0, 'end': undefined };
190 - }
191 - var data = this.model.getContentData(),
192 - render = es.ContentView.renderAnnotation,
193 - htmlChars = es.ContentView.htmlCharacters;
194 - var out = '',
195 - left = '',
196 - right,
197 - leftPlain,
198 - rightPlain,
199 - stack = [],
200 - chr,
201 - i,
202 - j;
203 - for ( i = 0; i < data.length; i++ ) {
204 - right = data[i];
205 - leftPlain = typeof left === 'string';
206 - rightPlain = typeof right === 'string';
207 - if ( !leftPlain && rightPlain ) {
208 - // [formatted][plain] pair, close any annotations for left
209 - for ( j = 1; j < left.length; j++ ) {
210 - out += render( 'close', left[j], stack );
211 - }
212 - } else if ( leftPlain && !rightPlain ) {
213 - // [plain][formatted] pair, open any annotations for right
214 - for ( j = 1; j < right.length; j++ ) {
215 - out += render( 'open', right[j], stack );
216 - }
217 - } else if ( !leftPlain && !rightPlain ) {
218 - // [formatted][formatted] pair, open/close any differences
219 - for ( j = 1; j < left.length; j++ ) {
220 - if ( es.inArray( left[j], right ) === -1 ) {
221 - out += render( 'close', left[j], stack );
222 - }
223 - }
224 - for ( j = 1; j < right.length; j++ ) {
225 - if ( es.inArray( right[j], left ) === -1 ) {
226 - out += render( 'open', right[j], stack );
227 - }
228 - }
229 - }
230 - chr = rightPlain ? right : right[0];
231 - out += chr in htmlChars ? htmlChars[chr] : chr;
232 - left = right;
233 - }
234 - // Close all remaining tags at the end of the content
235 - if ( !rightPlain && right ) {
236 - for ( j = 1; j < right.length; j++ ) {
237 - out += render( 'close', right[j], stack );
238 - }
239 - }
240 - return out;
241 -};
242 -
243 -/* Inheritance */
244 -
245 -es.extendClass( es.ContentView, es.EventEmitter );
 2+es.ContentView = function( $container, model ) {
 3+ // Inheritance
 4+ es.EventEmitter.call( this );
 5+
 6+ // Properties
 7+ this.$ = $container;
 8+ this.model = model;
 9+
 10+ if ( model ) {
 11+ // Events
 12+ var _this = this;
 13+ this.model.on( 'update', function( offset ) {
 14+ _this.render( offset || 0 );
 15+ } );
 16+ }
 17+}
 18+
 19+
 20+/* Static Members */
 21+
 22+/**
 23+ * List of annotation rendering implementations.
 24+ *
 25+ * Each supported annotation renderer must have an open and close property, each either a string or
 26+ * a function which accepts a data argument.
 27+ *
 28+ * @static
 29+ * @member
 30+ */
 31+es.ContentView.annotationRenderers = {
 32+ 'object/template': {
 33+ 'open': function( data ) {
 34+ return '<span class="es-contentView-format-object" contentEditable="false">' + data.html;
 35+ },
 36+ 'close': '</span>'
 37+ },
 38+ 'object/hook': {
 39+ 'open': function( data ) {
 40+ return '<span class="es-contentView-format-object">' + data.html;
 41+ },
 42+ 'close': '</span>'
 43+ },
 44+ 'textStyle/bold': {
 45+ //'open': '<span class="es-contentView-format-textStyle-bold">',
 46+ //'close': '</span>'
 47+ 'open': '<b>',
 48+ 'close': '</b>'
 49+ },
 50+ 'textStyle/italic': {
 51+ 'open': '<span class="es-contentView-format-textStyle-italic">',
 52+ 'close': '</span>'
 53+ },
 54+ 'textStyle/strong': {
 55+ 'open': '<span class="es-contentView-format-textStyle-strong">',
 56+ 'close': '</span>'
 57+ },
 58+ 'textStyle/emphasize': {
 59+ 'open': '<span class="es-contentView-format-textStyle-emphasize">',
 60+ 'close': '</span>'
 61+ },
 62+ 'textStyle/big': {
 63+ 'open': '<span class="es-contentView-format-textStyle-big">',
 64+ 'close': '</span>'
 65+ },
 66+ 'textStyle/small': {
 67+ 'open': '<span class="es-contentView-format-textStyle-small">',
 68+ 'close': '</span>'
 69+ },
 70+ 'textStyle/superScript': {
 71+ 'open': '<span class="es-contentView-format-textStyle-superScript">',
 72+ 'close': '</span>'
 73+ },
 74+ 'textStyle/subScript': {
 75+ 'open': '<span class="es-contentView-format-textStyle-subScript">',
 76+ 'close': '</span>'
 77+ },
 78+ 'link/external': {
 79+ 'open': function( data ) {
 80+ return '<span class="es-contentView-format-link" data-href="' + data.href + '">';
 81+ },
 82+ 'close': '</span>'
 83+ },
 84+ 'link/internal': {
 85+ 'open': function( data ) {
 86+ return '<span class="es-contentView-format-link" data-title="wiki/' + data.title + '">';
 87+ },
 88+ 'close': '</span>'
 89+ }
 90+};
 91+
 92+/**
 93+ * Mapping of character and HTML entities or renderings.
 94+ *
 95+ * @static
 96+ * @member
 97+ */
 98+es.ContentView.htmlCharacters = {
 99+ '&': '&amp;',
 100+ '<': '&lt;',
 101+ '>': '&gt;',
 102+ '\'': '&#039;',
 103+ '"': '&quot;',
 104+ '\n': '<span class="es-contentView-whitespace">&#182;</span>',
 105+ '\t': '<span class="es-contentView-whitespace">&#8702;</span>',
 106+ ' ': '&nbsp;'
 107+};
 108+
 109+/* Static Methods */
 110+
 111+/**
 112+ * Gets a rendered opening or closing of an annotation.
 113+ *
 114+ * Tag nesting is handled using a stack, which keeps track of what is currently open. A common stack
 115+ * argument should be used while rendering content.
 116+ *
 117+ * @static
 118+ * @method
 119+ * @param {String} bias Which side of the annotation to render, either "open" or "close"
 120+ * @param {Object} annotation Annotation to render
 121+ * @param {Array} stack List of currently open annotations
 122+ * @returns {String} Rendered annotation
 123+ */
 124+es.ContentView.renderAnnotation = function( bias, annotation, stack ) {
 125+ var renderers = es.ContentView.annotationRenderers,
 126+ type = annotation.type,
 127+ out = '';
 128+ if ( type in renderers ) {
 129+ if ( bias === 'open' ) {
 130+ // Add annotation to the top of the stack
 131+ stack.push( annotation );
 132+ // Open annotation
 133+ out += typeof renderers[type].open === 'function' ?
 134+ renderers[type].open( annotation.data ) : renderers[type].open;
 135+ } else {
 136+ if ( stack[stack.length - 1] === annotation ) {
 137+ // Remove annotation from top of the stack
 138+ stack.pop();
 139+ // Close annotation
 140+ out += typeof renderers[type].close === 'function' ?
 141+ renderers[type].close( annotation.data ) : renderers[type].close;
 142+ } else {
 143+ // Find the annotation in the stack
 144+ var depth = es.inArray( annotation, stack ),
 145+ i;
 146+ if ( depth === -1 ) {
 147+ throw 'Invalid stack error. An element is missing from the stack.';
 148+ }
 149+ // Close each already opened annotation
 150+ for ( i = stack.length - 1; i >= depth + 1; i-- ) {
 151+ out += typeof renderers[stack[i].type].close === 'function' ?
 152+ renderers[stack[i].type].close( stack[i].data ) :
 153+ renderers[stack[i].type].close;
 154+ }
 155+ // Close the buried annotation
 156+ out += typeof renderers[type].close === 'function' ?
 157+ renderers[type].close( annotation.data ) : renderers[type].close;
 158+ // Re-open each previously opened annotation
 159+ for ( i = depth + 1; i < stack.length; i++ ) {
 160+ out += typeof renderers[stack[i].type].open === 'function' ?
 161+ renderers[stack[i].type].open( stack[i].data ) :
 162+ renderers[stack[i].type].open;
 163+ }
 164+ // Remove the annotation from the middle of the stack
 165+ stack.splice( depth, 1 );
 166+ }
 167+ }
 168+ }
 169+ return out;
 170+};
 171+
 172+/* Methods */
 173+
 174+es.ContentView.prototype.render = function( offset ) {
 175+ this.$.html(this.getHtml(0, this.model.getContentLength()));
 176+};
 177+
 178+/**
 179+ * Gets an HTML rendering of a range of data within content model.
 180+ *
 181+ * @method
 182+ * @param {es.Range} range Range of content to render
 183+ * @param {String} Rendered HTML of data within content model
 184+ */
 185+es.ContentView.prototype.getHtml = function( range, options ) {
 186+ if ( range ) {
 187+ range.normalize();
 188+ } else {
 189+ range = { 'start': 0, 'end': undefined };
 190+ }
 191+ var data = this.model.getContentData(),
 192+ render = es.ContentView.renderAnnotation,
 193+ htmlChars = es.ContentView.htmlCharacters;
 194+ var out = '',
 195+ left = '',
 196+ right,
 197+ leftPlain,
 198+ rightPlain,
 199+ stack = [],
 200+ chr,
 201+ i,
 202+ j;
 203+ for ( i = 0; i < data.length; i++ ) {
 204+ right = data[i];
 205+ leftPlain = typeof left === 'string';
 206+ rightPlain = typeof right === 'string';
 207+ if ( !leftPlain && rightPlain ) {
 208+ // [formatted][plain] pair, close any annotations for left
 209+ for ( j = 1; j < left.length; j++ ) {
 210+ out += render( 'close', left[j], stack );
 211+ }
 212+ } else if ( leftPlain && !rightPlain ) {
 213+ // [plain][formatted] pair, open any annotations for right
 214+ for ( j = 1; j < right.length; j++ ) {
 215+ out += render( 'open', right[j], stack );
 216+ }
 217+ } else if ( !leftPlain && !rightPlain ) {
 218+ // [formatted][formatted] pair, open/close any differences
 219+ for ( j = 1; j < left.length; j++ ) {
 220+ if ( es.inArray( left[j], right ) === -1 ) {
 221+ out += render( 'close', left[j], stack );
 222+ }
 223+ }
 224+ for ( j = 1; j < right.length; j++ ) {
 225+ if ( es.inArray( right[j], left ) === -1 ) {
 226+ out += render( 'open', right[j], stack );
 227+ }
 228+ }
 229+ }
 230+ chr = rightPlain ? right : right[0];
 231+ out += chr in htmlChars ? htmlChars[chr] : chr;
 232+ left = right;
 233+ }
 234+ // Close all remaining tags at the end of the content
 235+ if ( !rightPlain && right ) {
 236+ for ( j = 1; j < right.length; j++ ) {
 237+ out += render( 'close', right[j], stack );
 238+ }
 239+ }
 240+ return out;
 241+};
 242+
 243+/* Inheritance */
 244+
 245+es.extendClass( es.ContentView, es.EventEmitter );
Property changes on: trunk/extensions/VisualEditor/contentEditable/views/es.ContentView.js
___________________________________________________________________
Added: svn:eol-style
246246 + native
Index: trunk/extensions/VisualEditor/contentEditable/views/es.DocumentViewLeafNode.js
@@ -1,91 +1,91 @@
2 -/**
3 - * Creates an es.DocumentViewLeafNode object.
4 - *
5 - * @class
6 - * @abstract
7 - * @constructor
8 - * @extends {es.DocumentLeafNode}
9 - * @extends {es.DocumentViewNode}
10 - * @param model {es.ModelNode} Model to observe
11 - * @param {jQuery} [$element] Element to use as a container
12 - */
13 -es.DocumentViewLeafNode = function( model, $element ) {
14 - // Inheritance
15 - es.DocumentLeafNode.call( this );
16 - es.DocumentViewNode.call( this, model, $element );
17 -
18 - this.$.data('view', this);
19 -
20 - // Properties
21 - this.$content = this.$;
22 - this.contentView = new es.ContentView( this.$content, model );
23 -
24 - // Events
25 - this.contentView.on( 'update', this.emitUpdate );
26 -};
27 -
28 -/* Methods */
29 -
30 -/**
31 - * Render content.
32 - *
33 - * @method
34 - */
35 -es.DocumentViewLeafNode.prototype.renderContent = function() {
36 - this.contentView.render();
37 -};
38 -
39 -/**
40 - * Draw selection around a given range.
41 - *
42 - * @method
43 - * @param {es.Range} range Range of content to draw selection around
44 - */
45 -es.DocumentViewLeafNode.prototype.drawSelection = function( range ) {
46 - this.contentView.drawSelection( range );
47 -};
48 -
49 -/**
50 - * Clear selection.
51 - *
52 - * @method
53 - */
54 -es.DocumentViewLeafNode.prototype.clearSelection = function() {
55 - this.contentView.clearSelection();
56 -};
57 -
58 -/**
59 - * Gets the nearest offset of a rendered position.
60 - *
61 - * @method
62 - * @param {es.Position} position Position to get offset for
63 - * @returns {Integer} Offset of position
64 - */
65 -es.DocumentViewLeafNode.prototype.getOffsetFromRenderedPosition = function( position ) {
66 - return this.contentView.getOffsetFromRenderedPosition( position );
67 -};
68 -
69 -/**
70 - * Gets rendered position of offset within content.
71 - *
72 - * @method
73 - * @param {Integer} offset Offset to get position for
74 - * @returns {es.Position} Position of offset
75 - */
76 -es.DocumentViewLeafNode.prototype.getRenderedPositionFromOffset = function( offset, leftBias ) {
77 - var position = this.contentView.getRenderedPositionFromOffset( offset, leftBias ),
78 - contentPosition = this.$content.offset();
79 - position.top += contentPosition.top;
80 - position.left += contentPosition.left;
81 - position.bottom += contentPosition.top;
82 - return position;
83 -};
84 -
85 -es.DocumentViewLeafNode.prototype.getRenderedLineRangeFromOffset = function( offset ) {
86 - return this.contentView.getRenderedLineRangeFromOffset( offset );
87 -};
88 -
89 -/* Inheritance */
90 -
91 -es.extendClass( es.DocumentViewLeafNode, es.DocumentLeafNode );
92 -es.extendClass( es.DocumentViewLeafNode, es.DocumentViewNode );
 2+/**
 3+ * Creates an es.DocumentViewLeafNode object.
 4+ *
 5+ * @class
 6+ * @abstract
 7+ * @constructor
 8+ * @extends {es.DocumentLeafNode}
 9+ * @extends {es.DocumentViewNode}
 10+ * @param model {es.ModelNode} Model to observe
 11+ * @param {jQuery} [$element] Element to use as a container
 12+ */
 13+es.DocumentViewLeafNode = function( model, $element ) {
 14+ // Inheritance
 15+ es.DocumentLeafNode.call( this );
 16+ es.DocumentViewNode.call( this, model, $element );
 17+
 18+ this.$.data('view', this);
 19+
 20+ // Properties
 21+ this.$content = this.$;
 22+ this.contentView = new es.ContentView( this.$content, model );
 23+
 24+ // Events
 25+ this.contentView.on( 'update', this.emitUpdate );
 26+};
 27+
 28+/* Methods */
 29+
 30+/**
 31+ * Render content.
 32+ *
 33+ * @method
 34+ */
 35+es.DocumentViewLeafNode.prototype.renderContent = function() {
 36+ this.contentView.render();
 37+};
 38+
 39+/**
 40+ * Draw selection around a given range.
 41+ *
 42+ * @method
 43+ * @param {es.Range} range Range of content to draw selection around
 44+ */
 45+es.DocumentViewLeafNode.prototype.drawSelection = function( range ) {
 46+ this.contentView.drawSelection( range );
 47+};
 48+
 49+/**
 50+ * Clear selection.
 51+ *
 52+ * @method
 53+ */
 54+es.DocumentViewLeafNode.prototype.clearSelection = function() {
 55+ this.contentView.clearSelection();
 56+};
 57+
 58+/**
 59+ * Gets the nearest offset of a rendered position.
 60+ *
 61+ * @method
 62+ * @param {es.Position} position Position to get offset for
 63+ * @returns {Integer} Offset of position
 64+ */
 65+es.DocumentViewLeafNode.prototype.getOffsetFromRenderedPosition = function( position ) {
 66+ return this.contentView.getOffsetFromRenderedPosition( position );
 67+};
 68+
 69+/**
 70+ * Gets rendered position of offset within content.
 71+ *
 72+ * @method
 73+ * @param {Integer} offset Offset to get position for
 74+ * @returns {es.Position} Position of offset
 75+ */
 76+es.DocumentViewLeafNode.prototype.getRenderedPositionFromOffset = function( offset, leftBias ) {
 77+ var position = this.contentView.getRenderedPositionFromOffset( offset, leftBias ),
 78+ contentPosition = this.$content.offset();
 79+ position.top += contentPosition.top;
 80+ position.left += contentPosition.left;
 81+ position.bottom += contentPosition.top;
 82+ return position;
 83+};
 84+
 85+es.DocumentViewLeafNode.prototype.getRenderedLineRangeFromOffset = function( offset ) {
 86+ return this.contentView.getRenderedLineRangeFromOffset( offset );
 87+};
 88+
 89+/* Inheritance */
 90+
 91+es.extendClass( es.DocumentViewLeafNode, es.DocumentLeafNode );
 92+es.extendClass( es.DocumentViewLeafNode, es.DocumentViewNode );
Property changes on: trunk/extensions/VisualEditor/contentEditable/views/es.DocumentViewLeafNode.js
___________________________________________________________________
Added: svn:eol-style
9393 + native
Index: trunk/extensions/VisualEditor/contentEditable/views/es.DocumentView.js
@@ -1,72 +1,72 @@
2 -/**
3 - * Creates an es.DocumentView object.
4 - *
5 - * @class
6 - * @constructor
7 - * @extends {es.DocumentViewBranchNode}
8 - * @param {es.DocumentModel} documentModel Document model to view
9 - * @param {es.SurfaceView} surfaceView Surface view this view is a child of
10 - */
11 -es.DocumentView = function( model, surfaceView ) {
12 - // Inheritance
13 - es.DocumentViewBranchNode.call( this, model );
14 -
15 - // Properties
16 - this.surfaceView = surfaceView;
17 -
18 - // DOM Changes
19 - this.$.addClass( 'es-documentView' );
20 - this.$.attr('contentEditable', 'true');
21 -};
22 -
23 -/* Static Members */
24 -
25 -
26 -/**
27 - * Mapping of symbolic names and splitting rules.
28 - *
29 - * Each rule is an object with a self and children property. Each of these properties may contain
30 - * one of two possible values:
31 - * Boolean - Whether a split is allowed
32 - * Null - Node is a leaf, so there's nothing to split
33 - *
34 - * @example Paragraph rules
35 - * {
36 - * 'self': true
37 - * 'children': null
38 - * }
39 - * @example List rules
40 - * {
41 - * 'self': false,
42 - * 'children': true
43 - * }
44 - * @example ListItem rules
45 - * {
46 - * 'self': true,
47 - * 'children': false
48 - * }
49 - */
50 -es.DocumentView.splitRules = {};
51 -
52 -/* Methods */
53 -
54 -/**
55 - * Get the document offset of a position created from passed DOM event
56 - *
57 - * @method
58 - * @param e {Event} Event to create es.Position from
59 - * @returns {Integer} Document offset
60 - */
61 -es.DocumentView.prototype.getOffsetFromEvent = function( e ) {
62 - var position = es.Position.newFromEventPagePosition( e );
63 - return this.getOffsetFromRenderedPosition( position );
64 -};
65 -
66 -es.DocumentView.splitRules.document = {
67 - 'self': false,
68 - 'children': true
69 -};
70 -
71 -/* Inheritance */
72 -
73 -es.extendClass( es.DocumentView, es.DocumentViewBranchNode );
 2+/**
 3+ * Creates an es.DocumentView object.
 4+ *
 5+ * @class
 6+ * @constructor
 7+ * @extends {es.DocumentViewBranchNode}
 8+ * @param {es.DocumentModel} documentModel Document model to view
 9+ * @param {es.SurfaceView} surfaceView Surface view this view is a child of
 10+ */
 11+es.DocumentView = function( model, surfaceView ) {
 12+ // Inheritance
 13+ es.DocumentViewBranchNode.call( this, model );
 14+
 15+ // Properties
 16+ this.surfaceView = surfaceView;
 17+
 18+ // DOM Changes
 19+ this.$.addClass( 'es-documentView' );
 20+ this.$.attr('contentEditable', 'true');
 21+};
 22+
 23+/* Static Members */
 24+
 25+
 26+/**
 27+ * Mapping of symbolic names and splitting rules.
 28+ *
 29+ * Each rule is an object with a self and children property. Each of these properties may contain
 30+ * one of two possible values:
 31+ * Boolean - Whether a split is allowed
 32+ * Null - Node is a leaf, so there's nothing to split
 33+ *
 34+ * @example Paragraph rules
 35+ * {
 36+ * 'self': true
 37+ * 'children': null
 38+ * }
 39+ * @example List rules
 40+ * {
 41+ * 'self': false,
 42+ * 'children': true
 43+ * }
 44+ * @example ListItem rules
 45+ * {
 46+ * 'self': true,
 47+ * 'children': false
 48+ * }
 49+ */
 50+es.DocumentView.splitRules = {};
 51+
 52+/* Methods */
 53+
 54+/**
 55+ * Get the document offset of a position created from passed DOM event
 56+ *
 57+ * @method
 58+ * @param e {Event} Event to create es.Position from
 59+ * @returns {Integer} Document offset
 60+ */
 61+es.DocumentView.prototype.getOffsetFromEvent = function( e ) {
 62+ var position = es.Position.newFromEventPagePosition( e );
 63+ return this.getOffsetFromRenderedPosition( position );
 64+};
 65+
 66+es.DocumentView.splitRules.document = {
 67+ 'self': false,
 68+ 'children': true
 69+};
 70+
 71+/* Inheritance */
 72+
 73+es.extendClass( es.DocumentView, es.DocumentViewBranchNode );
Property changes on: trunk/extensions/VisualEditor/contentEditable/views/es.DocumentView.js
___________________________________________________________________
Added: svn:eol-style
7474 + native
Property changes on: trunk/extensions/VisualEditor/contentEditable/diff_match_patch.js
___________________________________________________________________
Added: svn:eol-style
7575 + native
Property changes on: trunk/extensions/MwEmbedSupport/MwEmbedModules/MwEmbedSupport/jquery.loadingSpinner/spinner.js
___________________________________________________________________
Added: svn:eol-style
7676 + native
Property changes on: trunk/extensions/EducationProgram/specials/SpecialInstitutionHistory.php
___________________________________________________________________
Added: svn:eol-style
7777 + native
Property changes on: trunk/extensions/EducationProgram/specials/SpecialCourseHistory.php
___________________________________________________________________
Added: svn:eol-style
7878 + native
Property changes on: trunk/extensions/EducationProgram/specials/SpecialEPHistory.php
___________________________________________________________________
Added: svn:eol-style
7979 + native
Property changes on: trunk/extensions/EducationProgram/includes/EPRevisionPager.php
___________________________________________________________________
Added: svn:eol-style
8080 + native
Property changes on: trunk/extensions/EducationProgram/includes/EPRevisions.php
___________________________________________________________________
Added: svn:eol-style
8181 + native
Property changes on: trunk/extensions/EducationProgram/includes/EPRevision.php
___________________________________________________________________
Added: svn:eol-style
8282 + native
Property changes on: trunk/extensions/EducationProgram/includes/EPPageObject.php
___________________________________________________________________
Added: svn:eol-style
8383 + native

Status & tagging log