r111166 MediaWiki - Code Review archive

Repository:MediaWiki
Revision:r111165‎ | r111166 | r111167 >
Date:16:57, 10 February 2012
Author:ialex
Status:ok
Tags:
Comment:
svn:eol-style native
Modified paths:
  • /trunk/extensions/ArticleCreationWorkflow/INSTALL (modified) (history)
  • /trunk/extensions/ArticleCreationWorkflow/README (modified) (history)
  • /trunk/extensions/EducationProgram/includes/EPRevisionAction.php (modified) (history)
  • /trunk/extensions/Example/Example.body.php (modified) (history)
  • /trunk/extensions/Example/Example.i18n.php (modified) (history)
  • /trunk/extensions/Example/Example.php (modified) (history)
  • /trunk/extensions/Example/README (modified) (history)
  • /trunk/extensions/LiveTranslate/includes/ext.lt.load.js (modified) (history)
  • /trunk/extensions/VisualEditor/modules/rangy/rangy-cssclassapplier.js (modified) (history)
  • /trunk/extensions/VisualEditor/modules/rangy/rangy-position.js (modified) (history)
  • /trunk/extensions/VisualEditor/modules/rangy/rangy-selectionsaverestore.js (modified) (history)
  • /trunk/extensions/VisualEditor/modules/rangy/rangy-serializer.js (modified) (history)
  • /trunk/extensions/VisualEditor/modules/ve/ce/nodes/ve.es.ParagraphNode.js (modified) (history)
  • /trunk/extensions/VisualEditor/modules/ve/ce/styles/ve.es.Content.css (modified) (history)
  • /trunk/extensions/VisualEditor/modules/ve/ce/ve.es.LeafNode.js (modified) (history)
  • /trunk/phase3/maintenance/archives/patch-af_page_user_token-index.sql (modified) (history)

Diff [purge]

Property changes on: trunk/phase3/maintenance/archives/patch-af_page_user_token-index.sql
___________________________________________________________________
Added: svn:eol-style
11 + native
Property changes on: trunk/extensions/Example/Example.i18n.php
___________________________________________________________________
Added: svn:eol-style
22 + native
Property changes on: trunk/extensions/Example/Example.php
___________________________________________________________________
Added: svn:eol-style
33 + native
Property changes on: trunk/extensions/Example/Example.body.php
___________________________________________________________________
Added: svn:eol-style
44 + native
Property changes on: trunk/extensions/Example/README
___________________________________________________________________
Added: svn:eol-style
55 + native
Property changes on: trunk/extensions/ArticleCreationWorkflow/INSTALL
___________________________________________________________________
Added: svn:eol-style
66 + native
Property changes on: trunk/extensions/ArticleCreationWorkflow/README
___________________________________________________________________
Added: svn:eol-style
77 + native
Index: trunk/extensions/VisualEditor/modules/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/modules/rangy/rangy-selectionsaverestore.js
___________________________________________________________________
Added: svn:eol-style
197197 + native
Index: trunk/extensions/VisualEditor/modules/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/modules/rangy/rangy-serializer.js
___________________________________________________________________
Added: svn:eol-style
302302 + native
Index: trunk/extensions/VisualEditor/modules/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/modules/rangy/rangy-cssclassapplier.js
___________________________________________________________________
Added: svn:eol-style
715715 + native
Index: trunk/extensions/VisualEditor/modules/rangy/rangy-position.js
@@ -1,364 +1,364 @@
2 -/**
3 - * @license Position module for Rangy.
4 - * Extensions to Range and Selection objects to provide access to pixel positions relative to the viewport or document.
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 %%build:year%%, Tim Down
12 - * Licensed under the MIT license.
13 - * Version: %%build:version%%
14 - * Build date: %%build:date%%
15 - */
16 -rangy.createModule("Coordinates", function(api, module) {
17 - api.requireModules( ["WrappedSelection", "WrappedRange"] );
18 -
19 - var NUMBER = "number";
20 - var WrappedRange = api.WrappedRange;
21 - var dom = api.dom, util = api.util;
22 -
23 - // Since Rangy can deal with multiple documents, we have to do the checks every time, unless we cache a
24 - // getScrollPosition function in each document. This would necessarily pollute the document's global
25 - // namespace, which I'm choosing to view as a greater evil than a slight performance hit.
26 - function getScrollPosition(win) {
27 - var x = 0, y = 0;
28 - if (typeof win.pageXOffset == NUMBER && typeof win.pageYOffset == NUMBER) {
29 - x = win.pageXOffset;
30 - y = win.pageYOffset;
31 - } else {
32 - var doc = win.document;
33 - var docEl = doc.documentElement;
34 - var compatMode = doc.compatMode;
35 - var scrollEl = (typeof compatMode == "string" && compatMode.indexOf("CSS") >= 0 && docEl)
36 - ? docEl : dom.getBody(doc);
37 -
38 - if (scrollEl && typeof scrollEl.scrollLeft == NUMBER && typeof scrollEl.scrollTop == NUMBER) {
39 - try {
40 - x = scrollEl.scrollLeft;
41 - y = scrollEl.scrollTop;
42 - } catch (ex) {}
43 - }
44 - }
45 - return { x: x, y: y };
46 - }
47 -
48 - function getAncestorElement(node, tagName) {
49 - tagName = tagName.toLowerCase();
50 - while (node) {
51 - if (node.nodeType == 1 && node.tagName.toLowerCase() == tagName) {
52 - return node;
53 - }
54 - node = node.parentNode;
55 - }
56 - return null;
57 - }
58 -
59 - function Rect(top, right, bottom, left) {
60 - this.top = top;
61 - this.right = right;
62 - this.bottom = bottom;
63 - this.left = left;
64 - this.width = right - left;
65 - this.height = bottom - top;
66 - }
67 -
68 - function createRelativeRect(rect, dx, dy) {
69 - return new Rect(rect.top + dy, rect.right + dx, rect.bottom + dy, rect.left + dx);
70 - }
71 -
72 - function adjustClientRect(rect, doc) {
73 - // Older IEs have an issue with a two pixel margin on the body element
74 - var dx = 0, dy = 0;
75 - var docEl = doc.documentElement, body = dom.getBody(doc);
76 - var container = (docEl.clientWidth === 0 && typeof body.clientTop == NUMBER) ? body : docEl;
77 - var clientLeft = container.clientLeft, clientTop = container.clientTop;
78 - if (clientLeft) {
79 - dx = -clientLeft;
80 - }
81 - if (clientTop) {
82 - dy = -clientTop;
83 - }
84 - return createRelativeRect(rect, dx, dy);
85 - }
86 -
87 - function mergeRects(rects) {
88 - var tops = [], bottoms = [], lefts = [], rights = [];
89 - for (var i = 0, len = rects.length, rect; i < len; ++i) {
90 - rect = rects[i];
91 - if (rect) {
92 - tops.push(rect.top);
93 - bottoms.push(rect.bottom);
94 - lefts.push(rect.left);
95 - rights.push(rect.right);
96 - }
97 - }
98 - return new Rect(
99 - Math.min.apply(Math, tops),
100 - Math.max.apply(Math, rights),
101 - Math.max.apply(Math, bottoms),
102 - Math.min.apply(Math, lefts)
103 - );
104 - }
105 -
106 - (function() {
107 -
108 - // Test that <span> elements support getBoundingClientRect
109 - var span = document.createElement("span");
110 - var elementSupportsGetBoundingClientRect = util.isHostMethod(span, "getBoundingClientRect");
111 - span = null;
112 -
113 - // Test for getBoundingClientRect support in Range
114 - var rangeSupportsGetClientRects = false, rangeSupportsGetBoundingClientRect = false;
115 - if (api.features.implementsDomRange) {
116 - var testRange = api.createNativeRange();
117 - rangeSupportsGetClientRects = util.isHostMethod(testRange, "getClientRects");
118 - rangeSupportsGetBoundingClientRect = util.isHostMethod(testRange, "getBoundingClientRect");
119 - testRange.detach();
120 - }
121 -
122 - util.extend(api.features, {
123 - rangeSupportsGetBoundingClientRect: rangeSupportsGetBoundingClientRect,
124 - rangeSupportsGetClientRects: rangeSupportsGetClientRects,
125 - elementSupportsGetBoundingClientRect: elementSupportsGetBoundingClientRect
126 - });
127 -
128 - var createClientBoundaryPosGetter = function(isStart) {
129 - return function() {
130 - var boundaryRange = this.cloneRange();
131 - boundaryRange.collapse(isStart);
132 - var rect = boundaryRange.getBoundingClientRect();
133 - return { x: rect[isStart ? "left" : "right"], y: rect[isStart ? "top" : "bottom"] };
134 - };
135 - };
136 -
137 - var rangeProto = api.rangePrototype;
138 -
139 - if (api.features.implementsTextRange && elementSupportsGetBoundingClientRect) {
140 - rangeProto.getBoundingClientRect = function() {
141 - // We need a TextRange
142 - var textRange = WrappedRange.rangeToTextRange(this);
143 -
144 - // Work around table problems (table cell bounding rects seem not to count if TextRange spans cells)
145 - var cells = this.getNodes([1], function(el) {
146 - return /^t[dh]$/i.test(el.tagName);
147 - });
148 -
149 - // Merge rects for each cell selected by the range into overall rect
150 - var rect, rects = [];
151 - if (cells.length > 0) {
152 - var lastTable = getAncestorElement(this.startContainer, "table");
153 -
154 - for (var i = 0, cell, tempTextRange, table, subRange, subRect; cell = cells[i]; ++i) {
155 - // Handle non-table sections of the range
156 - table = getAncestorElement(cell, "table");
157 - if (!lastTable || table != lastTable) {
158 - // There is a section of the range prior to the current table, or lying between tables.
159 - // Merge in its rect
160 - subRange = this.cloneRange();
161 - if (lastTable) {
162 - subRange.setStartAfter(lastTable);
163 - }
164 - subRange.setEndBefore(table);
165 - rects.push(WrappedRange.rangeToTextRange(subRange).getBoundingClientRect());
166 - }
167 -
168 - if (this.containsNode(cell)) {
169 - rects.push(cell.getBoundingClientRect());
170 - } else {
171 - tempTextRange = textRange.duplicate();
172 - tempTextRange.moveToElementText(cell);
173 - if (tempTextRange.compareEndPoints("StartToStart", textRange) == -1) {
174 - tempTextRange.setEndPoint("StartToStart", textRange);
175 - } else if (tempTextRange.compareEndPoints("EndToEnd", textRange) == 1) {
176 - tempTextRange.setEndPoint("EndToEnd", textRange);
177 - }
178 - rects.push(tempTextRange.getBoundingClientRect());
179 - }
180 - lastTable = table;
181 - }
182 -
183 - // Merge in the rect for any content lying after the final table
184 - var endTable = getAncestorElement(this.endContainer, "table");
185 - if (!endTable && lastTable) {
186 - subRange = this.cloneRange();
187 - subRange.setStartAfter(lastTable);
188 - rects.push(WrappedRange.rangeToTextRange(subRange).getBoundingClientRect());
189 - }
190 - rect = mergeRects(rects);
191 - } else {
192 - rect = textRange.getBoundingClientRect();
193 - }
194 -
195 - return adjustClientRect(rect, dom.getDocument(this.startContainer));
196 - };
197 - } else if (api.features.implementsDomRange) {
198 - var createWrappedRange = function(range) {
199 - return (range instanceof WrappedRange) ? range : new WrappedRange(range);
200 - };
201 -
202 - if (rangeSupportsGetBoundingClientRect) {
203 - rangeProto.getBoundingClientRect = function() {
204 - var nativeRange = createWrappedRange(this).nativeRange;
205 - // Test for WebKit getBoundingClientRect bug (https://bugs.webkit.org/show_bug.cgi?id=65324)
206 - var rect = nativeRange.getBoundingClientRect() || nativeRange.getClientRects()[0];
207 - return adjustClientRect(rect, dom.getDocument(this.startContainer));
208 - };
209 -
210 - if (rangeSupportsGetClientRects) {
211 - createClientBoundaryPosGetter = function(isStart) {
212 - return function() {
213 - var rect, nativeRange = createWrappedRange(this).nativeRange;
214 - if (isStart) {
215 - rect = nativeRange.getClientRects()[0];
216 - return { x: rect.left, y: rect.top };
217 - } else {
218 - var rects = nativeRange.getClientRects();
219 - rect = rects[rects.length - 1];
220 - return { x: rect.right, y: rect.bottom };
221 - }
222 - };
223 - }
224 - }
225 - } else {
226 - var getElementBoundingClientRect = elementSupportsGetBoundingClientRect ?
227 - function(el) {
228 - return adjustClientRect(el.getBoundingClientRect(), dom.getDocument(el));
229 - } :
230 -
231 - // This implementation is very naive. There are many browser quirks that make it extremely
232 - // difficult to get accurate element coordinates in all situations
233 - function(el) {
234 - var x = 0, y = 0, offsetEl = el, width = el.offsetWidth, height = el.offsetHeight;
235 - while (offsetEl) {
236 - x += offsetEl.offsetLeft;
237 - y += offsetEl.offsetTop;
238 - offsetEl = offsetEl.offsetParent;
239 - }
240 -
241 - return adjustClientRect(new Rect(y, x + width, y + height, x), dom.getDocument(el));
242 - };
243 -
244 - var getRectFromBoundaries = function(range) {
245 - var rect;
246 - range.splitBoundaries();
247 - var span = document.createElement("span");
248 -
249 - if (range.collapsed) {
250 - range.insertNode(span);
251 - rect = getElementBoundingClientRect(span);
252 - span.parentNode.removeChild(span);
253 - } else {
254 - // TODO: This isn't right. I'm not sure it can be made right sensibly. Consider what to do.
255 - // This doesn't consider all the line boxes it needs to consider.
256 - var workingRange = range.cloneRange();
257 -
258 - // Get the start rectangle
259 - workingRange.collapse(true);
260 - workingRange.insertNode(span);
261 - var startRect = getElementBoundingClientRect(span);
262 - span.parentNode.removeChild(span);
263 -
264 - // Get the end rectangle
265 - workingRange.collapseToPoint(range.endContainer, range.endOffset);
266 - workingRange.insertNode(span);
267 - var endRect = getElementBoundingClientRect(span);
268 - span.parentNode.removeChild(span);
269 -
270 - // Merge the start and end rects
271 - var rects = [startRect, endRect];
272 -
273 - // Merge in rectangles for all elements in the range
274 - var elements = range.getNodes([1], function(el) {
275 - return range.containsNode(el);
276 - });
277 -
278 - for (var i = 0, len = elements.length; i < len; ++i) {
279 - rects.push(getElementBoundingClientRect(elements[i]));
280 - }
281 - rect = mergeRects(rects)
282 - }
283 -
284 - // Clean up
285 - range.normalizeBoundaries();
286 - return rect;
287 - };
288 -
289 - rangeProto.getBoundingClientRect = function(range) {
290 - return getRectFromBoundaries(createWrappedRange(range));
291 - };
292 - }
293 -
294 - function createDocumentBoundaryPosGetter(isStart) {
295 - return function() {
296 - var pos = this["get" + (isStart ? "Start" : "End") + "ClientPos"]();
297 - var scrollPos = getScrollPosition( dom.getWindow(this.startContainer) );
298 - return { x: pos.x + scrollPos.x, y: pos.y + scrollPos.y };
299 - };
300 - }
301 - }
302 -
303 - util.extend(rangeProto, {
304 - getBoundingDocumentRect: function() {
305 - var scrollPos = getScrollPosition( dom.getWindow(this.startContainer) );
306 - return createRelativeRect(this.getBoundingClientRect(), scrollPos.x, scrollPos.y);
307 - },
308 -
309 - getStartClientPos: createClientBoundaryPosGetter(true),
310 - getEndClientPos: createClientBoundaryPosGetter(false),
311 -
312 - getStartDocumentPos: createDocumentBoundaryPosGetter(true),
313 - getEndDocumentPos: createDocumentBoundaryPosGetter(false)
314 - });
315 - })();
316 -
317 - // Add Selection methods
318 - (function() {
319 - function compareRanges(r1, r2) {
320 - return r1.compareBoundaryPoints(r2.START_TO_START, r2);
321 - }
322 -
323 - function createSelectionRectGetter(isDocument) {
324 - return function() {
325 - var rangeMethodName = "getBounding" + (isDocument ? "Document" : "Client") + "Rect";
326 - var rects = [];
327 - for (var i = 0, rect = null, rangeRect; i < this.rangeCount; ++i) {
328 - rects.push(this.getRangeAt(i)[rangeMethodName]());
329 - }
330 - return mergeRects(rects);
331 - };
332 - }
333 -
334 - function createSelectionBoundaryPosGetter(isStart, isDocument) {
335 - return function() {
336 - if (this.rangeCount == 0) {
337 - return null;
338 - }
339 -
340 - var posType = isDocument ? "Document" : "Client";
341 -
342 - var ranges = this.getAllRanges();
343 - if (ranges.length > 1) {
344 - // Order the ranges by position within the DOM
345 - ranges.sort(compareRanges);
346 - }
347 -
348 - return isStart ?
349 - ranges[0]["getStart" + posType + "Pos"]() :
350 - ranges[ranges.length - 1]["getEnd" + posType + "Pos"]();
351 - };
352 - }
353 -
354 - util.extend(api.selectionPrototype, {
355 - getBoundingClientRect: createSelectionRectGetter(false),
356 - getBoundingDocumentRect: createSelectionRectGetter(true),
357 -
358 - getStartClientPos: createSelectionBoundaryPosGetter(true, false),
359 - getEndClientPos: createSelectionBoundaryPosGetter(false, false),
360 -
361 - getStartDocumentPos: createSelectionBoundaryPosGetter(true, true),
362 - getEndDocumentPos: createSelectionBoundaryPosGetter(false, true)
363 - });
364 - })();
365 -});
 2+/**
 3+ * @license Position module for Rangy.
 4+ * Extensions to Range and Selection objects to provide access to pixel positions relative to the viewport or document.
 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 %%build:year%%, Tim Down
 12+ * Licensed under the MIT license.
 13+ * Version: %%build:version%%
 14+ * Build date: %%build:date%%
 15+ */
 16+rangy.createModule("Coordinates", function(api, module) {
 17+ api.requireModules( ["WrappedSelection", "WrappedRange"] );
 18+
 19+ var NUMBER = "number";
 20+ var WrappedRange = api.WrappedRange;
 21+ var dom = api.dom, util = api.util;
 22+
 23+ // Since Rangy can deal with multiple documents, we have to do the checks every time, unless we cache a
 24+ // getScrollPosition function in each document. This would necessarily pollute the document's global
 25+ // namespace, which I'm choosing to view as a greater evil than a slight performance hit.
 26+ function getScrollPosition(win) {
 27+ var x = 0, y = 0;
 28+ if (typeof win.pageXOffset == NUMBER && typeof win.pageYOffset == NUMBER) {
 29+ x = win.pageXOffset;
 30+ y = win.pageYOffset;
 31+ } else {
 32+ var doc = win.document;
 33+ var docEl = doc.documentElement;
 34+ var compatMode = doc.compatMode;
 35+ var scrollEl = (typeof compatMode == "string" && compatMode.indexOf("CSS") >= 0 && docEl)
 36+ ? docEl : dom.getBody(doc);
 37+
 38+ if (scrollEl && typeof scrollEl.scrollLeft == NUMBER && typeof scrollEl.scrollTop == NUMBER) {
 39+ try {
 40+ x = scrollEl.scrollLeft;
 41+ y = scrollEl.scrollTop;
 42+ } catch (ex) {}
 43+ }
 44+ }
 45+ return { x: x, y: y };
 46+ }
 47+
 48+ function getAncestorElement(node, tagName) {
 49+ tagName = tagName.toLowerCase();
 50+ while (node) {
 51+ if (node.nodeType == 1 && node.tagName.toLowerCase() == tagName) {
 52+ return node;
 53+ }
 54+ node = node.parentNode;
 55+ }
 56+ return null;
 57+ }
 58+
 59+ function Rect(top, right, bottom, left) {
 60+ this.top = top;
 61+ this.right = right;
 62+ this.bottom = bottom;
 63+ this.left = left;
 64+ this.width = right - left;
 65+ this.height = bottom - top;
 66+ }
 67+
 68+ function createRelativeRect(rect, dx, dy) {
 69+ return new Rect(rect.top + dy, rect.right + dx, rect.bottom + dy, rect.left + dx);
 70+ }
 71+
 72+ function adjustClientRect(rect, doc) {
 73+ // Older IEs have an issue with a two pixel margin on the body element
 74+ var dx = 0, dy = 0;
 75+ var docEl = doc.documentElement, body = dom.getBody(doc);
 76+ var container = (docEl.clientWidth === 0 && typeof body.clientTop == NUMBER) ? body : docEl;
 77+ var clientLeft = container.clientLeft, clientTop = container.clientTop;
 78+ if (clientLeft) {
 79+ dx = -clientLeft;
 80+ }
 81+ if (clientTop) {
 82+ dy = -clientTop;
 83+ }
 84+ return createRelativeRect(rect, dx, dy);
 85+ }
 86+
 87+ function mergeRects(rects) {
 88+ var tops = [], bottoms = [], lefts = [], rights = [];
 89+ for (var i = 0, len = rects.length, rect; i < len; ++i) {
 90+ rect = rects[i];
 91+ if (rect) {
 92+ tops.push(rect.top);
 93+ bottoms.push(rect.bottom);
 94+ lefts.push(rect.left);
 95+ rights.push(rect.right);
 96+ }
 97+ }
 98+ return new Rect(
 99+ Math.min.apply(Math, tops),
 100+ Math.max.apply(Math, rights),
 101+ Math.max.apply(Math, bottoms),
 102+ Math.min.apply(Math, lefts)
 103+ );
 104+ }
 105+
 106+ (function() {
 107+
 108+ // Test that <span> elements support getBoundingClientRect
 109+ var span = document.createElement("span");
 110+ var elementSupportsGetBoundingClientRect = util.isHostMethod(span, "getBoundingClientRect");
 111+ span = null;
 112+
 113+ // Test for getBoundingClientRect support in Range
 114+ var rangeSupportsGetClientRects = false, rangeSupportsGetBoundingClientRect = false;
 115+ if (api.features.implementsDomRange) {
 116+ var testRange = api.createNativeRange();
 117+ rangeSupportsGetClientRects = util.isHostMethod(testRange, "getClientRects");
 118+ rangeSupportsGetBoundingClientRect = util.isHostMethod(testRange, "getBoundingClientRect");
 119+ testRange.detach();
 120+ }
 121+
 122+ util.extend(api.features, {
 123+ rangeSupportsGetBoundingClientRect: rangeSupportsGetBoundingClientRect,
 124+ rangeSupportsGetClientRects: rangeSupportsGetClientRects,
 125+ elementSupportsGetBoundingClientRect: elementSupportsGetBoundingClientRect
 126+ });
 127+
 128+ var createClientBoundaryPosGetter = function(isStart) {
 129+ return function() {
 130+ var boundaryRange = this.cloneRange();
 131+ boundaryRange.collapse(isStart);
 132+ var rect = boundaryRange.getBoundingClientRect();
 133+ return { x: rect[isStart ? "left" : "right"], y: rect[isStart ? "top" : "bottom"] };
 134+ };
 135+ };
 136+
 137+ var rangeProto = api.rangePrototype;
 138+
 139+ if (api.features.implementsTextRange && elementSupportsGetBoundingClientRect) {
 140+ rangeProto.getBoundingClientRect = function() {
 141+ // We need a TextRange
 142+ var textRange = WrappedRange.rangeToTextRange(this);
 143+
 144+ // Work around table problems (table cell bounding rects seem not to count if TextRange spans cells)
 145+ var cells = this.getNodes([1], function(el) {
 146+ return /^t[dh]$/i.test(el.tagName);
 147+ });
 148+
 149+ // Merge rects for each cell selected by the range into overall rect
 150+ var rect, rects = [];
 151+ if (cells.length > 0) {
 152+ var lastTable = getAncestorElement(this.startContainer, "table");
 153+
 154+ for (var i = 0, cell, tempTextRange, table, subRange, subRect; cell = cells[i]; ++i) {
 155+ // Handle non-table sections of the range
 156+ table = getAncestorElement(cell, "table");
 157+ if (!lastTable || table != lastTable) {
 158+ // There is a section of the range prior to the current table, or lying between tables.
 159+ // Merge in its rect
 160+ subRange = this.cloneRange();
 161+ if (lastTable) {
 162+ subRange.setStartAfter(lastTable);
 163+ }
 164+ subRange.setEndBefore(table);
 165+ rects.push(WrappedRange.rangeToTextRange(subRange).getBoundingClientRect());
 166+ }
 167+
 168+ if (this.containsNode(cell)) {
 169+ rects.push(cell.getBoundingClientRect());
 170+ } else {
 171+ tempTextRange = textRange.duplicate();
 172+ tempTextRange.moveToElementText(cell);
 173+ if (tempTextRange.compareEndPoints("StartToStart", textRange) == -1) {
 174+ tempTextRange.setEndPoint("StartToStart", textRange);
 175+ } else if (tempTextRange.compareEndPoints("EndToEnd", textRange) == 1) {
 176+ tempTextRange.setEndPoint("EndToEnd", textRange);
 177+ }
 178+ rects.push(tempTextRange.getBoundingClientRect());
 179+ }
 180+ lastTable = table;
 181+ }
 182+
 183+ // Merge in the rect for any content lying after the final table
 184+ var endTable = getAncestorElement(this.endContainer, "table");
 185+ if (!endTable && lastTable) {
 186+ subRange = this.cloneRange();
 187+ subRange.setStartAfter(lastTable);
 188+ rects.push(WrappedRange.rangeToTextRange(subRange).getBoundingClientRect());
 189+ }
 190+ rect = mergeRects(rects);
 191+ } else {
 192+ rect = textRange.getBoundingClientRect();
 193+ }
 194+
 195+ return adjustClientRect(rect, dom.getDocument(this.startContainer));
 196+ };
 197+ } else if (api.features.implementsDomRange) {
 198+ var createWrappedRange = function(range) {
 199+ return (range instanceof WrappedRange) ? range : new WrappedRange(range);
 200+ };
 201+
 202+ if (rangeSupportsGetBoundingClientRect) {
 203+ rangeProto.getBoundingClientRect = function() {
 204+ var nativeRange = createWrappedRange(this).nativeRange;
 205+ // Test for WebKit getBoundingClientRect bug (https://bugs.webkit.org/show_bug.cgi?id=65324)
 206+ var rect = nativeRange.getBoundingClientRect() || nativeRange.getClientRects()[0];
 207+ return adjustClientRect(rect, dom.getDocument(this.startContainer));
 208+ };
 209+
 210+ if (rangeSupportsGetClientRects) {
 211+ createClientBoundaryPosGetter = function(isStart) {
 212+ return function() {
 213+ var rect, nativeRange = createWrappedRange(this).nativeRange;
 214+ if (isStart) {
 215+ rect = nativeRange.getClientRects()[0];
 216+ return { x: rect.left, y: rect.top };
 217+ } else {
 218+ var rects = nativeRange.getClientRects();
 219+ rect = rects[rects.length - 1];
 220+ return { x: rect.right, y: rect.bottom };
 221+ }
 222+ };
 223+ }
 224+ }
 225+ } else {
 226+ var getElementBoundingClientRect = elementSupportsGetBoundingClientRect ?
 227+ function(el) {
 228+ return adjustClientRect(el.getBoundingClientRect(), dom.getDocument(el));
 229+ } :
 230+
 231+ // This implementation is very naive. There are many browser quirks that make it extremely
 232+ // difficult to get accurate element coordinates in all situations
 233+ function(el) {
 234+ var x = 0, y = 0, offsetEl = el, width = el.offsetWidth, height = el.offsetHeight;
 235+ while (offsetEl) {
 236+ x += offsetEl.offsetLeft;
 237+ y += offsetEl.offsetTop;
 238+ offsetEl = offsetEl.offsetParent;
 239+ }
 240+
 241+ return adjustClientRect(new Rect(y, x + width, y + height, x), dom.getDocument(el));
 242+ };
 243+
 244+ var getRectFromBoundaries = function(range) {
 245+ var rect;
 246+ range.splitBoundaries();
 247+ var span = document.createElement("span");
 248+
 249+ if (range.collapsed) {
 250+ range.insertNode(span);
 251+ rect = getElementBoundingClientRect(span);
 252+ span.parentNode.removeChild(span);
 253+ } else {
 254+ // TODO: This isn't right. I'm not sure it can be made right sensibly. Consider what to do.
 255+ // This doesn't consider all the line boxes it needs to consider.
 256+ var workingRange = range.cloneRange();
 257+
 258+ // Get the start rectangle
 259+ workingRange.collapse(true);
 260+ workingRange.insertNode(span);
 261+ var startRect = getElementBoundingClientRect(span);
 262+ span.parentNode.removeChild(span);
 263+
 264+ // Get the end rectangle
 265+ workingRange.collapseToPoint(range.endContainer, range.endOffset);
 266+ workingRange.insertNode(span);
 267+ var endRect = getElementBoundingClientRect(span);
 268+ span.parentNode.removeChild(span);
 269+
 270+ // Merge the start and end rects
 271+ var rects = [startRect, endRect];
 272+
 273+ // Merge in rectangles for all elements in the range
 274+ var elements = range.getNodes([1], function(el) {
 275+ return range.containsNode(el);
 276+ });
 277+
 278+ for (var i = 0, len = elements.length; i < len; ++i) {
 279+ rects.push(getElementBoundingClientRect(elements[i]));
 280+ }
 281+ rect = mergeRects(rects)
 282+ }
 283+
 284+ // Clean up
 285+ range.normalizeBoundaries();
 286+ return rect;
 287+ };
 288+
 289+ rangeProto.getBoundingClientRect = function(range) {
 290+ return getRectFromBoundaries(createWrappedRange(range));
 291+ };
 292+ }
 293+
 294+ function createDocumentBoundaryPosGetter(isStart) {
 295+ return function() {
 296+ var pos = this["get" + (isStart ? "Start" : "End") + "ClientPos"]();
 297+ var scrollPos = getScrollPosition( dom.getWindow(this.startContainer) );
 298+ return { x: pos.x + scrollPos.x, y: pos.y + scrollPos.y };
 299+ };
 300+ }
 301+ }
 302+
 303+ util.extend(rangeProto, {
 304+ getBoundingDocumentRect: function() {
 305+ var scrollPos = getScrollPosition( dom.getWindow(this.startContainer) );
 306+ return createRelativeRect(this.getBoundingClientRect(), scrollPos.x, scrollPos.y);
 307+ },
 308+
 309+ getStartClientPos: createClientBoundaryPosGetter(true),
 310+ getEndClientPos: createClientBoundaryPosGetter(false),
 311+
 312+ getStartDocumentPos: createDocumentBoundaryPosGetter(true),
 313+ getEndDocumentPos: createDocumentBoundaryPosGetter(false)
 314+ });
 315+ })();
 316+
 317+ // Add Selection methods
 318+ (function() {
 319+ function compareRanges(r1, r2) {
 320+ return r1.compareBoundaryPoints(r2.START_TO_START, r2);
 321+ }
 322+
 323+ function createSelectionRectGetter(isDocument) {
 324+ return function() {
 325+ var rangeMethodName = "getBounding" + (isDocument ? "Document" : "Client") + "Rect";
 326+ var rects = [];
 327+ for (var i = 0, rect = null, rangeRect; i < this.rangeCount; ++i) {
 328+ rects.push(this.getRangeAt(i)[rangeMethodName]());
 329+ }
 330+ return mergeRects(rects);
 331+ };
 332+ }
 333+
 334+ function createSelectionBoundaryPosGetter(isStart, isDocument) {
 335+ return function() {
 336+ if (this.rangeCount == 0) {
 337+ return null;
 338+ }
 339+
 340+ var posType = isDocument ? "Document" : "Client";
 341+
 342+ var ranges = this.getAllRanges();
 343+ if (ranges.length > 1) {
 344+ // Order the ranges by position within the DOM
 345+ ranges.sort(compareRanges);
 346+ }
 347+
 348+ return isStart ?
 349+ ranges[0]["getStart" + posType + "Pos"]() :
 350+ ranges[ranges.length - 1]["getEnd" + posType + "Pos"]();
 351+ };
 352+ }
 353+
 354+ util.extend(api.selectionPrototype, {
 355+ getBoundingClientRect: createSelectionRectGetter(false),
 356+ getBoundingDocumentRect: createSelectionRectGetter(true),
 357+
 358+ getStartClientPos: createSelectionBoundaryPosGetter(true, false),
 359+ getEndClientPos: createSelectionBoundaryPosGetter(false, false),
 360+
 361+ getStartDocumentPos: createSelectionBoundaryPosGetter(true, true),
 362+ getEndDocumentPos: createSelectionBoundaryPosGetter(false, true)
 363+ });
 364+ })();
 365+});
Property changes on: trunk/extensions/VisualEditor/modules/rangy/rangy-position.js
___________________________________________________________________
Added: svn:eol-style
366366 + native
Index: trunk/extensions/VisualEditor/modules/ve/ce/styles/ve.es.Content.css
@@ -1,89 +1,89 @@
2 -.es-contentView {
3 - position: relative;
4 - z-index: 1;
5 -}
6 -
7 -.es-contentView-line,
8 -.es-contentView-ruler {
9 - line-height: 1.5em;
10 - cursor: text;
11 - white-space: nowrap;
12 - color: #000000;
13 -}
14 -
15 -.es-contentView-ruler {
16 - position: absolute;
17 - top: 0;
18 - left: 0;
19 - display: inline-block;
20 - z-index: -1000;
21 -}
22 -
23 -.es-contentView-line.empty {
24 - display: block;
25 - width: 0px;
26 -}
27 -
28 -.es-contentView-whitespace {
29 - color: #ffffff;
30 -}
31 -
32 -.es-contentView-range {
33 - display: none;
34 - position: absolute;
35 - background-color: #b3d6f6;
36 - cursor: text;
37 - z-index: -1;
38 -}
39 -
40 -.es-contentView-format-object {
41 - background-color: rgba(0,0,0,0.05);
42 - border-radius: 0.25em;
43 - margin: 1px 0 1px 1px;
44 - padding: 0.25em 0;
45 - cursor: default;
46 -}
47 -
48 -.es-contentView-format-object * {
49 - cursor: default !important;
50 -}
51 -
52 -.es-contentView-format-object a:link,
53 -.es-contentView-format-object a:visited,
54 -.es-contentView-format-object a:active {
55 - color: #0645AD;
56 - text-decoration: none;
57 -}
58 -
59 -.es-contentView-format-textStyle-italic,
60 -.es-contentView-format-textStyle-emphasize {
61 - font-style: italic;
62 -}
63 -
64 -.es-contentView-format-textStyle-bold,
65 -.es-contentView-format-textStyle-strong {
66 - font-weight: bold;
67 -}
68 -
69 -.es-contentView-format-link {
70 - color: #0645AD;
71 - text-decoration: underline;
72 -}
73 -
74 -.es-contentView-format-textStyle-big {
75 - font-size: 1.2em;
76 -}
77 -
78 -.es-contentView-format-textStyle-small,
79 -.es-contentView-format-textStyle-subScript,
80 -.es-contentView-format-textStyle-superScript {
81 - font-size: .8em;
82 -}
83 -
84 -.es-contentView-format-textStyle-subScript {
85 - vertical-align: sub;
86 -}
87 -
88 -.es-contentView-format-textStyle-superScript {
89 - vertical-align: super;
90 -}
 2+.es-contentView {
 3+ position: relative;
 4+ z-index: 1;
 5+}
 6+
 7+.es-contentView-line,
 8+.es-contentView-ruler {
 9+ line-height: 1.5em;
 10+ cursor: text;
 11+ white-space: nowrap;
 12+ color: #000000;
 13+}
 14+
 15+.es-contentView-ruler {
 16+ position: absolute;
 17+ top: 0;
 18+ left: 0;
 19+ display: inline-block;
 20+ z-index: -1000;
 21+}
 22+
 23+.es-contentView-line.empty {
 24+ display: block;
 25+ width: 0px;
 26+}
 27+
 28+.es-contentView-whitespace {
 29+ color: #ffffff;
 30+}
 31+
 32+.es-contentView-range {
 33+ display: none;
 34+ position: absolute;
 35+ background-color: #b3d6f6;
 36+ cursor: text;
 37+ z-index: -1;
 38+}
 39+
 40+.es-contentView-format-object {
 41+ background-color: rgba(0,0,0,0.05);
 42+ border-radius: 0.25em;
 43+ margin: 1px 0 1px 1px;
 44+ padding: 0.25em 0;
 45+ cursor: default;
 46+}
 47+
 48+.es-contentView-format-object * {
 49+ cursor: default !important;
 50+}
 51+
 52+.es-contentView-format-object a:link,
 53+.es-contentView-format-object a:visited,
 54+.es-contentView-format-object a:active {
 55+ color: #0645AD;
 56+ text-decoration: none;
 57+}
 58+
 59+.es-contentView-format-textStyle-italic,
 60+.es-contentView-format-textStyle-emphasize {
 61+ font-style: italic;
 62+}
 63+
 64+.es-contentView-format-textStyle-bold,
 65+.es-contentView-format-textStyle-strong {
 66+ font-weight: bold;
 67+}
 68+
 69+.es-contentView-format-link {
 70+ color: #0645AD;
 71+ text-decoration: underline;
 72+}
 73+
 74+.es-contentView-format-textStyle-big {
 75+ font-size: 1.2em;
 76+}
 77+
 78+.es-contentView-format-textStyle-small,
 79+.es-contentView-format-textStyle-subScript,
 80+.es-contentView-format-textStyle-superScript {
 81+ font-size: .8em;
 82+}
 83+
 84+.es-contentView-format-textStyle-subScript {
 85+ vertical-align: sub;
 86+}
 87+
 88+.es-contentView-format-textStyle-superScript {
 89+ vertical-align: super;
 90+}
Property changes on: trunk/extensions/VisualEditor/modules/ve/ce/styles/ve.es.Content.css
___________________________________________________________________
Added: svn:eol-style
9191 + native
Index: trunk/extensions/VisualEditor/modules/ve/ce/ve.es.LeafNode.js
@@ -1,41 +1,41 @@
2 -/**
3 - * Creates an ve.es.LeafNode object.
4 - *
5 - * @class
6 - * @abstract
7 - * @constructor
8 - * @extends {ve.LeafNode}
9 - * @extends {ve.es.Node}
10 - * @param model {ve.ModelNode} Model to observe
11 - * @param {jQuery} [$element] Element to use as a container
12 - */
13 -ve.es.LeafNode = function( model, $element ) {
14 - // Inheritance
15 - ve.LeafNode.call( this );
16 - ve.es.Node.call( this, model, $element );
17 -
18 - this.$.data('view', this);
19 - this.$.addClass('ce-leafNode');
20 -
21 - // Properties
22 - this.contentView = new ve.es.Content( this.$, 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 -ve.es.LeafNode.prototype.renderContent = function() {
36 - this.contentView.render();
37 -};
38 -
39 -/* Inheritance */
40 -
41 -ve.extendClass( ve.es.LeafNode, ve.LeafNode );
42 -ve.extendClass( ve.es.LeafNode, ve.es.Node );
 2+/**
 3+ * Creates an ve.es.LeafNode object.
 4+ *
 5+ * @class
 6+ * @abstract
 7+ * @constructor
 8+ * @extends {ve.LeafNode}
 9+ * @extends {ve.es.Node}
 10+ * @param model {ve.ModelNode} Model to observe
 11+ * @param {jQuery} [$element] Element to use as a container
 12+ */
 13+ve.es.LeafNode = function( model, $element ) {
 14+ // Inheritance
 15+ ve.LeafNode.call( this );
 16+ ve.es.Node.call( this, model, $element );
 17+
 18+ this.$.data('view', this);
 19+ this.$.addClass('ce-leafNode');
 20+
 21+ // Properties
 22+ this.contentView = new ve.es.Content( this.$, 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+ve.es.LeafNode.prototype.renderContent = function() {
 36+ this.contentView.render();
 37+};
 38+
 39+/* Inheritance */
 40+
 41+ve.extendClass( ve.es.LeafNode, ve.LeafNode );
 42+ve.extendClass( ve.es.LeafNode, ve.es.Node );
Property changes on: trunk/extensions/VisualEditor/modules/ve/ce/ve.es.LeafNode.js
___________________________________________________________________
Added: svn:eol-style
4343 + native
Index: trunk/extensions/VisualEditor/modules/ve/ce/nodes/ve.es.ParagraphNode.js
@@ -1,26 +1,26 @@
2 -/**
3 - * Creates an ve.es.ParagraphNode object.
4 - *
5 - * @class
6 - * @constructor
7 - * @extends {ve.es.LeafNode}
8 - * @param {ve.dm.ParagraphNode} model Paragraph model to view
9 - */
10 -ve.es.ParagraphNode = function( model ) {
11 - // Inheritance
12 - ve.es.LeafNode.call( this, model, $( '<p></p>' ) );
13 -
14 - // DOM Changes
15 - this.$.addClass( 'es-paragraphView' );
16 -};
17 -
18 -/* Registration */
19 -
20 -ve.es.DocumentNode.splitRules.paragraph = {
21 - 'self': true,
22 - 'children': null
23 -};
24 -
25 -/* Inheritance */
26 -
27 -ve.extendClass( ve.es.ParagraphNode, ve.es.LeafNode );
 2+/**
 3+ * Creates an ve.es.ParagraphNode object.
 4+ *
 5+ * @class
 6+ * @constructor
 7+ * @extends {ve.es.LeafNode}
 8+ * @param {ve.dm.ParagraphNode} model Paragraph model to view
 9+ */
 10+ve.es.ParagraphNode = function( model ) {
 11+ // Inheritance
 12+ ve.es.LeafNode.call( this, model, $( '<p></p>' ) );
 13+
 14+ // DOM Changes
 15+ this.$.addClass( 'es-paragraphView' );
 16+};
 17+
 18+/* Registration */
 19+
 20+ve.es.DocumentNode.splitRules.paragraph = {
 21+ 'self': true,
 22+ 'children': null
 23+};
 24+
 25+/* Inheritance */
 26+
 27+ve.extendClass( ve.es.ParagraphNode, ve.es.LeafNode );
Property changes on: trunk/extensions/VisualEditor/modules/ve/ce/nodes/ve.es.ParagraphNode.js
___________________________________________________________________
Added: svn:eol-style
2828 + native
Property changes on: trunk/extensions/LiveTranslate/includes/ext.lt.load.js
___________________________________________________________________
Added: svn:eol-style
2929 + native
Property changes on: trunk/extensions/EducationProgram/includes/EPRevisionAction.php
___________________________________________________________________
Added: svn:eol-style
3030 + native

Status & tagging log