1 | /*
|
---|
2 | * file: Changes.js
|
---|
3 | *
|
---|
4 | * @BEGINLICENSE
|
---|
5 | * Copyright 2010 Brook Novak (email : [email protected])
|
---|
6 | * This program is free software; you can redistribute it and/or modify
|
---|
7 | * it under the terms of the GNU General Public License as published by
|
---|
8 | * the Free Software Foundation; either version 2 of the License, or
|
---|
9 | * (at your option) any later version.
|
---|
10 | * This program is distributed in the hope that it will be useful,
|
---|
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
---|
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
---|
13 | * GNU General Public License for more details.
|
---|
14 | * You should have received a copy of the GNU General Public License
|
---|
15 | * along with this program; if not, write to the Free Software
|
---|
16 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
---|
17 | * @ENDLICENSE
|
---|
18 | */
|
---|
19 | bootstrap.provides("Changes");
|
---|
20 |
|
---|
21 | (function() {
|
---|
22 |
|
---|
23 | /* All editable sections and their initial states since startup or the last clear. */
|
---|
24 | var esStartStates = [];
|
---|
25 |
|
---|
26 | $enqueueInit("Changes", function() {
|
---|
27 |
|
---|
28 | de.Changes.clear();
|
---|
29 | de.doc.addObserver({
|
---|
30 | onSectionAdded : function(editSection) {
|
---|
31 |
|
---|
32 | // Safety check: make sure editSection is not already registered
|
---|
33 | // @DEBUG ON
|
---|
34 | for (var i in esStartStates) {
|
---|
35 | debug.assert(esStartStates[i].esNode != editSection);
|
---|
36 | }
|
---|
37 | // @DEBUG OFF
|
---|
38 |
|
---|
39 | // Add the new section to the state array
|
---|
40 | esStartStates.push({
|
---|
41 | esNode : editSection,
|
---|
42 | initHTML : editSection.innerHTML
|
---|
43 | });
|
---|
44 |
|
---|
45 | }
|
---|
46 | });
|
---|
47 |
|
---|
48 | }, "Doc");
|
---|
49 |
|
---|
50 | /**
|
---|
51 | * @class A singleton that records the editable sections that have been changed/added/removed over time
|
---|
52 | */
|
---|
53 | de.Changes = {
|
---|
54 |
|
---|
55 | /**
|
---|
56 | * Gets all changes since last clear
|
---|
57 | *
|
---|
58 | * @return {[Element]} A list of changed editable section nodes. NOTE:
|
---|
59 | * Does not include removed editable sections .. it only checks the
|
---|
60 | * editable section contents.
|
---|
61 | *
|
---|
62 | * @see de.Changes.clear
|
---|
63 | */
|
---|
64 | getChangedEditableSections : function() {
|
---|
65 |
|
---|
66 | var changedSections = [],
|
---|
67 | stipEmptiesRE = /(<\s*\w+\s[^>]*?)(?:style|class|id|value)\s*=\s*(?:""|'')([^<]*?>)/i,
|
---|
68 | stipEmptiesREPresto = /(<\s*\w+\s[^>]*?)\s*(?:style|class|id|value)\s*(>|(?:[^=][^<]*?>))/i,
|
---|
69 | stripAttribWSRE = /<[^\/][^<>]*?\s[^<>]*>/,
|
---|
70 | wsRE = /(?:[\t\n\r ]| )/g;
|
---|
71 |
|
---|
72 | // Don't consider highlighting as part of HTML
|
---|
73 | _toggleSectionHighlight(false);
|
---|
74 |
|
---|
75 | // Look for changes
|
---|
76 | for (var i in esStartStates) {
|
---|
77 | var esSection = esStartStates[i];
|
---|
78 | if (stripIrrelevants(esSection.esNode.innerHTML) != stripIrrelevants(esSection.initHTML) || esSection.dirty)
|
---|
79 | changedSections.push(esSection.esNode);
|
---|
80 | }
|
---|
81 |
|
---|
82 | _toggleSectionHighlight(true);
|
---|
83 |
|
---|
84 | return changedSections;
|
---|
85 |
|
---|
86 | /**
|
---|
87 | * Strip irrelevent html from markup when comparing differences.
|
---|
88 | * E.G. Empty attibutes or different whitespace encodings.
|
---|
89 | * @param {String} str The html to stip irrelevent data from
|
---|
90 | */
|
---|
91 | function stripIrrelevants(str) {
|
---|
92 |
|
---|
93 | // Make all whitespaces normal whitespace
|
---|
94 | str = str.replace(wsRE, " ");
|
---|
95 |
|
---|
96 | var match, i, re, newStr = "";
|
---|
97 |
|
---|
98 | // Strip empty attributes
|
---|
99 | // One regexp matching pass for all browsers except for opera...
|
---|
100 | // Opera can leave empty attrbiutes without ="".
|
---|
101 | for (i = 0; i < (_engine == _Platform.PRESTO ? 2 : 1); i++) {
|
---|
102 |
|
---|
103 | // Select the regexp according to pass
|
---|
104 | re = i==0 ? stipEmptiesRE : stipEmptiesREPresto;
|
---|
105 |
|
---|
106 | while(match = re.exec(str)) {
|
---|
107 | str = str.substr(0, match.index) + match[1] + match[2] + str.substr(match.index + match[0].length);
|
---|
108 | }
|
---|
109 |
|
---|
110 | }
|
---|
111 |
|
---|
112 | if (match) {
|
---|
113 |
|
---|
114 | // Due to attributes from being stripped must clear whitespaces which separate attibutes in html tags..
|
---|
115 | // since whitespaces may only be present for the empty tags
|
---|
116 | while (match = stripAttribWSRE.exec(str)) {
|
---|
117 | newStr += str.substr(0, match.index) + match[0].replace(wsRE, "");
|
---|
118 | str = str.substr(match.index + match[0].length);
|
---|
119 | }
|
---|
120 | newStr += str;
|
---|
121 |
|
---|
122 | } else newStr = str;
|
---|
123 |
|
---|
124 | return de.spell.stripSpellWrapperHTML(newStr);
|
---|
125 | }
|
---|
126 |
|
---|
127 | },
|
---|
128 |
|
---|
129 | /**
|
---|
130 | * Wipes all recorded changes and prepares for recording new changes for all or a specific edit section.
|
---|
131 | *
|
---|
132 | * @param {Element} es (Optional) The edit section to wipe. If not provided then all edit sections will be wiped
|
---|
133 | *
|
---|
134 | * @see de.Changes.reset
|
---|
135 | */
|
---|
136 | clear : function(es) {
|
---|
137 |
|
---|
138 | // Exclude highlighting in HTML snapshots
|
---|
139 | _toggleSectionHighlight(false);
|
---|
140 |
|
---|
141 | if (es) {
|
---|
142 |
|
---|
143 | // Locate specific editable section to wipe changes
|
---|
144 | for (var i in esStartStates) {
|
---|
145 | if (esStartStates[i].esNode == es) {
|
---|
146 | esStartStates[i].initHTML = es.innerHTML;
|
---|
147 | esStartStates[i].dirty = 0;
|
---|
148 | break;
|
---|
149 | }
|
---|
150 | }
|
---|
151 |
|
---|
152 | } else {
|
---|
153 |
|
---|
154 | // Wipe all previous state information
|
---|
155 | esStartStates = [];
|
---|
156 |
|
---|
157 | // Build up the state information based on the current document state
|
---|
158 | var editableSections = de.doc.getAllEditSections();
|
---|
159 |
|
---|
160 | for (var i in editableSections) {
|
---|
161 | var domNode = editableSections[i];
|
---|
162 |
|
---|
163 | esStartStates.push({
|
---|
164 | esNode: domNode,
|
---|
165 | initHTML: domNode.innerHTML
|
---|
166 | });
|
---|
167 | }
|
---|
168 | }
|
---|
169 |
|
---|
170 | _toggleSectionHighlight(true);
|
---|
171 | },
|
---|
172 |
|
---|
173 |
|
---|
174 |
|
---|
175 | /**
|
---|
176 | * Use to mark all or a specific editable section as being dirty.
|
---|
177 | *
|
---|
178 | * If marked as dirty, then the next request for changed editable sections will also return the
|
---|
179 | * editable section marked dirty even if they have not changed / have been cleared.
|
---|
180 | *
|
---|
181 | * The next time the edit section is cleared it will be unmarked as being dirty.
|
---|
182 | *
|
---|
183 | * @param {Object} es (Optional) The edit section to dirty. If not provided then all edit sections will be made dirty.
|
---|
184 | */
|
---|
185 | dirty : function(es) {
|
---|
186 |
|
---|
187 | for (var i in esStartStates) {
|
---|
188 | if (!es || esStartStates[i].esNode == es) {
|
---|
189 | esStartStates[i].dirty = 1;
|
---|
190 | }
|
---|
191 | }
|
---|
192 |
|
---|
193 | },
|
---|
194 |
|
---|
195 | /**
|
---|
196 | * Clears the changes, and resets all edit sections html to their initial state since
|
---|
197 | * start up or the last clear/reset operation. All undo/redo history will be cleared;
|
---|
198 | *
|
---|
199 | * @see de.Changes.clear
|
---|
200 | */
|
---|
201 | reset : function() {
|
---|
202 |
|
---|
203 | // Clear undo/redo history
|
---|
204 | de.UndoMan.clear();
|
---|
205 |
|
---|
206 | // Reset edit section html back to their last captured start states
|
---|
207 | for (var i in esStartStates) {
|
---|
208 | esStartStates[i].esNode.innerHTML = esStartStates[i].initHTML;
|
---|
209 | }
|
---|
210 |
|
---|
211 | // Setup changes again
|
---|
212 | this.clear();
|
---|
213 | }
|
---|
214 |
|
---|
215 | }; // End de.Changes singleton
|
---|
216 |
|
---|
217 | })();
|
---|
218 |
|
---|