Mercurial > public > sg101
comparison media/js/tiny_mce/tiny_mce_src.js @ 183:149c3567fec1
Updated to TinyMCE version 3.3.2. This is for #57.
author | Brian Neal <bgneal@gmail.com> |
---|---|
date | Sun, 28 Mar 2010 21:47:48 +0000 |
parents | a5b4c5ce0658 |
children | 237710206167 |
comparison
equal
deleted
inserted
replaced
182:5c889b587416 | 183:149c3567fec1 |
---|---|
1 var tinymce = { | 1 (function(win) { |
2 majorVersion : '3', | 2 var whiteSpaceRe = /^\s*|\s*$/g, |
3 minorVersion : '2.2.3', | 3 undefined; |
4 releaseDate : '2009-03-26', | 4 |
5 | 5 var tinymce = { |
6 _init : function() { | 6 majorVersion : '3', |
7 var t = this, d = document, w = window, na = navigator, ua = na.userAgent, i, nl, n, base, p, v; | 7 |
8 | 8 minorVersion : '3.2', |
9 // Browser checks | 9 |
10 t.isOpera = w.opera && opera.buildNumber; | 10 releaseDate : '2010-03-25', |
11 t.isWebKit = /WebKit/.test(ua); | 11 |
12 t.isIE = !t.isWebKit && !t.isOpera && (/MSIE/gi).test(ua) && (/Explorer/gi).test(na.appName); | 12 _init : function() { |
13 t.isIE6 = t.isIE && /MSIE [56]/.test(ua); | 13 var t = this, d = document, na = navigator, ua = na.userAgent, i, nl, n, base, p, v; |
14 t.isGecko = !t.isWebKit && /Gecko/.test(ua); | 14 |
15 t.isMac = ua.indexOf('Mac') != -1; | 15 t.isOpera = win.opera && opera.buildNumber; |
16 t.isAir = /adobeair/i.test(ua); | 16 |
17 | 17 t.isWebKit = /WebKit/.test(ua); |
18 // TinyMCE .NET webcontrol might be setting the values for TinyMCE | 18 |
19 if (w.tinyMCEPreInit) { | 19 t.isIE = !t.isWebKit && !t.isOpera && (/MSIE/gi).test(ua) && (/Explorer/gi).test(na.appName); |
20 t.suffix = tinyMCEPreInit.suffix; | 20 |
21 t.baseURL = tinyMCEPreInit.base; | 21 t.isIE6 = t.isIE && /MSIE [56]/.test(ua); |
22 t.query = tinyMCEPreInit.query; | 22 |
23 return; | 23 t.isGecko = !t.isWebKit && /Gecko/.test(ua); |
24 } | 24 |
25 | 25 t.isMac = ua.indexOf('Mac') != -1; |
26 // Get suffix and base | 26 |
27 t.suffix = ''; | 27 t.isAir = /adobeair/i.test(ua); |
28 | 28 |
29 // If base element found, add that infront of baseURL | 29 // TinyMCE .NET webcontrol might be setting the values for TinyMCE |
30 nl = d.getElementsByTagName('base'); | 30 if (win.tinyMCEPreInit) { |
31 for (i=0; i<nl.length; i++) { | 31 t.suffix = tinyMCEPreInit.suffix; |
32 if (v = nl[i].href) { | 32 t.baseURL = tinyMCEPreInit.base; |
33 // Host only value like http://site.com or http://site.com:8008 | 33 t.query = tinyMCEPreInit.query; |
34 if (/^https?:\/\/[^\/]+$/.test(v)) | |
35 v += '/'; | |
36 | |
37 base = v ? v.match(/.*\//)[0] : ''; // Get only directory | |
38 } | |
39 } | |
40 | |
41 function getBase(n) { | |
42 if (n.src && /tiny_mce(|_dev|_src|_gzip|_jquery|_prototype).js/.test(n.src)) { | |
43 if (/_(src|dev)\.js/g.test(n.src)) | |
44 t.suffix = '_src'; | |
45 | |
46 if ((p = n.src.indexOf('?')) != -1) | |
47 t.query = n.src.substring(p + 1); | |
48 | |
49 t.baseURL = n.src.substring(0, n.src.lastIndexOf('/')); | |
50 | |
51 // If path to script is relative and a base href was found add that one infront | |
52 if (base && t.baseURL.indexOf('://') == -1) | |
53 t.baseURL = base + t.baseURL; | |
54 | |
55 return t.baseURL; | |
56 } | |
57 | |
58 return null; | |
59 }; | |
60 | |
61 // Check document | |
62 nl = d.getElementsByTagName('script'); | |
63 for (i=0; i<nl.length; i++) { | |
64 if (getBase(nl[i])) | |
65 return; | 34 return; |
66 } | 35 } |
67 | 36 |
68 // Check head | 37 // Get suffix and base |
69 n = d.getElementsByTagName('head')[0]; | 38 t.suffix = ''; |
70 if (n) { | 39 |
71 nl = n.getElementsByTagName('script'); | 40 // If base element found, add that infront of baseURL |
41 nl = d.getElementsByTagName('base'); | |
42 for (i=0; i<nl.length; i++) { | |
43 if (v = nl[i].href) { | |
44 // Host only value like http://site.com or http://site.com:8008 | |
45 if (/^https?:\/\/[^\/]+$/.test(v)) | |
46 v += '/'; | |
47 | |
48 base = v ? v.match(/.*\//)[0] : ''; // Get only directory | |
49 } | |
50 } | |
51 | |
52 function getBase(n) { | |
53 if (n.src && /tiny_mce(|_gzip|_jquery|_prototype)(_dev|_src)?.js/.test(n.src)) { | |
54 if (/_(src|dev)\.js/g.test(n.src)) | |
55 t.suffix = '_src'; | |
56 | |
57 if ((p = n.src.indexOf('?')) != -1) | |
58 t.query = n.src.substring(p + 1); | |
59 | |
60 t.baseURL = n.src.substring(0, n.src.lastIndexOf('/')); | |
61 | |
62 // If path to script is relative and a base href was found add that one infront | |
63 // the src property will always be an absolute one on non IE browsers and IE 8 | |
64 // so this logic will basically only be executed on older IE versions | |
65 if (base && t.baseURL.indexOf('://') == -1 && t.baseURL.indexOf('/') !== 0) | |
66 t.baseURL = base + t.baseURL; | |
67 | |
68 return t.baseURL; | |
69 } | |
70 | |
71 return null; | |
72 }; | |
73 | |
74 // Check document | |
75 nl = d.getElementsByTagName('script'); | |
72 for (i=0; i<nl.length; i++) { | 76 for (i=0; i<nl.length; i++) { |
73 if (getBase(nl[i])) | 77 if (getBase(nl[i])) |
74 return; | 78 return; |
75 } | 79 } |
76 } | 80 |
77 | 81 // Check head |
78 return; | 82 n = d.getElementsByTagName('head')[0]; |
79 }, | 83 if (n) { |
80 | 84 nl = n.getElementsByTagName('script'); |
81 is : function(o, t) { | 85 for (i=0; i<nl.length; i++) { |
82 var n = typeof(o); | 86 if (getBase(nl[i])) |
83 | 87 return; |
84 if (!t) | 88 } |
85 return n != 'undefined'; | 89 } |
86 | 90 |
87 if (t == 'array' && (o.hasOwnProperty && o instanceof Array)) | 91 return; |
88 return true; | 92 }, |
89 | 93 |
90 return n == t; | 94 is : function(o, t) { |
91 }, | 95 if (!t) |
92 | 96 return o !== undefined; |
93 | 97 |
94 each : function(o, cb, s) { | 98 if (t == 'array' && (o.hasOwnProperty && o instanceof Array)) |
95 var n, l; | 99 return true; |
96 | 100 |
97 if (!o) | 101 return typeof(o) == t; |
98 return 0; | 102 }, |
99 | 103 |
100 s = s || o; | 104 each : function(o, cb, s) { |
101 | 105 var n, l; |
102 if (typeof(o.length) != 'undefined') { | 106 |
103 // Indexed arrays, needed for Safari | 107 if (!o) |
104 for (n=0, l = o.length; n<l; n++) { | 108 return 0; |
105 if (cb.call(s, o[n], n, o) === false) | 109 |
106 return 0; | 110 s = s || o; |
107 } | 111 |
108 } else { | 112 if (o.length !== undefined) { |
109 // Hashtables | 113 // Indexed arrays, needed for Safari |
110 for (n in o) { | 114 for (n=0, l = o.length; n < l; n++) { |
111 if (o.hasOwnProperty(n)) { | |
112 if (cb.call(s, o[n], n, o) === false) | 115 if (cb.call(s, o[n], n, o) === false) |
113 return 0; | 116 return 0; |
114 } | 117 } |
115 } | 118 } else { |
116 } | 119 // Hashtables |
117 | 120 for (n in o) { |
118 return 1; | 121 if (o.hasOwnProperty(n)) { |
119 }, | 122 if (cb.call(s, o[n], n, o) === false) |
120 | 123 return 0; |
121 map : function(a, f) { | 124 } |
122 var o = []; | 125 } |
123 | 126 } |
124 tinymce.each(a, function(v) { | 127 |
125 o.push(f(v)); | 128 return 1; |
126 }); | 129 }, |
127 | 130 |
128 return o; | 131 |
129 }, | 132 map : function(a, f) { |
130 | 133 var o = []; |
131 grep : function(a, f) { | 134 |
132 var o = []; | 135 tinymce.each(a, function(v) { |
133 | 136 o.push(f(v)); |
134 tinymce.each(a, function(v) { | |
135 if (!f || f(v)) | |
136 o.push(v); | |
137 }); | |
138 | |
139 return o; | |
140 }, | |
141 | |
142 inArray : function(a, v) { | |
143 var i, l; | |
144 | |
145 if (a) { | |
146 for (i = 0, l = a.length; i < l; i++) { | |
147 if (a[i] === v) | |
148 return i; | |
149 } | |
150 } | |
151 | |
152 return -1; | |
153 }, | |
154 | |
155 extend : function(o, e) { | |
156 var i, a = arguments; | |
157 | |
158 for (i=1; i<a.length; i++) { | |
159 e = a[i]; | |
160 | |
161 tinymce.each(e, function(v, n) { | |
162 if (typeof(v) !== 'undefined') | |
163 o[n] = v; | |
164 }); | 137 }); |
165 } | 138 |
166 | 139 return o; |
167 return o; | 140 }, |
168 }, | 141 |
169 | 142 grep : function(a, f) { |
170 trim : function(s) { | 143 var o = []; |
171 return (s ? '' + s : '').replace(/^\s*|\s*$/g, ''); | 144 |
172 }, | 145 tinymce.each(a, function(v) { |
173 | 146 if (!f || f(v)) |
174 | 147 o.push(v); |
175 create : function(s, p) { | |
176 var t = this, sp, ns, cn, scn, c, de = 0; | |
177 | |
178 // Parse : <prefix> <class>:<super class> | |
179 s = /^((static) )?([\w.]+)(:([\w.]+))?/.exec(s); | |
180 cn = s[3].match(/(^|\.)(\w+)$/i)[2]; // Class name | |
181 | |
182 // Create namespace for new class | |
183 ns = t.createNS(s[3].replace(/\.\w+$/, '')); | |
184 | |
185 // Class already exists | |
186 if (ns[cn]) | |
187 return; | |
188 | |
189 // Make pure static class | |
190 if (s[2] == 'static') { | |
191 ns[cn] = p; | |
192 | |
193 if (this.onCreate) | |
194 this.onCreate(s[2], s[3], ns[cn]); | |
195 | |
196 return; | |
197 } | |
198 | |
199 // Create default constructor | |
200 if (!p[cn]) { | |
201 p[cn] = function() {}; | |
202 de = 1; | |
203 } | |
204 | |
205 // Add constructor and methods | |
206 ns[cn] = p[cn]; | |
207 t.extend(ns[cn].prototype, p); | |
208 | |
209 // Extend | |
210 if (s[5]) { | |
211 sp = t.resolve(s[5]).prototype; | |
212 scn = s[5].match(/\.(\w+)$/i)[1]; // Class name | |
213 | |
214 // Extend constructor | |
215 c = ns[cn]; | |
216 if (de) { | |
217 // Add passthrough constructor | |
218 ns[cn] = function() { | |
219 return sp[scn].apply(this, arguments); | |
220 }; | |
221 } else { | |
222 // Add inherit constructor | |
223 ns[cn] = function() { | |
224 this.parent = sp[scn]; | |
225 return c.apply(this, arguments); | |
226 }; | |
227 } | |
228 ns[cn].prototype[cn] = ns[cn]; | |
229 | |
230 // Add super methods | |
231 t.each(sp, function(f, n) { | |
232 ns[cn].prototype[n] = sp[n]; | |
233 }); | 148 }); |
234 | 149 |
235 // Add overridden methods | 150 return o; |
236 t.each(p, function(f, n) { | 151 }, |
237 // Extend methods if needed | 152 |
238 if (sp[n]) { | 153 inArray : function(a, v) { |
239 ns[cn].prototype[n] = function() { | 154 var i, l; |
240 this.parent = sp[n]; | 155 |
241 return f.apply(this, arguments); | 156 if (a) { |
157 for (i = 0, l = a.length; i < l; i++) { | |
158 if (a[i] === v) | |
159 return i; | |
160 } | |
161 } | |
162 | |
163 return -1; | |
164 }, | |
165 | |
166 extend : function(o, e) { | |
167 var i, l, a = arguments; | |
168 | |
169 for (i = 1, l = a.length; i < l; i++) { | |
170 e = a[i]; | |
171 | |
172 tinymce.each(e, function(v, n) { | |
173 if (v !== undefined) | |
174 o[n] = v; | |
175 }); | |
176 } | |
177 | |
178 return o; | |
179 }, | |
180 | |
181 | |
182 trim : function(s) { | |
183 return (s ? '' + s : '').replace(whiteSpaceRe, ''); | |
184 }, | |
185 | |
186 create : function(s, p) { | |
187 var t = this, sp, ns, cn, scn, c, de = 0; | |
188 | |
189 // Parse : <prefix> <class>:<super class> | |
190 s = /^((static) )?([\w.]+)(:([\w.]+))?/.exec(s); | |
191 cn = s[3].match(/(^|\.)(\w+)$/i)[2]; // Class name | |
192 | |
193 // Create namespace for new class | |
194 ns = t.createNS(s[3].replace(/\.\w+$/, '')); | |
195 | |
196 // Class already exists | |
197 if (ns[cn]) | |
198 return; | |
199 | |
200 // Make pure static class | |
201 if (s[2] == 'static') { | |
202 ns[cn] = p; | |
203 | |
204 if (this.onCreate) | |
205 this.onCreate(s[2], s[3], ns[cn]); | |
206 | |
207 return; | |
208 } | |
209 | |
210 // Create default constructor | |
211 if (!p[cn]) { | |
212 p[cn] = function() {}; | |
213 de = 1; | |
214 } | |
215 | |
216 // Add constructor and methods | |
217 ns[cn] = p[cn]; | |
218 t.extend(ns[cn].prototype, p); | |
219 | |
220 // Extend | |
221 if (s[5]) { | |
222 sp = t.resolve(s[5]).prototype; | |
223 scn = s[5].match(/\.(\w+)$/i)[1]; // Class name | |
224 | |
225 // Extend constructor | |
226 c = ns[cn]; | |
227 if (de) { | |
228 // Add passthrough constructor | |
229 ns[cn] = function() { | |
230 return sp[scn].apply(this, arguments); | |
242 }; | 231 }; |
243 } else { | 232 } else { |
244 if (n != cn) | 233 // Add inherit constructor |
245 ns[cn].prototype[n] = f; | 234 ns[cn] = function() { |
246 } | 235 this.parent = sp[scn]; |
236 return c.apply(this, arguments); | |
237 }; | |
238 } | |
239 ns[cn].prototype[cn] = ns[cn]; | |
240 | |
241 // Add super methods | |
242 t.each(sp, function(f, n) { | |
243 ns[cn].prototype[n] = sp[n]; | |
244 }); | |
245 | |
246 // Add overridden methods | |
247 t.each(p, function(f, n) { | |
248 // Extend methods if needed | |
249 if (sp[n]) { | |
250 ns[cn].prototype[n] = function() { | |
251 this.parent = sp[n]; | |
252 return f.apply(this, arguments); | |
253 }; | |
254 } else { | |
255 if (n != cn) | |
256 ns[cn].prototype[n] = f; | |
257 } | |
258 }); | |
259 } | |
260 | |
261 // Add static methods | |
262 t.each(p['static'], function(f, n) { | |
263 ns[cn][n] = f; | |
247 }); | 264 }); |
265 | |
266 if (this.onCreate) | |
267 this.onCreate(s[2], s[3], ns[cn].prototype); | |
268 }, | |
269 | |
270 walk : function(o, f, n, s) { | |
271 s = s || this; | |
272 | |
273 if (o) { | |
274 if (n) | |
275 o = o[n]; | |
276 | |
277 tinymce.each(o, function(o, i) { | |
278 if (f.call(s, o, i, n) === false) | |
279 return false; | |
280 | |
281 tinymce.walk(o, f, n, s); | |
282 }); | |
283 } | |
284 }, | |
285 | |
286 createNS : function(n, o) { | |
287 var i, v; | |
288 | |
289 o = o || win; | |
290 | |
291 n = n.split('.'); | |
292 for (i=0; i<n.length; i++) { | |
293 v = n[i]; | |
294 | |
295 if (!o[v]) | |
296 o[v] = {}; | |
297 | |
298 o = o[v]; | |
299 } | |
300 | |
301 return o; | |
302 }, | |
303 | |
304 resolve : function(n, o) { | |
305 var i, l; | |
306 | |
307 o = o || win; | |
308 | |
309 n = n.split('.'); | |
310 for (i = 0, l = n.length; i < l; i++) { | |
311 o = o[n[i]]; | |
312 | |
313 if (!o) | |
314 break; | |
315 } | |
316 | |
317 return o; | |
318 }, | |
319 | |
320 addUnload : function(f, s) { | |
321 var t = this; | |
322 | |
323 f = {func : f, scope : s || this}; | |
324 | |
325 if (!t.unloads) { | |
326 function unload() { | |
327 var li = t.unloads, o, n; | |
328 | |
329 if (li) { | |
330 // Call unload handlers | |
331 for (n in li) { | |
332 o = li[n]; | |
333 | |
334 if (o && o.func) | |
335 o.func.call(o.scope, 1); // Send in one arg to distinct unload and user destroy | |
336 } | |
337 | |
338 // Detach unload function | |
339 if (win.detachEvent) { | |
340 win.detachEvent('onbeforeunload', fakeUnload); | |
341 win.detachEvent('onunload', unload); | |
342 } else if (win.removeEventListener) | |
343 win.removeEventListener('unload', unload, false); | |
344 | |
345 // Destroy references | |
346 t.unloads = o = li = w = unload = 0; | |
347 | |
348 // Run garbarge collector on IE | |
349 if (win.CollectGarbage) | |
350 CollectGarbage(); | |
351 } | |
352 }; | |
353 | |
354 function fakeUnload() { | |
355 var d = document; | |
356 | |
357 // Is there things still loading, then do some magic | |
358 if (d.readyState == 'interactive') { | |
359 function stop() { | |
360 // Prevent memory leak | |
361 d.detachEvent('onstop', stop); | |
362 | |
363 // Call unload handler | |
364 if (unload) | |
365 unload(); | |
366 | |
367 d = 0; | |
368 }; | |
369 | |
370 // Fire unload when the currently loading page is stopped | |
371 if (d) | |
372 d.attachEvent('onstop', stop); | |
373 | |
374 // Remove onstop listener after a while to prevent the unload function | |
375 // to execute if the user presses cancel in an onbeforeunload | |
376 // confirm dialog and then presses the browser stop button | |
377 win.setTimeout(function() { | |
378 if (d) | |
379 d.detachEvent('onstop', stop); | |
380 }, 0); | |
381 } | |
382 }; | |
383 | |
384 // Attach unload handler | |
385 if (win.attachEvent) { | |
386 win.attachEvent('onunload', unload); | |
387 win.attachEvent('onbeforeunload', fakeUnload); | |
388 } else if (win.addEventListener) | |
389 win.addEventListener('unload', unload, false); | |
390 | |
391 // Setup initial unload handler array | |
392 t.unloads = [f]; | |
393 } else | |
394 t.unloads.push(f); | |
395 | |
396 return f; | |
397 }, | |
398 | |
399 removeUnload : function(f) { | |
400 var u = this.unloads, r = null; | |
401 | |
402 tinymce.each(u, function(o, i) { | |
403 if (o && o.func == f) { | |
404 u.splice(i, 1); | |
405 r = f; | |
406 return false; | |
407 } | |
408 }); | |
409 | |
410 return r; | |
411 }, | |
412 | |
413 explode : function(s, d) { | |
414 return s ? tinymce.map(s.split(d || ','), tinymce.trim) : s; | |
415 }, | |
416 | |
417 _addVer : function(u) { | |
418 var v; | |
419 | |
420 if (!this.query) | |
421 return u; | |
422 | |
423 v = (u.indexOf('?') == -1 ? '?' : '&') + this.query; | |
424 | |
425 if (u.indexOf('#') == -1) | |
426 return u + v; | |
427 | |
428 return u.replace('#', v + '#'); | |
248 } | 429 } |
249 | 430 |
250 // Add static methods | 431 }; |
251 t.each(p['static'], function(f, n) { | 432 |
252 ns[cn][n] = f; | 433 // Initialize the API |
253 }); | 434 tinymce._init(); |
254 | 435 |
255 if (this.onCreate) | 436 // Expose tinymce namespace to the global namespace (window) |
256 this.onCreate(s[2], s[3], ns[cn].prototype); | 437 win.tinymce = win.tinyMCE = tinymce; |
257 }, | 438 })(window); |
258 | 439 |
259 walk : function(o, f, n, s) { | 440 |
260 s = s || this; | |
261 | |
262 if (o) { | |
263 if (n) | |
264 o = o[n]; | |
265 | |
266 tinymce.each(o, function(o, i) { | |
267 if (f.call(s, o, i, n) === false) | |
268 return false; | |
269 | |
270 tinymce.walk(o, f, n, s); | |
271 }); | |
272 } | |
273 }, | |
274 | |
275 createNS : function(n, o) { | |
276 var i, v; | |
277 | |
278 o = o || window; | |
279 | |
280 n = n.split('.'); | |
281 for (i=0; i<n.length; i++) { | |
282 v = n[i]; | |
283 | |
284 if (!o[v]) | |
285 o[v] = {}; | |
286 | |
287 o = o[v]; | |
288 } | |
289 | |
290 return o; | |
291 }, | |
292 | |
293 resolve : function(n, o) { | |
294 var i, l; | |
295 | |
296 o = o || window; | |
297 | |
298 n = n.split('.'); | |
299 for (i=0, l = n.length; i<l; i++) { | |
300 o = o[n[i]]; | |
301 | |
302 if (!o) | |
303 break; | |
304 } | |
305 | |
306 return o; | |
307 }, | |
308 | |
309 addUnload : function(f, s) { | |
310 var t = this, w = window; | |
311 | |
312 f = {func : f, scope : s || this}; | |
313 | |
314 if (!t.unloads) { | |
315 function unload() { | |
316 var li = t.unloads, o, n; | |
317 | |
318 if (li) { | |
319 // Call unload handlers | |
320 for (n in li) { | |
321 o = li[n]; | |
322 | |
323 if (o && o.func) | |
324 o.func.call(o.scope, 1); // Send in one arg to distinct unload and user destroy | |
325 } | |
326 | |
327 // Detach unload function | |
328 if (w.detachEvent) { | |
329 w.detachEvent('onbeforeunload', fakeUnload); | |
330 w.detachEvent('onunload', unload); | |
331 } else if (w.removeEventListener) | |
332 w.removeEventListener('unload', unload, false); | |
333 | |
334 // Destroy references | |
335 t.unloads = o = li = w = unload = null; | |
336 | |
337 // Run garbarge collector on IE | |
338 if (window.CollectGarbage) | |
339 window.CollectGarbage(); | |
340 } | |
341 }; | |
342 | |
343 function fakeUnload() { | |
344 var d = document; | |
345 | |
346 // Is there things still loading, then do some magic | |
347 if (d.readyState == 'interactive') { | |
348 function stop() { | |
349 // Prevent memory leak | |
350 d.detachEvent('onstop', stop); | |
351 | |
352 // Call unload handler | |
353 unload(); | |
354 | |
355 d = null; | |
356 }; | |
357 | |
358 // Fire unload when the currently loading page is stopped | |
359 d.attachEvent('onstop', stop); | |
360 | |
361 // Remove onstop listener after a while to prevent the unload function | |
362 // to execute if the user presses cancel in an onbeforeunload | |
363 // confirm dialog and then presses the browser stop button | |
364 window.setTimeout(function() { | |
365 d.detachEvent('onstop', stop); | |
366 }, 0); | |
367 } | |
368 }; | |
369 | |
370 // Attach unload handler | |
371 if (w.attachEvent) { | |
372 w.attachEvent('onunload', unload); | |
373 w.attachEvent('onbeforeunload', fakeUnload); | |
374 } else if (w.addEventListener) | |
375 w.addEventListener('unload', unload, false); | |
376 | |
377 // Setup initial unload handler array | |
378 t.unloads = [f]; | |
379 } else | |
380 t.unloads.push(f); | |
381 | |
382 return f; | |
383 }, | |
384 | |
385 removeUnload : function(f) { | |
386 var u = this.unloads, r = null; | |
387 | |
388 tinymce.each(u, function(o, i) { | |
389 if (o && o.func == f) { | |
390 u.splice(i, 1); | |
391 r = f; | |
392 return false; | |
393 } | |
394 }); | |
395 | |
396 return r; | |
397 }, | |
398 | |
399 explode : function(s, d) { | |
400 return s ? tinymce.map(s.split(d || ','), tinymce.trim) : s; | |
401 }, | |
402 | |
403 _addVer : function(u) { | |
404 var v; | |
405 | |
406 if (!this.query) | |
407 return u; | |
408 | |
409 v = (u.indexOf('?') == -1 ? '?' : '&') + this.query; | |
410 | |
411 if (u.indexOf('#') == -1) | |
412 return u + v; | |
413 | |
414 return u.replace('#', v + '#'); | |
415 } | |
416 | |
417 }; | |
418 | |
419 // Required for GZip AJAX loading | |
420 window.tinymce = tinymce; | |
421 | |
422 // Initialize the API | |
423 tinymce._init(); | |
424 tinymce.create('tinymce.util.Dispatcher', { | 441 tinymce.create('tinymce.util.Dispatcher', { |
425 scope : null, | 442 scope : null, |
426 listeners : null, | 443 listeners : null, |
427 | 444 |
428 Dispatcher : function(s) { | 445 Dispatcher : function(s) { |
471 | 488 |
472 return s; | 489 return s; |
473 } | 490 } |
474 | 491 |
475 }); | 492 }); |
493 | |
476 (function() { | 494 (function() { |
477 var each = tinymce.each; | 495 var each = tinymce.each; |
478 | 496 |
479 tinymce.create('tinymce.util.URI', { | 497 tinymce.create('tinymce.util.URI', { |
480 URI : function(u, s) { | 498 URI : function(u, s) { |
481 var t = this, o, a, b; | 499 var t = this, o, a, b; |
482 | 500 |
501 // Trim whitespace | |
502 u = tinymce.trim(u); | |
503 | |
483 // Default settings | 504 // Default settings |
484 s = t.settings = s || {}; | 505 s = t.settings = s || {}; |
485 | 506 |
486 // Strange app protocol or local anchor | 507 // Strange app protocol or local anchor |
487 if (/^(mailto|tel|news|javascript|about):/i.test(u) || /^\s*#/.test(u)) { | 508 if (/^(mailto|tel|news|javascript|about|data):/i.test(u) || /^\s*#/.test(u)) { |
488 t.source = u; | 509 t.source = u; |
489 return; | 510 return; |
490 } | 511 } |
491 | 512 |
492 // Absolute path with no host, fake host and protocol | 513 // Absolute path with no host, fake host and protocol |
493 if (u.indexOf('/') === 0 && u.indexOf('//') !== 0) | 514 if (u.indexOf('/') === 0 && u.indexOf('//') !== 0) |
494 u = (s.base_uri ? s.base_uri.protocol || 'http' : 'http') + '://mce_host' + u; | 515 u = (s.base_uri ? s.base_uri.protocol || 'http' : 'http') + '://mce_host' + u; |
495 | 516 |
496 // Relative path | 517 // Relative path http:// or protocol relative //path |
497 if (u.indexOf(':/') === -1 && u.indexOf('//') !== 0) | 518 if (!/^\w*:?\/\//.test(u)) |
498 u = (s.base_uri.protocol || 'http') + '://mce_host' + t.toAbsPath(s.base_uri.path, u); | 519 u = (s.base_uri.protocol || 'http') + '://mce_host' + t.toAbsPath(s.base_uri.path, u); |
499 | 520 |
500 // Parse URL (Credits goes to Steave, http://blog.stevenlevithan.com/archives/parseuri) | 521 // Parse URL (Credits goes to Steave, http://blog.stevenlevithan.com/archives/parseuri) |
501 u = u.replace(/@@/g, '(mce_at)'); // Zope 3 workaround, they use @@something | 522 u = u.replace(/@@/g, '(mce_at)'); // Zope 3 workaround, they use @@something |
502 u = /^(?:(?![^:@]+:[^:@\/]*@)([^:\/?#.]+):)?(?:\/\/)?((?:(([^:@]*):?([^:@]*))?@)?([^:\/?#]*)(?::(\d*))?)(((\/(?:[^?#](?![^?#\/]*\.[^?#\/.]+(?:[?#]|$)))*\/?)?([^?#\/]*))(?:\?([^#]*))?(?:#(.*))?)/.exec(u); | 523 u = /^(?:(?![^:@]+:[^:@\/]*@)([^:\/?#.]+):)?(?:\/\/)?((?:(([^:@]*):?([^:@]*))?@)?([^:\/?#]*)(?::(\d*))?)(((\/(?:[^?#](?![^?#\/]*\.[^?#\/.]+(?:[?#]|$)))*\/?)?([^?#\/]*))(?:\?([^#]*))?(?:#(.*))?)/.exec(u); |
570 }, | 591 }, |
571 | 592 |
572 toAbsolute : function(u, nh) { | 593 toAbsolute : function(u, nh) { |
573 var u = new tinymce.util.URI(u, {base_uri : this}); | 594 var u = new tinymce.util.URI(u, {base_uri : this}); |
574 | 595 |
575 return u.getURI(this.host == u.host ? nh : 0); | 596 return u.getURI(this.host == u.host && this.protocol == u.protocol ? nh : 0); |
576 }, | 597 }, |
577 | 598 |
578 toRelPath : function(base, path) { | 599 toRelPath : function(base, path) { |
579 var items, bp = 0, out = '', i, l; | 600 var items, bp = 0, out = '', i, l; |
580 | 601 |
616 | 637 |
617 return out; | 638 return out; |
618 }, | 639 }, |
619 | 640 |
620 toAbsPath : function(base, path) { | 641 toAbsPath : function(base, path) { |
621 var i, nb = 0, o = [], tr; | 642 var i, nb = 0, o = [], tr, outPath; |
622 | 643 |
623 // Split paths | 644 // Split paths |
624 tr = /\/$/.test(path) ? '/' : ''; | 645 tr = /\/$/.test(path) ? '/' : ''; |
625 base = base.split('/'); | 646 base = base.split('/'); |
626 path = path.split('/'); | 647 path = path.split('/'); |
656 | 677 |
657 i = base.length - nb; | 678 i = base.length - nb; |
658 | 679 |
659 // If /a/b/c or / | 680 // If /a/b/c or / |
660 if (i <= 0) | 681 if (i <= 0) |
661 return '/' + o.reverse().join('/') + tr; | 682 outPath = o.reverse().join('/'); |
662 | 683 else |
663 return '/' + base.slice(0, i).join('/') + '/' + o.reverse().join('/') + tr; | 684 outPath = base.slice(0, i).join('/') + '/' + o.reverse().join('/'); |
685 | |
686 // Add front / if it's needed | |
687 if (outPath.indexOf('/') !== 0) | |
688 outPath = '/' + outPath; | |
689 | |
690 // Add traling / if it's needed | |
691 if (tr && outPath.lastIndexOf('/') !== outPath.length - 1) | |
692 outPath += tr; | |
693 | |
694 return outPath; | |
664 }, | 695 }, |
665 | 696 |
666 getURI : function(nh) { | 697 getURI : function(nh) { |
667 var s, t = this; | 698 var s, t = this; |
668 | 699 |
696 t.source = s; | 727 t.source = s; |
697 } | 728 } |
698 | 729 |
699 return t.source; | 730 return t.source; |
700 } | 731 } |
701 | 732 }); |
702 }); | |
703 })(); | 733 })(); |
734 | |
704 (function() { | 735 (function() { |
705 var each = tinymce.each; | 736 var each = tinymce.each; |
706 | 737 |
707 tinymce.create('static tinymce.util.Cookie', { | 738 tinymce.create('static tinymce.util.Cookie', { |
708 getHash : function(n) { | 739 getHash : function(n) { |
767 | 798 |
768 d.setTime(d.getTime() - 1000); | 799 d.setTime(d.getTime() - 1000); |
769 | 800 |
770 this.set(n, '', d, p, d); | 801 this.set(n, '', d, p, d); |
771 } | 802 } |
772 | 803 }); |
773 }); | |
774 })(); | 804 })(); |
805 | |
775 tinymce.create('static tinymce.util.JSON', { | 806 tinymce.create('static tinymce.util.JSON', { |
776 serialize : function(o) { | 807 serialize : function(o) { |
777 var i, v, s = tinymce.util.JSON.serialize, t; | 808 var i, v, s = tinymce.util.JSON.serialize, t; |
778 | 809 |
779 if (o == null) | 810 if (o == null) |
822 // Ignore | 853 // Ignore |
823 } | 854 } |
824 } | 855 } |
825 | 856 |
826 }); | 857 }); |
858 | |
827 tinymce.create('static tinymce.util.XHR', { | 859 tinymce.create('static tinymce.util.XHR', { |
828 send : function(o) { | 860 send : function(o) { |
829 var x, t, w = window, c = 0; | 861 var x, t, w = window, c = 0; |
830 | 862 |
831 // Default settings | 863 // Default settings |
855 x.open(o.type || (o.data ? 'POST' : 'GET'), o.url, o.async); | 887 x.open(o.type || (o.data ? 'POST' : 'GET'), o.url, o.async); |
856 | 888 |
857 if (o.content_type) | 889 if (o.content_type) |
858 x.setRequestHeader('Content-Type', o.content_type); | 890 x.setRequestHeader('Content-Type', o.content_type); |
859 | 891 |
892 x.setRequestHeader('X-Requested-With', 'XMLHttpRequest'); | |
893 | |
860 x.send(o.data); | 894 x.send(o.data); |
861 | 895 |
862 function ready() { | 896 function ready() { |
863 if (!o.async || x.readyState == 4 || c++ > 10000) { | 897 if (!o.async || x.readyState == 4 || c++ > 10000) { |
864 if (o.success && c < 10000 && x.status == 200) | 898 if (o.success && c < 10000 && x.status == 200) |
876 return ready(); | 910 return ready(); |
877 | 911 |
878 // Wait for response, onReadyStateChange can not be used since it leaks memory in IE | 912 // Wait for response, onReadyStateChange can not be used since it leaks memory in IE |
879 t = w.setTimeout(ready, 10); | 913 t = w.setTimeout(ready, 10); |
880 } | 914 } |
881 | 915 } |
882 } | |
883 }); | 916 }); |
917 | |
884 (function() { | 918 (function() { |
885 var extend = tinymce.extend, JSON = tinymce.util.JSON, XHR = tinymce.util.XHR; | 919 var extend = tinymce.extend, JSON = tinymce.util.JSON, XHR = tinymce.util.XHR; |
886 | 920 |
887 tinymce.create('tinymce.util.JSONRequest', { | 921 tinymce.create('tinymce.util.JSONRequest', { |
888 JSONRequest : function(s) { | 922 JSONRequest : function(s) { |
930 'static' : { | 964 'static' : { |
931 sendRPC : function(o) { | 965 sendRPC : function(o) { |
932 return new tinymce.util.JSONRequest().send(o); | 966 return new tinymce.util.JSONRequest().send(o); |
933 } | 967 } |
934 } | 968 } |
935 | 969 }); |
936 }); | 970 }()); |
937 }());(function(tinymce) { | 971 (function(tinymce) { |
938 // Shorten names | 972 // Shorten names |
939 var each = tinymce.each, is = tinymce.is; | 973 var each = tinymce.each, |
940 var isWebKit = tinymce.isWebKit, isIE = tinymce.isIE; | 974 is = tinymce.is, |
975 isWebKit = tinymce.isWebKit, | |
976 isIE = tinymce.isIE, | |
977 blockRe = /^(H[1-6R]|P|DIV|ADDRESS|PRE|FORM|T(ABLE|BODY|HEAD|FOOT|H|R|D)|LI|OL|UL|CAPTION|BLOCKQUOTE|CENTER|DL|DT|DD|DIR|FIELDSET|NOSCRIPT|MENU|ISINDEX|SAMP)$/, | |
978 boolAttrs = makeMap('checked,compact,declare,defer,disabled,ismap,multiple,nohref,noresize,noshade,nowrap,readonly,selected'), | |
979 mceAttribs = makeMap('src,href,style,coords,shape'), | |
980 encodedChars = {'&' : '&', '"' : '"', '<' : '<', '>' : '>'}, | |
981 encodeCharsRe = /[<>&\"]/g, | |
982 simpleSelectorRe = /^([a-z0-9],?)+$/i, | |
983 tagRegExp = /<(\w+)((?:\s+\w+(?:\s*=\s*(?:(?:"[^"]*")|(?:'[^']*')|[^>\s]+))?)*)(\s*\/?)>/g, | |
984 attrRegExp = /(\w+)(?:\s*=\s*(?:(?:"((?:\\.|[^"])*)")|(?:'((?:\\.|[^'])*)')|([^>\s]+)))?/g; | |
985 | |
986 function makeMap(str) { | |
987 var map = {}, i; | |
988 | |
989 str = str.split(','); | |
990 for (i = str.length; i >= 0; i--) | |
991 map[str[i]] = 1; | |
992 | |
993 return map; | |
994 }; | |
941 | 995 |
942 tinymce.create('tinymce.dom.DOMUtils', { | 996 tinymce.create('tinymce.dom.DOMUtils', { |
943 doc : null, | 997 doc : null, |
944 root : null, | 998 root : null, |
945 files : null, | 999 files : null, |
958 name : "name", | 1012 name : "name", |
959 type : "type" | 1013 type : "type" |
960 }, | 1014 }, |
961 | 1015 |
962 DOMUtils : function(d, s) { | 1016 DOMUtils : function(d, s) { |
963 var t = this; | 1017 var t = this, globalStyle; |
964 | 1018 |
965 t.doc = d; | 1019 t.doc = d; |
966 t.win = window; | 1020 t.win = window; |
967 t.files = {}; | 1021 t.files = {}; |
968 t.cssFlicker = false; | 1022 t.cssFlicker = false; |
969 t.counter = 0; | 1023 t.counter = 0; |
970 t.boxModel = !tinymce.isIE || d.compatMode == "CSS1Compat"; | 1024 t.boxModel = !tinymce.isIE || d.compatMode == "CSS1Compat"; |
971 t.stdMode = d.documentMode === 8; | 1025 t.stdMode = d.documentMode === 8; |
972 | 1026 |
973 this.settings = s = tinymce.extend({ | 1027 t.settings = s = tinymce.extend({ |
974 keep_values : false, | 1028 keep_values : false, |
975 hex_colors : 1, | 1029 hex_colors : 1, |
976 process_html : 1 | 1030 process_html : 1 |
977 }, s); | 1031 }, s); |
978 | 1032 |
981 try { | 1035 try { |
982 d.execCommand('BackgroundImageCache', false, true); | 1036 d.execCommand('BackgroundImageCache', false, true); |
983 } catch (e) { | 1037 } catch (e) { |
984 t.cssFlicker = true; | 1038 t.cssFlicker = true; |
985 } | 1039 } |
1040 } | |
1041 | |
1042 // Build styles list | |
1043 if (s.valid_styles) { | |
1044 t._styles = {}; | |
1045 | |
1046 // Convert styles into a rule list | |
1047 each(s.valid_styles, function(value, key) { | |
1048 t._styles[key] = tinymce.explode(value); | |
1049 }); | |
986 } | 1050 } |
987 | 1051 |
988 tinymce.addUnload(t.destroy, t); | 1052 tinymce.addUnload(t.destroy, t); |
989 }, | 1053 }, |
990 | 1054 |
1044 w : parseInt(w) || e.offsetWidth || e.clientWidth, | 1108 w : parseInt(w) || e.offsetWidth || e.clientWidth, |
1045 h : parseInt(h) || e.offsetHeight || e.clientHeight | 1109 h : parseInt(h) || e.offsetHeight || e.clientHeight |
1046 }; | 1110 }; |
1047 }, | 1111 }, |
1048 | 1112 |
1049 is : function(n, patt) { | |
1050 return tinymce.dom.Sizzle.matches(patt, n.nodeType ? [n] : n).length > 0; | |
1051 }, | |
1052 | |
1053 getParent : function(n, f, r) { | 1113 getParent : function(n, f, r) { |
1054 return this.getParents(n, f, r, false); | 1114 return this.getParents(n, f, r, false); |
1055 }, | 1115 }, |
1056 | 1116 |
1057 getParents : function(n, f, r, c) { | 1117 getParents : function(n, f, r, c) { |
1075 }; | 1135 }; |
1076 } | 1136 } |
1077 } | 1137 } |
1078 | 1138 |
1079 while (n) { | 1139 while (n) { |
1080 if (n == r) | 1140 if (n == r || !n.nodeType || n.nodeType === 9) |
1081 break; | 1141 break; |
1082 | 1142 |
1083 if (!f || f(n)) { | 1143 if (!f || f(n)) { |
1084 if (c) | 1144 if (c) |
1085 o.push(n); | 1145 o.push(n); |
1106 } | 1166 } |
1107 | 1167 |
1108 return e; | 1168 return e; |
1109 }, | 1169 }, |
1110 | 1170 |
1171 getNext : function(node, selector) { | |
1172 return this._findSib(node, selector, 'nextSibling'); | |
1173 }, | |
1174 | |
1175 getPrev : function(node, selector) { | |
1176 return this._findSib(node, selector, 'previousSibling'); | |
1177 }, | |
1178 | |
1111 | 1179 |
1112 select : function(pa, s) { | 1180 select : function(pa, s) { |
1113 var t = this; | 1181 var t = this; |
1114 | 1182 |
1115 return tinymce.dom.Sizzle(pa, t.get(s) || t.get(t.settings.root_element) || t.doc, []); | 1183 return tinymce.dom.Sizzle(pa, t.get(s) || t.get(t.settings.root_element) || t.doc, []); |
1184 }, | |
1185 | |
1186 is : function(n, selector) { | |
1187 var i; | |
1188 | |
1189 // If it isn't an array then try to do some simple selectors instead of Sizzle for to boost performance | |
1190 if (n.length === undefined) { | |
1191 // Simple all selector | |
1192 if (selector === '*') | |
1193 return n.nodeType == 1; | |
1194 | |
1195 // Simple selector just elements | |
1196 if (simpleSelectorRe.test(selector)) { | |
1197 selector = selector.toLowerCase().split(/,/); | |
1198 n = n.nodeName.toLowerCase(); | |
1199 | |
1200 for (i = selector.length - 1; i >= 0; i--) { | |
1201 if (selector[i] == n) | |
1202 return true; | |
1203 } | |
1204 | |
1205 return false; | |
1206 } | |
1207 } | |
1208 | |
1209 return tinymce.dom.Sizzle.matches(selector, n.nodeType ? [n] : n).length > 0; | |
1116 }, | 1210 }, |
1117 | 1211 |
1118 | 1212 |
1119 add : function(p, n, a, h, c) { | 1213 add : function(p, n, a, h, c) { |
1120 var t = this; | 1214 var t = this; |
1154 return o + '>' + h + '</' + n + '>'; | 1248 return o + '>' + h + '</' + n + '>'; |
1155 | 1249 |
1156 return o + ' />'; | 1250 return o + ' />'; |
1157 }, | 1251 }, |
1158 | 1252 |
1159 remove : function(n, k) { | 1253 remove : function(node, keep_children) { |
1160 var t = this; | 1254 return this.run(node, function(node) { |
1161 | 1255 var parent, child; |
1162 return this.run(n, function(n) { | 1256 |
1163 var p, g, i; | 1257 parent = node.parentNode; |
1164 | 1258 |
1165 p = n.parentNode; | 1259 if (!parent) |
1166 | |
1167 if (!p) | |
1168 return null; | 1260 return null; |
1169 | 1261 |
1170 if (k) { | 1262 if (keep_children) { |
1171 for (i = n.childNodes.length - 1; i >= 0; i--) | 1263 while (child = node.firstChild) { |
1172 t.insertAfter(n.childNodes[i], n); | 1264 // IE 8 will crash if you don't remove completely empty text nodes |
1173 | 1265 if (child.nodeType !== 3 || child.nodeValue) |
1174 //each(n.childNodes, function(c) { | 1266 parent.insertBefore(child, node); |
1175 // p.insertBefore(c.cloneNode(true), n); | 1267 else |
1176 //}); | 1268 node.removeChild(child); |
1177 } | 1269 } |
1178 | 1270 } |
1179 // Fix IE psuedo leak | 1271 |
1180 if (t.fixPsuedoLeaks) { | 1272 return parent.removeChild(node); |
1181 p = n.cloneNode(true); | |
1182 k = 'IELeakGarbageBin'; | |
1183 g = t.get(k) || t.add(t.doc.body, 'div', {id : k, style : 'display:none'}); | |
1184 g.appendChild(n); | |
1185 g.innerHTML = ''; | |
1186 | |
1187 return p; | |
1188 } | |
1189 | |
1190 return p.removeChild(n); | |
1191 }); | 1273 }); |
1192 }, | 1274 }, |
1193 | |
1194 | 1275 |
1195 setStyle : function(n, na, v) { | 1276 setStyle : function(n, na, v) { |
1196 var t = this; | 1277 var t = this; |
1197 | 1278 |
1198 return t.run(n, function(e) { | 1279 return t.run(n, function(e) { |
1231 s[na] = v || ''; | 1312 s[na] = v || ''; |
1232 } | 1313 } |
1233 | 1314 |
1234 // Force update of the style data | 1315 // Force update of the style data |
1235 if (t.settings.update_styles) | 1316 if (t.settings.update_styles) |
1236 t.setAttrib(e, 'mce_style'); | 1317 t.setAttrib(e, '_mce_style'); |
1237 }); | 1318 }); |
1238 }, | 1319 }, |
1239 | 1320 |
1240 getStyle : function(n, na, c) { | 1321 getStyle : function(n, na, c) { |
1241 n = this.get(n); | 1322 n = this.get(n); |
1314 } | 1395 } |
1315 | 1396 |
1316 // No mce_style for elements with these since they might get resized by the user | 1397 // No mce_style for elements with these since they might get resized by the user |
1317 if (s.keep_values) { | 1398 if (s.keep_values) { |
1318 if (v && !t._isRes(v)) | 1399 if (v && !t._isRes(v)) |
1319 e.setAttribute('mce_style', v, 2); | 1400 e.setAttribute('_mce_style', v, 2); |
1320 else | 1401 else |
1321 e.removeAttribute('mce_style', 2); | 1402 e.removeAttribute('_mce_style', 2); |
1322 } | 1403 } |
1323 | 1404 |
1324 e.style.cssText = v; | 1405 e.style.cssText = v; |
1325 break; | 1406 break; |
1326 | 1407 |
1332 case "href": | 1413 case "href": |
1333 if (s.keep_values) { | 1414 if (s.keep_values) { |
1334 if (s.url_converter) | 1415 if (s.url_converter) |
1335 v = s.url_converter.call(s.url_converter_scope || t, v, n, e); | 1416 v = s.url_converter.call(s.url_converter_scope || t, v, n, e); |
1336 | 1417 |
1337 t.setAttrib(e, 'mce_' + n, v, 2); | 1418 t.setAttrib(e, '_mce_' + n, v, 2); |
1338 } | 1419 } |
1339 | 1420 |
1340 break; | 1421 break; |
1341 | 1422 |
1342 case "shape": | 1423 case "shape": |
1343 e.setAttribute('mce_style', v); | 1424 e.setAttribute('_mce_style', v); |
1344 break; | 1425 break; |
1345 } | 1426 } |
1346 | 1427 |
1347 if (is(v) && v !== null && v.length !== 0) | 1428 if (is(v) && v !== null && v.length !== 0) |
1348 e.setAttribute(n, '' + v, 2); | 1429 e.setAttribute(n, '' + v, 2); |
1359 t.setAttrib(e, n, v); | 1440 t.setAttrib(e, n, v); |
1360 }); | 1441 }); |
1361 }); | 1442 }); |
1362 }, | 1443 }, |
1363 | 1444 |
1364 | |
1365 getAttrib : function(e, n, dv) { | 1445 getAttrib : function(e, n, dv) { |
1366 var v, t = this; | 1446 var v, t = this; |
1367 | 1447 |
1368 e = t.get(e); | 1448 e = t.get(e); |
1369 | 1449 |
1373 if (!is(dv)) | 1453 if (!is(dv)) |
1374 dv = ''; | 1454 dv = ''; |
1375 | 1455 |
1376 // Try the mce variant for these | 1456 // Try the mce variant for these |
1377 if (/^(src|href|style|coords|shape)$/.test(n)) { | 1457 if (/^(src|href|style|coords|shape)$/.test(n)) { |
1378 v = e.getAttribute("mce_" + n); | 1458 v = e.getAttribute("_mce_" + n); |
1379 | 1459 |
1380 if (v) | 1460 if (v) |
1381 return v; | 1461 return v; |
1382 } | 1462 } |
1383 | 1463 |
1387 } | 1467 } |
1388 | 1468 |
1389 if (!v) | 1469 if (!v) |
1390 v = e.getAttribute(n, 2); | 1470 v = e.getAttribute(n, 2); |
1391 | 1471 |
1472 // Check boolean attribs | |
1473 if (/^(checked|compact|declare|defer|disabled|ismap|multiple|nohref|noshade|nowrap|readonly|selected)$/.test(n)) { | |
1474 if (e[t.props[n]] === true && v === '') | |
1475 return n; | |
1476 | |
1477 return v ? n : ''; | |
1478 } | |
1479 | |
1480 // Inner input elements will override attributes on form elements | |
1481 if (e.nodeName === "FORM" && e.getAttributeNode(n)) | |
1482 return e.getAttributeNode(n).nodeValue; | |
1483 | |
1392 if (n === 'style') { | 1484 if (n === 'style') { |
1393 v = v || e.style.cssText; | 1485 v = v || e.style.cssText; |
1394 | 1486 |
1395 if (v) { | 1487 if (v) { |
1396 v = t.serializeStyle(t.parseStyle(v)); | 1488 v = t.serializeStyle(t.parseStyle(v), e.nodeName); |
1397 | 1489 |
1398 if (t.settings.keep_values && !t._isRes(v)) | 1490 if (t.settings.keep_values && !t._isRes(v)) |
1399 e.setAttribute('mce_style', v); | 1491 e.setAttribute('_mce_style', v); |
1400 } | 1492 } |
1401 } | 1493 } |
1402 | 1494 |
1403 // Remove Apple and WebKit stuff | 1495 // Remove Apple and WebKit stuff |
1404 if (isWebKit && n === "class" && v) | 1496 if (isWebKit && n === "class" && v) |
1462 break; | 1554 break; |
1463 | 1555 |
1464 default: | 1556 default: |
1465 // IE has odd anonymous function for event attributes | 1557 // IE has odd anonymous function for event attributes |
1466 if (n.indexOf('on') === 0 && v) | 1558 if (n.indexOf('on') === 0 && v) |
1467 v = ('' + v).replace(/^function\s+anonymous\(\)\s+\{\s+(.*)\s+\}$/, '$1'); | 1559 v = ('' + v).replace(/^function\s+\w+\(\)\s+\{\s+(.*)\s+\}$/, '$1'); |
1468 } | 1560 } |
1469 } | 1561 } |
1470 | 1562 |
1471 return (v !== undefined && v !== null && v !== '') ? '' + v : dv; | 1563 return (v !== undefined && v !== null && v !== '') ? '' + v : dv; |
1472 }, | 1564 }, |
1473 | 1565 |
1474 getPos : function(n) { | 1566 getPos : function(n, ro) { |
1475 var t = this, x = 0, y = 0, e, d = t.doc, r; | 1567 var t = this, x = 0, y = 0, e, d = t.doc, r; |
1476 | 1568 |
1477 n = t.get(n); | 1569 n = t.get(n); |
1478 | 1570 ro = ro || d.body; |
1479 // Use getBoundingClientRect on IE, Opera has it but it's not perfect | 1571 |
1480 if (n && isIE && !t.stdMode) { | 1572 if (n) { |
1481 n = n.getBoundingClientRect(); | 1573 // Use getBoundingClientRect on IE, Opera has it but it's not perfect |
1482 e = t.boxModel ? d.documentElement : d.body; | 1574 if (isIE && !t.stdMode) { |
1483 x = t.getStyle(t.select('html')[0], 'borderWidth'); // Remove border | 1575 n = n.getBoundingClientRect(); |
1484 x = (x == 'medium' || t.boxModel && !t.isIE6) && 2 || x; | 1576 e = t.boxModel ? d.documentElement : d.body; |
1485 n.top += t.win.self != t.win.top ? 2 : 0; // IE adds some strange extra cord if used in a frameset | 1577 x = t.getStyle(t.select('html')[0], 'borderWidth'); // Remove border |
1486 | 1578 x = (x == 'medium' || t.boxModel && !t.isIE6) && 2 || x; |
1487 return {x : n.left + e.scrollLeft - x, y : n.top + e.scrollTop - x}; | 1579 n.top += t.win.self != t.win.top ? 2 : 0; // IE adds some strange extra cord if used in a frameset |
1488 } | 1580 |
1489 | 1581 return {x : n.left + e.scrollLeft - x, y : n.top + e.scrollTop - x}; |
1490 r = n; | 1582 } |
1491 while (r) { | 1583 |
1492 x += r.offsetLeft || 0; | 1584 r = n; |
1493 y += r.offsetTop || 0; | 1585 while (r && r != ro && r.nodeType) { |
1494 r = r.offsetParent; | 1586 x += r.offsetLeft || 0; |
1495 } | 1587 y += r.offsetTop || 0; |
1496 | 1588 r = r.offsetParent; |
1497 r = n; | 1589 } |
1498 while (r) { | 1590 |
1499 // Opera 9.25 bug fix, fixed in 9.50 | 1591 r = n.parentNode; |
1500 if (!/^table-row|inline.*/i.test(t.getStyle(r, "display", 1))) { | 1592 while (r && r != ro && r.nodeType) { |
1501 x -= r.scrollLeft || 0; | 1593 x -= r.scrollLeft || 0; |
1502 y -= r.scrollTop || 0; | 1594 y -= r.scrollTop || 0; |
1503 } | 1595 r = r.parentNode; |
1504 | 1596 } |
1505 r = r.parentNode; | |
1506 | |
1507 // No node type or document type | |
1508 if (!r.nodeType || r.nodeType == 9 || r.nodeName.toLowerCase() == 'body') | |
1509 break; | |
1510 } | 1597 } |
1511 | 1598 |
1512 return {x : x, y : y}; | 1599 return {x : x, y : y}; |
1513 }, | 1600 }, |
1514 | 1601 |
1609 } | 1696 } |
1610 | 1697 |
1611 return o; | 1698 return o; |
1612 }, | 1699 }, |
1613 | 1700 |
1614 serializeStyle : function(o) { | 1701 serializeStyle : function(o, name) { |
1615 var s = ''; | 1702 var t = this, s = ''; |
1616 | 1703 |
1617 each(o, function(v, k) { | 1704 function add(v, k) { |
1618 if (k && v) { | 1705 if (k && v) { |
1619 if (tinymce.isGecko && k.indexOf('-moz-') === 0) | 1706 // Remove browser specific styles like -moz- or -webkit- |
1707 if (k.indexOf('-') === 0) | |
1620 return; | 1708 return; |
1621 | 1709 |
1622 switch (k) { | 1710 switch (k) { |
1711 case 'font-weight': | |
1712 // Opera will output bold as 700 | |
1713 if (v == 700) | |
1714 v = 'bold'; | |
1715 | |
1716 break; | |
1717 | |
1623 case 'color': | 1718 case 'color': |
1624 case 'background-color': | 1719 case 'background-color': |
1625 v = v.toLowerCase(); | 1720 v = v.toLowerCase(); |
1626 break; | 1721 break; |
1627 } | 1722 } |
1628 | 1723 |
1629 s += (s ? ' ' : '') + k + ': ' + v + ';'; | 1724 s += (s ? ' ' : '') + k + ': ' + v + ';'; |
1630 } | 1725 } |
1631 }); | 1726 }; |
1727 | |
1728 // Validate style output | |
1729 if (name && t._styles) { | |
1730 each(t._styles['*'], function(name) { | |
1731 add(o[name], name); | |
1732 }); | |
1733 | |
1734 each(t._styles[name.toLowerCase()], function(name) { | |
1735 add(o[name], name); | |
1736 }); | |
1737 } else | |
1738 each(o, add); | |
1632 | 1739 |
1633 return s; | 1740 return s; |
1634 }, | 1741 }, |
1635 | 1742 |
1636 loadCSS : function(u) { | 1743 loadCSS : function(u) { |
1637 var t = this, d = t.doc; | 1744 var t = this, d = t.doc, head; |
1638 | 1745 |
1639 if (!u) | 1746 if (!u) |
1640 u = ''; | 1747 u = ''; |
1641 | 1748 |
1749 head = t.select('head')[0]; | |
1750 | |
1642 each(u.split(','), function(u) { | 1751 each(u.split(','), function(u) { |
1752 var link; | |
1753 | |
1643 if (t.files[u]) | 1754 if (t.files[u]) |
1644 return; | 1755 return; |
1645 | 1756 |
1646 t.files[u] = true; | 1757 t.files[u] = true; |
1647 t.add(t.select('head')[0], 'link', {rel : 'stylesheet', href : tinymce._addVer(u)}); | 1758 link = t.create('link', {rel : 'stylesheet', href : tinymce._addVer(u)}); |
1759 | |
1760 // IE 8 has a bug where dynamically loading stylesheets would produce a 1 item remaining bug | |
1761 // This fix seems to resolve that issue by realcing the document ones a stylesheet finishes loading | |
1762 // It's ugly but it seems to work fine. | |
1763 if (isIE && d.documentMode) { | |
1764 link.onload = function() { | |
1765 d.recalc(); | |
1766 link.onload = null; | |
1767 }; | |
1768 } | |
1769 | |
1770 head.appendChild(link); | |
1648 }); | 1771 }); |
1649 }, | 1772 }, |
1650 | |
1651 | 1773 |
1652 addClass : function(e, c) { | 1774 addClass : function(e, c) { |
1653 return this.run(e, function(e) { | 1775 return this.run(e, function(e) { |
1654 var o; | 1776 var o; |
1655 | 1777 |
1674 if (t.hasClass(e, c)) { | 1796 if (t.hasClass(e, c)) { |
1675 if (!re) | 1797 if (!re) |
1676 re = new RegExp("(^|\\s+)" + c + "(\\s+|$)", "g"); | 1798 re = new RegExp("(^|\\s+)" + c + "(\\s+|$)", "g"); |
1677 | 1799 |
1678 v = e.className.replace(re, ' '); | 1800 v = e.className.replace(re, ' '); |
1679 | 1801 v = tinymce.trim(v != ' ' ? v : ''); |
1680 return e.className = tinymce.trim(v != ' ' ? v : ''); | 1802 |
1803 e.className = v; | |
1804 | |
1805 // Empty class attr | |
1806 if (!v) { | |
1807 e.removeAttribute('class'); | |
1808 e.removeAttribute('className'); | |
1809 } | |
1810 | |
1811 return v; | |
1681 } | 1812 } |
1682 | 1813 |
1683 return e.className; | 1814 return e.className; |
1684 }); | 1815 }); |
1685 }, | 1816 }, |
1705 e = this.get(e); | 1836 e = this.get(e); |
1706 | 1837 |
1707 return !e || e.style.display == 'none' || this.getStyle(e, 'display') == 'none'; | 1838 return !e || e.style.display == 'none' || this.getStyle(e, 'display') == 'none'; |
1708 }, | 1839 }, |
1709 | 1840 |
1710 | |
1711 uniqueId : function(p) { | 1841 uniqueId : function(p) { |
1712 return (!p ? 'mce_' : p) + (this.counter++); | 1842 return (!p ? 'mce_' : p) + (this.counter++); |
1713 }, | 1843 }, |
1714 | 1844 |
1715 setHTML : function(e, h) { | 1845 setHTML : function(e, h) { |
1720 | 1850 |
1721 h = t.processHTML(h); | 1851 h = t.processHTML(h); |
1722 | 1852 |
1723 if (isIE) { | 1853 if (isIE) { |
1724 function set() { | 1854 function set() { |
1855 // Remove all child nodes | |
1856 while (e.firstChild) | |
1857 e.firstChild.removeNode(); | |
1858 | |
1725 try { | 1859 try { |
1726 // IE will remove comments from the beginning | 1860 // IE will remove comments from the beginning |
1727 // unless you padd the contents with something | 1861 // unless you padd the contents with something |
1728 e.innerHTML = '<br />' + h; | 1862 e.innerHTML = '<br />' + h; |
1729 e.removeChild(e.firstChild); | 1863 e.removeChild(e.firstChild); |
1730 } catch (ex) { | 1864 } catch (ex) { |
1731 // IE sometimes produces an unknown runtime error on innerHTML if it's an block element within a block element for example a div inside a p | 1865 // IE sometimes produces an unknown runtime error on innerHTML if it's an block element within a block element for example a div inside a p |
1732 // This seems to fix this problem | 1866 // This seems to fix this problem |
1733 | 1867 |
1734 // Remove all child nodes | |
1735 while (e.firstChild) | |
1736 e.firstChild.removeNode(); | |
1737 | |
1738 // Create new div with HTML contents and a BR infront to keep comments | 1868 // Create new div with HTML contents and a BR infront to keep comments |
1739 x = t.create('div'); | 1869 x = t.create('div'); |
1740 x.innerHTML = '<br />' + h; | 1870 x.innerHTML = '<br />' + h; |
1741 | 1871 |
1742 // Add all children from div to target | 1872 // Add all children from div to target |
1750 | 1880 |
1751 // IE has a serious bug when it comes to paragraphs it can produce an invalid | 1881 // IE has a serious bug when it comes to paragraphs it can produce an invalid |
1752 // DOM tree if contents like this <p><ul><li>Item 1</li></ul></p> is inserted | 1882 // DOM tree if contents like this <p><ul><li>Item 1</li></ul></p> is inserted |
1753 // It seems to be that IE doesn't like a root block element placed inside another root block element | 1883 // It seems to be that IE doesn't like a root block element placed inside another root block element |
1754 if (t.settings.fix_ie_paragraphs) | 1884 if (t.settings.fix_ie_paragraphs) |
1755 h = h.replace(/<p><\/p>|<p([^>]+)><\/p>|<p[^\/+]\/>/gi, '<p$1 mce_keep="true"> </p>'); | 1885 h = h.replace(/<p><\/p>|<p([^>]+)><\/p>|<p[^\/+]\/>/gi, '<p$1 _mce_keep="true"> </p>'); |
1756 | 1886 |
1757 set(); | 1887 set(); |
1758 | 1888 |
1759 if (t.settings.fix_ie_paragraphs) { | 1889 if (t.settings.fix_ie_paragraphs) { |
1760 // Check for odd paragraphs this is a sign of a broken DOM | 1890 // Check for odd paragraphs this is a sign of a broken DOM |
1761 nl = e.getElementsByTagName("p"); | 1891 nl = e.getElementsByTagName("p"); |
1762 for (i = nl.length - 1, x = 0; i >= 0; i--) { | 1892 for (i = nl.length - 1, x = 0; i >= 0; i--) { |
1763 n = nl[i]; | 1893 n = nl[i]; |
1764 | 1894 |
1765 if (!n.hasChildNodes()) { | 1895 if (!n.hasChildNodes()) { |
1766 if (!n.mce_keep) { | 1896 if (!n._mce_keep) { |
1767 x = 1; // Is broken | 1897 x = 1; // Is broken |
1768 break; | 1898 break; |
1769 } | 1899 } |
1770 | 1900 |
1771 n.removeAttribute('mce_keep'); | 1901 n.removeAttribute('_mce_keep'); |
1772 } | 1902 } |
1773 } | 1903 } |
1774 } | 1904 } |
1775 | 1905 |
1776 // Time to fix the madness IE left us | 1906 // Time to fix the madness IE left us |
1777 if (x) { | 1907 if (x) { |
1778 // So if we replace the p elements with divs and mark them and then replace them back to paragraphs | 1908 // So if we replace the p elements with divs and mark them and then replace them back to paragraphs |
1779 // after we use innerHTML we can fix the DOM tree | 1909 // after we use innerHTML we can fix the DOM tree |
1780 h = h.replace(/<p ([^>]+)>|<p>/g, '<div $1 mce_tmp="1">'); | 1910 h = h.replace(/<p ([^>]+)>|<p>/ig, '<div $1 _mce_tmp="1">'); |
1781 h = h.replace(/<\/p>/g, '</div>'); | 1911 h = h.replace(/<\/p>/g, '</div>'); |
1782 | 1912 |
1783 // Set the new HTML with DIVs | 1913 // Set the new HTML with DIVs |
1784 set(); | 1914 set(); |
1785 | 1915 |
1786 // Replace all DIV elements with he mce_tmp attibute back to paragraphs | 1916 // Replace all DIV elements with the _mce_tmp attibute back to paragraphs |
1787 // This is needed since IE has a annoying bug see above for details | 1917 // This is needed since IE has a annoying bug see above for details |
1788 // This is a slow process but it has to be done. :( | 1918 // This is a slow process but it has to be done. :( |
1789 if (t.settings.fix_ie_paragraphs) { | 1919 if (t.settings.fix_ie_paragraphs) { |
1790 nl = e.getElementsByTagName("DIV"); | 1920 nl = e.getElementsByTagName("DIV"); |
1791 for (i = nl.length - 1; i >= 0; i--) { | 1921 for (i = nl.length - 1; i >= 0; i--) { |
1792 n = nl[i]; | 1922 n = nl[i]; |
1793 | 1923 |
1794 // Is it a temp div | 1924 // Is it a temp div |
1795 if (n.mce_tmp) { | 1925 if (n._mce_tmp) { |
1796 // Create new paragraph | 1926 // Create new paragraph |
1797 p = t.doc.createElement('p'); | 1927 p = t.doc.createElement('p'); |
1798 | 1928 |
1799 // Copy all attributes | 1929 // Copy all attributes |
1800 n.cloneNode(false).outerHTML.replace(/([a-z0-9\-_]+)=/gi, function(a, b) { | 1930 n.cloneNode(false).outerHTML.replace(/([a-z0-9\-_]+)=/gi, function(a, b) { |
1801 var v; | 1931 var v; |
1802 | 1932 |
1803 if (b !== 'mce_tmp') { | 1933 if (b !== '_mce_tmp') { |
1804 v = n.getAttribute(b); | 1934 v = n.getAttribute(b); |
1805 | 1935 |
1806 if (!v && b === 'class') | 1936 if (!v && b === 'class') |
1807 v = n.className; | 1937 v = n.className; |
1808 | 1938 |
1826 return h; | 1956 return h; |
1827 }); | 1957 }); |
1828 }, | 1958 }, |
1829 | 1959 |
1830 processHTML : function(h) { | 1960 processHTML : function(h) { |
1831 var t = this, s = t.settings; | 1961 var t = this, s = t.settings, codeBlocks = []; |
1832 | 1962 |
1833 if (!s.process_html) | 1963 if (!s.process_html) |
1834 return h; | 1964 return h; |
1835 | 1965 |
1836 // Convert strong and em to b and i in FF since it can't handle them | 1966 if (isIE) { |
1837 if (tinymce.isGecko) { | |
1838 h = h.replace(/<(\/?)strong>|<strong( [^>]+)>/gi, '<$1b$2>'); | |
1839 h = h.replace(/<(\/?)em>|<em( [^>]+)>/gi, '<$1i$2>'); | |
1840 } else if (isIE) { | |
1841 h = h.replace(/'/g, '''); // IE can't handle apos | 1967 h = h.replace(/'/g, '''); // IE can't handle apos |
1842 h = h.replace(/\s+(disabled|checked|readonly|selected)\s*=\s*[\"\']?(false|0)[\"\']?/gi, ''); // IE doesn't handle default values correct | 1968 h = h.replace(/\s+(disabled|checked|readonly|selected)\s*=\s*[\"\']?(false|0)[\"\']?/gi, ''); // IE doesn't handle default values correct |
1843 } | 1969 } |
1844 | 1970 |
1845 // Fix some issues | 1971 // Fix some issues |
1846 h = h.replace(/<a( )([^>]+)\/>|<a\/>/gi, '<a$1$2></a>'); // Force open | 1972 h = h.replace(/<a( )([^>]+)\/>|<a\/>/gi, '<a$1$2></a>'); // Force open |
1847 | 1973 |
1848 // Store away src and href in mce_src and mce_href since browsers mess them up | 1974 // Store away src and href in _mce_src and mce_href since browsers mess them up |
1849 if (s.keep_values) { | 1975 if (s.keep_values) { |
1850 // Wrap scripts and styles in comments for serialization purposes | 1976 // Wrap scripts and styles in comments for serialization purposes |
1851 if (/<script|style/.test(h)) { | 1977 if (/<script|noscript|style/i.test(h)) { |
1852 function trim(s) { | 1978 function trim(s) { |
1853 // Remove prefix and suffix code for element | 1979 // Remove prefix and suffix code for element |
1854 s = s.replace(/(<!--\[CDATA\[|\]\]-->)/g, '\n'); | 1980 s = s.replace(/(<!--\[CDATA\[|\]\]-->)/g, '\n'); |
1855 s = s.replace(/^[\r\n]*|[\r\n]*$/g, ''); | 1981 s = s.replace(/^[\r\n]*|[\r\n]*$/g, ''); |
1856 s = s.replace(/^\s*(\/\/\s*<!--|\/\/\s*<!\[CDATA\[|<!--|<!\[CDATA\[)[\r\n]*/g, ''); | 1982 s = s.replace(/^\s*(\/\/\s*<!--|\/\/\s*<!\[CDATA\[|<!--|<!\[CDATA\[)[\r\n]*/g, ''); |
1857 s = s.replace(/\s*(\/\/\s*\]\]>|\/\/\s*-->|\]\]>|-->|\]\]-->)\s*$/g, ''); | 1983 s = s.replace(/\s*(\/\/\s*\]\]>|\/\/\s*-->|\]\]>|-->|\]\]-->)\s*$/g, ''); |
1858 | 1984 |
1859 return s; | 1985 return s; |
1860 }; | 1986 }; |
1861 | 1987 |
1862 // Preserve script elements | 1988 // Wrap the script contents in CDATA and keep them from executing |
1863 h = h.replace(/<script([^>]+|)>([\s\S]*?)<\/script>/g, function(v, a, b) { | 1989 h = h.replace(/<script([^>]+|)>([\s\S]*?)<\/script>/gi, function(v, attribs, text) { |
1864 // Remove prefix and suffix code for script element | |
1865 b = trim(b); | |
1866 | |
1867 // Force type attribute | 1990 // Force type attribute |
1868 if (!a) | 1991 if (!attribs) |
1869 a = ' type="text/javascript"'; | 1992 attribs = ' type="text/javascript"'; |
1870 | 1993 |
1871 // Wrap contents in a comment | 1994 // Convert the src attribute of the scripts |
1872 if (b) | 1995 attribs = attribs.replace(/src=\"([^\"]+)\"?/i, function(a, url) { |
1873 b = '<!--\n' + b + '\n// -->'; | 1996 if (s.url_converter) |
1874 | 1997 url = t.encode(s.url_converter.call(s.url_converter_scope || t, t.decode(url), 'src', 'script')); |
1875 // Output fake element | 1998 |
1876 return '<mce:script' + a + '>' + b + '</mce:script>'; | 1999 return '_mce_src="' + url + '"'; |
2000 }); | |
2001 | |
2002 // Wrap text contents | |
2003 if (tinymce.trim(text)) { | |
2004 codeBlocks.push(trim(text)); | |
2005 text = '<!--\nMCE_SCRIPT:' + (codeBlocks.length - 1) + '\n// -->'; | |
2006 } | |
2007 | |
2008 return '<mce:script' + attribs + '>' + text + '</mce:script>'; | |
1877 }); | 2009 }); |
1878 | 2010 |
1879 // Preserve style elements | 2011 // Wrap style elements |
1880 h = h.replace(/<style([^>]+|)>([\s\S]*?)<\/style>/g, function(v, a, b) { | 2012 h = h.replace(/<style([^>]+|)>([\s\S]*?)<\/style>/gi, function(v, attribs, text) { |
1881 b = trim(b); | 2013 // Wrap text contents |
1882 return '<mce:style' + a + '><!--\n' + b + '\n--></mce:style><style' + a + ' mce_bogus="1">' + b + '</style>'; | 2014 if (text) { |
2015 codeBlocks.push(trim(text)); | |
2016 text = '<!--\nMCE_SCRIPT:' + (codeBlocks.length - 1) + '\n-->'; | |
2017 } | |
2018 | |
2019 return '<mce:style' + attribs + '>' + text + '</mce:style><style ' + attribs + ' _mce_bogus="1">' + text + '</style>'; | |
1883 }); | 2020 }); |
2021 | |
2022 // Wrap noscript elements | |
2023 h = h.replace(/<noscript([^>]+|)>([\s\S]*?)<\/noscript>/g, function(v, attribs, text) { | |
2024 return '<mce:noscript' + attribs + '><!--' + t.encode(text).replace(/--/g, '--') + '--></mce:noscript>'; | |
2025 }); | |
1884 } | 2026 } |
1885 | 2027 |
1886 h = h.replace(/<!\[CDATA\[([\s\S]+)\]\]>/g, '<!--[CDATA[$1]]-->'); | 2028 h = h.replace(/<!\[CDATA\[([\s\S]+)\]\]>/g, '<!--[CDATA[$1]]-->'); |
1887 | 2029 |
1888 // Process all tags with src, href or style | 2030 // This function processes the attributes in the HTML string to force boolean |
1889 h = h.replace(/<([\w:]+) [^>]*(src|href|style|shape|coords)[^>]*>/gi, function(a, n) { | 2031 // attributes to the attr="attr" format and convert style, src and href to _mce_ versions |
1890 function handle(m, b, c) { | 2032 function processTags(html) { |
1891 var u = c; | 2033 return html.replace(tagRegExp, function(match, elm_name, attrs, end) { |
1892 | 2034 return '<' + elm_name + attrs.replace(attrRegExp, function(match, name, value, val2, val3) { |
1893 // Tag already got a mce_ version | 2035 var mceValue; |
1894 if (a.indexOf('mce_' + b) != -1) | 2036 |
1895 return m; | 2037 name = name.toLowerCase(); |
1896 | 2038 value = value || val2 || val3 || ""; |
1897 if (b == 'style') { | 2039 |
1898 // Why did I need this one? | 2040 // Treat boolean attributes |
1899 //if (isIE) | 2041 if (boolAttrs[name]) { |
1900 // u = t.serializeStyle(t.parseStyle(u)); | 2042 // false or 0 is treated as a missing attribute |
1901 | 2043 if (value === 'false' || value === '0') |
1902 // No mce_style for elements with these since they might get resized by the user | 2044 return; |
1903 if (t._isRes(c)) | 2045 |
1904 return m; | 2046 return name + '="' + name + '"'; |
1905 | |
1906 if (s.hex_colors) { | |
1907 u = u.replace(/rgb\([^\)]+\)/g, function(v) { | |
1908 return t.toHex(v); | |
1909 }); | |
1910 } | 2047 } |
1911 | 2048 |
1912 if (s.url_converter) { | 2049 // Is attribute one that needs special treatment |
1913 u = u.replace(/url\([\'\"]?([^\)\'\"]+)\)/g, function(x, c) { | 2050 if (mceAttribs[name] && attrs.indexOf('_mce_' + name) == -1) { |
1914 return 'url(' + t.encode(s.url_converter.call(s.url_converter_scope || t, t.decode(c), b, n)) + ')'; | 2051 mceValue = t.decode(value); |
1915 }); | 2052 |
2053 // Convert URLs to relative/absolute ones | |
2054 if (s.url_converter && (name == "src" || name == "href")) | |
2055 mceValue = s.url_converter.call(s.url_converter_scope || t, mceValue, name, elm_name); | |
2056 | |
2057 // Process styles lowercases them and compresses them | |
2058 if (name == 'style') | |
2059 mceValue = t.serializeStyle(t.parseStyle(mceValue), name); | |
2060 | |
2061 return name + '="' + value + '"' + ' _mce_' + name + '="' + t.encode(mceValue) + '"'; | |
1916 } | 2062 } |
1917 } else if (b != 'coords' && b != 'shape') { | 2063 |
1918 if (s.url_converter) | 2064 return match; |
1919 u = t.encode(s.url_converter.call(s.url_converter_scope || t, t.decode(c), b, n)); | 2065 }) + end + '>'; |
1920 } | 2066 }); |
1921 | 2067 }; |
1922 return ' ' + b + '="' + c + '" mce_' + b + '="' + u + '"'; | 2068 |
1923 }; | 2069 h = processTags(h); |
1924 | 2070 |
1925 a = a.replace(/ (src|href|style|coords|shape)=[\"]([^\"]+)[\"]/gi, handle); // W3C | 2071 // Restore script blocks |
1926 a = a.replace(/ (src|href|style|coords|shape)=[\']([^\']+)[\']/gi, handle); // W3C | 2072 h = h.replace(/MCE_SCRIPT:([0-9]+)/g, function(val, idx) { |
1927 | 2073 return codeBlocks[idx]; |
1928 return a.replace(/ (src|href|style|coords|shape)=([^\s\"\'>]+)/gi, handle); // IE | |
1929 }); | 2074 }); |
1930 } | 2075 } |
1931 | 2076 |
1932 return h; | 2077 return h; |
1933 }, | 2078 }, |
1950 }, | 2095 }, |
1951 | 2096 |
1952 setOuterHTML : function(e, h, d) { | 2097 setOuterHTML : function(e, h, d) { |
1953 var t = this; | 2098 var t = this; |
1954 | 2099 |
2100 function setHTML(e, h, d) { | |
2101 var n, tp; | |
2102 | |
2103 tp = d.createElement("body"); | |
2104 tp.innerHTML = h; | |
2105 | |
2106 n = tp.lastChild; | |
2107 while (n) { | |
2108 t.insertAfter(n.cloneNode(true), e); | |
2109 n = n.previousSibling; | |
2110 } | |
2111 | |
2112 t.remove(e); | |
2113 }; | |
2114 | |
1955 return this.run(e, function(e) { | 2115 return this.run(e, function(e) { |
1956 var n, tp; | |
1957 | |
1958 e = t.get(e); | 2116 e = t.get(e); |
1959 d = d || e.ownerDocument || t.doc; | 2117 |
1960 | 2118 // Only set HTML on elements |
1961 if (isIE && e.nodeType == 1) | 2119 if (e.nodeType == 1) { |
1962 e.outerHTML = h; | 2120 d = d || e.ownerDocument || t.doc; |
1963 else { | 2121 |
1964 tp = d.createElement("body"); | 2122 if (isIE) { |
1965 tp.innerHTML = h; | 2123 try { |
1966 | 2124 // Try outerHTML for IE it sometimes produces an unknown runtime error |
1967 n = tp.lastChild; | 2125 if (isIE && e.nodeType == 1) |
1968 while (n) { | 2126 e.outerHTML = h; |
1969 t.insertAfter(n.cloneNode(true), e); | 2127 else |
1970 n = n.previousSibling; | 2128 setHTML(e, h, d); |
1971 } | 2129 } catch (ex) { |
1972 | 2130 // Fix for unknown runtime error |
1973 t.remove(e); | 2131 setHTML(e, h, d); |
2132 } | |
2133 } else | |
2134 setHTML(e, h, d); | |
1974 } | 2135 } |
1975 }); | 2136 }); |
1976 }, | 2137 }, |
1977 | 2138 |
1978 decode : function(s) { | 2139 decode : function(s) { |
1979 var e, n, v; | 2140 var e, n, v; |
1980 | 2141 |
1981 // Look for entities to decode | 2142 // Look for entities to decode |
1982 if (/&[^;]+;/.test(s)) { | 2143 if (/&[\w#]+;/.test(s)) { |
1983 // Decode the entities using a div element not super efficient but less code | 2144 // Decode the entities using a div element not super efficient but less code |
1984 e = this.doc.createElement("div"); | 2145 e = this.doc.createElement("div"); |
1985 e.innerHTML = s; | 2146 e.innerHTML = s; |
1986 n = e.firstChild; | 2147 n = e.firstChild; |
1987 v = ''; | 2148 v = ''; |
1988 | 2149 |
1989 if (n) { | 2150 if (n) { |
1990 do { | 2151 do { |
1991 v += n.nodeValue; | 2152 v += n.nodeValue; |
1992 } while (n.nextSibling); | 2153 } while (n = n.nextSibling); |
1993 } | 2154 } |
1994 | 2155 |
1995 return v || s; | 2156 return v || s; |
1996 } | 2157 } |
1997 | 2158 |
1998 return s; | 2159 return s; |
1999 }, | 2160 }, |
2000 | 2161 |
2001 encode : function(s) { | 2162 encode : function(str) { |
2002 return s ? ('' + s).replace(/[<>&\"]/g, function (c, b) { | 2163 return ('' + str).replace(encodeCharsRe, function(chr) { |
2003 switch (c) { | 2164 return encodedChars[chr]; |
2004 case '&': | 2165 }); |
2005 return '&'; | 2166 }, |
2006 | 2167 |
2007 case '"': | 2168 insertAfter : function(node, reference_node) { |
2008 return '"'; | 2169 reference_node = this.get(reference_node); |
2009 | 2170 |
2010 case '<': | 2171 return this.run(node, function(node) { |
2011 return '<'; | 2172 var parent, nextSibling; |
2012 | 2173 |
2013 case '>': | 2174 parent = reference_node.parentNode; |
2014 return '>'; | 2175 nextSibling = reference_node.nextSibling; |
2015 } | 2176 |
2016 | 2177 if (nextSibling) |
2017 return c; | 2178 parent.insertBefore(node, nextSibling); |
2018 }) : s; | |
2019 }, | |
2020 | |
2021 | |
2022 insertAfter : function(n, r) { | |
2023 var t = this; | |
2024 | |
2025 r = t.get(r); | |
2026 | |
2027 return this.run(n, function(n) { | |
2028 var p, ns; | |
2029 | |
2030 p = r.parentNode; | |
2031 ns = r.nextSibling; | |
2032 | |
2033 if (ns) | |
2034 p.insertBefore(n, ns); | |
2035 else | 2179 else |
2036 p.appendChild(n); | 2180 parent.appendChild(node); |
2037 | 2181 |
2038 return n; | 2182 return node; |
2039 }); | 2183 }); |
2040 }, | 2184 }, |
2041 | |
2042 | 2185 |
2043 isBlock : function(n) { | 2186 isBlock : function(n) { |
2044 if (n.nodeType && n.nodeType !== 1) | 2187 if (n.nodeType && n.nodeType !== 1) |
2045 return false; | 2188 return false; |
2046 | 2189 |
2047 n = n.nodeName || n; | 2190 n = n.nodeName || n; |
2048 | 2191 |
2049 return /^(H[1-6]|HR|P|DIV|ADDRESS|PRE|FORM|TABLE|LI|OL|UL|TR|TD|CAPTION|BLOCKQUOTE|CENTER|DL|DT|DD|DIR|FIELDSET|NOSCRIPT|NOFRAMES|MENU|ISINDEX|SAMP)$/.test(n); | 2192 return blockRe.test(n); |
2050 }, | 2193 }, |
2051 | |
2052 | 2194 |
2053 replace : function(n, o, k) { | 2195 replace : function(n, o, k) { |
2054 var t = this; | 2196 var t = this; |
2055 | 2197 |
2056 if (is(o, 'array')) | 2198 if (is(o, 'array')) |
2057 n = n.cloneNode(true); | 2199 n = n.cloneNode(true); |
2058 | 2200 |
2059 return t.run(o, function(o) { | 2201 return t.run(o, function(o) { |
2060 if (k) { | 2202 if (k) { |
2061 each(o.childNodes, function(c) { | 2203 each(tinymce.grep(o.childNodes), function(c) { |
2062 n.appendChild(c.cloneNode(true)); | 2204 n.appendChild(c); |
2063 }); | 2205 }); |
2064 } | |
2065 | |
2066 // Fix IE psuedo leak for elements since replacing elements if fairly common | |
2067 // Will break parentNode for some unknown reason | |
2068 if (t.fixPsuedoLeaks && o.nodeType === 1) { | |
2069 o.parentNode.insertBefore(n, o); | |
2070 t.remove(o); | |
2071 return n; | |
2072 } | 2206 } |
2073 | 2207 |
2074 return o.parentNode.replaceChild(n, o); | 2208 return o.parentNode.replaceChild(n, o); |
2075 }); | 2209 }); |
2076 }, | 2210 }, |
2077 | 2211 |
2212 rename : function(elm, name) { | |
2213 var t = this, newElm; | |
2214 | |
2215 if (elm.nodeName != name.toUpperCase()) { | |
2216 // Rename block element | |
2217 newElm = t.create(name); | |
2218 | |
2219 // Copy attribs to new block | |
2220 each(t.getAttribs(elm), function(attr_node) { | |
2221 t.setAttrib(newElm, attr_node.nodeName, t.getAttrib(elm, attr_node.nodeName)); | |
2222 }); | |
2223 | |
2224 // Replace block | |
2225 t.replace(newElm, elm, 1); | |
2226 } | |
2227 | |
2228 return newElm || elm; | |
2229 }, | |
2078 | 2230 |
2079 findCommonAncestor : function(a, b) { | 2231 findCommonAncestor : function(a, b) { |
2080 var ps = a, pe; | 2232 var ps = a, pe; |
2081 | 2233 |
2082 while (ps) { | 2234 while (ps) { |
2217 | 2369 |
2218 // Object will throw exception in IE | 2370 // Object will throw exception in IE |
2219 if (n.nodeName == 'OBJECT') | 2371 if (n.nodeName == 'OBJECT') |
2220 return n.attributes; | 2372 return n.attributes; |
2221 | 2373 |
2374 // IE doesn't keep the selected attribute if you clone option elements | |
2375 if (n.nodeName === 'OPTION' && this.getAttrib(n, 'selected')) | |
2376 o.push({specified : 1, nodeName : 'selected'}); | |
2377 | |
2222 // It's crazy that this is faster in IE but it's because it returns all attributes all the time | 2378 // It's crazy that this is faster in IE but it's because it returns all attributes all the time |
2223 n.cloneNode(false).outerHTML.replace(/([a-z0-9\:\-_]+)=/gi, function(a, b) { | 2379 n.cloneNode(false).outerHTML.replace(/<\/?[\w:\-]+ ?|=[\"][^\"]+\"|=\'[^\']+\'|=[\w\-]+|>/gi, '').replace(/[\w:\-]+/gi, function(a) { |
2224 o.push({specified : 1, nodeName : b}); | 2380 o.push({specified : 1, nodeName : a}); |
2225 }); | 2381 }); |
2226 | 2382 |
2227 return o; | 2383 return o; |
2228 } | 2384 } |
2229 | 2385 |
2231 }, | 2387 }, |
2232 | 2388 |
2233 destroy : function(s) { | 2389 destroy : function(s) { |
2234 var t = this; | 2390 var t = this; |
2235 | 2391 |
2236 t.win = t.doc = t.root = null; | 2392 if (t.events) |
2393 t.events.destroy(); | |
2394 | |
2395 t.win = t.doc = t.root = t.events = null; | |
2237 | 2396 |
2238 // Manual destroy then remove unload handler | 2397 // Manual destroy then remove unload handler |
2239 if (!s) | 2398 if (!s) |
2240 tinymce.removeUnload(t.destroy); | 2399 tinymce.removeUnload(t.destroy); |
2241 }, | 2400 }, |
2244 var d = this.doc; | 2403 var d = this.doc; |
2245 | 2404 |
2246 return d.createRange ? d.createRange() : new tinymce.dom.Range(this); | 2405 return d.createRange ? d.createRange() : new tinymce.dom.Range(this); |
2247 }, | 2406 }, |
2248 | 2407 |
2408 nodeIndex : function(node, normalized) { | |
2409 var idx = 0, lastNodeType, lastNode, nodeType; | |
2410 | |
2411 if (node) { | |
2412 for (lastNodeType = node.nodeType, node = node.previousSibling, lastNode = node; node; node = node.previousSibling) { | |
2413 nodeType = node.nodeType; | |
2414 | |
2415 // Handle normalization of text nodes | |
2416 if (!normalized || nodeType != 3 || (lastNodeType != nodeType && node.nodeValue.length)) | |
2417 idx++; | |
2418 | |
2419 lastNodeType = nodeType; | |
2420 } | |
2421 } | |
2422 | |
2423 return idx; | |
2424 }, | |
2425 | |
2249 split : function(pe, e, re) { | 2426 split : function(pe, e, re) { |
2250 var t = this, r = t.createRng(), bef, aft, pa; | 2427 var t = this, r = t.createRng(), bef, aft, pa; |
2251 | 2428 |
2252 // W3C valid browsers tend to leave empty nodes to the left/right side of the contents, this makes sence | 2429 // W3C valid browsers tend to leave empty nodes to the left/right side of the contents, this makes sense |
2253 // but we don't want that in our code since it serves no purpose | 2430 // but we don't want that in our code since it serves no purpose for the end user |
2254 // For example if this is chopped: | 2431 // For example if this is chopped: |
2255 // <p>text 1<span><b>CHOP</b></span>text 2</p> | 2432 // <p>text 1<span><b>CHOP</b></span>text 2</p> |
2256 // would produce: | 2433 // would produce: |
2257 // <p>text 1<span></span></p><b>CHOP</b><p><span></span>text 2</p> | 2434 // <p>text 1<span></span></p><b>CHOP</b><p><span></span>text 2</p> |
2258 // this function will then trim of empty edges and produce: | 2435 // this function will then trim of empty edges and produce: |
2259 // <p>text 1</p><b>CHOP</b><p>text 2</p> | 2436 // <p>text 1</p><b>CHOP</b><p>text 2</p> |
2260 function trimEdge(n, na) { | 2437 function trim(node) { |
2261 n = n[na]; | 2438 var i, children = node.childNodes; |
2262 | 2439 |
2263 if (n && n[na] && n[na].nodeType == 1 && isEmpty(n[na])) | 2440 if (node.nodeType == 1 && node.getAttribute('_mce_type') == 'bookmark') |
2264 t.remove(n[na]); | 2441 return; |
2265 }; | 2442 |
2266 | 2443 for (i = children.length - 1; i >= 0; i--) |
2267 function isEmpty(n) { | 2444 trim(children[i]); |
2268 n = t.getOuterHTML(n); | 2445 |
2269 n = n.replace(/<(img|hr|table)/gi, '-'); // Keep these convert them to - chars | 2446 if (node.nodeType != 9) { |
2270 n = n.replace(/<[^>]+>/g, ''); // Remove all tags | 2447 // Keep non whitespace text nodes |
2271 | 2448 if (node.nodeType == 3 && node.nodeValue.length > 0) |
2272 return n.replace(/[ \t\r\n]+| | /g, '') == ''; | 2449 return; |
2450 | |
2451 if (node.nodeType == 1) { | |
2452 // If the only child is a bookmark then move it up | |
2453 children = node.childNodes; | |
2454 if (children.length == 1 && children[0] && children[0].nodeType == 1 && children[0].getAttribute('_mce_type') == 'bookmark') | |
2455 node.parentNode.insertBefore(children[0], node); | |
2456 | |
2457 // Keep non empty elements or img, hr etc | |
2458 if (children.length || /^(br|hr|input|img)$/i.test(node.nodeName)) | |
2459 return; | |
2460 } | |
2461 | |
2462 t.remove(node); | |
2463 } | |
2464 | |
2465 return node; | |
2273 }; | 2466 }; |
2274 | 2467 |
2275 if (pe && e) { | 2468 if (pe && e) { |
2276 // Get before chunk | 2469 // Get before chunk |
2277 r.setStartBefore(pe); | 2470 r.setStart(pe.parentNode, t.nodeIndex(pe)); |
2278 r.setEndBefore(e); | 2471 r.setEnd(e.parentNode, t.nodeIndex(e)); |
2279 bef = r.extractContents(); | 2472 bef = r.extractContents(); |
2280 | 2473 |
2281 // Get after chunk | 2474 // Get after chunk |
2282 r = t.createRng(); | 2475 r = t.createRng(); |
2283 r.setStartAfter(e); | 2476 r.setStart(e.parentNode, t.nodeIndex(e) + 1); |
2284 r.setEndAfter(pe); | 2477 r.setEnd(pe.parentNode, t.nodeIndex(pe) + 1); |
2285 aft = r.extractContents(); | 2478 aft = r.extractContents(); |
2286 | 2479 |
2287 // Insert chunks and remove parent | 2480 // Insert before chunk |
2288 pa = pe.parentNode; | 2481 pa = pe.parentNode; |
2289 | 2482 pa.insertBefore(trim(bef), pe); |
2290 // Remove right side edge of the before contents | 2483 |
2291 trimEdge(bef, 'lastChild'); | 2484 // Insert middle chunk |
2292 | |
2293 if (!isEmpty(bef)) | |
2294 pa.insertBefore(bef, pe); | |
2295 | |
2296 if (re) | 2485 if (re) |
2297 pa.replaceChild(re, e); | 2486 pa.replaceChild(re, e); |
2298 else | 2487 else |
2299 pa.insertBefore(e, pe); | 2488 pa.insertBefore(e, pe); |
2300 | 2489 |
2301 // Remove left site edge of the after contents | 2490 // Insert after chunk |
2302 trimEdge(aft, 'firstChild'); | 2491 pa.insertBefore(trim(aft), pe); |
2303 | |
2304 if (!isEmpty(aft)) | |
2305 pa.insertBefore(aft, pe); | |
2306 | |
2307 t.remove(pe); | 2492 t.remove(pe); |
2308 | 2493 |
2309 return re || e; | 2494 return re || e; |
2310 } | 2495 } |
2496 }, | |
2497 | |
2498 bind : function(target, name, func, scope) { | |
2499 var t = this; | |
2500 | |
2501 if (!t.events) | |
2502 t.events = new tinymce.dom.EventUtils(); | |
2503 | |
2504 return t.events.add(target, name, func, scope || this); | |
2505 }, | |
2506 | |
2507 unbind : function(target, name, func) { | |
2508 var t = this; | |
2509 | |
2510 if (!t.events) | |
2511 t.events = new tinymce.dom.EventUtils(); | |
2512 | |
2513 return t.events.remove(target, name, func); | |
2514 }, | |
2515 | |
2516 | |
2517 _findSib : function(node, selector, name) { | |
2518 var t = this, f = selector; | |
2519 | |
2520 if (node) { | |
2521 // If expression make a function of it using is | |
2522 if (is(f, 'string')) { | |
2523 f = function(node) { | |
2524 return t.is(node, selector); | |
2525 }; | |
2526 } | |
2527 | |
2528 // Loop all siblings | |
2529 for (node = node[name]; node; node = node[name]) { | |
2530 if (f(node)) | |
2531 return node; | |
2532 } | |
2533 } | |
2534 | |
2535 return null; | |
2311 }, | 2536 }, |
2312 | 2537 |
2313 _isRes : function(c) { | 2538 _isRes : function(c) { |
2314 // Is live resizble element | 2539 // Is live resizble element |
2315 return /^(top|left|bottom|right|width|height)/i.test(c) || /;\s*(top|left|bottom|right|width|height)/i.test(c); | 2540 return /^(top|left|bottom|right|width|height)/i.test(c) || /;\s*(top|left|bottom|right|width|height)/i.test(c); |
2342 } | 2567 } |
2343 | 2568 |
2344 return s; | 2569 return s; |
2345 } | 2570 } |
2346 */ | 2571 */ |
2347 | 2572 }); |
2348 }); | 2573 |
2349 | |
2350 // Setup page DOM | |
2351 tinymce.DOM = new tinymce.dom.DOMUtils(document, {process_html : 0}); | 2574 tinymce.DOM = new tinymce.dom.DOMUtils(document, {process_html : 0}); |
2352 })(tinymce); | 2575 })(tinymce); |
2576 | |
2353 (function(ns) { | 2577 (function(ns) { |
2354 // Traverse constants | |
2355 var EXTRACT = 0, CLONE = 1, DELETE = 2, extend = tinymce.extend; | |
2356 | |
2357 function indexOf(child, parent) { | |
2358 var i, node; | |
2359 | |
2360 if (child.parentNode != parent) | |
2361 return -1; | |
2362 | |
2363 for (node = parent.firstChild, i = 0; node != child; node = node.nextSibling) | |
2364 i++; | |
2365 | |
2366 return i; | |
2367 }; | |
2368 | |
2369 function nodeIndex(n) { | |
2370 var i = 0; | |
2371 | |
2372 while (n.previousSibling) { | |
2373 i++; | |
2374 n = n.previousSibling; | |
2375 } | |
2376 | |
2377 return i; | |
2378 }; | |
2379 | |
2380 function getSelectedNode(container, offset) { | |
2381 var child; | |
2382 | |
2383 if (container.nodeType == 3 /* TEXT_NODE */) | |
2384 return container; | |
2385 | |
2386 if (offset < 0) | |
2387 return container; | |
2388 | |
2389 child = container.firstChild; | |
2390 while (child != null && offset > 0) { | |
2391 --offset; | |
2392 child = child.nextSibling; | |
2393 } | |
2394 | |
2395 if (child != null) | |
2396 return child; | |
2397 | |
2398 return container; | |
2399 }; | |
2400 | |
2401 // Range constructor | 2578 // Range constructor |
2402 function Range(dom) { | 2579 function Range(dom) { |
2403 var d = dom.doc; | 2580 var t = this, |
2404 | 2581 doc = dom.doc, |
2405 extend(this, { | 2582 EXTRACT = 0, |
2406 dom : dom, | 2583 CLONE = 1, |
2407 | 2584 DELETE = 2, |
2585 TRUE = true, | |
2586 FALSE = false, | |
2587 START_OFFSET = 'startOffset', | |
2588 START_CONTAINER = 'startContainer', | |
2589 END_CONTAINER = 'endContainer', | |
2590 END_OFFSET = 'endOffset', | |
2591 extend = tinymce.extend, | |
2592 nodeIndex = dom.nodeIndex; | |
2593 | |
2594 extend(t, { | |
2408 // Inital states | 2595 // Inital states |
2409 startContainer : d, | 2596 startContainer : doc, |
2410 startOffset : 0, | 2597 startOffset : 0, |
2411 endContainer : d, | 2598 endContainer : doc, |
2412 endOffset : 0, | 2599 endOffset : 0, |
2413 collapsed : true, | 2600 collapsed : TRUE, |
2414 commonAncestorContainer : d, | 2601 commonAncestorContainer : doc, |
2415 | 2602 |
2416 // Range constants | 2603 // Range constants |
2417 START_TO_START : 0, | 2604 START_TO_START : 0, |
2418 START_TO_END : 1, | 2605 START_TO_END : 1, |
2419 END_TO_END : 2, | 2606 END_TO_END : 2, |
2420 END_TO_START : 3 | 2607 END_TO_START : 3, |
2608 | |
2609 // Public methods | |
2610 setStart : setStart, | |
2611 setEnd : setEnd, | |
2612 setStartBefore : setStartBefore, | |
2613 setStartAfter : setStartAfter, | |
2614 setEndBefore : setEndBefore, | |
2615 setEndAfter : setEndAfter, | |
2616 collapse : collapse, | |
2617 selectNode : selectNode, | |
2618 selectNodeContents : selectNodeContents, | |
2619 compareBoundaryPoints : compareBoundaryPoints, | |
2620 deleteContents : deleteContents, | |
2621 extractContents : extractContents, | |
2622 cloneContents : cloneContents, | |
2623 insertNode : insertNode, | |
2624 surroundContents : surroundContents, | |
2625 cloneRange : cloneRange | |
2421 }); | 2626 }); |
2422 }; | 2627 |
2423 | 2628 function setStart(n, o) { |
2424 // Add range methods | 2629 _setEndPoint(TRUE, n, o); |
2425 extend(Range.prototype, { | 2630 }; |
2426 setStart : function(n, o) { | 2631 |
2427 this._setEndPoint(true, n, o); | 2632 function setEnd(n, o) { |
2428 }, | 2633 _setEndPoint(FALSE, n, o); |
2429 | 2634 }; |
2430 setEnd : function(n, o) { | 2635 |
2431 this._setEndPoint(false, n, o); | 2636 function setStartBefore(n) { |
2432 }, | 2637 setStart(n.parentNode, nodeIndex(n)); |
2433 | 2638 }; |
2434 setStartBefore : function(n) { | 2639 |
2435 this.setStart(n.parentNode, nodeIndex(n)); | 2640 function setStartAfter(n) { |
2436 }, | 2641 setStart(n.parentNode, nodeIndex(n) + 1); |
2437 | 2642 }; |
2438 setStartAfter : function(n) { | 2643 |
2439 this.setStart(n.parentNode, nodeIndex(n) + 1); | 2644 function setEndBefore(n) { |
2440 }, | 2645 setEnd(n.parentNode, nodeIndex(n)); |
2441 | 2646 }; |
2442 setEndBefore : function(n) { | 2647 |
2443 this.setEnd(n.parentNode, nodeIndex(n)); | 2648 function setEndAfter(n) { |
2444 }, | 2649 setEnd(n.parentNode, nodeIndex(n) + 1); |
2445 | 2650 }; |
2446 setEndAfter : function(n) { | 2651 |
2447 this.setEnd(n.parentNode, nodeIndex(n) + 1); | 2652 function collapse(ts) { |
2448 }, | |
2449 | |
2450 collapse : function(ts) { | |
2451 var t = this; | |
2452 | |
2453 if (ts) { | 2653 if (ts) { |
2454 t.endContainer = t.startContainer; | 2654 t[END_CONTAINER] = t[START_CONTAINER]; |
2455 t.endOffset = t.startOffset; | 2655 t[END_OFFSET] = t[START_OFFSET]; |
2456 } else { | 2656 } else { |
2457 t.startContainer = t.endContainer; | 2657 t[START_CONTAINER] = t[END_CONTAINER]; |
2458 t.startOffset = t.endOffset; | 2658 t[START_OFFSET] = t[END_OFFSET]; |
2459 } | 2659 } |
2460 | 2660 |
2461 t.collapsed = true; | 2661 t.collapsed = TRUE; |
2462 }, | 2662 }; |
2463 | 2663 |
2464 selectNode : function(n) { | 2664 function selectNode(n) { |
2465 this.setStartBefore(n); | 2665 setStartBefore(n); |
2466 this.setEndAfter(n); | 2666 setEndAfter(n); |
2467 }, | 2667 }; |
2468 | 2668 |
2469 selectNodeContents : function(n) { | 2669 function selectNodeContents(n) { |
2470 this.setStart(n, 0); | 2670 setStart(n, 0); |
2471 this.setEnd(n, n.nodeType === 1 ? n.childNodes.length : n.nodeValue.length); | 2671 setEnd(n, n.nodeType === 1 ? n.childNodes.length : n.nodeValue.length); |
2472 }, | 2672 }; |
2473 | 2673 |
2474 compareBoundaryPoints : function(h, r) { | 2674 function compareBoundaryPoints(h, r) { |
2475 var t = this, sc = t.startContainer, so = t.startOffset, ec = t.endContainer, eo = t.endOffset; | 2675 var sc = t[START_CONTAINER], so = t[START_OFFSET], ec = t[END_CONTAINER], eo = t[END_OFFSET]; |
2476 | 2676 |
2477 // Check START_TO_START | 2677 // Check START_TO_START |
2478 if (h === 0) | 2678 if (h === 0) |
2479 return t._compareBoundaryPoints(sc, so, sc, so); | 2679 return _compareBoundaryPoints(sc, so, sc, so); |
2480 | 2680 |
2481 // Check START_TO_END | 2681 // Check START_TO_END |
2482 if (h === 1) | 2682 if (h === 1) |
2483 return t._compareBoundaryPoints(sc, so, ec, eo); | 2683 return _compareBoundaryPoints(sc, so, ec, eo); |
2484 | 2684 |
2485 // Check END_TO_END | 2685 // Check END_TO_END |
2486 if (h === 2) | 2686 if (h === 2) |
2487 return t._compareBoundaryPoints(ec, eo, ec, eo); | 2687 return _compareBoundaryPoints(ec, eo, ec, eo); |
2488 | 2688 |
2489 // Check END_TO_START | 2689 // Check END_TO_START |
2490 if (h === 3) | 2690 if (h === 3) |
2491 return t._compareBoundaryPoints(ec, eo, sc, so); | 2691 return _compareBoundaryPoints(ec, eo, sc, so); |
2492 }, | 2692 }; |
2493 | 2693 |
2494 deleteContents : function() { | 2694 function deleteContents() { |
2495 this._traverse(DELETE); | 2695 _traverse(DELETE); |
2496 }, | 2696 }; |
2497 | 2697 |
2498 extractContents : function() { | 2698 function extractContents() { |
2499 return this._traverse(EXTRACT); | 2699 return _traverse(EXTRACT); |
2500 }, | 2700 }; |
2501 | 2701 |
2502 cloneContents : function() { | 2702 function cloneContents() { |
2503 return this._traverse(CLONE); | 2703 return _traverse(CLONE); |
2504 }, | 2704 }; |
2505 | 2705 |
2506 insertNode : function(n) { | 2706 function insertNode(n) { |
2507 var t = this, nn, o; | 2707 var startContainer = this[START_CONTAINER], |
2708 startOffset = this[START_OFFSET], nn, o; | |
2508 | 2709 |
2509 // Node is TEXT_NODE or CDATA | 2710 // Node is TEXT_NODE or CDATA |
2510 if (n.nodeType === 3 || n.nodeType === 4) { | 2711 if ((startContainer.nodeType === 3 || startContainer.nodeType === 4) && startContainer.nodeValue) { |
2511 nn = t.startContainer.splitText(t.startOffset); | 2712 if (!startOffset) { |
2512 t.startContainer.parentNode.insertBefore(n, nn); | 2713 // At the start of text |
2714 startContainer.parentNode.insertBefore(n, startContainer); | |
2715 } else if (startOffset >= startContainer.nodeValue.length) { | |
2716 // At the end of text | |
2717 dom.insertAfter(n, startContainer); | |
2718 } else { | |
2719 // Middle, need to split | |
2720 nn = startContainer.splitText(startOffset); | |
2721 startContainer.parentNode.insertBefore(n, nn); | |
2722 } | |
2513 } else { | 2723 } else { |
2514 // Insert element node | 2724 // Insert element node |
2515 if (t.startContainer.childNodes.length > 0) | 2725 if (startContainer.childNodes.length > 0) |
2516 o = t.startContainer.childNodes[t.startOffset]; | 2726 o = startContainer.childNodes[startOffset]; |
2517 | 2727 |
2518 t.startContainer.insertBefore(n, o); | 2728 if (o) |
2519 } | 2729 startContainer.insertBefore(n, o); |
2520 }, | 2730 else |
2521 | 2731 startContainer.appendChild(n); |
2522 surroundContents : function(n) { | 2732 } |
2523 var t = this, f = t.extractContents(); | 2733 }; |
2734 | |
2735 function surroundContents(n) { | |
2736 var f = t.extractContents(); | |
2524 | 2737 |
2525 t.insertNode(n); | 2738 t.insertNode(n); |
2526 n.appendChild(f); | 2739 n.appendChild(f); |
2527 t.selectNode(n); | 2740 t.selectNode(n); |
2528 }, | 2741 }; |
2529 | 2742 |
2530 cloneRange : function() { | 2743 function cloneRange() { |
2531 var t = this; | 2744 return extend(new Range(dom), { |
2532 | 2745 startContainer : t[START_CONTAINER], |
2533 return extend(new Range(t.dom), { | 2746 startOffset : t[START_OFFSET], |
2534 startContainer : t.startContainer, | 2747 endContainer : t[END_CONTAINER], |
2535 startOffset : t.startOffset, | 2748 endOffset : t[END_OFFSET], |
2536 endContainer : t.endContainer, | |
2537 endOffset : t.endOffset, | |
2538 collapsed : t.collapsed, | 2749 collapsed : t.collapsed, |
2539 commonAncestorContainer : t.commonAncestorContainer | 2750 commonAncestorContainer : t.commonAncestorContainer |
2540 }); | 2751 }); |
2541 }, | 2752 }; |
2542 | 2753 |
2543 /* | 2754 // Private methods |
2544 toString : function() { | 2755 |
2545 // Not implemented | 2756 function _getSelectedNode(container, offset) { |
2546 }, | 2757 var child; |
2547 | 2758 |
2548 detach : function() { | 2759 if (container.nodeType == 3 /* TEXT_NODE */) |
2549 // Not implemented | 2760 return container; |
2550 }, | 2761 |
2551 */ | 2762 if (offset < 0) |
2552 // Internal methods | 2763 return container; |
2553 | 2764 |
2554 _isCollapsed : function() { | 2765 child = container.firstChild; |
2555 return (this.startContainer == this.endContainer && this.startOffset == this.endOffset); | 2766 while (child && offset > 0) { |
2556 }, | 2767 --offset; |
2557 | 2768 child = child.nextSibling; |
2558 _compareBoundaryPoints : function (containerA, offsetA, containerB, offsetB) { | 2769 } |
2770 | |
2771 if (child) | |
2772 return child; | |
2773 | |
2774 return container; | |
2775 }; | |
2776 | |
2777 function _isCollapsed() { | |
2778 return (t[START_CONTAINER] == t[END_CONTAINER] && t[START_OFFSET] == t[END_OFFSET]); | |
2779 }; | |
2780 | |
2781 function _compareBoundaryPoints(containerA, offsetA, containerB, offsetB) { | |
2559 var c, offsetC, n, cmnRoot, childA, childB; | 2782 var c, offsetC, n, cmnRoot, childA, childB; |
2560 | 2783 |
2561 // In the first case the boundary-points have the same container. A is before B | 2784 // In the first case the boundary-points have the same container. A is before B |
2562 // if its offset is less than the offset of B, A is equal to B if its offset is | 2785 // if its offset is less than the offset of B, A is equal to B if its offset is |
2563 // equal to the offset of B, and A is after B if its offset is greater than the | 2786 // equal to the offset of B, and A is after B if its offset is greater than the |
2564 // offset of B. | 2787 // offset of B. |
2565 if (containerA == containerB) { | 2788 if (containerA == containerB) { |
2566 if (offsetA == offsetB) { | 2789 if (offsetA == offsetB) |
2567 return 0; // equal | 2790 return 0; // equal |
2568 } else if (offsetA < offsetB) { | 2791 |
2792 if (offsetA < offsetB) | |
2569 return -1; // before | 2793 return -1; // before |
2570 } else { | 2794 |
2571 return 1; // after | 2795 return 1; // after |
2572 } | 2796 } |
2573 } | 2797 |
2574 | 2798 // In the second case a child node C of the container of A is an ancestor |
2575 // In the second case a child node C of the container of A is an ancestor | 2799 // container of B. In this case, A is before B if the offset of A is less than or |
2576 // container of B. In this case, A is before B if the offset of A is less than or | |
2577 // equal to the index of the child node C and A is after B otherwise. | 2800 // equal to the index of the child node C and A is after B otherwise. |
2578 c = containerB; | 2801 c = containerB; |
2579 while (c && c.parentNode != containerA) { | 2802 while (c && c.parentNode != containerA) |
2580 c = c.parentNode; | 2803 c = c.parentNode; |
2581 } | 2804 |
2582 if (c) { | 2805 if (c) { |
2583 offsetC = 0; | 2806 offsetC = 0; |
2584 n = containerA.firstChild; | 2807 n = containerA.firstChild; |
2585 | 2808 |
2586 while (n != c && offsetC < offsetA) { | 2809 while (n != c && offsetC < offsetA) { |
2587 offsetC++; | 2810 offsetC++; |
2588 n = n.nextSibling; | 2811 n = n.nextSibling; |
2589 } | 2812 } |
2590 | 2813 |
2591 if (offsetA <= offsetC) { | 2814 if (offsetA <= offsetC) |
2592 return -1; // before | 2815 return -1; // before |
2593 } else { | 2816 |
2594 return 1; // after | 2817 return 1; // after |
2595 } | 2818 } |
2596 } | 2819 |
2597 | 2820 // In the third case a child node C of the container of B is an ancestor container |
2598 // In the third case a child node C of the container of B is an ancestor container | 2821 // of A. In this case, A is before B if the index of the child node C is less than |
2599 // of A. In this case, A is before B if the index of the child node C is less than | |
2600 // the offset of B and A is after B otherwise. | 2822 // the offset of B and A is after B otherwise. |
2601 c = containerA; | 2823 c = containerA; |
2602 while (c && c.parentNode != containerB) { | 2824 while (c && c.parentNode != containerB) { |
2603 c = c.parentNode; | 2825 c = c.parentNode; |
2604 } | 2826 } |
2610 while (n != c && offsetC < offsetB) { | 2832 while (n != c && offsetC < offsetB) { |
2611 offsetC++; | 2833 offsetC++; |
2612 n = n.nextSibling; | 2834 n = n.nextSibling; |
2613 } | 2835 } |
2614 | 2836 |
2615 if (offsetC < offsetB) { | 2837 if (offsetC < offsetB) |
2616 return -1; // before | 2838 return -1; // before |
2617 } else { | 2839 |
2618 return 1; // after | 2840 return 1; // after |
2619 } | 2841 } |
2620 } | 2842 |
2621 | 2843 // In the fourth case, none of three other cases hold: the containers of A and B |
2622 // In the fourth case, none of three other cases hold: the containers of A and B | 2844 // are siblings or descendants of sibling nodes. In this case, A is before B if |
2623 // are siblings or descendants of sibling nodes. In this case, A is before B if | |
2624 // the container of A is before the container of B in a pre-order traversal of the | 2845 // the container of A is before the container of B in a pre-order traversal of the |
2625 // Ranges' context tree and A is after B otherwise. | 2846 // Ranges' context tree and A is after B otherwise. |
2626 cmnRoot = this.dom.findCommonAncestor(containerA, containerB); | 2847 cmnRoot = dom.findCommonAncestor(containerA, containerB); |
2627 childA = containerA; | 2848 childA = containerA; |
2628 | 2849 |
2629 while (childA && childA.parentNode != cmnRoot) { | 2850 while (childA && childA.parentNode != cmnRoot) |
2630 childA = childA.parentNode; | 2851 childA = childA.parentNode; |
2631 } | 2852 |
2632 | 2853 if (!childA) |
2633 if (!childA) { | |
2634 childA = cmnRoot; | 2854 childA = cmnRoot; |
2635 } | |
2636 | 2855 |
2637 childB = containerB; | 2856 childB = containerB; |
2638 while (childB && childB.parentNode != cmnRoot) { | 2857 while (childB && childB.parentNode != cmnRoot) |
2639 childB = childB.parentNode; | 2858 childB = childB.parentNode; |
2640 } | 2859 |
2641 | 2860 if (!childB) |
2642 if (!childB) { | |
2643 childB = cmnRoot; | 2861 childB = cmnRoot; |
2644 } | 2862 |
2645 | 2863 if (childA == childB) |
2646 if (childA == childB) { | |
2647 return 0; // equal | 2864 return 0; // equal |
2648 } | |
2649 | 2865 |
2650 n = cmnRoot.firstChild; | 2866 n = cmnRoot.firstChild; |
2651 while (n) { | 2867 while (n) { |
2652 if (n == childA) { | 2868 if (n == childA) |
2653 return -1; // before | 2869 return -1; // before |
2654 } | 2870 |
2655 | 2871 if (n == childB) |
2656 if (n == childB) { | |
2657 return 1; // after | 2872 return 1; // after |
2658 } | |
2659 | 2873 |
2660 n = n.nextSibling; | 2874 n = n.nextSibling; |
2661 } | 2875 } |
2662 }, | 2876 }; |
2663 | 2877 |
2664 _setEndPoint : function(st, n, o) { | 2878 function _setEndPoint(st, n, o) { |
2665 var t = this, ec, sc; | 2879 var ec, sc; |
2666 | 2880 |
2667 if (st) { | 2881 if (st) { |
2668 t.startContainer = n; | 2882 t[START_CONTAINER] = n; |
2669 t.startOffset = o; | 2883 t[START_OFFSET] = o; |
2670 } else { | 2884 } else { |
2671 t.endContainer = n; | 2885 t[END_CONTAINER] = n; |
2672 t.endOffset = o; | 2886 t[END_OFFSET] = o; |
2673 } | 2887 } |
2674 | 2888 |
2675 // If one boundary-point of a Range is set to have a root container | 2889 // If one boundary-point of a Range is set to have a root container |
2676 // other than the current one for the Range, the Range is collapsed to | 2890 // other than the current one for the Range, the Range is collapsed to |
2677 // the new position. This enforces the restriction that both boundary- | 2891 // the new position. This enforces the restriction that both boundary- |
2678 // points of a Range must have the same root container. | 2892 // points of a Range must have the same root container. |
2679 ec = t.endContainer; | 2893 ec = t[END_CONTAINER]; |
2680 while (ec.parentNode) | 2894 while (ec.parentNode) |
2681 ec = ec.parentNode; | 2895 ec = ec.parentNode; |
2682 | 2896 |
2683 sc = t.startContainer; | 2897 sc = t[START_CONTAINER]; |
2684 while (sc.parentNode) | 2898 while (sc.parentNode) |
2685 sc = sc.parentNode; | 2899 sc = sc.parentNode; |
2686 | 2900 |
2687 if (sc != ec) { | 2901 if (sc == ec) { |
2902 // The start position of a Range is guaranteed to never be after the | |
2903 // end position. To enforce this restriction, if the start is set to | |
2904 // be at a position after the end, the Range is collapsed to that | |
2905 // position. | |
2906 if (_compareBoundaryPoints(t[START_CONTAINER], t[START_OFFSET], t[END_CONTAINER], t[END_OFFSET]) > 0) | |
2907 t.collapse(st); | |
2908 } else | |
2688 t.collapse(st); | 2909 t.collapse(st); |
2689 } else { | 2910 |
2690 // The start position of a Range is guaranteed to never be after the | 2911 t.collapsed = _isCollapsed(); |
2691 // end position. To enforce this restriction, if the start is set to | 2912 t.commonAncestorContainer = dom.findCommonAncestor(t[START_CONTAINER], t[END_CONTAINER]); |
2692 // be at a position after the end, the Range is collapsed to that | 2913 }; |
2693 // position. | 2914 |
2694 if (t._compareBoundaryPoints(t.startContainer, t.startOffset, t.endContainer, t.endOffset) > 0) | 2915 function _traverse(how) { |
2695 t.collapse(st); | 2916 var c, endContainerDepth = 0, startContainerDepth = 0, p, depthDiff, startNode, endNode, sp, ep; |
2696 } | 2917 |
2697 | 2918 if (t[START_CONTAINER] == t[END_CONTAINER]) |
2698 t.collapsed = t._isCollapsed(); | 2919 return _traverseSameContainer(how); |
2699 t.commonAncestorContainer = t.dom.findCommonAncestor(t.startContainer, t.endContainer); | 2920 |
2700 }, | 2921 for (c = t[END_CONTAINER], p = c.parentNode; p; c = p, p = p.parentNode) { |
2701 | 2922 if (p == t[START_CONTAINER]) |
2702 // This code is heavily "inspired" by the Apache Xerces implementation. I hope they don't mind. :) | 2923 return _traverseCommonStartContainer(c, how); |
2703 | |
2704 _traverse : function(how) { | |
2705 var t = this, c, endContainerDepth = 0, startContainerDepth = 0, p, depthDiff, startNode, endNode, sp, ep; | |
2706 | |
2707 if (t.startContainer == t.endContainer) | |
2708 return t._traverseSameContainer(how); | |
2709 | |
2710 for (c = t.endContainer, p = c.parentNode; p != null; c = p, p = p.parentNode) { | |
2711 if (p == t.startContainer) | |
2712 return t._traverseCommonStartContainer(c, how); | |
2713 | 2924 |
2714 ++endContainerDepth; | 2925 ++endContainerDepth; |
2715 } | 2926 } |
2716 | 2927 |
2717 for (c = t.startContainer, p = c.parentNode; p != null; c = p, p = p.parentNode) { | 2928 for (c = t[START_CONTAINER], p = c.parentNode; p; c = p, p = p.parentNode) { |
2718 if (p == t.endContainer) | 2929 if (p == t[END_CONTAINER]) |
2719 return t._traverseCommonEndContainer(c, how); | 2930 return _traverseCommonEndContainer(c, how); |
2720 | 2931 |
2721 ++startContainerDepth; | 2932 ++startContainerDepth; |
2722 } | 2933 } |
2723 | 2934 |
2724 depthDiff = startContainerDepth - endContainerDepth; | 2935 depthDiff = startContainerDepth - endContainerDepth; |
2725 | 2936 |
2726 startNode = t.startContainer; | 2937 startNode = t[START_CONTAINER]; |
2727 while (depthDiff > 0) { | 2938 while (depthDiff > 0) { |
2728 startNode = startNode.parentNode; | 2939 startNode = startNode.parentNode; |
2729 depthDiff--; | 2940 depthDiff--; |
2730 } | 2941 } |
2731 | 2942 |
2732 endNode = t.endContainer; | 2943 endNode = t[END_CONTAINER]; |
2733 while (depthDiff < 0) { | 2944 while (depthDiff < 0) { |
2734 endNode = endNode.parentNode; | 2945 endNode = endNode.parentNode; |
2735 depthDiff++; | 2946 depthDiff++; |
2736 } | 2947 } |
2737 | 2948 |
2739 for (sp = startNode.parentNode, ep = endNode.parentNode; sp != ep; sp = sp.parentNode, ep = ep.parentNode) { | 2950 for (sp = startNode.parentNode, ep = endNode.parentNode; sp != ep; sp = sp.parentNode, ep = ep.parentNode) { |
2740 startNode = sp; | 2951 startNode = sp; |
2741 endNode = ep; | 2952 endNode = ep; |
2742 } | 2953 } |
2743 | 2954 |
2744 return t._traverseCommonAncestors(startNode, endNode, how); | 2955 return _traverseCommonAncestors(startNode, endNode, how); |
2745 }, | 2956 }; |
2746 | 2957 |
2747 _traverseSameContainer : function(how) { | 2958 function _traverseSameContainer(how) { |
2748 var t = this, frag, s, sub, n, cnt, sibling, xferNode; | 2959 var frag, s, sub, n, cnt, sibling, xferNode; |
2749 | 2960 |
2750 if (how != DELETE) | 2961 if (how != DELETE) |
2751 frag = t.dom.doc.createDocumentFragment(); | 2962 frag = doc.createDocumentFragment(); |
2752 | 2963 |
2753 // If selection is empty, just return the fragment | 2964 // If selection is empty, just return the fragment |
2754 if (t.startOffset == t.endOffset) | 2965 if (t[START_OFFSET] == t[END_OFFSET]) |
2755 return frag; | 2966 return frag; |
2756 | 2967 |
2757 // Text node needs special case handling | 2968 // Text node needs special case handling |
2758 if (t.startContainer.nodeType == 3 /* TEXT_NODE */) { | 2969 if (t[START_CONTAINER].nodeType == 3 /* TEXT_NODE */) { |
2759 // get the substring | 2970 // get the substring |
2760 s = t.startContainer.nodeValue; | 2971 s = t[START_CONTAINER].nodeValue; |
2761 sub = s.substring(t.startOffset, t.endOffset); | 2972 sub = s.substring(t[START_OFFSET], t[END_OFFSET]); |
2762 | 2973 |
2763 // set the original text node to its new value | 2974 // set the original text node to its new value |
2764 if (how != CLONE) { | 2975 if (how != CLONE) { |
2765 t.startContainer.deleteData(t.startOffset, t.endOffset - t.startOffset); | 2976 t[START_CONTAINER].deleteData(t[START_OFFSET], t[END_OFFSET] - t[START_OFFSET]); |
2766 | 2977 |
2767 // Nothing is partially selected, so collapse to start point | 2978 // Nothing is partially selected, so collapse to start point |
2768 t.collapse(true); | 2979 t.collapse(TRUE); |
2769 } | 2980 } |
2770 | 2981 |
2771 if (how == DELETE) | 2982 if (how == DELETE) |
2772 return null; | 2983 return; |
2773 | 2984 |
2774 frag.appendChild(t.dom.doc.createTextNode(sub)); | 2985 frag.appendChild(doc.createTextNode(sub)); |
2775 return frag; | 2986 return frag; |
2776 } | 2987 } |
2777 | 2988 |
2778 // Copy nodes between the start/end offsets. | 2989 // Copy nodes between the start/end offsets. |
2779 n = getSelectedNode(t.startContainer, t.startOffset); | 2990 n = _getSelectedNode(t[START_CONTAINER], t[START_OFFSET]); |
2780 cnt = t.endOffset - t.startOffset; | 2991 cnt = t[END_OFFSET] - t[START_OFFSET]; |
2781 | 2992 |
2782 while (cnt > 0) { | 2993 while (cnt > 0) { |
2783 sibling = n.nextSibling; | 2994 sibling = n.nextSibling; |
2784 xferNode = t._traverseFullySelected(n, how); | 2995 xferNode = _traverseFullySelected(n, how); |
2785 | 2996 |
2786 if (frag) | 2997 if (frag) |
2787 frag.appendChild( xferNode ); | 2998 frag.appendChild( xferNode ); |
2788 | 2999 |
2789 --cnt; | 3000 --cnt; |
2790 n = sibling; | 3001 n = sibling; |
2791 } | 3002 } |
2792 | 3003 |
2793 // Nothing is partially selected, so collapse to start point | 3004 // Nothing is partially selected, so collapse to start point |
2794 if (how != CLONE) | 3005 if (how != CLONE) |
2795 t.collapse(true); | 3006 t.collapse(TRUE); |
2796 | 3007 |
2797 return frag; | 3008 return frag; |
2798 }, | 3009 }; |
2799 | 3010 |
2800 _traverseCommonStartContainer : function(endAncestor, how) { | 3011 function _traverseCommonStartContainer(endAncestor, how) { |
2801 var t = this, frag, n, endIdx, cnt, sibling, xferNode; | 3012 var frag, n, endIdx, cnt, sibling, xferNode; |
2802 | 3013 |
2803 if (how != DELETE) | 3014 if (how != DELETE) |
2804 frag = t.dom.doc.createDocumentFragment(); | 3015 frag = doc.createDocumentFragment(); |
2805 | 3016 |
2806 n = t._traverseRightBoundary(endAncestor, how); | 3017 n = _traverseRightBoundary(endAncestor, how); |
2807 | 3018 |
2808 if (frag) | 3019 if (frag) |
2809 frag.appendChild(n); | 3020 frag.appendChild(n); |
2810 | 3021 |
2811 endIdx = indexOf(endAncestor, t.startContainer); | 3022 endIdx = nodeIndex(endAncestor); |
2812 cnt = endIdx - t.startOffset; | 3023 cnt = endIdx - t[START_OFFSET]; |
2813 | 3024 |
2814 if (cnt <= 0) { | 3025 if (cnt <= 0) { |
2815 // Collapse to just before the endAncestor, which | 3026 // Collapse to just before the endAncestor, which |
2816 // is partially selected. | 3027 // is partially selected. |
2817 if (how != CLONE) { | 3028 if (how != CLONE) { |
2818 t.setEndBefore(endAncestor); | 3029 t.setEndBefore(endAncestor); |
2819 t.collapse(false); | 3030 t.collapse(FALSE); |
2820 } | 3031 } |
2821 | 3032 |
2822 return frag; | 3033 return frag; |
2823 } | 3034 } |
2824 | 3035 |
2825 n = endAncestor.previousSibling; | 3036 n = endAncestor.previousSibling; |
2826 while (cnt > 0) { | 3037 while (cnt > 0) { |
2827 sibling = n.previousSibling; | 3038 sibling = n.previousSibling; |
2828 xferNode = t._traverseFullySelected(n, how); | 3039 xferNode = _traverseFullySelected(n, how); |
2829 | 3040 |
2830 if (frag) | 3041 if (frag) |
2831 frag.insertBefore(xferNode, frag.firstChild); | 3042 frag.insertBefore(xferNode, frag.firstChild); |
2832 | 3043 |
2833 --cnt; | 3044 --cnt; |
2834 n = sibling; | 3045 n = sibling; |
2835 } | 3046 } |
2836 | 3047 |
2837 // Collapse to just before the endAncestor, which | 3048 // Collapse to just before the endAncestor, which |
2838 // is partially selected. | 3049 // is partially selected. |
2839 if (how != CLONE) { | 3050 if (how != CLONE) { |
2840 t.setEndBefore(endAncestor); | 3051 t.setEndBefore(endAncestor); |
2841 t.collapse(false); | 3052 t.collapse(FALSE); |
2842 } | 3053 } |
2843 | 3054 |
2844 return frag; | 3055 return frag; |
2845 }, | 3056 }; |
2846 | 3057 |
2847 _traverseCommonEndContainer : function(startAncestor, how) { | 3058 function _traverseCommonEndContainer(startAncestor, how) { |
2848 var t = this, frag, startIdx, n, cnt, sibling, xferNode; | 3059 var frag, startIdx, n, cnt, sibling, xferNode; |
2849 | 3060 |
2850 if (how != DELETE) | 3061 if (how != DELETE) |
2851 frag = t.dom.doc.createDocumentFragment(); | 3062 frag = doc.createDocumentFragment(); |
2852 | 3063 |
2853 n = t._traverseLeftBoundary(startAncestor, how); | 3064 n = _traverseLeftBoundary(startAncestor, how); |
2854 if (frag) | 3065 if (frag) |
2855 frag.appendChild(n); | 3066 frag.appendChild(n); |
2856 | 3067 |
2857 startIdx = indexOf(startAncestor, t.endContainer); | 3068 startIdx = nodeIndex(startAncestor); |
2858 ++startIdx; // Because we already traversed it.... | 3069 ++startIdx; // Because we already traversed it.... |
2859 | 3070 |
2860 cnt = t.endOffset - startIdx; | 3071 cnt = t[END_OFFSET] - startIdx; |
2861 n = startAncestor.nextSibling; | 3072 n = startAncestor.nextSibling; |
2862 while (cnt > 0) { | 3073 while (cnt > 0) { |
2863 sibling = n.nextSibling; | 3074 sibling = n.nextSibling; |
2864 xferNode = t._traverseFullySelected(n, how); | 3075 xferNode = _traverseFullySelected(n, how); |
2865 | 3076 |
2866 if (frag) | 3077 if (frag) |
2867 frag.appendChild(xferNode); | 3078 frag.appendChild(xferNode); |
2868 | 3079 |
2869 --cnt; | 3080 --cnt; |
2870 n = sibling; | 3081 n = sibling; |
2871 } | 3082 } |
2872 | 3083 |
2873 if (how != CLONE) { | 3084 if (how != CLONE) { |
2874 t.setStartAfter(startAncestor); | 3085 t.setStartAfter(startAncestor); |
2875 t.collapse(true); | 3086 t.collapse(TRUE); |
2876 } | 3087 } |
2877 | 3088 |
2878 return frag; | 3089 return frag; |
2879 }, | 3090 }; |
2880 | 3091 |
2881 _traverseCommonAncestors : function(startAncestor, endAncestor, how) { | 3092 function _traverseCommonAncestors(startAncestor, endAncestor, how) { |
2882 var t = this, n, frag, commonParent, startOffset, endOffset, cnt, sibling, nextSibling; | 3093 var n, frag, commonParent, startOffset, endOffset, cnt, sibling, nextSibling; |
2883 | 3094 |
2884 if (how != DELETE) | 3095 if (how != DELETE) |
2885 frag = t.dom.doc.createDocumentFragment(); | 3096 frag = doc.createDocumentFragment(); |
2886 | 3097 |
2887 n = t._traverseLeftBoundary(startAncestor, how); | 3098 n = _traverseLeftBoundary(startAncestor, how); |
2888 if (frag) | 3099 if (frag) |
2889 frag.appendChild(n); | 3100 frag.appendChild(n); |
2890 | 3101 |
2891 commonParent = startAncestor.parentNode; | 3102 commonParent = startAncestor.parentNode; |
2892 startOffset = indexOf(startAncestor, commonParent); | 3103 startOffset = nodeIndex(startAncestor); |
2893 endOffset = indexOf(endAncestor, commonParent); | 3104 endOffset = nodeIndex(endAncestor); |
2894 ++startOffset; | 3105 ++startOffset; |
2895 | 3106 |
2896 cnt = endOffset - startOffset; | 3107 cnt = endOffset - startOffset; |
2897 sibling = startAncestor.nextSibling; | 3108 sibling = startAncestor.nextSibling; |
2898 | 3109 |
2899 while (cnt > 0) { | 3110 while (cnt > 0) { |
2900 nextSibling = sibling.nextSibling; | 3111 nextSibling = sibling.nextSibling; |
2901 n = t._traverseFullySelected(sibling, how); | 3112 n = _traverseFullySelected(sibling, how); |
2902 | 3113 |
2903 if (frag) | 3114 if (frag) |
2904 frag.appendChild(n); | 3115 frag.appendChild(n); |
2905 | 3116 |
2906 sibling = nextSibling; | 3117 sibling = nextSibling; |
2907 --cnt; | 3118 --cnt; |
2908 } | 3119 } |
2909 | 3120 |
2910 n = t._traverseRightBoundary(endAncestor, how); | 3121 n = _traverseRightBoundary(endAncestor, how); |
2911 | 3122 |
2912 if (frag) | 3123 if (frag) |
2913 frag.appendChild(n); | 3124 frag.appendChild(n); |
2914 | 3125 |
2915 if (how != CLONE) { | 3126 if (how != CLONE) { |
2916 t.setStartAfter(startAncestor); | 3127 t.setStartAfter(startAncestor); |
2917 t.collapse(true); | 3128 t.collapse(TRUE); |
2918 } | 3129 } |
2919 | 3130 |
2920 return frag; | 3131 return frag; |
2921 }, | 3132 }; |
2922 | 3133 |
2923 _traverseRightBoundary : function(root, how) { | 3134 function _traverseRightBoundary(root, how) { |
2924 var t = this, next = getSelectedNode(t.endContainer, t.endOffset - 1), parent, clonedParent, prevSibling, clonedChild, clonedGrandParent; | 3135 var next = _getSelectedNode(t[END_CONTAINER], t[END_OFFSET] - 1), parent, clonedParent, prevSibling, clonedChild, clonedGrandParent, isFullySelected = next != t[END_CONTAINER]; |
2925 var isFullySelected = next != t.endContainer; | |
2926 | 3136 |
2927 if (next == root) | 3137 if (next == root) |
2928 return t._traverseNode(next, isFullySelected, false, how); | 3138 return _traverseNode(next, isFullySelected, FALSE, how); |
2929 | 3139 |
2930 parent = next.parentNode; | 3140 parent = next.parentNode; |
2931 clonedParent = t._traverseNode(parent, false, false, how); | 3141 clonedParent = _traverseNode(parent, FALSE, FALSE, how); |
2932 | 3142 |
2933 while (parent != null) { | 3143 while (parent) { |
2934 while (next != null) { | 3144 while (next) { |
2935 prevSibling = next.previousSibling; | 3145 prevSibling = next.previousSibling; |
2936 clonedChild = t._traverseNode(next, isFullySelected, false, how); | 3146 clonedChild = _traverseNode(next, isFullySelected, FALSE, how); |
2937 | 3147 |
2938 if (how != DELETE) | 3148 if (how != DELETE) |
2939 clonedParent.insertBefore(clonedChild, clonedParent.firstChild); | 3149 clonedParent.insertBefore(clonedChild, clonedParent.firstChild); |
2940 | 3150 |
2941 isFullySelected = true; | 3151 isFullySelected = TRUE; |
2942 next = prevSibling; | 3152 next = prevSibling; |
2943 } | 3153 } |
2944 | 3154 |
2945 if (parent == root) | 3155 if (parent == root) |
2946 return clonedParent; | 3156 return clonedParent; |
2947 | 3157 |
2948 next = parent.previousSibling; | 3158 next = parent.previousSibling; |
2949 parent = parent.parentNode; | 3159 parent = parent.parentNode; |
2950 | 3160 |
2951 clonedGrandParent = t._traverseNode(parent, false, false, how); | 3161 clonedGrandParent = _traverseNode(parent, FALSE, FALSE, how); |
2952 | 3162 |
2953 if (how != DELETE) | 3163 if (how != DELETE) |
2954 clonedGrandParent.appendChild(clonedParent); | 3164 clonedGrandParent.appendChild(clonedParent); |
2955 | 3165 |
2956 clonedParent = clonedGrandParent; | 3166 clonedParent = clonedGrandParent; |
2957 } | 3167 } |
2958 | 3168 }; |
2959 // should never occur | 3169 |
2960 return null; | 3170 function _traverseLeftBoundary(root, how) { |
2961 }, | 3171 var next = _getSelectedNode(t[START_CONTAINER], t[START_OFFSET]), isFullySelected = next != t[START_CONTAINER], parent, clonedParent, nextSibling, clonedChild, clonedGrandParent; |
2962 | |
2963 _traverseLeftBoundary : function(root, how) { | |
2964 var t = this, next = getSelectedNode(t.startContainer, t.startOffset); | |
2965 var isFullySelected = next != t.startContainer, parent, clonedParent, nextSibling, clonedChild, clonedGrandParent; | |
2966 | 3172 |
2967 if (next == root) | 3173 if (next == root) |
2968 return t._traverseNode(next, isFullySelected, true, how); | 3174 return _traverseNode(next, isFullySelected, TRUE, how); |
2969 | 3175 |
2970 parent = next.parentNode; | 3176 parent = next.parentNode; |
2971 clonedParent = t._traverseNode(parent, false, true, how); | 3177 clonedParent = _traverseNode(parent, FALSE, TRUE, how); |
2972 | 3178 |
2973 while (parent != null) { | 3179 while (parent) { |
2974 while (next != null) { | 3180 while (next) { |
2975 nextSibling = next.nextSibling; | 3181 nextSibling = next.nextSibling; |
2976 clonedChild = t._traverseNode(next, isFullySelected, true, how); | 3182 clonedChild = _traverseNode(next, isFullySelected, TRUE, how); |
2977 | 3183 |
2978 if (how != DELETE) | 3184 if (how != DELETE) |
2979 clonedParent.appendChild(clonedChild); | 3185 clonedParent.appendChild(clonedChild); |
2980 | 3186 |
2981 isFullySelected = true; | 3187 isFullySelected = TRUE; |
2982 next = nextSibling; | 3188 next = nextSibling; |
2983 } | 3189 } |
2984 | 3190 |
2985 if (parent == root) | 3191 if (parent == root) |
2986 return clonedParent; | 3192 return clonedParent; |
2987 | 3193 |
2988 next = parent.nextSibling; | 3194 next = parent.nextSibling; |
2989 parent = parent.parentNode; | 3195 parent = parent.parentNode; |
2990 | 3196 |
2991 clonedGrandParent = t._traverseNode(parent, false, true, how); | 3197 clonedGrandParent = _traverseNode(parent, FALSE, TRUE, how); |
2992 | 3198 |
2993 if (how != DELETE) | 3199 if (how != DELETE) |
2994 clonedGrandParent.appendChild(clonedParent); | 3200 clonedGrandParent.appendChild(clonedParent); |
2995 | 3201 |
2996 clonedParent = clonedGrandParent; | 3202 clonedParent = clonedGrandParent; |
2997 } | 3203 } |
2998 | 3204 }; |
2999 // should never occur | 3205 |
3000 return null; | 3206 function _traverseNode(n, isFullySelected, isLeft, how) { |
3001 }, | 3207 var txtValue, newNodeValue, oldNodeValue, offset, newNode; |
3002 | |
3003 _traverseNode : function(n, isFullySelected, isLeft, how) { | |
3004 var t = this, txtValue, newNodeValue, oldNodeValue, offset, newNode; | |
3005 | 3208 |
3006 if (isFullySelected) | 3209 if (isFullySelected) |
3007 return t._traverseFullySelected(n, how); | 3210 return _traverseFullySelected(n, how); |
3008 | 3211 |
3009 if (n.nodeType == 3 /* TEXT_NODE */) { | 3212 if (n.nodeType == 3 /* TEXT_NODE */) { |
3010 txtValue = n.nodeValue; | 3213 txtValue = n.nodeValue; |
3011 | 3214 |
3012 if (isLeft) { | 3215 if (isLeft) { |
3013 offset = t.startOffset; | 3216 offset = t[START_OFFSET]; |
3014 newNodeValue = txtValue.substring(offset); | 3217 newNodeValue = txtValue.substring(offset); |
3015 oldNodeValue = txtValue.substring(0, offset); | 3218 oldNodeValue = txtValue.substring(0, offset); |
3016 } else { | 3219 } else { |
3017 offset = t.endOffset; | 3220 offset = t[END_OFFSET]; |
3018 newNodeValue = txtValue.substring(0, offset); | 3221 newNodeValue = txtValue.substring(0, offset); |
3019 oldNodeValue = txtValue.substring(offset); | 3222 oldNodeValue = txtValue.substring(offset); |
3020 } | 3223 } |
3021 | 3224 |
3022 if (how != CLONE) | 3225 if (how != CLONE) |
3023 n.nodeValue = oldNodeValue; | 3226 n.nodeValue = oldNodeValue; |
3024 | 3227 |
3025 if (how == DELETE) | 3228 if (how == DELETE) |
3026 return null; | 3229 return; |
3027 | 3230 |
3028 newNode = n.cloneNode(false); | 3231 newNode = n.cloneNode(FALSE); |
3029 newNode.nodeValue = newNodeValue; | 3232 newNode.nodeValue = newNodeValue; |
3030 | 3233 |
3031 return newNode; | 3234 return newNode; |
3032 } | 3235 } |
3033 | 3236 |
3034 if (how == DELETE) | 3237 if (how == DELETE) |
3035 return null; | 3238 return; |
3036 | 3239 |
3037 return n.cloneNode(false); | 3240 return n.cloneNode(FALSE); |
3038 }, | 3241 }; |
3039 | 3242 |
3040 _traverseFullySelected : function(n, how) { | 3243 function _traverseFullySelected(n, how) { |
3041 var t = this; | |
3042 | |
3043 if (how != DELETE) | 3244 if (how != DELETE) |
3044 return how == CLONE ? n.cloneNode(true) : n; | 3245 return how == CLONE ? n.cloneNode(TRUE) : n; |
3045 | 3246 |
3046 n.parentNode.removeChild(n); | 3247 n.parentNode.removeChild(n); |
3047 return null; | 3248 }; |
3048 } | 3249 }; |
3049 }); | |
3050 | 3250 |
3051 ns.Range = Range; | 3251 ns.Range = Range; |
3052 })(tinymce.dom); | 3252 })(tinymce.dom); |
3253 | |
3053 (function() { | 3254 (function() { |
3054 function Selection(selection) { | 3255 function Selection(selection) { |
3055 var t = this; | 3256 var t = this, invisibleChar = '\uFEFF', range, lastIERng, dom = selection.dom, TRUE = true, FALSE = false; |
3056 | 3257 |
3258 // Compares two IE specific ranges to see if they are different | |
3259 // this method is useful when invalidating the cached selection range | |
3260 function compareRanges(rng1, rng2) { | |
3261 if (rng1 && rng2) { | |
3262 // Both are control ranges and the selected element matches | |
3263 if (rng1.item && rng2.item && rng1.item(0) === rng2.item(0)) | |
3264 return TRUE; | |
3265 | |
3266 // Both are text ranges and the range matches | |
3267 if (rng1.isEqual && rng2.isEqual && rng2.isEqual(rng1)) { | |
3268 // IE will say that the range is equal then produce an invalid argument exception | |
3269 // if you perform specific operations in a keyup event. For example Ctrl+Del. | |
3270 // This hack will invalidate the range cache if the exception occurs | |
3271 try { | |
3272 // Try accessing nextSibling will producer an invalid argument some times | |
3273 range.startContainer.nextSibling; | |
3274 return TRUE; | |
3275 } catch (ex) { | |
3276 // Ignore | |
3277 } | |
3278 } | |
3279 } | |
3280 | |
3281 return FALSE; | |
3282 }; | |
3283 | |
3284 // Returns a W3C DOM compatible range object by using the IE Range API | |
3057 function getRange() { | 3285 function getRange() { |
3058 var dom = selection.dom, ieRange = selection.getRng(), domRange = dom.createRng(), startPos = {}, endPos = {}; | 3286 var ieRange = selection.getRng(), domRange = dom.createRng(), ieRange2, element, collapsed, isMerged; |
3059 | 3287 |
3060 // Handle control selection | 3288 // If selection is outside the current document just return an empty range |
3061 if (ieRange.item) { | 3289 element = ieRange.item ? ieRange.item(0) : ieRange.parentElement(); |
3062 domRange.setStartBefore(ieRange.item(0)); | 3290 if (element.ownerDocument != dom.doc) |
3063 domRange.setEndAfter(ieRange.item(0)); | |
3064 | |
3065 return domRange; | 3291 return domRange; |
3066 } | 3292 |
3067 | 3293 // Handle control selection or text selection of a image |
3068 function findEndPoint(ie_rng, start, pos) { | 3294 if (ieRange.item || !element.hasChildNodes()) { |
3069 var rng, rng2, startElement; | 3295 domRange.setStart(element.parentNode, dom.nodeIndex(element)); |
3070 | 3296 domRange.setEnd(domRange.startContainer, domRange.startOffset + 1); |
3071 rng = ie_rng.duplicate(); | 3297 |
3072 rng.collapse(start); | 3298 return domRange; |
3073 element = rng.parentElement(); | 3299 } |
3074 | 3300 |
3075 // If element is block then we need to move one character | 3301 // Duplicare IE selection range and check if the range is collapsed |
3076 // since the selection has a extra invisible character | 3302 ieRange2 = ieRange.duplicate(); |
3077 if (element.currentStyle.display == 'block') { | 3303 collapsed = selection.isCollapsed(); |
3078 rng = ie_rng.duplicate(); | 3304 |
3079 rng2 = ie_rng.duplicate(); | 3305 // Insert invisible start marker |
3080 | 3306 ieRange.collapse(); |
3081 // Move one character at beginning/end of selection | 3307 ieRange.pasteHTML('<span id="_mce_start" style="display:none;line-height:0">' + invisibleChar + '</span>'); |
3082 if (start) | 3308 |
3083 rng.moveStart('character', 1); | 3309 // Insert invisible end marker |
3084 else | 3310 if (!collapsed) { |
3085 rng.moveEnd('character', -1); | 3311 ieRange2.collapse(FALSE); |
3086 | 3312 ieRange2.pasteHTML('<span id="_mce_end" style="display:none;line-height:0">' + invisibleChar + '</span>'); |
3087 // The range shouldn't have been changed so lets restore it | 3313 } |
3088 if (rng.text != rng2.text) | 3314 |
3089 rng = rng2; | 3315 // Sets the end point of the range by looking for the marker |
3090 | 3316 // This method also merges the text nodes it splits so that |
3091 rng.collapse(start); | 3317 // the DOM doesn't get fragmented. |
3092 element = rng.parentElement(); | 3318 function setEndPoint(start) { |
3093 } | 3319 var container, offset, marker, sibling; |
3094 | 3320 |
3095 pos.parent = element; | 3321 // Look for endpoint marker |
3096 pos.range = rng; | 3322 marker = dom.get('_mce_' + (start ? 'start' : 'end')); |
3323 sibling = marker.previousSibling; | |
3324 | |
3325 // Is marker after a text node | |
3326 if (sibling && sibling.nodeType == 3) { | |
3327 // Get container node and calc offset | |
3328 container = sibling; | |
3329 offset = container.nodeValue.length; | |
3330 dom.remove(marker); | |
3331 | |
3332 // Merge text nodes to reduce DOM fragmentation | |
3333 sibling = container.nextSibling; | |
3334 if (sibling && sibling.nodeType == 3) { | |
3335 isMerged = TRUE; | |
3336 container.appendData(sibling.nodeValue); | |
3337 dom.remove(sibling); | |
3338 } | |
3339 } else { | |
3340 sibling = marker.nextSibling; | |
3341 | |
3342 // Is marker before a text node | |
3343 if (sibling && sibling.nodeType == 3) { | |
3344 container = sibling; | |
3345 offset = 0; | |
3346 } else { | |
3347 // Is marker before an element | |
3348 if (sibling) | |
3349 offset = dom.nodeIndex(sibling) - 1; | |
3350 else | |
3351 offset = dom.nodeIndex(marker); | |
3352 | |
3353 container = marker.parentNode; | |
3354 } | |
3355 | |
3356 dom.remove(marker); | |
3357 } | |
3358 | |
3359 // Set start of range | |
3360 if (start) | |
3361 domRange.setStart(container, offset); | |
3362 | |
3363 // Set end of range or automatically if it's collapsed to increase performance | |
3364 if (!start || collapsed) | |
3365 domRange.setEnd(container, offset); | |
3097 }; | 3366 }; |
3098 | 3367 |
3099 function findIndexAndOffset(pos) { | 3368 // Set start of range |
3100 var rng = pos.range, i, nl, marker, sibling, idx = 0; | 3369 setEndPoint(TRUE); |
3101 | 3370 |
3102 // Set parent and offset | 3371 // Set end of range if needed |
3103 pos.offset = 0; | 3372 if (!collapsed) |
3104 pos.parent = rng.parentElement(); | 3373 setEndPoint(FALSE); |
3105 | 3374 |
3106 // Insert marker | 3375 // Restore selection if the range contents was merged |
3107 rng.pasteHTML('<span id="_mce"></span>'); | 3376 // since the selection was then moved since the text nodes got changed |
3108 marker = dom.get('_mce'); | 3377 if (isMerged) |
3109 | 3378 t.addRange(domRange); |
3110 // Find the makers node index excluding text node fragmentation | |
3111 nl = pos.parent.childNodes; | |
3112 for (i = 0; i < nl.length; i++) { | |
3113 if (nl[i] == marker) { | |
3114 pos.index = idx; | |
3115 break; | |
3116 } | |
3117 | |
3118 if (i > 0 && (nl[i].nodeType != 3 || nl[i - 1].nodeType != 3)) | |
3119 idx++; | |
3120 } | |
3121 | |
3122 // Figure out the character offset excluding text node fragmentation | |
3123 sibling = marker.previousSibling; | |
3124 if (sibling) { | |
3125 if (sibling.nodeType === 3) { | |
3126 do { | |
3127 pos.offset += sibling.nodeValue.length; | |
3128 } while ((sibling = sibling.previousSibling) && sibling.nodeType == 3); | |
3129 } else | |
3130 pos.index++; | |
3131 } | |
3132 | |
3133 // Remove the marker | |
3134 dom.remove(marker); | |
3135 | |
3136 return pos; | |
3137 }; | |
3138 | |
3139 // Find end points | |
3140 findEndPoint(ieRange, true, startPos); | |
3141 findEndPoint(ieRange, false, endPos); | |
3142 | |
3143 // Find start and end positions | |
3144 findIndexAndOffset(startPos); | |
3145 findIndexAndOffset(endPos); | |
3146 | |
3147 // Normalize the elements to avoid fragmented dom | |
3148 startPos.parent.normalize(); | |
3149 endPos.parent.normalize(); | |
3150 | |
3151 // Set start and end points of the domRange | |
3152 domRange.setStart(startPos.parent.childNodes[startPos.index], startPos.offset); | |
3153 domRange.setEnd(endPos.parent.childNodes[endPos.index], endPos.offset); | |
3154 | |
3155 // Restore selection to new range | |
3156 t.addRange(domRange); | |
3157 | 3379 |
3158 return domRange; | 3380 return domRange; |
3159 }; | 3381 }; |
3160 | 3382 |
3161 this.addRange = function(rng) { | 3383 this.addRange = function(rng) { |
3162 var ieRng, startPos, endPos, body = selection.dom.doc.body; | 3384 var ieRng, ieRng2, doc = selection.dom.doc, body = doc.body, startPos, endPos, sc, so, ec, eo, marker, lastIndex, skipStart, skipEnd; |
3163 | 3385 |
3164 // Element selection, then make a control range | 3386 this.destroy(); |
3165 if (rng.startContainer.nodeType == 1) { | 3387 |
3166 ieRng = body.createControlRange(); | 3388 // Setup some shorter versions |
3167 ieRng.addElement(rng.startContainer.childNodes[rng.startOffset]); | 3389 sc = rng.startContainer; |
3390 so = rng.startOffset; | |
3391 ec = rng.endContainer; | |
3392 eo = rng.endOffset; | |
3393 ieRng = body.createTextRange(); | |
3394 | |
3395 // If document selection move caret to first node in document | |
3396 if (sc == doc || ec == doc) { | |
3397 ieRng = body.createTextRange(); | |
3398 ieRng.collapse(); | |
3399 ieRng.select(); | |
3168 return; | 3400 return; |
3169 } | 3401 } |
3170 | 3402 |
3171 function findPos(start) { | 3403 // If child index resolve it |
3172 var container, offset, rng2, pos; | 3404 if (sc.nodeType == 1 && sc.hasChildNodes()) { |
3173 | 3405 lastIndex = sc.childNodes.length - 1; |
3174 // Get container and offset | 3406 |
3175 container = start ? rng.startContainer : rng.endContainer; | 3407 // Index is higher that the child count then we need to jump over the start container |
3176 offset = start ? rng.startOffset : rng.endOffset; | 3408 if (so > lastIndex) { |
3177 | 3409 skipStart = 1; |
3178 // Insert marker character | 3410 sc = sc.childNodes[lastIndex]; |
3179 container.nodeValue = container.nodeValue.substring(0, offset) + '\uFEFF' + container.nodeValue.substring(offset); | 3411 } else |
3180 | 3412 sc = sc.childNodes[so]; |
3181 // Create range for whole parent element | 3413 |
3182 rng2 = body.createTextRange(); | 3414 // Child was text node then move offset to start of it |
3183 rng2.moveToElementText(container.parentNode); | 3415 if (sc.nodeType == 3) |
3184 pos = rng2.text.indexOf('\uFEFF'); | 3416 so = 0; |
3185 container.nodeValue = container.nodeValue.replace(/\uFEFF/, ''); | 3417 } |
3186 | 3418 |
3187 if (start) | 3419 // If child index resolve it |
3188 startPos = pos; | 3420 if (ec.nodeType == 1 && ec.hasChildNodes()) { |
3421 lastIndex = ec.childNodes.length - 1; | |
3422 | |
3423 if (eo == 0) { | |
3424 skipEnd = 1; | |
3425 ec = ec.childNodes[0]; | |
3426 } else { | |
3427 ec = ec.childNodes[Math.min(lastIndex, eo - 1)]; | |
3428 | |
3429 // Child was text node then move offset to end of text node | |
3430 if (ec.nodeType == 3) | |
3431 eo = ec.nodeValue.length; | |
3432 } | |
3433 } | |
3434 | |
3435 // Single element selection | |
3436 if (sc == ec && sc.nodeType == 1) { | |
3437 // Make control selection for some elements | |
3438 if (/^(IMG|TABLE)$/.test(sc.nodeName) && so != eo) { | |
3439 ieRng = body.createControlRange(); | |
3440 ieRng.addElement(sc); | |
3441 } else { | |
3442 ieRng = body.createTextRange(); | |
3443 | |
3444 // Padd empty elements with invisible character | |
3445 if (!sc.hasChildNodes() && sc.canHaveHTML) | |
3446 sc.innerHTML = invisibleChar; | |
3447 | |
3448 // Select element contents | |
3449 ieRng.moveToElementText(sc); | |
3450 | |
3451 // If it's only containing a padding remove it so the caret remains | |
3452 if (sc.innerHTML == invisibleChar) { | |
3453 ieRng.collapse(TRUE); | |
3454 sc.removeChild(sc.firstChild); | |
3455 } | |
3456 } | |
3457 | |
3458 if (so == eo) | |
3459 ieRng.collapse(eo <= rng.endContainer.childNodes.length - 1); | |
3460 | |
3461 ieRng.select(); | |
3462 ieRng.scrollIntoView(); | |
3463 return; | |
3464 } | |
3465 | |
3466 // Create range and marker | |
3467 ieRng = body.createTextRange(); | |
3468 marker = doc.createElement('span'); | |
3469 marker.innerHTML = ' '; | |
3470 | |
3471 // Set start of range to startContainer/startOffset | |
3472 if (sc.nodeType == 3) { | |
3473 // Insert marker after/before startContainer | |
3474 if (skipStart) | |
3475 dom.insertAfter(marker, sc); | |
3189 else | 3476 else |
3190 endPos = pos; | 3477 sc.parentNode.insertBefore(marker, sc); |
3191 }; | 3478 |
3192 | 3479 // Select marker the caret to offset position |
3193 function setPos(start) { | 3480 ieRng.moveToElementText(marker); |
3194 var rng2, container = start ? rng.startContainer : rng.endContainer; | 3481 marker.parentNode.removeChild(marker); |
3195 | 3482 ieRng.move('character', so); |
3196 rng2 = body.createTextRange(); | 3483 } else { |
3197 rng2.moveToElementText(container.parentNode); | 3484 ieRng.moveToElementText(sc); |
3198 rng2.collapse(true); | 3485 |
3199 rng2.move('character', start ? startPos : endPos); | 3486 if (skipStart) |
3200 | 3487 ieRng.collapse(FALSE); |
3201 if (start) | 3488 } |
3202 ieRng.setEndPoint('StartToStart', rng2); | 3489 |
3203 else | 3490 // If same text container then we can do a more simple move |
3204 ieRng.setEndPoint('EndToStart', rng2); | 3491 if (sc == ec && sc.nodeType == 3) { |
3205 }; | 3492 ieRng.moveEnd('character', eo - so); |
3206 | 3493 ieRng.select(); |
3207 // Create IE specific range | 3494 ieRng.scrollIntoView(); |
3208 ieRng = body.createTextRange(); | 3495 return; |
3209 | 3496 } |
3210 // Find start/end pos | 3497 |
3211 findPos(true); | 3498 // Set end of range to endContainer/endOffset |
3212 findPos(false); | 3499 ieRng2 = body.createTextRange(); |
3213 | 3500 if (ec.nodeType == 3) { |
3214 // Set start/end pos | 3501 // Insert marker after/before startContainer |
3215 setPos(true); | 3502 ec.parentNode.insertBefore(marker, ec); |
3216 setPos(false); | 3503 |
3504 // Move selection to end marker and move caret to end offset | |
3505 ieRng2.moveToElementText(marker); | |
3506 marker.parentNode.removeChild(marker); | |
3507 ieRng2.move('character', eo); | |
3508 ieRng.setEndPoint('EndToStart', ieRng2); | |
3509 } else { | |
3510 ieRng2.moveToElementText(ec); | |
3511 ieRng2.collapse(!!skipEnd); | |
3512 ieRng.setEndPoint('EndToEnd', ieRng2); | |
3513 } | |
3217 | 3514 |
3218 ieRng.select(); | 3515 ieRng.select(); |
3516 ieRng.scrollIntoView(); | |
3219 }; | 3517 }; |
3220 | 3518 |
3221 this.getRangeAt = function() { | 3519 this.getRangeAt = function() { |
3222 // todo: Implement range caching here later | 3520 // Setup new range if the cache is empty |
3223 return getRange(); | 3521 if (!range || !compareRanges(lastIERng, selection.getRng())) { |
3522 range = getRange(); | |
3523 | |
3524 // Store away text range for next call | |
3525 lastIERng = selection.getRng(); | |
3526 } | |
3527 | |
3528 // Return cached range | |
3529 return range; | |
3224 }; | 3530 }; |
3531 | |
3532 this.destroy = function() { | |
3533 // Destroy cached range and last IE range to avoid memory leaks | |
3534 lastIERng = range = null; | |
3535 }; | |
3536 | |
3537 // IE has an issue where you can't select/move the caret by clicking outside the body if the document is in standards mode | |
3538 if (selection.dom.boxModel) { | |
3539 (function() { | |
3540 var doc = dom.doc, body = doc.body, started, startRng; | |
3541 | |
3542 // Make HTML element unselectable since we are going to handle selection by hand | |
3543 doc.documentElement.unselectable = TRUE; | |
3544 | |
3545 // Return range from point or null if it failed | |
3546 function rngFromPoint(x, y) { | |
3547 var rng = body.createTextRange(); | |
3548 | |
3549 try { | |
3550 rng.moveToPoint(x, y); | |
3551 } catch (ex) { | |
3552 // IE sometimes throws and exception, so lets just ignore it | |
3553 rng = null; | |
3554 } | |
3555 | |
3556 return rng; | |
3557 }; | |
3558 | |
3559 // Fires while the selection is changing | |
3560 function selectionChange(e) { | |
3561 var pointRng; | |
3562 | |
3563 // Check if the button is down or not | |
3564 if (e.button) { | |
3565 // Create range from mouse position | |
3566 pointRng = rngFromPoint(e.x, e.y); | |
3567 | |
3568 if (pointRng) { | |
3569 // Check if pointRange is before/after selection then change the endPoint | |
3570 if (pointRng.compareEndPoints('StartToStart', startRng) > 0) | |
3571 pointRng.setEndPoint('StartToStart', startRng); | |
3572 else | |
3573 pointRng.setEndPoint('EndToEnd', startRng); | |
3574 | |
3575 pointRng.select(); | |
3576 } | |
3577 } else | |
3578 endSelection(); | |
3579 } | |
3580 | |
3581 // Removes listeners | |
3582 function endSelection() { | |
3583 dom.unbind(doc, 'mouseup', endSelection); | |
3584 dom.unbind(doc, 'mousemove', selectionChange); | |
3585 started = 0; | |
3586 }; | |
3587 | |
3588 // Detect when user selects outside BODY | |
3589 dom.bind(doc, 'mousedown', function(e) { | |
3590 if (e.target.nodeName === 'HTML') { | |
3591 if (started) | |
3592 endSelection(); | |
3593 | |
3594 started = 1; | |
3595 | |
3596 // Setup start position | |
3597 startRng = rngFromPoint(e.x, e.y); | |
3598 if (startRng) { | |
3599 // Listen for selection change events | |
3600 dom.bind(doc, 'mouseup', endSelection); | |
3601 dom.bind(doc, 'mousemove', selectionChange); | |
3602 | |
3603 startRng.select(); | |
3604 } | |
3605 } | |
3606 }); | |
3607 })(); | |
3608 } | |
3225 }; | 3609 }; |
3226 | 3610 |
3227 // Expose the selection object | 3611 // Expose the selection object |
3228 tinymce.dom.TridentSelection = Selection; | 3612 tinymce.dom.TridentSelection = Selection; |
3229 })(); | 3613 })(); |
3614 | |
3230 | 3615 |
3231 /* | 3616 /* |
3232 * Sizzle CSS Selector Engine - v1.0 | 3617 * Sizzle CSS Selector Engine - v1.0 |
3233 * Copyright 2009, The Dojo Foundation | 3618 * Copyright 2009, The Dojo Foundation |
3234 * Released under the MIT, BSD, and GPL Licenses. | 3619 * Released under the MIT, BSD, and GPL Licenses. |
3237 (function(){ | 3622 (function(){ |
3238 | 3623 |
3239 var chunker = /((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^[\]]*\]|['"][^'"]*['"]|[^[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?/g, | 3624 var chunker = /((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^[\]]*\]|['"][^'"]*['"]|[^[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?/g, |
3240 done = 0, | 3625 done = 0, |
3241 toString = Object.prototype.toString, | 3626 toString = Object.prototype.toString, |
3242 arraySplice = Array.prototype.splice, | 3627 hasDuplicate = false; |
3243 arrayPush = Array.prototype.push, | |
3244 arraySort = Array.prototype.sort; | |
3245 | 3628 |
3246 var Sizzle = function(selector, context, results, seed) { | 3629 var Sizzle = function(selector, context, results, seed) { |
3247 results = results || []; | 3630 results = results || []; |
3248 var origContext = context = context || document; | 3631 var origContext = context = context || document; |
3249 | 3632 |
3335 throw "Syntax error, unrecognized expression: " + (cur || selector); | 3718 throw "Syntax error, unrecognized expression: " + (cur || selector); |
3336 } | 3719 } |
3337 | 3720 |
3338 if ( toString.call(checkSet) === "[object Array]" ) { | 3721 if ( toString.call(checkSet) === "[object Array]" ) { |
3339 if ( !prune ) { | 3722 if ( !prune ) { |
3340 arrayPush.apply( results, checkSet ); | 3723 results.push.apply( results, checkSet ); |
3341 } else if ( context && context.nodeType === 1 ) { | 3724 } else if ( context && context.nodeType === 1 ) { |
3342 for ( var i = 0; checkSet[i] != null; i++ ) { | 3725 for ( var i = 0; checkSet[i] != null; i++ ) { |
3343 if ( checkSet[i] && (checkSet[i] === true || checkSet[i].nodeType === 1 && contains(context, checkSet[i])) ) { | 3726 if ( checkSet[i] && (checkSet[i] === true || checkSet[i].nodeType === 1 && contains(context, checkSet[i])) ) { |
3344 arrayPush.call( results, set[i] ); | 3727 results.push( set[i] ); |
3345 } | 3728 } |
3346 } | 3729 } |
3347 } else { | 3730 } else { |
3348 for ( var i = 0; checkSet[i] != null; i++ ) { | 3731 for ( var i = 0; checkSet[i] != null; i++ ) { |
3349 if ( checkSet[i] && checkSet[i].nodeType === 1 ) { | 3732 if ( checkSet[i] && checkSet[i].nodeType === 1 ) { |
3350 arrayPush.call( results, set[i] ); | 3733 results.push( set[i] ); |
3351 } | 3734 } |
3352 } | 3735 } |
3353 } | 3736 } |
3354 } else { | 3737 } else { |
3355 makeArray( checkSet, results ); | 3738 makeArray( checkSet, results ); |
3364 }; | 3747 }; |
3365 | 3748 |
3366 Sizzle.uniqueSort = function(results){ | 3749 Sizzle.uniqueSort = function(results){ |
3367 if ( sortOrder ) { | 3750 if ( sortOrder ) { |
3368 hasDuplicate = false; | 3751 hasDuplicate = false; |
3369 arraySort.call(results, sortOrder); | 3752 results.sort(sortOrder); |
3370 | 3753 |
3371 if ( hasDuplicate ) { | 3754 if ( hasDuplicate ) { |
3372 for ( var i = 1; i < results.length; i++ ) { | 3755 for ( var i = 1; i < results.length; i++ ) { |
3373 if ( results[i] === results[i-1] ) { | 3756 if ( results[i] === results[i-1] ) { |
3374 arraySplice.call(results, i--, 1); | 3757 results.splice(i--, 1); |
3375 } | 3758 } |
3376 } | 3759 } |
3377 } | 3760 } |
3378 } | 3761 } |
3379 }; | 3762 }; |
3894 | 4277 |
3895 var makeArray = function(array, results) { | 4278 var makeArray = function(array, results) { |
3896 array = Array.prototype.slice.call( array ); | 4279 array = Array.prototype.slice.call( array ); |
3897 | 4280 |
3898 if ( results ) { | 4281 if ( results ) { |
3899 arrayPush.apply( results, array ); | 4282 results.push.apply( results, array ); |
3900 return results; | 4283 return results; |
3901 } | 4284 } |
3902 | 4285 |
3903 return array; | 4286 return array; |
3904 }; | 4287 }; |
3950 return ret; | 4333 return ret; |
3951 }; | 4334 }; |
3952 } else if ( document.createRange ) { | 4335 } else if ( document.createRange ) { |
3953 sortOrder = function( a, b ) { | 4336 sortOrder = function( a, b ) { |
3954 var aRange = a.ownerDocument.createRange(), bRange = b.ownerDocument.createRange(); | 4337 var aRange = a.ownerDocument.createRange(), bRange = b.ownerDocument.createRange(); |
3955 aRange.selectNode(a); | 4338 aRange.setStart(a, 0); |
3956 aRange.collapse(true); | 4339 aRange.setEnd(a, 0); |
3957 bRange.selectNode(b); | 4340 bRange.setStart(b, 0); |
3958 bRange.collapse(true); | 4341 bRange.setEnd(b, 0); |
3959 var ret = aRange.compareBoundaryPoints(Range.START_TO_END, bRange); | 4342 var ret = aRange.compareBoundaryPoints(Range.START_TO_END, bRange); |
3960 if ( ret === 0 ) { | 4343 if ( ret === 0 ) { |
3961 hasDuplicate = true; | 4344 hasDuplicate = true; |
3962 } | 4345 } |
3963 return ret; | 4346 return ret; |
3966 | 4349 |
3967 // Check to see if the browser returns elements by name when | 4350 // Check to see if the browser returns elements by name when |
3968 // querying by getElementById (and provide a workaround) | 4351 // querying by getElementById (and provide a workaround) |
3969 (function(){ | 4352 (function(){ |
3970 // We're going to inject a fake input element with a specified name | 4353 // We're going to inject a fake input element with a specified name |
3971 var form = document.createElement("form"), | 4354 var form = document.createElement("div"), |
3972 id = "script" + (new Date).getTime(); | 4355 id = "script" + (new Date).getTime(); |
3973 form.innerHTML = "<input name='" + id + "'/>"; | 4356 form.innerHTML = "<a name='" + id + "'/>"; |
3974 | 4357 |
3975 // Inject it into the root element, check its status, and remove it quickly | 4358 // Inject it into the root element, check its status, and remove it quickly |
3976 var root = document.documentElement; | 4359 var root = document.documentElement; |
3977 root.insertBefore( form, root.firstChild ); | 4360 root.insertBefore( form, root.firstChild ); |
3978 | 4361 |
4199 // EXPOSE | 4582 // EXPOSE |
4200 | 4583 |
4201 window.tinymce.dom.Sizzle = Sizzle; | 4584 window.tinymce.dom.Sizzle = Sizzle; |
4202 | 4585 |
4203 })(); | 4586 })(); |
4587 | |
4588 | |
4204 (function(tinymce) { | 4589 (function(tinymce) { |
4205 // Shorten names | 4590 // Shorten names |
4206 var each = tinymce.each, DOM = tinymce.DOM, isIE = tinymce.isIE, isWebKit = tinymce.isWebKit, Event; | 4591 var each = tinymce.each, DOM = tinymce.DOM, isIE = tinymce.isIE, isWebKit = tinymce.isWebKit, Event; |
4207 | 4592 |
4208 tinymce.create('static tinymce.dom.Event', { | 4593 tinymce.create('tinymce.dom.EventUtils', { |
4209 inits : [], | 4594 EventUtils : function() { |
4210 events : [], | 4595 this.inits = []; |
4211 | 4596 this.events = []; |
4597 }, | |
4212 | 4598 |
4213 add : function(o, n, f, s) { | 4599 add : function(o, n, f, s) { |
4214 var cb, t = this, el = t.events, r; | 4600 var cb, t = this, el = t.events, r; |
4601 | |
4602 if (n instanceof Array) { | |
4603 r = []; | |
4604 | |
4605 each(n, function(n) { | |
4606 r.push(t.add(o, n, f, s)); | |
4607 }); | |
4608 | |
4609 return r; | |
4610 } | |
4215 | 4611 |
4216 // Handle array | 4612 // Handle array |
4217 if (o && o.hasOwnProperty && o instanceof Array) { | 4613 if (o && o.hasOwnProperty && o instanceof Array) { |
4218 r = []; | 4614 r = []; |
4219 | 4615 |
4230 if (!o) | 4626 if (!o) |
4231 return; | 4627 return; |
4232 | 4628 |
4233 // Setup event callback | 4629 // Setup event callback |
4234 cb = function(e) { | 4630 cb = function(e) { |
4631 // Is all events disabled | |
4632 if (t.disabled) | |
4633 return; | |
4634 | |
4235 e = e || window.event; | 4635 e = e || window.event; |
4236 | 4636 |
4237 // Patch in target in IE it's W3C valid | 4637 // Patch in target, preventDefault and stopPropagation in IE it's W3C valid |
4238 if (e && !e.target && isIE) | 4638 if (e && isIE) { |
4239 e.target = e.srcElement; | 4639 if (!e.target) |
4640 e.target = e.srcElement; | |
4641 | |
4642 // Patch in preventDefault, stopPropagation methods for W3C compatibility | |
4643 tinymce.extend(e, t._stoppers); | |
4644 } | |
4240 | 4645 |
4241 if (!s) | 4646 if (!s) |
4242 return f(e); | 4647 return f(e); |
4243 | 4648 |
4244 return f.call(s, e); | 4649 return f.call(s, e); |
4317 } | 4722 } |
4318 } | 4723 } |
4319 } | 4724 } |
4320 }, | 4725 }, |
4321 | 4726 |
4322 | |
4323 cancel : function(e) { | 4727 cancel : function(e) { |
4324 if (!e) | 4728 if (!e) |
4325 return false; | 4729 return false; |
4326 | 4730 |
4327 this.stop(e); | 4731 this.stop(e); |
4732 | |
4328 return this.prevent(e); | 4733 return this.prevent(e); |
4329 }, | 4734 }, |
4330 | 4735 |
4331 stop : function(e) { | 4736 stop : function(e) { |
4332 if (e.stopPropagation) | 4737 if (e.stopPropagation) |
4344 e.returnValue = false; | 4749 e.returnValue = false; |
4345 | 4750 |
4346 return false; | 4751 return false; |
4347 }, | 4752 }, |
4348 | 4753 |
4349 _unload : function() { | 4754 destroy : function() { |
4350 var t = Event; | 4755 var t = this; |
4351 | 4756 |
4352 each(t.events, function(e, i) { | 4757 each(t.events, function(e, i) { |
4353 t._remove(e.obj, e.name, e.cfunc); | 4758 t._remove(e.obj, e.name, e.cfunc); |
4354 e.obj = e.cfunc = null; | 4759 e.obj = e.cfunc = null; |
4355 }); | 4760 }); |
4380 // Might fail with permission denined on IE so we just ignore that | 4785 // Might fail with permission denined on IE so we just ignore that |
4381 } | 4786 } |
4382 } | 4787 } |
4383 }, | 4788 }, |
4384 | 4789 |
4385 _pageInit : function() { | 4790 _pageInit : function(win) { |
4386 var e = Event; | 4791 var t = this; |
4387 | 4792 |
4388 // Safari on Mac fires this twice | 4793 // Keep it from running more than once |
4389 if (e.domLoaded) | 4794 if (t.domLoaded) |
4390 return; | 4795 return; |
4391 | 4796 |
4392 e._remove(window, 'DOMContentLoaded', e._pageInit); | 4797 t.domLoaded = true; |
4393 e.domLoaded = true; | 4798 |
4394 | 4799 each(t.inits, function(c) { |
4395 each(e.inits, function(c) { | |
4396 c(); | 4800 c(); |
4397 }); | 4801 }); |
4398 | 4802 |
4399 e.inits = []; | 4803 t.inits = []; |
4400 }, | 4804 }, |
4401 | 4805 |
4402 _wait : function() { | 4806 _wait : function(win) { |
4403 var t; | 4807 var t = this, doc = win.document; |
4404 | 4808 |
4405 // No need since the document is already loaded | 4809 // No need since the document is already loaded |
4406 if (window.tinyMCE_GZ && tinyMCE_GZ.loaded) { | 4810 if (win.tinyMCE_GZ && tinyMCE_GZ.loaded) { |
4407 Event.domLoaded = 1; | 4811 t.domLoaded = 1; |
4408 return; | 4812 return; |
4409 } | 4813 } |
4410 | 4814 |
4411 if (isIE && document.location.protocol != 'https:') { | 4815 // Use IE method |
4412 // Fake DOMContentLoaded on IE | 4816 if (doc.attachEvent) { |
4413 document.write('<script id=__ie_onload defer src=\'javascript:""\';><\/script>'); | 4817 doc.attachEvent("onreadystatechange", function() { |
4414 DOM.get("__ie_onload").onreadystatechange = function() { | 4818 if (doc.readyState === "complete") { |
4415 if (this.readyState == "complete") { | 4819 doc.detachEvent("onreadystatechange", arguments.callee); |
4416 Event._pageInit(); | 4820 t._pageInit(win); |
4417 DOM.get("__ie_onload").onreadystatechange = null; // Prevent leak | |
4418 } | 4821 } |
4419 }; | 4822 }); |
4420 } else { | 4823 |
4421 Event._add(window, 'DOMContentLoaded', Event._pageInit, Event); | 4824 if (doc.documentElement.doScroll && win == win.top) { |
4422 | 4825 (function() { |
4423 if (isIE || isWebKit) { | 4826 if (t.domLoaded) |
4424 t = setInterval(function() { | 4827 return; |
4425 if (/loaded|complete/.test(document.readyState)) { | 4828 |
4426 clearInterval(t); | 4829 try { |
4427 Event._pageInit(); | 4830 // If IE is used, use the trick by Diego Perini |
4831 // http://javascript.nwbox.com/IEContentLoaded/ | |
4832 doc.documentElement.doScroll("left"); | |
4833 } catch (ex) { | |
4834 setTimeout(arguments.callee, 0); | |
4835 return; | |
4428 } | 4836 } |
4429 }, 10); | 4837 |
4430 } | 4838 t._pageInit(win); |
4839 })(); | |
4840 } | |
4841 } else if (doc.addEventListener) { | |
4842 t._add(win, 'DOMContentLoaded', function() { | |
4843 t._pageInit(win); | |
4844 }); | |
4845 } | |
4846 | |
4847 t._add(win, 'load', function() { | |
4848 t._pageInit(win); | |
4849 }); | |
4850 }, | |
4851 | |
4852 _stoppers : { | |
4853 preventDefault : function() { | |
4854 this.returnValue = false; | |
4855 }, | |
4856 | |
4857 stopPropagation : function() { | |
4858 this.cancelBubble = true; | |
4431 } | 4859 } |
4432 } | 4860 } |
4433 | 4861 }); |
4434 }); | 4862 |
4435 | 4863 Event = tinymce.dom.Event = new tinymce.dom.EventUtils(); |
4436 // Shorten name | |
4437 Event = tinymce.dom.Event; | |
4438 | 4864 |
4439 // Dispatch DOM content loaded event for IE and Safari | 4865 // Dispatch DOM content loaded event for IE and Safari |
4440 Event._wait(); | 4866 Event._wait(window); |
4441 tinymce.addUnload(Event._unload); | 4867 |
4868 tinymce.addUnload(function() { | |
4869 Event.destroy(); | |
4870 }); | |
4442 })(tinymce); | 4871 })(tinymce); |
4872 | |
4443 (function(tinymce) { | 4873 (function(tinymce) { |
4444 var each = tinymce.each; | 4874 tinymce.dom.Element = function(id, settings) { |
4445 | 4875 var t = this, dom, el; |
4446 tinymce.create('tinymce.dom.Element', { | 4876 |
4447 Element : function(id, s) { | 4877 t.settings = settings = settings || {}; |
4448 var t = this, dom, el; | 4878 t.id = id; |
4449 | 4879 t.dom = dom = settings.dom || tinymce.DOM; |
4450 s = s || {}; | 4880 |
4451 t.id = id; | 4881 // Only IE leaks DOM references, this is a lot faster |
4452 t.dom = dom = s.dom || tinymce.DOM; | 4882 if (!tinymce.isIE) |
4453 t.settings = s; | 4883 el = dom.get(t.id); |
4454 | 4884 |
4455 // Only IE leaks DOM references, this is a lot faster | 4885 tinymce.each( |
4456 if (!tinymce.isIE) | 4886 ('getPos,getRect,getParent,add,setStyle,getStyle,setStyles,' + |
4457 el = t.dom.get(t.id); | 4887 'setAttrib,setAttribs,getAttrib,addClass,removeClass,' + |
4458 | 4888 'hasClass,getOuterHTML,setOuterHTML,remove,show,hide,' + |
4459 each([ | 4889 'isHidden,setHTML,get').split(/,/) |
4460 'getPos', | 4890 , function(k) { |
4461 'getRect', | |
4462 'getParent', | |
4463 'add', | |
4464 'setStyle', | |
4465 'getStyle', | |
4466 'setStyles', | |
4467 'setAttrib', | |
4468 'setAttribs', | |
4469 'getAttrib', | |
4470 'addClass', | |
4471 'removeClass', | |
4472 'hasClass', | |
4473 'getOuterHTML', | |
4474 'setOuterHTML', | |
4475 'remove', | |
4476 'show', | |
4477 'hide', | |
4478 'isHidden', | |
4479 'setHTML', | |
4480 'get' | |
4481 ], function(k) { | |
4482 t[k] = function() { | 4891 t[k] = function() { |
4483 var a = [id], i; | 4892 var a = [id], i; |
4484 | 4893 |
4485 for (i = 0; i < arguments.length; i++) | 4894 for (i = 0; i < arguments.length; i++) |
4486 a.push(arguments[i]); | 4895 a.push(arguments[i]); |
4488 a = dom[k].apply(dom, a); | 4897 a = dom[k].apply(dom, a); |
4489 t.update(k); | 4898 t.update(k); |
4490 | 4899 |
4491 return a; | 4900 return a; |
4492 }; | 4901 }; |
4493 }); | |
4494 }, | |
4495 | |
4496 on : function(n, f, s) { | |
4497 return tinymce.dom.Event.add(this.id, n, f, s); | |
4498 }, | |
4499 | |
4500 getXY : function() { | |
4501 return { | |
4502 x : parseInt(this.getStyle('left')), | |
4503 y : parseInt(this.getStyle('top')) | |
4504 }; | |
4505 }, | |
4506 | |
4507 getSize : function() { | |
4508 var n = this.dom.get(this.id); | |
4509 | |
4510 return { | |
4511 w : parseInt(this.getStyle('width') || n.clientWidth), | |
4512 h : parseInt(this.getStyle('height') || n.clientHeight) | |
4513 }; | |
4514 }, | |
4515 | |
4516 moveTo : function(x, y) { | |
4517 this.setStyles({left : x, top : y}); | |
4518 }, | |
4519 | |
4520 moveBy : function(x, y) { | |
4521 var p = this.getXY(); | |
4522 | |
4523 this.moveTo(p.x + x, p.y + y); | |
4524 }, | |
4525 | |
4526 resizeTo : function(w, h) { | |
4527 this.setStyles({width : w, height : h}); | |
4528 }, | |
4529 | |
4530 resizeBy : function(w, h) { | |
4531 var s = this.getSize(); | |
4532 | |
4533 this.resizeTo(s.w + w, s.h + h); | |
4534 }, | |
4535 | |
4536 update : function(k) { | |
4537 var t = this, b, dom = t.dom; | |
4538 | |
4539 if (tinymce.isIE6 && t.settings.blocker) { | |
4540 k = k || ''; | |
4541 | |
4542 // Ignore getters | |
4543 if (k.indexOf('get') === 0 || k.indexOf('has') === 0 || k.indexOf('is') === 0) | |
4544 return; | |
4545 | |
4546 // Remove blocker on remove | |
4547 if (k == 'remove') { | |
4548 dom.remove(t.blocker); | |
4549 return; | |
4550 } | |
4551 | |
4552 if (!t.blocker) { | |
4553 t.blocker = dom.uniqueId(); | |
4554 b = dom.add(t.settings.container || dom.getRoot(), 'iframe', {id : t.blocker, style : 'position:absolute;', frameBorder : 0, src : 'javascript:""'}); | |
4555 dom.setStyle(b, 'opacity', 0); | |
4556 } else | |
4557 b = dom.get(t.blocker); | |
4558 | |
4559 dom.setStyle(b, 'left', t.getStyle('left', 1)); | |
4560 dom.setStyle(b, 'top', t.getStyle('top', 1)); | |
4561 dom.setStyle(b, 'width', t.getStyle('width', 1)); | |
4562 dom.setStyle(b, 'height', t.getStyle('height', 1)); | |
4563 dom.setStyle(b, 'display', t.getStyle('display', 1)); | |
4564 dom.setStyle(b, 'zIndex', parseInt(t.getStyle('zIndex', 1) || 0) - 1); | |
4565 } | |
4566 } | |
4567 | |
4568 }); | 4902 }); |
4903 | |
4904 tinymce.extend(t, { | |
4905 on : function(n, f, s) { | |
4906 return tinymce.dom.Event.add(t.id, n, f, s); | |
4907 }, | |
4908 | |
4909 getXY : function() { | |
4910 return { | |
4911 x : parseInt(t.getStyle('left')), | |
4912 y : parseInt(t.getStyle('top')) | |
4913 }; | |
4914 }, | |
4915 | |
4916 getSize : function() { | |
4917 var n = dom.get(t.id); | |
4918 | |
4919 return { | |
4920 w : parseInt(t.getStyle('width') || n.clientWidth), | |
4921 h : parseInt(t.getStyle('height') || n.clientHeight) | |
4922 }; | |
4923 }, | |
4924 | |
4925 moveTo : function(x, y) { | |
4926 t.setStyles({left : x, top : y}); | |
4927 }, | |
4928 | |
4929 moveBy : function(x, y) { | |
4930 var p = t.getXY(); | |
4931 | |
4932 t.moveTo(p.x + x, p.y + y); | |
4933 }, | |
4934 | |
4935 resizeTo : function(w, h) { | |
4936 t.setStyles({width : w, height : h}); | |
4937 }, | |
4938 | |
4939 resizeBy : function(w, h) { | |
4940 var s = t.getSize(); | |
4941 | |
4942 t.resizeTo(s.w + w, s.h + h); | |
4943 }, | |
4944 | |
4945 update : function(k) { | |
4946 var b; | |
4947 | |
4948 if (tinymce.isIE6 && settings.blocker) { | |
4949 k = k || ''; | |
4950 | |
4951 // Ignore getters | |
4952 if (k.indexOf('get') === 0 || k.indexOf('has') === 0 || k.indexOf('is') === 0) | |
4953 return; | |
4954 | |
4955 // Remove blocker on remove | |
4956 if (k == 'remove') { | |
4957 dom.remove(t.blocker); | |
4958 return; | |
4959 } | |
4960 | |
4961 if (!t.blocker) { | |
4962 t.blocker = dom.uniqueId(); | |
4963 b = dom.add(settings.container || dom.getRoot(), 'iframe', {id : t.blocker, style : 'position:absolute;', frameBorder : 0, src : 'javascript:""'}); | |
4964 dom.setStyle(b, 'opacity', 0); | |
4965 } else | |
4966 b = dom.get(t.blocker); | |
4967 | |
4968 dom.setStyles(b, { | |
4969 left : t.getStyle('left', 1), | |
4970 top : t.getStyle('top', 1), | |
4971 width : t.getStyle('width', 1), | |
4972 height : t.getStyle('height', 1), | |
4973 display : t.getStyle('display', 1), | |
4974 zIndex : parseInt(t.getStyle('zIndex', 1) || 0) - 1 | |
4975 }); | |
4976 } | |
4977 } | |
4978 }); | |
4979 }; | |
4569 })(tinymce); | 4980 })(tinymce); |
4981 | |
4570 (function(tinymce) { | 4982 (function(tinymce) { |
4571 function trimNl(s) { | 4983 function trimNl(s) { |
4572 return s.replace(/[\n\r]+/g, ''); | 4984 return s.replace(/[\n\r]+/g, ''); |
4573 }; | 4985 }; |
4574 | 4986 |
4652 if (r.insertNode) { | 5064 if (r.insertNode) { |
4653 // Make caret marker since insertNode places the caret in the beginning of text after insert | 5065 // Make caret marker since insertNode places the caret in the beginning of text after insert |
4654 h += '<span id="__caret">_</span>'; | 5066 h += '<span id="__caret">_</span>'; |
4655 | 5067 |
4656 // Delete and insert new node | 5068 // Delete and insert new node |
4657 r.deleteContents(); | 5069 if (r.startContainer == d && r.endContainer == d) { |
4658 r.insertNode(t.getRng().createContextualFragment(h)); | 5070 // WebKit will fail if the body is empty since the range is then invalid and it can't insert contents |
5071 d.body.innerHTML = h; | |
5072 } else { | |
5073 r.deleteContents(); | |
5074 r.insertNode(t.getRng().createContextualFragment(h)); | |
5075 } | |
4659 | 5076 |
4660 // Move to caret marker | 5077 // Move to caret marker |
4661 c = t.dom.get('__caret'); | 5078 c = t.dom.get('__caret'); |
4662 | 5079 |
4663 // Make sure we wrap it compleatly, Opera fails with a simple select call | 5080 // Make sure we wrap it compleatly, Opera fails with a simple select call |
4664 r = d.createRange(); | 5081 r = d.createRange(); |
4665 r.setStartBefore(c); | 5082 r.setStartBefore(c); |
4666 r.setEndAfter(c); | 5083 r.setEndBefore(c); |
4667 t.setRng(r); | 5084 t.setRng(r); |
4668 | |
4669 // Delete the marker, and hopefully the caret gets placed in the right location | |
4670 // Removed this since it seems to remove in FF and simply deleting it | |
4671 // doesn't seem to affect the caret position in any browser | |
4672 //d.execCommand('Delete', false, null); | |
4673 | 5085 |
4674 // Remove the caret position | 5086 // Remove the caret position |
4675 t.dom.remove('__caret'); | 5087 t.dom.remove('__caret'); |
4676 } else { | 5088 } else { |
4677 if (r.item) { | 5089 if (r.item) { |
4688 }, | 5100 }, |
4689 | 5101 |
4690 getStart : function() { | 5102 getStart : function() { |
4691 var t = this, r = t.getRng(), e; | 5103 var t = this, r = t.getRng(), e; |
4692 | 5104 |
4693 if (isIE) { | 5105 if (r.duplicate || r.item) { |
4694 if (r.item) | 5106 if (r.item) |
4695 return r.item(0); | 5107 return r.item(0); |
4696 | 5108 |
4697 r = r.duplicate(); | 5109 r = r.duplicate(); |
4698 r.collapse(1); | 5110 r.collapse(1); |
4699 e = r.parentElement(); | 5111 e = r.parentElement(); |
4700 | 5112 |
4701 if (e && e.nodeName == 'BODY') | 5113 if (e && e.nodeName == 'BODY') |
4702 return e.firstChild; | 5114 return e.firstChild || e; |
4703 | 5115 |
4704 return e; | 5116 return e; |
4705 } else { | 5117 } else { |
4706 e = r.startContainer; | 5118 e = r.startContainer; |
4707 | 5119 |
4708 if (e.nodeName == 'BODY') | 5120 if (e.nodeType == 1 && e.hasChildNodes()) |
4709 return e.firstChild; | 5121 e = e.childNodes[Math.min(e.childNodes.length - 1, r.startOffset)]; |
4710 | 5122 |
4711 return t.dom.getParent(e, '*'); | 5123 if (e && e.nodeType == 3) |
5124 return e.parentNode; | |
5125 | |
5126 return e; | |
4712 } | 5127 } |
4713 }, | 5128 }, |
4714 | 5129 |
4715 getEnd : function() { | 5130 getEnd : function() { |
4716 var t = this, r = t.getRng(), e; | 5131 var t = this, r = t.getRng(), e, eo; |
4717 | 5132 |
4718 if (isIE) { | 5133 if (r.duplicate || r.item) { |
4719 if (r.item) | 5134 if (r.item) |
4720 return r.item(0); | 5135 return r.item(0); |
4721 | 5136 |
4722 r = r.duplicate(); | 5137 r = r.duplicate(); |
4723 r.collapse(0); | 5138 r.collapse(0); |
4724 e = r.parentElement(); | 5139 e = r.parentElement(); |
4725 | 5140 |
4726 if (e && e.nodeName == 'BODY') | 5141 if (e && e.nodeName == 'BODY') |
4727 return e.lastChild; | 5142 return e.lastChild || e; |
4728 | 5143 |
4729 return e; | 5144 return e; |
4730 } else { | 5145 } else { |
4731 e = r.endContainer; | 5146 e = r.endContainer; |
4732 | 5147 eo = r.endOffset; |
4733 if (e.nodeName == 'BODY') | 5148 |
4734 return e.lastChild; | 5149 if (e.nodeType == 1 && e.hasChildNodes()) |
4735 | 5150 e = e.childNodes[eo > 0 ? eo - 1 : eo]; |
4736 return t.dom.getParent(e, '*'); | 5151 |
4737 } | 5152 if (e && e.nodeType == 3) |
4738 }, | 5153 return e.parentNode; |
4739 | 5154 |
4740 getBookmark : function(si) { | 5155 return e; |
4741 var t = this, r = t.getRng(), tr, sx, sy, vp = t.dom.getViewPort(t.win), e, sp, bp, le, c = -0xFFFFFF, s, ro = t.dom.getRoot(), wb = 0, wa = 0, nv; | 5156 } |
4742 sx = vp.x; | 5157 }, |
4743 sy = vp.y; | 5158 |
4744 | 5159 getBookmark : function(type, normalized) { |
4745 // Simple bookmark fast but not as persistent | 5160 var t = this, dom = t.dom, rng, rng2, id, collapsed, name, element, index, chr = '\uFEFF', styles; |
4746 if (si == 'simple') | 5161 |
4747 return {rng : r, scrollX : sx, scrollY : sy}; | 5162 function findIndex(name, element) { |
4748 | 5163 var index = 0; |
4749 // Handle IE | 5164 |
4750 if (isIE) { | 5165 each(dom.select(name), function(node, i) { |
4751 // Control selection | 5166 if (node == element) |
4752 if (r.item) { | 5167 index = i; |
4753 e = r.item(0); | 5168 }); |
4754 | 5169 |
4755 each(t.dom.select(e.nodeName), function(n, i) { | 5170 return index; |
4756 if (e == n) { | 5171 }; |
4757 sp = i; | 5172 |
4758 return false; | 5173 if (type == 2) { |
5174 function getLocation() { | |
5175 var rng = t.getRng(true), root = dom.getRoot(), bookmark = {}; | |
5176 | |
5177 function getPoint(rng, start) { | |
5178 var container = rng[start ? 'startContainer' : 'endContainer'], | |
5179 offset = rng[start ? 'startOffset' : 'endOffset'], point = [], node, childNodes, after = 0; | |
5180 | |
5181 if (container.nodeType == 3) { | |
5182 if (normalized) { | |
5183 for (node = container.previousSibling; node && node.nodeType == 3; node = node.previousSibling) | |
5184 offset += node.nodeValue.length; | |
5185 } | |
5186 | |
5187 point.push(offset); | |
5188 } else { | |
5189 childNodes = container.childNodes; | |
5190 | |
5191 if (offset >= childNodes.length) { | |
5192 after = 1; | |
5193 offset = childNodes.length - 1; | |
5194 } | |
5195 | |
5196 point.push(t.dom.nodeIndex(childNodes[offset], normalized) + after); | |
4759 } | 5197 } |
4760 }); | 5198 |
4761 | 5199 for (; container && container != root; container = container.parentNode) |
4762 return { | 5200 point.push(t.dom.nodeIndex(container, normalized)); |
4763 tag : e.nodeName, | 5201 |
4764 index : sp, | 5202 return point; |
4765 scrollX : sx, | |
4766 scrollY : sy | |
4767 }; | 5203 }; |
4768 } | 5204 |
4769 | 5205 bookmark.start = getPoint(rng, true); |
5206 | |
5207 if (!t.isCollapsed()) | |
5208 bookmark.end = getPoint(rng); | |
5209 | |
5210 return bookmark; | |
5211 }; | |
5212 | |
5213 return getLocation(); | |
5214 } | |
5215 | |
5216 // Handle simple range | |
5217 if (type) | |
5218 return {rng : t.getRng()}; | |
5219 | |
5220 rng = t.getRng(); | |
5221 id = dom.uniqueId(); | |
5222 collapsed = tinyMCE.activeEditor.selection.isCollapsed(); | |
5223 styles = 'overflow:hidden;line-height:0px'; | |
5224 | |
5225 // Explorer method | |
5226 if (rng.duplicate || rng.item) { | |
4770 // Text selection | 5227 // Text selection |
4771 tr = t.dom.doc.body.createTextRange(); | 5228 if (!rng.item) { |
4772 tr.moveToElementText(ro); | 5229 rng2 = rng.duplicate(); |
4773 tr.collapse(true); | 5230 |
4774 bp = Math.abs(tr.move('character', c)); | 5231 // Insert start marker |
4775 | 5232 rng.collapse(); |
4776 tr = r.duplicate(); | 5233 rng.pasteHTML('<span _mce_type="bookmark" id="' + id + '_start" style="' + styles + '">' + chr + '</span>'); |
4777 tr.collapse(true); | 5234 |
4778 sp = Math.abs(tr.move('character', c)); | 5235 // Insert end marker |
4779 | 5236 if (!collapsed) { |
4780 tr = r.duplicate(); | 5237 rng2.collapse(false); |
4781 tr.collapse(false); | 5238 rng2.pasteHTML('<span _mce_type="bookmark" id="' + id + '_end" style="' + styles + '">' + chr + '</span>'); |
4782 le = Math.abs(tr.move('character', c)) - sp; | 5239 } |
4783 | 5240 } else { |
4784 return { | 5241 // Control selection |
4785 start : sp - bp, | 5242 element = rng.item(0); |
4786 length : le, | 5243 name = element.nodeName; |
4787 scrollX : sx, | 5244 |
4788 scrollY : sy | 5245 return {name : name, index : findIndex(name, element)}; |
5246 } | |
5247 } else { | |
5248 element = t.getNode(); | |
5249 name = element.nodeName; | |
5250 if (name == 'IMG') | |
5251 return {name : name, index : findIndex(name, element)}; | |
5252 | |
5253 // W3C method | |
5254 rng2 = rng.cloneRange(); | |
5255 | |
5256 // Insert end marker | |
5257 if (!collapsed) { | |
5258 rng2.collapse(false); | |
5259 rng2.insertNode(dom.create('span', {_mce_type : "bookmark", id : id + '_end', style : styles}, chr)); | |
5260 } | |
5261 | |
5262 rng.collapse(true); | |
5263 rng.insertNode(dom.create('span', {_mce_type : "bookmark", id : id + '_start', style : styles}, chr)); | |
5264 } | |
5265 | |
5266 t.moveToBookmark({id : id, keep : 1}); | |
5267 | |
5268 return {id : id}; | |
5269 }, | |
5270 | |
5271 moveToBookmark : function(bookmark) { | |
5272 var t = this, dom = t.dom, marker1, marker2, rng, root; | |
5273 | |
5274 // Clear selection cache | |
5275 if (t.tridentSel) | |
5276 t.tridentSel.destroy(); | |
5277 | |
5278 if (bookmark) { | |
5279 if (bookmark.start) { | |
5280 rng = dom.createRng(); | |
5281 root = dom.getRoot(); | |
5282 | |
5283 function setEndPoint(start) { | |
5284 var point = bookmark[start ? 'start' : 'end'], i, node, offset; | |
5285 | |
5286 if (point) { | |
5287 // Find container node | |
5288 for (node = root, i = point.length - 1; i >= 1; i--) | |
5289 node = node.childNodes[point[i]]; | |
5290 | |
5291 // Set offset within container node | |
5292 if (start) | |
5293 rng.setStart(node, point[0]); | |
5294 else | |
5295 rng.setEnd(node, point[0]); | |
5296 } | |
5297 }; | |
5298 | |
5299 setEndPoint(true); | |
5300 setEndPoint(); | |
5301 | |
5302 t.setRng(rng); | |
5303 } else if (bookmark.id) { | |
5304 rng = dom.createRng(); | |
5305 | |
5306 function restoreEndPoint(suffix) { | |
5307 var marker = dom.get(bookmark.id + '_' + suffix), node, idx, next, prev, keep = bookmark.keep; | |
5308 | |
5309 if (marker) { | |
5310 node = marker.parentNode; | |
5311 | |
5312 if (suffix == 'start') { | |
5313 if (!keep) { | |
5314 idx = dom.nodeIndex(marker); | |
5315 } else { | |
5316 node = marker; | |
5317 idx = 1; | |
5318 } | |
5319 | |
5320 rng.setStart(node, idx); | |
5321 rng.setEnd(node, idx); | |
5322 } else { | |
5323 if (!keep) { | |
5324 idx = dom.nodeIndex(marker); | |
5325 } else { | |
5326 node = marker; | |
5327 idx = 1; | |
5328 } | |
5329 | |
5330 rng.setEnd(node, idx); | |
5331 } | |
5332 | |
5333 if (!keep) { | |
5334 prev = marker.previousSibling; | |
5335 next = marker.nextSibling; | |
5336 | |
5337 // Remove all marker text nodes | |
5338 each(tinymce.grep(marker.childNodes), function(node) { | |
5339 if (node.nodeType == 3) | |
5340 node.nodeValue = node.nodeValue.replace(/\uFEFF/g, ''); | |
5341 }); | |
5342 | |
5343 // Remove marker but keep children if for example contents where inserted into the marker | |
5344 // Also remove duplicated instances of the marker for example by a split operation or by WebKit auto split on paste feature | |
5345 while (marker = dom.get(bookmark.id + '_' + suffix)) | |
5346 dom.remove(marker, 1); | |
5347 | |
5348 // If siblings are text nodes then merge them | |
5349 if (prev && next && prev.nodeType == next.nodeType && prev.nodeType == 3) { | |
5350 idx = prev.nodeValue.length; | |
5351 prev.appendData(next.nodeValue); | |
5352 dom.remove(next); | |
5353 | |
5354 if (suffix == 'start') { | |
5355 rng.setStart(prev, idx); | |
5356 rng.setEnd(prev, idx); | |
5357 } else | |
5358 rng.setEnd(prev, idx); | |
5359 } | |
5360 } | |
5361 } | |
5362 }; | |
5363 | |
5364 // Restore start/end points | |
5365 restoreEndPoint('start'); | |
5366 restoreEndPoint('end'); | |
5367 | |
5368 t.setRng(rng); | |
5369 } else if (bookmark.name) { | |
5370 t.select(dom.select(bookmark.name)[bookmark.index]); | |
5371 } else if (bookmark.rng) | |
5372 t.setRng(bookmark.rng); | |
5373 } | |
5374 }, | |
5375 | |
5376 select : function(node, content) { | |
5377 var t = this, dom = t.dom, rng = dom.createRng(), idx; | |
5378 | |
5379 idx = dom.nodeIndex(node); | |
5380 rng.setStart(node.parentNode, idx); | |
5381 rng.setEnd(node.parentNode, idx + 1); | |
5382 | |
5383 // Find first/last text node or BR element | |
5384 if (content) { | |
5385 function setPoint(node, start) { | |
5386 var walker = new tinymce.dom.TreeWalker(node, node); | |
5387 | |
5388 do { | |
5389 // Text node | |
5390 if (node.nodeType == 3 && tinymce.trim(node.nodeValue).length != 0) { | |
5391 if (start) | |
5392 rng.setStart(node, 0); | |
5393 else | |
5394 rng.setEnd(node, node.nodeValue.length); | |
5395 | |
5396 return; | |
5397 } | |
5398 | |
5399 // BR element | |
5400 if (node.nodeName == 'BR') { | |
5401 if (start) | |
5402 rng.setStartBefore(node); | |
5403 else | |
5404 rng.setEndBefore(node); | |
5405 | |
5406 return; | |
5407 } | |
5408 } while (node = (start ? walker.next() : walker.prev())); | |
4789 }; | 5409 }; |
4790 } | 5410 |
4791 | 5411 setPoint(node, 1); |
4792 // Handle W3C | 5412 setPoint(node); |
4793 e = t.getNode(); | 5413 } |
4794 s = t.getSel(); | 5414 |
4795 | 5415 t.setRng(rng); |
4796 if (!s) | 5416 |
4797 return null; | 5417 return node; |
4798 | |
4799 // Image selection | |
4800 if (e && e.nodeName == 'IMG') { | |
4801 return { | |
4802 scrollX : sx, | |
4803 scrollY : sy | |
4804 }; | |
4805 } | |
4806 | |
4807 // Text selection | |
4808 | |
4809 function getPos(r, sn, en) { | |
4810 var w = t.dom.doc.createTreeWalker(r, NodeFilter.SHOW_TEXT, null, false), n, p = 0, d = {}; | |
4811 | |
4812 while ((n = w.nextNode()) != null) { | |
4813 if (n == sn) | |
4814 d.start = p; | |
4815 | |
4816 if (n == en) { | |
4817 d.end = p; | |
4818 return d; | |
4819 } | |
4820 | |
4821 p += trimNl(n.nodeValue || '').length; | |
4822 } | |
4823 | |
4824 return null; | |
4825 }; | |
4826 | |
4827 // Caret or selection | |
4828 if (s.anchorNode == s.focusNode && s.anchorOffset == s.focusOffset) { | |
4829 e = getPos(ro, s.anchorNode, s.focusNode); | |
4830 | |
4831 if (!e) | |
4832 return {scrollX : sx, scrollY : sy}; | |
4833 | |
4834 // Count whitespace before | |
4835 trimNl(s.anchorNode.nodeValue || '').replace(/^\s+/, function(a) {wb = a.length;}); | |
4836 | |
4837 return { | |
4838 start : Math.max(e.start + s.anchorOffset - wb, 0), | |
4839 end : Math.max(e.end + s.focusOffset - wb, 0), | |
4840 scrollX : sx, | |
4841 scrollY : sy, | |
4842 beg : s.anchorOffset - wb == 0 | |
4843 }; | |
4844 } else { | |
4845 e = getPos(ro, r.startContainer, r.endContainer); | |
4846 | |
4847 // Count whitespace before start and end container | |
4848 //(r.startContainer.nodeValue || '').replace(/^\s+/, function(a) {wb = a.length;}); | |
4849 //(r.endContainer.nodeValue || '').replace(/^\s+/, function(a) {wa = a.length;}); | |
4850 | |
4851 if (!e) | |
4852 return {scrollX : sx, scrollY : sy}; | |
4853 | |
4854 return { | |
4855 start : Math.max(e.start + r.startOffset - wb, 0), | |
4856 end : Math.max(e.end + r.endOffset - wa, 0), | |
4857 scrollX : sx, | |
4858 scrollY : sy, | |
4859 beg : r.startOffset - wb == 0 | |
4860 }; | |
4861 } | |
4862 }, | |
4863 | |
4864 moveToBookmark : function(b) { | |
4865 var t = this, r = t.getRng(), s = t.getSel(), ro = t.dom.getRoot(), sd, nvl, nv; | |
4866 | |
4867 function getPos(r, sp, ep) { | |
4868 var w = t.dom.doc.createTreeWalker(r, NodeFilter.SHOW_TEXT, null, false), n, p = 0, d = {}, o, v, wa, wb; | |
4869 | |
4870 while ((n = w.nextNode()) != null) { | |
4871 wa = wb = 0; | |
4872 | |
4873 nv = n.nodeValue || ''; | |
4874 //nv.replace(/^\s+[^\s]/, function(a) {wb = a.length - 1;}); | |
4875 //nv.replace(/[^\s]\s+$/, function(a) {wa = a.length - 1;}); | |
4876 | |
4877 nvl = trimNl(nv).length; | |
4878 p += nvl; | |
4879 | |
4880 if (p >= sp && !d.startNode) { | |
4881 o = sp - (p - nvl); | |
4882 | |
4883 // Fix for odd quirk in FF | |
4884 if (b.beg && o >= nvl) | |
4885 continue; | |
4886 | |
4887 d.startNode = n; | |
4888 d.startOffset = o + wb; | |
4889 } | |
4890 | |
4891 if (p >= ep) { | |
4892 d.endNode = n; | |
4893 d.endOffset = ep - (p - nvl) + wb; | |
4894 return d; | |
4895 } | |
4896 } | |
4897 | |
4898 return null; | |
4899 }; | |
4900 | |
4901 if (!b) | |
4902 return false; | |
4903 | |
4904 t.win.scrollTo(b.scrollX, b.scrollY); | |
4905 | |
4906 // Handle explorer | |
4907 if (isIE) { | |
4908 // Handle simple | |
4909 if (r = b.rng) { | |
4910 try { | |
4911 r.select(); | |
4912 } catch (ex) { | |
4913 // Ignore | |
4914 } | |
4915 | |
4916 return true; | |
4917 } | |
4918 | |
4919 t.win.focus(); | |
4920 | |
4921 // Handle control bookmark | |
4922 if (b.tag) { | |
4923 r = ro.createControlRange(); | |
4924 | |
4925 each(t.dom.select(b.tag), function(n, i) { | |
4926 if (i == b.index) | |
4927 r.addElement(n); | |
4928 }); | |
4929 } else { | |
4930 // Try/catch needed since this operation breaks when TinyMCE is placed in hidden divs/tabs | |
4931 try { | |
4932 // Incorrect bookmark | |
4933 if (b.start < 0) | |
4934 return true; | |
4935 | |
4936 r = s.createRange(); | |
4937 r.moveToElementText(ro); | |
4938 r.collapse(true); | |
4939 r.moveStart('character', b.start); | |
4940 r.moveEnd('character', b.length); | |
4941 } catch (ex2) { | |
4942 return true; | |
4943 } | |
4944 } | |
4945 | |
4946 try { | |
4947 r.select(); | |
4948 } catch (ex) { | |
4949 // Needed for some odd IE bug #1843306 | |
4950 } | |
4951 | |
4952 return true; | |
4953 } | |
4954 | |
4955 // Handle W3C | |
4956 if (!s) | |
4957 return false; | |
4958 | |
4959 // Handle simple | |
4960 if (b.rng) { | |
4961 s.removeAllRanges(); | |
4962 s.addRange(b.rng); | |
4963 } else { | |
4964 if (is(b.start) && is(b.end)) { | |
4965 try { | |
4966 sd = getPos(ro, b.start, b.end); | |
4967 | |
4968 if (sd) { | |
4969 r = t.dom.doc.createRange(); | |
4970 r.setStart(sd.startNode, sd.startOffset); | |
4971 r.setEnd(sd.endNode, sd.endOffset); | |
4972 s.removeAllRanges(); | |
4973 s.addRange(r); | |
4974 } | |
4975 | |
4976 if (!tinymce.isOpera) | |
4977 t.win.focus(); | |
4978 } catch (ex) { | |
4979 // Ignore | |
4980 } | |
4981 } | |
4982 } | |
4983 }, | |
4984 | |
4985 select : function(n, c) { | |
4986 var t = this, r = t.getRng(), s = t.getSel(), b, fn, ln, d = t.win.document; | |
4987 | |
4988 function find(n, start) { | |
4989 var walker, o; | |
4990 | |
4991 if (n) { | |
4992 walker = d.createTreeWalker(n, NodeFilter.SHOW_TEXT, null, false); | |
4993 | |
4994 // Find first/last non empty text node | |
4995 while (n = walker.nextNode()) { | |
4996 o = n; | |
4997 | |
4998 if (tinymce.trim(n.nodeValue).length != 0) { | |
4999 if (start) | |
5000 return n; | |
5001 else | |
5002 o = n; | |
5003 } | |
5004 } | |
5005 } | |
5006 | |
5007 return o; | |
5008 }; | |
5009 | |
5010 if (isIE) { | |
5011 try { | |
5012 b = d.body; | |
5013 | |
5014 if (/^(IMG|TABLE)$/.test(n.nodeName)) { | |
5015 r = b.createControlRange(); | |
5016 r.addElement(n); | |
5017 } else { | |
5018 r = b.createTextRange(); | |
5019 r.moveToElementText(n); | |
5020 } | |
5021 | |
5022 r.select(); | |
5023 } catch (ex) { | |
5024 // Throws illigal agrument in IE some times | |
5025 } | |
5026 } else { | |
5027 if (c) { | |
5028 fn = find(n, 1) || t.dom.select('br:first', n)[0]; | |
5029 ln = find(n, 0) || t.dom.select('br:last', n)[0]; | |
5030 | |
5031 if (fn && ln) { | |
5032 r = d.createRange(); | |
5033 | |
5034 if (fn.nodeName == 'BR') | |
5035 r.setStartBefore(fn); | |
5036 else | |
5037 r.setStart(fn, 0); | |
5038 | |
5039 if (ln.nodeName == 'BR') | |
5040 r.setEndBefore(ln); | |
5041 else | |
5042 r.setEnd(ln, ln.nodeValue.length); | |
5043 } else | |
5044 r.selectNode(n); | |
5045 } else | |
5046 r.selectNode(n); | |
5047 | |
5048 t.setRng(r); | |
5049 } | |
5050 | |
5051 return n; | |
5052 }, | 5418 }, |
5053 | 5419 |
5054 isCollapsed : function() { | 5420 isCollapsed : function() { |
5055 var t = this, r = t.getRng(), s = t.getSel(); | 5421 var t = this, r = t.getRng(), s = t.getSel(); |
5056 | 5422 |
5057 if (!r || r.item) | 5423 if (!r || r.item) |
5058 return false; | 5424 return false; |
5059 | 5425 |
5060 return !s || r.boundingWidth == 0 || r.collapsed; | 5426 if (r.compareEndPoints) |
5427 return r.compareEndPoints('StartToEnd', r) === 0; | |
5428 | |
5429 return !s || r.collapsed; | |
5061 }, | 5430 }, |
5062 | 5431 |
5063 collapse : function(b) { | 5432 collapse : function(b) { |
5064 var t = this, r = t.getRng(), n; | 5433 var t = this, r = t.getRng(), n; |
5065 | 5434 |
5096 | 5465 |
5097 // No range found then create an empty one | 5466 // No range found then create an empty one |
5098 // This can occur when the editor is placed in a hidden container element on Gecko | 5467 // This can occur when the editor is placed in a hidden container element on Gecko |
5099 // Or on IE when there was an exception | 5468 // Or on IE when there was an exception |
5100 if (!r) | 5469 if (!r) |
5101 r = isIE ? t.win.document.body.createTextRange() : t.win.document.createRange(); | 5470 r = t.win.document.createRange ? t.win.document.createRange() : t.win.document.body.createTextRange(); |
5102 | 5471 |
5103 return r; | 5472 return r; |
5104 }, | 5473 }, |
5105 | 5474 |
5106 setRng : function(r) { | 5475 setRng : function(r) { |
5136 | 5505 |
5137 return n; | 5506 return n; |
5138 }, | 5507 }, |
5139 | 5508 |
5140 getNode : function() { | 5509 getNode : function() { |
5141 var t = this, r = t.getRng(), s = t.getSel(), e; | 5510 var t = this, rng = t.getRng(), sel = t.getSel(), elm; |
5142 | 5511 |
5143 if (!isIE) { | 5512 if (rng.setStart) { |
5144 // Range maybe lost after the editor is made visible again | 5513 // Range maybe lost after the editor is made visible again |
5145 if (!r) | 5514 if (!rng) |
5146 return t.dom.getRoot(); | 5515 return t.dom.getRoot(); |
5147 | 5516 |
5148 e = r.commonAncestorContainer; | 5517 elm = rng.commonAncestorContainer; |
5149 | 5518 |
5150 // Handle selection a image or other control like element such as anchors | 5519 // Handle selection a image or other control like element such as anchors |
5151 if (!r.collapsed) { | 5520 if (!rng.collapsed) { |
5152 // If the anchor node is a element instead of a text node then return this element | 5521 if (rng.startContainer == rng.endContainer) { |
5153 if (tinymce.isWebKit && s.anchorNode && s.anchorNode.nodeType == 1) | 5522 if (rng.startOffset - rng.endOffset < 2) { |
5154 return s.anchorNode.childNodes[s.anchorOffset]; | 5523 if (rng.startContainer.hasChildNodes()) |
5155 | 5524 elm = rng.startContainer.childNodes[rng.startOffset]; |
5156 if (r.startContainer == r.endContainer) { | |
5157 if (r.startOffset - r.endOffset < 2) { | |
5158 if (r.startContainer.hasChildNodes()) | |
5159 e = r.startContainer.childNodes[r.startOffset]; | |
5160 } | 5525 } |
5161 } | 5526 } |
5162 } | 5527 |
5163 | 5528 // If the anchor node is a element instead of a text node then return this element |
5164 return t.dom.getParent(e, '*'); | 5529 if (tinymce.isWebKit && sel.anchorNode && sel.anchorNode.nodeType == 1) |
5165 } | 5530 return sel.anchorNode.childNodes[sel.anchorOffset]; |
5166 | 5531 } |
5167 return r.item ? r.item(0) : r.parentElement(); | 5532 |
5533 if (elm && elm.nodeType == 3) | |
5534 return elm.parentNode; | |
5535 | |
5536 return elm; | |
5537 } | |
5538 | |
5539 return rng.item ? rng.item(0) : rng.parentElement(); | |
5168 }, | 5540 }, |
5169 | 5541 |
5170 getSelectedBlocks : function(st, en) { | 5542 getSelectedBlocks : function(st, en) { |
5171 var t = this, dom = t.dom, sb, eb, n, bl = []; | 5543 var t = this, dom = t.dom, sb, eb, n, bl = []; |
5172 | 5544 |
5193 | 5565 |
5194 destroy : function(s) { | 5566 destroy : function(s) { |
5195 var t = this; | 5567 var t = this; |
5196 | 5568 |
5197 t.win = null; | 5569 t.win = null; |
5570 | |
5571 if (t.tridentSel) | |
5572 t.tridentSel.destroy(); | |
5198 | 5573 |
5199 // Manual destroy then remove unload handler | 5574 // Manual destroy then remove unload handler |
5200 if (!s) | 5575 if (!s) |
5201 tinymce.removeUnload(t.destroy); | 5576 tinymce.removeUnload(t.destroy); |
5202 } | 5577 } |
5203 | 5578 }); |
5204 }); | |
5205 })(tinymce); | 5579 })(tinymce); |
5580 | |
5206 (function(tinymce) { | 5581 (function(tinymce) { |
5207 tinymce.create('tinymce.dom.XMLWriter', { | 5582 tinymce.create('tinymce.dom.XMLWriter', { |
5208 node : null, | 5583 node : null, |
5209 | 5584 |
5210 XMLWriter : function(s) { | 5585 XMLWriter : function(s) { |
5267 | 5642 |
5268 this.node.appendChild(this.doc.createTextNode(v)); | 5643 this.node.appendChild(this.doc.createTextNode(v)); |
5269 }, | 5644 }, |
5270 | 5645 |
5271 writeCDATA : function(v) { | 5646 writeCDATA : function(v) { |
5272 this.node.appendChild(this.doc.createCDATA(v)); | 5647 this.node.appendChild(this.doc.createCDATASection(v)); |
5273 }, | 5648 }, |
5274 | 5649 |
5275 writeComment : function(v) { | 5650 writeComment : function(v) { |
5276 // Fix for bug #2035694 | 5651 // Fix for bug #2035694 |
5277 if (tinymce.isIE) | 5652 if (tinymce.isIE) |
5290 if (this.valid) | 5665 if (this.valid) |
5291 h = h.replace(/\%MCGT%/g, '>'); | 5666 h = h.replace(/\%MCGT%/g, '>'); |
5292 | 5667 |
5293 return h; | 5668 return h; |
5294 } | 5669 } |
5295 | 5670 }); |
5296 }); | |
5297 })(tinymce); | 5671 })(tinymce); |
5672 | |
5298 (function(tinymce) { | 5673 (function(tinymce) { |
5299 tinymce.create('tinymce.dom.StringWriter', { | 5674 tinymce.create('tinymce.dom.StringWriter', { |
5300 str : null, | 5675 str : null, |
5301 tags : null, | 5676 tags : null, |
5302 count : 0, | 5677 count : 0, |
5304 indent : null, | 5679 indent : null, |
5305 | 5680 |
5306 StringWriter : function(s) { | 5681 StringWriter : function(s) { |
5307 this.settings = tinymce.extend({ | 5682 this.settings = tinymce.extend({ |
5308 indent_char : ' ', | 5683 indent_char : ' ', |
5309 indentation : 1 | 5684 indentation : 0 |
5310 }, s); | 5685 }, s); |
5311 | 5686 |
5312 this.reset(); | 5687 this.reset(); |
5313 }, | 5688 }, |
5314 | 5689 |
5417 | 5792 |
5418 this.writeRaw('>'); | 5793 this.writeRaw('>'); |
5419 | 5794 |
5420 return true; | 5795 return true; |
5421 } | 5796 } |
5422 | 5797 }); |
5423 }); | |
5424 })(tinymce); | 5798 })(tinymce); |
5799 | |
5425 (function(tinymce) { | 5800 (function(tinymce) { |
5426 // Shorten names | 5801 // Shorten names |
5427 var extend = tinymce.extend, each = tinymce.each, Dispatcher = tinymce.util.Dispatcher, isIE = tinymce.isIE, isGecko = tinymce.isGecko; | 5802 var extend = tinymce.extend, each = tinymce.each, Dispatcher = tinymce.util.Dispatcher, isIE = tinymce.isIE, isGecko = tinymce.isGecko; |
5428 | 5803 |
5429 function wildcardToRE(s) { | 5804 function wildcardToRE(s) { |
5449 t.settings = s = extend({ | 5824 t.settings = s = extend({ |
5450 dom : tinymce.DOM, | 5825 dom : tinymce.DOM, |
5451 valid_nodes : 0, | 5826 valid_nodes : 0, |
5452 node_filter : 0, | 5827 node_filter : 0, |
5453 attr_filter : 0, | 5828 attr_filter : 0, |
5454 invalid_attrs : /^(mce_|_moz_)/, | 5829 invalid_attrs : /^(_mce_|_moz_|sizset|sizcache)/, |
5455 closed : /(br|hr|input|meta|img|link|param)/, | 5830 closed : /^(br|hr|input|meta|img|link|param|area)$/, |
5456 entity_encoding : 'named', | 5831 entity_encoding : 'named', |
5457 entities : '160,nbsp,161,iexcl,162,cent,163,pound,164,curren,165,yen,166,brvbar,167,sect,168,uml,169,copy,170,ordf,171,laquo,172,not,173,shy,174,reg,175,macr,176,deg,177,plusmn,178,sup2,179,sup3,180,acute,181,micro,182,para,183,middot,184,cedil,185,sup1,186,ordm,187,raquo,188,frac14,189,frac12,190,frac34,191,iquest,192,Agrave,193,Aacute,194,Acirc,195,Atilde,196,Auml,197,Aring,198,AElig,199,Ccedil,200,Egrave,201,Eacute,202,Ecirc,203,Euml,204,Igrave,205,Iacute,206,Icirc,207,Iuml,208,ETH,209,Ntilde,210,Ograve,211,Oacute,212,Ocirc,213,Otilde,214,Ouml,215,times,216,Oslash,217,Ugrave,218,Uacute,219,Ucirc,220,Uuml,221,Yacute,222,THORN,223,szlig,224,agrave,225,aacute,226,acirc,227,atilde,228,auml,229,aring,230,aelig,231,ccedil,232,egrave,233,eacute,234,ecirc,235,euml,236,igrave,237,iacute,238,icirc,239,iuml,240,eth,241,ntilde,242,ograve,243,oacute,244,ocirc,245,otilde,246,ouml,247,divide,248,oslash,249,ugrave,250,uacute,251,ucirc,252,uuml,253,yacute,254,thorn,255,yuml,402,fnof,913,Alpha,914,Beta,915,Gamma,916,Delta,917,Epsilon,918,Zeta,919,Eta,920,Theta,921,Iota,922,Kappa,923,Lambda,924,Mu,925,Nu,926,Xi,927,Omicron,928,Pi,929,Rho,931,Sigma,932,Tau,933,Upsilon,934,Phi,935,Chi,936,Psi,937,Omega,945,alpha,946,beta,947,gamma,948,delta,949,epsilon,950,zeta,951,eta,952,theta,953,iota,954,kappa,955,lambda,956,mu,957,nu,958,xi,959,omicron,960,pi,961,rho,962,sigmaf,963,sigma,964,tau,965,upsilon,966,phi,967,chi,968,psi,969,omega,977,thetasym,978,upsih,982,piv,8226,bull,8230,hellip,8242,prime,8243,Prime,8254,oline,8260,frasl,8472,weierp,8465,image,8476,real,8482,trade,8501,alefsym,8592,larr,8593,uarr,8594,rarr,8595,darr,8596,harr,8629,crarr,8656,lArr,8657,uArr,8658,rArr,8659,dArr,8660,hArr,8704,forall,8706,part,8707,exist,8709,empty,8711,nabla,8712,isin,8713,notin,8715,ni,8719,prod,8721,sum,8722,minus,8727,lowast,8730,radic,8733,prop,8734,infin,8736,ang,8743,and,8744,or,8745,cap,8746,cup,8747,int,8756,there4,8764,sim,8773,cong,8776,asymp,8800,ne,8801,equiv,8804,le,8805,ge,8834,sub,8835,sup,8836,nsub,8838,sube,8839,supe,8853,oplus,8855,otimes,8869,perp,8901,sdot,8968,lceil,8969,rceil,8970,lfloor,8971,rfloor,9001,lang,9002,rang,9674,loz,9824,spades,9827,clubs,9829,hearts,9830,diams,338,OElig,339,oelig,352,Scaron,353,scaron,376,Yuml,710,circ,732,tilde,8194,ensp,8195,emsp,8201,thinsp,8204,zwnj,8205,zwj,8206,lrm,8207,rlm,8211,ndash,8212,mdash,8216,lsquo,8217,rsquo,8218,sbquo,8220,ldquo,8221,rdquo,8222,bdquo,8224,dagger,8225,Dagger,8240,permil,8249,lsaquo,8250,rsaquo,8364,euro', | 5832 entities : '160,nbsp,161,iexcl,162,cent,163,pound,164,curren,165,yen,166,brvbar,167,sect,168,uml,169,copy,170,ordf,171,laquo,172,not,173,shy,174,reg,175,macr,176,deg,177,plusmn,178,sup2,179,sup3,180,acute,181,micro,182,para,183,middot,184,cedil,185,sup1,186,ordm,187,raquo,188,frac14,189,frac12,190,frac34,191,iquest,192,Agrave,193,Aacute,194,Acirc,195,Atilde,196,Auml,197,Aring,198,AElig,199,Ccedil,200,Egrave,201,Eacute,202,Ecirc,203,Euml,204,Igrave,205,Iacute,206,Icirc,207,Iuml,208,ETH,209,Ntilde,210,Ograve,211,Oacute,212,Ocirc,213,Otilde,214,Ouml,215,times,216,Oslash,217,Ugrave,218,Uacute,219,Ucirc,220,Uuml,221,Yacute,222,THORN,223,szlig,224,agrave,225,aacute,226,acirc,227,atilde,228,auml,229,aring,230,aelig,231,ccedil,232,egrave,233,eacute,234,ecirc,235,euml,236,igrave,237,iacute,238,icirc,239,iuml,240,eth,241,ntilde,242,ograve,243,oacute,244,ocirc,245,otilde,246,ouml,247,divide,248,oslash,249,ugrave,250,uacute,251,ucirc,252,uuml,253,yacute,254,thorn,255,yuml,402,fnof,913,Alpha,914,Beta,915,Gamma,916,Delta,917,Epsilon,918,Zeta,919,Eta,920,Theta,921,Iota,922,Kappa,923,Lambda,924,Mu,925,Nu,926,Xi,927,Omicron,928,Pi,929,Rho,931,Sigma,932,Tau,933,Upsilon,934,Phi,935,Chi,936,Psi,937,Omega,945,alpha,946,beta,947,gamma,948,delta,949,epsilon,950,zeta,951,eta,952,theta,953,iota,954,kappa,955,lambda,956,mu,957,nu,958,xi,959,omicron,960,pi,961,rho,962,sigmaf,963,sigma,964,tau,965,upsilon,966,phi,967,chi,968,psi,969,omega,977,thetasym,978,upsih,982,piv,8226,bull,8230,hellip,8242,prime,8243,Prime,8254,oline,8260,frasl,8472,weierp,8465,image,8476,real,8482,trade,8501,alefsym,8592,larr,8593,uarr,8594,rarr,8595,darr,8596,harr,8629,crarr,8656,lArr,8657,uArr,8658,rArr,8659,dArr,8660,hArr,8704,forall,8706,part,8707,exist,8709,empty,8711,nabla,8712,isin,8713,notin,8715,ni,8719,prod,8721,sum,8722,minus,8727,lowast,8730,radic,8733,prop,8734,infin,8736,ang,8743,and,8744,or,8745,cap,8746,cup,8747,int,8756,there4,8764,sim,8773,cong,8776,asymp,8800,ne,8801,equiv,8804,le,8805,ge,8834,sub,8835,sup,8836,nsub,8838,sube,8839,supe,8853,oplus,8855,otimes,8869,perp,8901,sdot,8968,lceil,8969,rceil,8970,lfloor,8971,rfloor,9001,lang,9002,rang,9674,loz,9824,spades,9827,clubs,9829,hearts,9830,diams,338,OElig,339,oelig,352,Scaron,353,scaron,376,Yuml,710,circ,732,tilde,8194,ensp,8195,emsp,8201,thinsp,8204,zwnj,8205,zwj,8206,lrm,8207,rlm,8211,ndash,8212,mdash,8216,lsquo,8217,rsquo,8218,sbquo,8220,ldquo,8221,rdquo,8222,bdquo,8224,dagger,8225,Dagger,8240,permil,8249,lsaquo,8250,rsaquo,8364,euro', |
5458 bool_attrs : /(checked|disabled|readonly|selected|nowrap)/, | |
5459 valid_elements : '*[*]', | 5833 valid_elements : '*[*]', |
5460 extended_valid_elements : 0, | 5834 extended_valid_elements : 0, |
5461 valid_child_elements : 0, | |
5462 invalid_elements : 0, | 5835 invalid_elements : 0, |
5463 fix_table_elements : 1, | 5836 fix_table_elements : 1, |
5464 fix_list_elements : true, | 5837 fix_list_elements : true, |
5465 fix_content_duplication : true, | 5838 fix_content_duplication : true, |
5466 convert_fonts_to_spans : false, | 5839 convert_fonts_to_spans : false, |
5467 font_size_classes : 0, | 5840 font_size_classes : 0, |
5468 font_size_style_values : 0, | |
5469 apply_source_formatting : 0, | 5841 apply_source_formatting : 0, |
5470 indent_mode : 'simple', | 5842 indent_mode : 'simple', |
5471 indent_char : '\t', | 5843 indent_char : '\t', |
5472 indent_levels : 1, | 5844 indent_levels : 1, |
5473 remove_linebreaks : 1, | 5845 remove_linebreaks : 1, |
5474 remove_redundant_brs : 1, | 5846 remove_redundant_brs : 1, |
5475 element_format : 'xhtml' | 5847 element_format : 'xhtml' |
5476 }, s); | 5848 }, s); |
5477 | 5849 |
5478 t.dom = s.dom; | 5850 t.dom = s.dom; |
5851 t.schema = s.schema; | |
5852 | |
5853 // Use raw entities if no entities are defined | |
5854 if (s.entity_encoding == 'named' && !s.entities) | |
5855 s.entity_encoding = 'raw'; | |
5479 | 5856 |
5480 if (s.remove_redundant_brs) { | 5857 if (s.remove_redundant_brs) { |
5481 t.onPostProcess.add(function(se, o) { | 5858 t.onPostProcess.add(function(se, o) { |
5482 // Remove BR elements at end of list elements since they get rendered in IE | 5859 // Remove single BR at end of block elements since they get rendered |
5483 o.content = o.content.replace(/<br \/>(\s*<\/li>)/g, '$1'); | 5860 o.content = o.content.replace(/(<br \/>\s*)+<\/(p|h[1-6]|div|li)>/gi, function(a, b, c) { |
5861 // Check if it's a single element | |
5862 if (/^<br \/>\s*<\//.test(a)) | |
5863 return '</' + c + '>'; | |
5864 | |
5865 return a; | |
5866 }); | |
5484 }); | 5867 }); |
5485 } | 5868 } |
5486 | 5869 |
5487 // Remove XHTML element endings i.e. produce crap :) XHTML is better | 5870 // Remove XHTML element endings i.e. produce crap :) XHTML is better |
5488 if (s.element_format == 'html') { | 5871 if (s.element_format == 'html') { |
5531 }); | 5914 }); |
5532 } | 5915 } |
5533 | 5916 |
5534 if (s.fix_table_elements) { | 5917 if (s.fix_table_elements) { |
5535 t.onPreProcess.add(function(se, o) { | 5918 t.onPreProcess.add(function(se, o) { |
5536 each(t.dom.select('p table', o.node), function(n) { | 5919 // Since Opera will crash if you attach the node to a dynamic document we need to brrowser sniff a specific build |
5537 t.dom.split(t.dom.getParent(n, 'p'), n); | 5920 // so Opera users with an older version will have to live with less compaible output not much we can do here |
5538 }); | 5921 if (!tinymce.isOpera || opera.buildNumber() >= 1767) { |
5922 each(t.dom.select('p table', o.node).reverse(), function(n) { | |
5923 var parent = t.dom.getParent(n.parentNode, 'table,p'); | |
5924 | |
5925 if (parent.nodeName != 'TABLE') { | |
5926 try { | |
5927 t.dom.split(parent, n); | |
5928 } catch (ex) { | |
5929 // IE can sometimes fire an unknown runtime error so we just ignore it | |
5930 } | |
5931 } | |
5932 }); | |
5933 } | |
5539 }); | 5934 }); |
5540 } | 5935 } |
5541 }, | 5936 }, |
5542 | 5937 |
5543 setEntities : function(s) { | 5938 setEntities : function(s) { |
5544 var t = this, a, i, l = {}, re = '', v; | 5939 var t = this, a, i, l = {}, v; |
5545 | 5940 |
5546 // No need to setup more than once | 5941 // No need to setup more than once |
5547 if (t.entityLookup) | 5942 if (t.entityLookup) |
5548 return; | 5943 return; |
5549 | 5944 |
5557 continue; | 5952 continue; |
5558 | 5953 |
5559 l[String.fromCharCode(a[i])] = a[i + 1]; | 5954 l[String.fromCharCode(a[i])] = a[i + 1]; |
5560 | 5955 |
5561 v = parseInt(a[i]).toString(16); | 5956 v = parseInt(a[i]).toString(16); |
5562 re += '\\u' + '0000'.substring(v.length) + v; | 5957 } |
5563 } | 5958 |
5564 | |
5565 if (!re) { | |
5566 t.settings.entity_encoding = 'raw'; | |
5567 return; | |
5568 } | |
5569 | |
5570 t.entitiesRE = new RegExp('[' + re + ']', 'g'); | |
5571 t.entityLookup = l; | 5959 t.entityLookup = l; |
5572 }, | |
5573 | |
5574 setValidChildRules : function(s) { | |
5575 this.childRules = null; | |
5576 this.addValidChildRules(s); | |
5577 }, | |
5578 | |
5579 addValidChildRules : function(s) { | |
5580 var t = this, inst, intr, bloc; | |
5581 | |
5582 if (!s) | |
5583 return; | |
5584 | |
5585 inst = 'A|BR|SPAN|BDO|MAP|OBJECT|IMG|TT|I|B|BIG|SMALL|EM|STRONG|DFN|CODE|Q|SAMP|KBD|VAR|CITE|ABBR|ACRONYM|SUB|SUP|#text|#comment'; | |
5586 intr = 'A|BR|SPAN|BDO|OBJECT|APPLET|IMG|MAP|IFRAME|TT|I|B|U|S|STRIKE|BIG|SMALL|FONT|BASEFONT|EM|STRONG|DFN|CODE|Q|SAMP|KBD|VAR|CITE|ABBR|ACRONYM|SUB|SUP|INPUT|SELECT|TEXTAREA|LABEL|BUTTON|#text|#comment'; | |
5587 bloc = 'H[1-6]|P|DIV|ADDRESS|PRE|FORM|TABLE|LI|OL|UL|TD|CAPTION|BLOCKQUOTE|CENTER|DL|DT|DD|DIR|FIELDSET|FORM|NOSCRIPT|NOFRAMES|MENU|ISINDEX|SAMP'; | |
5588 | |
5589 each(s.split(','), function(s) { | |
5590 var p = s.split(/\[|\]/), re; | |
5591 | |
5592 s = ''; | |
5593 each(p[1].split('|'), function(v) { | |
5594 if (s) | |
5595 s += '|'; | |
5596 | |
5597 switch (v) { | |
5598 case '%itrans': | |
5599 v = intr; | |
5600 break; | |
5601 | |
5602 case '%itrans_na': | |
5603 v = intr.substring(2); | |
5604 break; | |
5605 | |
5606 case '%istrict': | |
5607 v = inst; | |
5608 break; | |
5609 | |
5610 case '%istrict_na': | |
5611 v = inst.substring(2); | |
5612 break; | |
5613 | |
5614 case '%btrans': | |
5615 v = bloc; | |
5616 break; | |
5617 | |
5618 case '%bstrict': | |
5619 v = bloc; | |
5620 break; | |
5621 } | |
5622 | |
5623 s += v; | |
5624 }); | |
5625 re = new RegExp('^(' + s.toLowerCase() + ')$', 'i'); | |
5626 | |
5627 each(p[0].split('/'), function(s) { | |
5628 t.childRules = t.childRules || {}; | |
5629 t.childRules[s] = re; | |
5630 }); | |
5631 }); | |
5632 | |
5633 // Build regex | |
5634 s = ''; | |
5635 each(t.childRules, function(v, k) { | |
5636 if (s) | |
5637 s += '|'; | |
5638 | |
5639 s += k; | |
5640 }); | |
5641 | |
5642 t.parentElementsRE = new RegExp('^(' + s.toLowerCase() + ')$', 'i'); | |
5643 | |
5644 /*console.debug(t.parentElementsRE.toString()); | |
5645 each(t.childRules, function(v) { | |
5646 console.debug(v.toString()); | |
5647 });*/ | |
5648 }, | 5960 }, |
5649 | 5961 |
5650 setRules : function(s) { | 5962 setRules : function(s) { |
5651 var t = this; | 5963 var t = this; |
5652 | 5964 |
5849 | 6161 |
5850 return null; | 6162 return null; |
5851 }, | 6163 }, |
5852 | 6164 |
5853 serialize : function(n, o) { | 6165 serialize : function(n, o) { |
5854 var h, t = this; | 6166 var h, t = this, doc, oldDoc, impl, selected; |
5855 | 6167 |
5856 t._setup(); | 6168 t._setup(); |
5857 o = o || {}; | 6169 o = o || {}; |
5858 o.format = o.format || 'html'; | 6170 o.format = o.format || 'html'; |
5859 t.processObj = o; | 6171 t.processObj = o; |
6172 | |
6173 // IE looses the selected attribute on option elements so we need to store it | |
6174 // See: http://support.microsoft.com/kb/829907 | |
6175 if (isIE) { | |
6176 selected = []; | |
6177 each(n.getElementsByTagName('option'), function(n) { | |
6178 var v = t.dom.getAttrib(n, 'selected'); | |
6179 | |
6180 selected.push(v ? v : null); | |
6181 }); | |
6182 } | |
6183 | |
5860 n = n.cloneNode(true); | 6184 n = n.cloneNode(true); |
6185 | |
6186 // IE looses the selected attribute on option elements so we need to restore it | |
6187 if (isIE) { | |
6188 each(n.getElementsByTagName('option'), function(n, i) { | |
6189 t.dom.setAttrib(n, 'selected', selected[i]); | |
6190 }); | |
6191 } | |
6192 | |
6193 // Nodes needs to be attached to something in WebKit/Opera | |
6194 // Older builds of Opera crashes if you attach the node to an document created dynamically | |
6195 // and since we can't feature detect a crash we need to sniff the acutal build number | |
6196 // This fix will make DOM ranges and make Sizzle happy! | |
6197 impl = n.ownerDocument.implementation; | |
6198 if (impl.createHTMLDocument && (tinymce.isOpera && opera.buildNumber() >= 1767)) { | |
6199 // Create an empty HTML document | |
6200 doc = impl.createHTMLDocument(""); | |
6201 | |
6202 // Add the element or it's children if it's a body element to the new document | |
6203 each(n.nodeName == 'BODY' ? n.childNodes : [n], function(node) { | |
6204 doc.body.appendChild(doc.importNode(node, true)); | |
6205 }); | |
6206 | |
6207 // Grab first child or body element for serialization | |
6208 if (n.nodeName != 'BODY') | |
6209 n = doc.body.firstChild; | |
6210 else | |
6211 n = doc.body; | |
6212 | |
6213 // set the new document in DOMUtils so createElement etc works | |
6214 oldDoc = t.dom.doc; | |
6215 t.dom.doc = doc; | |
6216 } | |
6217 | |
5861 t.key = '' + (parseInt(t.key) + 1); | 6218 t.key = '' + (parseInt(t.key) + 1); |
5862 | 6219 |
5863 // Pre process | 6220 // Pre process |
5864 if (!o.no_events) { | 6221 if (!o.no_events) { |
5865 o.node = n; | 6222 o.node = n; |
5866 t.onPreProcess.dispatch(t, o); | 6223 t.onPreProcess.dispatch(t, o); |
5867 } | 6224 } |
5868 | 6225 |
5869 // Serialize HTML DOM into a string | 6226 // Serialize HTML DOM into a string |
5870 t.writer.reset(); | 6227 t.writer.reset(); |
6228 t._info = o; | |
5871 t._serializeNode(n, o.getInner); | 6229 t._serializeNode(n, o.getInner); |
5872 | 6230 |
5873 // Post process | 6231 // Post process |
5874 o.content = t.writer.getContent(); | 6232 o.content = t.writer.getContent(); |
6233 | |
6234 // Restore the old document if it was changed | |
6235 if (oldDoc) | |
6236 t.dom.doc = oldDoc; | |
5875 | 6237 |
5876 if (!o.no_events) | 6238 if (!o.no_events) |
5877 t.onPostProcess.dispatch(t, o); | 6239 t.onPostProcess.dispatch(t, o); |
5878 | 6240 |
5879 t._postProcess(o); | 6241 t._postProcess(o); |
5891 // Protect some elements | 6253 // Protect some elements |
5892 p = t._protect({ | 6254 p = t._protect({ |
5893 content : h, | 6255 content : h, |
5894 patterns : [ | 6256 patterns : [ |
5895 {pattern : /(<script[^>]*>)(.*?)(<\/script>)/g}, | 6257 {pattern : /(<script[^>]*>)(.*?)(<\/script>)/g}, |
6258 {pattern : /(<noscript[^>]*>)(.*?)(<\/noscript>)/g}, | |
5896 {pattern : /(<style[^>]*>)(.*?)(<\/style>)/g}, | 6259 {pattern : /(<style[^>]*>)(.*?)(<\/style>)/g}, |
5897 {pattern : /(<pre[^>]*>)(.*?)(<\/pre>)/g, encode : 1}, | 6260 {pattern : /(<pre[^>]*>)(.*?)(<\/pre>)/g, encode : 1}, |
5898 {pattern : /(<!--\[CDATA\[)(.*?)(\]\]-->)/g} | 6261 {pattern : /(<!--\[CDATA\[)(.*?)(\]\]-->)/g} |
5899 ] | 6262 ] |
5900 }); | 6263 }); |
5943 h = h.replace(/<!--\[CDATA\[([\s\S]+)\]\]-->/g, '<![CDATA[$1]]>'); | 6306 h = h.replace(/<!--\[CDATA\[([\s\S]+)\]\]-->/g, '<![CDATA[$1]]>'); |
5944 | 6307 |
5945 // Restore the \u00a0 character if raw mode is enabled | 6308 // Restore the \u00a0 character if raw mode is enabled |
5946 if (s.entity_encoding == 'raw') | 6309 if (s.entity_encoding == 'raw') |
5947 h = h.replace(/<p> <\/p>|<p([^>]+)> <\/p>/g, '<p$1>\u00a0</p>'); | 6310 h = h.replace(/<p> <\/p>|<p([^>]+)> <\/p>/g, '<p$1>\u00a0</p>'); |
6311 | |
6312 // Restore noscript elements | |
6313 h = h.replace(/<noscript([^>]+|)>([\s\S]*?)<\/noscript>/g, function(v, attribs, text) { | |
6314 return '<noscript' + attribs + '>' + t.dom.decode(text.replace(/<!--|-->/g, '')) + '</noscript>'; | |
6315 }); | |
5948 } | 6316 } |
5949 | 6317 |
5950 o.content = h; | 6318 o.content = h; |
5951 }, | 6319 }, |
5952 | 6320 |
5953 _serializeNode : function(n, inn) { | 6321 _serializeNode : function(n, inner) { |
5954 var t = this, s = t.settings, w = t.writer, hc, el, cn, i, l, a, at, no, v, nn, ru, ar, iv; | 6322 var t = this, s = t.settings, w = t.writer, hc, el, cn, i, l, a, at, no, v, nn, ru, ar, iv, closed, keep, type; |
5955 | 6323 |
5956 if (!s.node_filter || s.node_filter(n)) { | 6324 if (!s.node_filter || s.node_filter(n)) { |
5957 switch (n.nodeType) { | 6325 switch (n.nodeType) { |
5958 case 1: // Element | 6326 case 1: // Element |
5959 if (n.hasAttribute ? n.hasAttribute('mce_bogus') : n.getAttribute('mce_bogus')) | 6327 if (n.hasAttribute ? n.hasAttribute('_mce_bogus') : n.getAttribute('_mce_bogus')) |
5960 return; | 6328 return; |
5961 | 6329 |
5962 iv = false; | 6330 iv = keep = false; |
5963 hc = n.hasChildNodes(); | 6331 hc = n.hasChildNodes(); |
5964 nn = n.getAttribute('mce_name') || n.nodeName.toLowerCase(); | 6332 nn = n.getAttribute('_mce_name') || n.nodeName.toLowerCase(); |
6333 | |
6334 // Get internal type | |
6335 type = n.getAttribute('_mce_type'); | |
6336 if (type) { | |
6337 if (!t._info.cleanup) { | |
6338 iv = true; | |
6339 return; | |
6340 } else | |
6341 keep = 1; | |
6342 } | |
5965 | 6343 |
5966 // Add correct prefix on IE | 6344 // Add correct prefix on IE |
5967 if (isIE) { | 6345 if (isIE) { |
5968 if (n.scopeName !== 'HTML' && n.scopeName !== 'html') | 6346 if (n.scopeName !== 'HTML' && n.scopeName !== 'html') |
5969 nn = n.scopeName + ':' + nn; | 6347 nn = n.scopeName + ':' + nn; |
5972 // Remove mce prefix on IE needed for the abbr element | 6350 // Remove mce prefix on IE needed for the abbr element |
5973 if (nn.indexOf('mce:') === 0) | 6351 if (nn.indexOf('mce:') === 0) |
5974 nn = nn.substring(4); | 6352 nn = nn.substring(4); |
5975 | 6353 |
5976 // Check if valid | 6354 // Check if valid |
5977 if (!t.validElementsRE.test(nn) || (t.invalidElementsRE && t.invalidElementsRE.test(nn)) || inn) { | 6355 if (!keep) { |
5978 iv = true; | 6356 if (!t.validElementsRE || !t.validElementsRE.test(nn) || (t.invalidElementsRE && t.invalidElementsRE.test(nn)) || inner) { |
5979 break; | 6357 iv = true; |
6358 break; | |
6359 } | |
5980 } | 6360 } |
5981 | 6361 |
5982 if (isIE) { | 6362 if (isIE) { |
5983 // Fix IE content duplication (DOM can have multiple copies of the same node) | 6363 // Fix IE content duplication (DOM can have multiple copies of the same node) |
5984 if (s.fix_content_duplication) { | 6364 if (s.fix_content_duplication) { |
5985 if (n.mce_serialized == t.key) | 6365 if (n._mce_serialized == t.key) |
5986 return; | 6366 return; |
5987 | 6367 |
5988 n.mce_serialized = t.key; | 6368 n._mce_serialized = t.key; |
5989 } | 6369 } |
5990 | 6370 |
5991 // IE sometimes adds a / infront of the node name | 6371 // IE sometimes adds a / infront of the node name |
5992 if (nn.charAt(0) == '/') | 6372 if (nn.charAt(0) == '/') |
5993 nn = nn.substring(1); | 6373 nn = nn.substring(1); |
5996 if (n.nodeName === 'BR' && n.getAttribute('type') == '_moz') | 6376 if (n.nodeName === 'BR' && n.getAttribute('type') == '_moz') |
5997 return; | 6377 return; |
5998 } | 6378 } |
5999 | 6379 |
6000 // Check if valid child | 6380 // Check if valid child |
6001 if (t.childRules) { | 6381 if (s.validate_children) { |
6002 if (t.parentElementsRE.test(t.elementName)) { | 6382 if (t.elementName && !t.schema.isValid(t.elementName, nn)) { |
6003 if (!t.childRules[t.elementName].test(nn)) { | 6383 iv = true; |
6004 iv = true; | 6384 break; |
6005 break; | |
6006 } | |
6007 } | 6385 } |
6008 | 6386 |
6009 t.elementName = nn; | 6387 t.elementName = nn; |
6010 } | 6388 } |
6011 | 6389 |
6012 ru = t.findRule(nn); | 6390 ru = t.findRule(nn); |
6391 | |
6392 // No valid rule for this element could be found then skip it | |
6393 if (!ru) { | |
6394 iv = true; | |
6395 break; | |
6396 } | |
6397 | |
6013 nn = ru.name || nn; | 6398 nn = ru.name || nn; |
6399 closed = s.closed.test(nn); | |
6014 | 6400 |
6015 // Skip empty nodes or empty node name in IE | 6401 // Skip empty nodes or empty node name in IE |
6016 if ((!hc && ru.noEmpty) || (isIE && !nn)) { | 6402 if ((!hc && ru.noEmpty) || (isIE && !nn)) { |
6017 iv = true; | 6403 iv = true; |
6018 break; | 6404 break; |
6066 w.writeAttribute(a, v); | 6452 w.writeAttribute(a, v); |
6067 } | 6453 } |
6068 } | 6454 } |
6069 } | 6455 } |
6070 | 6456 |
6457 // Keep type attribute | |
6458 if (type && keep) | |
6459 w.writeAttribute('_mce_type', type); | |
6460 | |
6461 // Write text from script | |
6462 if (nn === 'script' && tinymce.trim(n.innerHTML)) { | |
6463 w.writeText('// '); // Padd it with a comment so it will parse on older browsers | |
6464 w.writeCDATA(n.innerHTML.replace(/<!--|-->|<\[CDATA\[|\]\]>/g, '')); // Remove comments and cdata stuctures | |
6465 hc = false; | |
6466 break; | |
6467 } | |
6468 | |
6071 // Padd empty nodes with a | 6469 // Padd empty nodes with a |
6072 if (ru.padd) { | 6470 if (ru.padd) { |
6073 // If it has only one bogus child, padd it anyway workaround for <td><br /></td> bug | 6471 // If it has only one bogus child, padd it anyway workaround for <td><br /></td> bug |
6074 if (hc && (cn = n.firstChild) && cn.nodeType === 1 && n.childNodes.length === 1) { | 6472 if (hc && (cn = n.firstChild) && cn.nodeType === 1 && n.childNodes.length === 1) { |
6075 if (cn.hasAttribute ? cn.hasAttribute('mce_bogus') : cn.getAttribute('mce_bogus')) | 6473 if (cn.hasAttribute ? cn.hasAttribute('_mce_bogus') : cn.getAttribute('_mce_bogus')) |
6076 w.writeText('\u00a0'); | 6474 w.writeText('\u00a0'); |
6077 } else if (!hc) | 6475 } else if (!hc) |
6078 w.writeText('\u00a0'); // No children then padd it | 6476 w.writeText('\u00a0'); // No children then padd it |
6079 } | 6477 } |
6080 | 6478 |
6081 break; | 6479 break; |
6082 | 6480 |
6083 case 3: // Text | 6481 case 3: // Text |
6084 // Check if valid child | 6482 // Check if valid child |
6085 if (t.childRules && t.parentElementsRE.test(t.elementName)) { | 6483 if (s.validate_children && t.elementName && !t.schema.isValid(t.elementName, '#text')) |
6086 if (!t.childRules[t.elementName].test(n.nodeName)) | 6484 return; |
6087 return; | |
6088 } | |
6089 | 6485 |
6090 return w.writeText(n.nodeValue); | 6486 return w.writeText(n.nodeValue); |
6091 | 6487 |
6092 case 4: // CDATA | 6488 case 4: // CDATA |
6093 return w.writeCDATA(n.nodeValue); | 6489 return w.writeCDATA(n.nodeValue); |
6096 return w.writeComment(n.nodeValue); | 6492 return w.writeComment(n.nodeValue); |
6097 } | 6493 } |
6098 } else if (n.nodeType == 1) | 6494 } else if (n.nodeType == 1) |
6099 hc = n.hasChildNodes(); | 6495 hc = n.hasChildNodes(); |
6100 | 6496 |
6101 if (hc) { | 6497 if (hc && !closed) { |
6102 cn = n.firstChild; | 6498 cn = n.firstChild; |
6103 | 6499 |
6104 while (cn) { | 6500 while (cn) { |
6105 t._serializeNode(cn); | 6501 t._serializeNode(cn); |
6106 t.elementName = nn; | 6502 t.elementName = nn; |
6108 } | 6504 } |
6109 } | 6505 } |
6110 | 6506 |
6111 // Write element end | 6507 // Write element end |
6112 if (!iv) { | 6508 if (!iv) { |
6113 if (hc || !s.closed.test(nn)) | 6509 if (!closed) |
6114 w.writeFullEndElement(); | 6510 w.writeFullEndElement(); |
6115 else | 6511 else |
6116 w.writeEndElement(); | 6512 w.writeEndElement(); |
6117 } | 6513 } |
6118 }, | 6514 }, |
6176 if (s.entity_encoding !== 'raw') { | 6572 if (s.entity_encoding !== 'raw') { |
6177 if (s.entity_encoding.indexOf('named') != -1) { | 6573 if (s.entity_encoding.indexOf('named') != -1) { |
6178 t.setEntities(s.entities); | 6574 t.setEntities(s.entities); |
6179 l = t.entityLookup; | 6575 l = t.entityLookup; |
6180 | 6576 |
6181 h = h.replace(t.entitiesRE, function(a) { | 6577 h = h.replace(/[\u007E-\uFFFF]/g, function(a) { |
6182 var v; | 6578 var v; |
6183 | 6579 |
6184 if (v = l[a]) | 6580 if (v = l[a]) |
6185 a = '&' + v + ';'; | 6581 a = '&' + v + ';'; |
6186 | 6582 |
6206 | 6602 |
6207 t.done = 1; | 6603 t.done = 1; |
6208 | 6604 |
6209 t.setRules(s.valid_elements); | 6605 t.setRules(s.valid_elements); |
6210 t.addRules(s.extended_valid_elements); | 6606 t.addRules(s.extended_valid_elements); |
6211 t.addValidChildRules(s.valid_child_elements); | |
6212 | 6607 |
6213 if (s.invalid_elements) | 6608 if (s.invalid_elements) |
6214 t.invalidElementsRE = new RegExp('^(' + wildcardToRE(s.invalid_elements.replace(/,/g, '|').toLowerCase()) + ')$'); | 6609 t.invalidElementsRE = new RegExp('^(' + wildcardToRE(s.invalid_elements.replace(/,/g, '|').toLowerCase()) + ')$'); |
6215 | 6610 |
6216 if (s.attrib_value_filter) | 6611 if (s.attrib_value_filter) |
6228 | 6623 |
6229 return v; | 6624 return v; |
6230 } | 6625 } |
6231 | 6626 |
6232 v = this.dom.getAttrib(n, na); | 6627 v = this.dom.getAttrib(n, na); |
6233 | |
6234 // Bool attr | |
6235 if (this.settings.bool_attrs.test(na) && v) { | |
6236 v = ('' + v).toLowerCase(); | |
6237 | |
6238 if (v === 'false' || v === '0') | |
6239 return null; | |
6240 | |
6241 v = na; | |
6242 } | |
6243 | 6628 |
6244 switch (na) { | 6629 switch (na) { |
6245 case 'rowspan': | 6630 case 'rowspan': |
6246 case 'colspan': | 6631 case 'colspan': |
6247 // Whats the point? Remove usless attribute value | 6632 // Whats the point? Remove usless attribute value |
6281 return null; | 6666 return null; |
6282 | 6667 |
6283 | 6668 |
6284 return v; | 6669 return v; |
6285 } | 6670 } |
6286 | 6671 }); |
6287 }); | |
6288 })(tinymce); | 6672 })(tinymce); |
6673 | |
6289 (function(tinymce) { | 6674 (function(tinymce) { |
6290 var each = tinymce.each, Event = tinymce.dom.Event; | 6675 tinymce.dom.ScriptLoader = function(settings) { |
6291 | 6676 var QUEUED = 0, |
6292 tinymce.create('tinymce.dom.ScriptLoader', { | 6677 LOADING = 1, |
6293 ScriptLoader : function(s) { | 6678 LOADED = 2, |
6294 this.settings = s || {}; | 6679 states = {}, |
6295 this.queue = []; | 6680 queue = [], |
6296 this.lookup = {}; | 6681 scriptLoadedCallbacks = {}, |
6297 }, | 6682 queueLoadedCallbacks = [], |
6298 | 6683 loading = 0, |
6299 isDone : function(u) { | 6684 undefined; |
6300 return this.lookup[u] ? this.lookup[u].state == 2 : 0; | 6685 |
6301 }, | 6686 function loadScript(url, callback) { |
6302 | 6687 var t = this, dom = tinymce.DOM, elm, uri, loc, id; |
6303 markDone : function(u) { | 6688 |
6304 this.lookup[u] = {state : 2, url : u}; | 6689 // Execute callback when script is loaded |
6305 }, | 6690 function done() { |
6306 | 6691 dom.remove(id); |
6307 add : function(u, cb, s, pr) { | 6692 |
6308 var t = this, lo = t.lookup, o; | 6693 if (elm) |
6309 | 6694 elm.onreadystatechange = elm.onload = elm = null; |
6310 if (o = lo[u]) { | 6695 |
6311 // Is loaded fire callback | 6696 callback(); |
6312 if (cb && o.state == 2) | 6697 }; |
6313 cb.call(s || this); | 6698 |
6314 | 6699 id = dom.uniqueId(); |
6315 return o; | 6700 |
6316 } | 6701 if (tinymce.isIE6) { |
6317 | 6702 uri = new tinymce.util.URI(url); |
6318 o = {state : 0, url : u, func : cb, scope : s || this}; | 6703 loc = location; |
6319 | 6704 |
6320 if (pr) | 6705 // If script is from same domain and we |
6321 t.queue.unshift(o); | 6706 // use IE 6 then use XHR since it's more reliable |
6322 else | 6707 if (uri.host == loc.hostname && uri.port == loc.port && (uri.protocol + ':') == loc.protocol) { |
6323 t.queue.push(o); | |
6324 | |
6325 lo[u] = o; | |
6326 | |
6327 return o; | |
6328 }, | |
6329 | |
6330 load : function(u, cb, s) { | |
6331 var t = this, o; | |
6332 | |
6333 if (o = t.lookup[u]) { | |
6334 // Is loaded fire callback | |
6335 if (cb && o.state == 2) | |
6336 cb.call(s || t); | |
6337 | |
6338 return o; | |
6339 } | |
6340 | |
6341 function loadScript(u) { | |
6342 if (Event.domLoaded || t.settings.strict_mode) { | |
6343 tinymce.util.XHR.send({ | 6708 tinymce.util.XHR.send({ |
6344 url : tinymce._addVer(u), | 6709 url : tinymce._addVer(uri.getURI()), |
6345 error : t.settings.error, | 6710 success : function(content) { |
6346 async : false, | 6711 // Create new temp script element |
6347 success : function(co) { | 6712 var script = dom.create('script', { |
6348 t.eval(co); | 6713 type : 'text/javascript' |
6349 } | 6714 }); |
6350 }); | 6715 |
6351 } else | 6716 // Evaluate script in global scope |
6352 document.write('<script type="text/javascript" src="' + tinymce._addVer(u) + '"></script>'); | 6717 script.text = content; |
6353 }; | 6718 document.getElementsByTagName('head')[0].appendChild(script); |
6354 | 6719 dom.remove(script); |
6355 if (!tinymce.is(u, 'string')) { | 6720 |
6356 each(u, function(u) { | |
6357 loadScript(u); | |
6358 }); | |
6359 | |
6360 if (cb) | |
6361 cb.call(s || t); | |
6362 } else { | |
6363 loadScript(u); | |
6364 | |
6365 if (cb) | |
6366 cb.call(s || t); | |
6367 } | |
6368 }, | |
6369 | |
6370 loadQueue : function(cb, s) { | |
6371 var t = this; | |
6372 | |
6373 if (!t.queueLoading) { | |
6374 t.queueLoading = 1; | |
6375 t.queueCallbacks = []; | |
6376 | |
6377 t.loadScripts(t.queue, function() { | |
6378 t.queueLoading = 0; | |
6379 | |
6380 if (cb) | |
6381 cb.call(s || t); | |
6382 | |
6383 each(t.queueCallbacks, function(o) { | |
6384 o.func.call(o.scope); | |
6385 }); | |
6386 }); | |
6387 } else if (cb) | |
6388 t.queueCallbacks.push({func : cb, scope : s || t}); | |
6389 }, | |
6390 | |
6391 eval : function(co) { | |
6392 var w = window; | |
6393 | |
6394 // Evaluate script | |
6395 if (!w.execScript) { | |
6396 try { | |
6397 eval.call(w, co); | |
6398 } catch (ex) { | |
6399 eval(co, w); // Firefox 3.0a8 | |
6400 } | |
6401 } else | |
6402 w.execScript(co); // IE | |
6403 }, | |
6404 | |
6405 loadScripts : function(sc, cb, s) { | |
6406 var t = this, lo = t.lookup; | |
6407 | |
6408 function done(o) { | |
6409 o.state = 2; // Has been loaded | |
6410 | |
6411 // Run callback | |
6412 if (o.func) | |
6413 o.func.call(o.scope || t); | |
6414 }; | |
6415 | |
6416 function allDone() { | |
6417 var l; | |
6418 | |
6419 // Check if all files are loaded | |
6420 l = sc.length; | |
6421 each(sc, function(o) { | |
6422 o = lo[o.url]; | |
6423 | |
6424 if (o.state === 2) {// It has finished loading | |
6425 done(o); | |
6426 l--; | |
6427 } else | |
6428 load(o); | |
6429 }); | |
6430 | |
6431 // They are all loaded | |
6432 if (l === 0 && cb) { | |
6433 cb.call(s || t); | |
6434 cb = 0; | |
6435 } | |
6436 }; | |
6437 | |
6438 function load(o) { | |
6439 if (o.state > 0) | |
6440 return; | |
6441 | |
6442 o.state = 1; // Is loading | |
6443 | |
6444 tinymce.dom.ScriptLoader.loadScript(o.url, function() { | |
6445 done(o); | |
6446 allDone(); | |
6447 }); | |
6448 | |
6449 /* | |
6450 tinymce.util.XHR.send({ | |
6451 url : o.url, | |
6452 error : t.settings.error, | |
6453 success : function(co) { | |
6454 t.eval(co); | |
6455 done(o); | |
6456 allDone(); | |
6457 } | |
6458 }); | |
6459 */ | |
6460 }; | |
6461 | |
6462 each(sc, function(o) { | |
6463 var u = o.url; | |
6464 | |
6465 // Add to queue if needed | |
6466 if (!lo[u]) { | |
6467 lo[u] = o; | |
6468 t.queue.push(o); | |
6469 } else | |
6470 o = lo[u]; | |
6471 | |
6472 // Is already loading or has been loaded | |
6473 if (o.state > 0) | |
6474 return; | |
6475 | |
6476 if (!Event.domLoaded && !t.settings.strict_mode) { | |
6477 var ix, ol = ''; | |
6478 | |
6479 // Add onload events | |
6480 if (cb || o.func) { | |
6481 o.state = 1; // Is loading | |
6482 | |
6483 ix = tinymce.dom.ScriptLoader._addOnLoad(function() { | |
6484 done(o); | |
6485 allDone(); | |
6486 }); | |
6487 | |
6488 if (tinymce.isIE) | |
6489 ol = ' onreadystatechange="'; | |
6490 else | |
6491 ol = ' onload="'; | |
6492 | |
6493 ol += 'tinymce.dom.ScriptLoader._onLoad(this,\'' + u + '\',' + ix + ');"'; | |
6494 } | |
6495 | |
6496 document.write('<script type="text/javascript" src="' + tinymce._addVer(u) + '"' + ol + '></script>'); | |
6497 | |
6498 if (!o.func) | |
6499 done(o); | |
6500 } else | |
6501 load(o); | |
6502 }); | |
6503 | |
6504 allDone(); | |
6505 }, | |
6506 | |
6507 // Static methods | |
6508 'static' : { | |
6509 _addOnLoad : function(f) { | |
6510 var t = this; | |
6511 | |
6512 t._funcs = t._funcs || []; | |
6513 t._funcs.push(f); | |
6514 | |
6515 return t._funcs.length - 1; | |
6516 }, | |
6517 | |
6518 _onLoad : function(e, u, ix) { | |
6519 if (!tinymce.isIE || e.readyState == 'complete') | |
6520 this._funcs[ix].call(this); | |
6521 }, | |
6522 | |
6523 loadScript : function(u, cb) { | |
6524 var id = tinymce.DOM.uniqueId(), e; | |
6525 | |
6526 function done() { | |
6527 Event.clear(id); | |
6528 tinymce.DOM.remove(id); | |
6529 | |
6530 if (cb) { | |
6531 cb.call(document, u); | |
6532 cb = 0; | |
6533 } | |
6534 }; | |
6535 | |
6536 if (tinymce.isIE) { | |
6537 /* Event.add(e, 'readystatechange', function(e) { | |
6538 if (e.target && e.target.readyState == 'complete') | |
6539 done(); | |
6540 });*/ | |
6541 | |
6542 tinymce.util.XHR.send({ | |
6543 url : tinymce._addVer(u), | |
6544 async : false, | |
6545 success : function(co) { | |
6546 window.execScript(co); | |
6547 done(); | 6721 done(); |
6548 } | 6722 } |
6549 }); | 6723 }); |
6550 } else { | 6724 |
6551 e = tinymce.DOM.create('script', {id : id, type : 'text/javascript', src : tinymce._addVer(u)}); | 6725 return; |
6552 Event.add(e, 'load', done); | 6726 } |
6553 | 6727 } |
6554 // Check for head or body | 6728 |
6555 (document.getElementsByTagName('head')[0] || document.body).appendChild(e); | 6729 // Create new script element |
6556 } | 6730 elm = dom.create('script', { |
6557 } | 6731 id : id, |
6558 } | 6732 type : 'text/javascript', |
6559 | 6733 src : tinymce._addVer(url) |
6560 }); | 6734 }); |
6735 | |
6736 // Add onload and readystate listeners | |
6737 elm.onload = done; | |
6738 elm.onreadystatechange = function() { | |
6739 var state = elm.readyState; | |
6740 | |
6741 // Loaded state is passed on IE 6 however there | |
6742 // are known issues with this method but we can't use | |
6743 // XHR in a cross domain loading | |
6744 if (state == 'complete' || state == 'loaded') | |
6745 done(); | |
6746 }; | |
6747 | |
6748 // Most browsers support this feature so we report errors | |
6749 // for those at least to help users track their missing plugins etc | |
6750 // todo: Removed since it produced error if the document is unloaded by navigating away, re-add it as an option | |
6751 /*elm.onerror = function() { | |
6752 alert('Failed to load: ' + url); | |
6753 };*/ | |
6754 | |
6755 // Add script to document | |
6756 (document.getElementsByTagName('head')[0] || document.body).appendChild(elm); | |
6757 }; | |
6758 | |
6759 this.isDone = function(url) { | |
6760 return states[url] == LOADED; | |
6761 }; | |
6762 | |
6763 this.markDone = function(url) { | |
6764 states[url] = LOADED; | |
6765 }; | |
6766 | |
6767 this.add = this.load = function(url, callback, scope) { | |
6768 var item, state = states[url]; | |
6769 | |
6770 // Add url to load queue | |
6771 if (state == undefined) { | |
6772 queue.push(url); | |
6773 states[url] = QUEUED; | |
6774 } | |
6775 | |
6776 if (callback) { | |
6777 // Store away callback for later execution | |
6778 if (!scriptLoadedCallbacks[url]) | |
6779 scriptLoadedCallbacks[url] = []; | |
6780 | |
6781 scriptLoadedCallbacks[url].push({ | |
6782 func : callback, | |
6783 scope : scope || this | |
6784 }); | |
6785 } | |
6786 }; | |
6787 | |
6788 this.loadQueue = function(callback, scope) { | |
6789 this.loadScripts(queue, callback, scope); | |
6790 }; | |
6791 | |
6792 this.loadScripts = function(scripts, callback, scope) { | |
6793 var loadScripts; | |
6794 | |
6795 function execScriptLoadedCallbacks(url) { | |
6796 // Execute URL callback functions | |
6797 tinymce.each(scriptLoadedCallbacks[url], function(callback) { | |
6798 callback.func.call(callback.scope); | |
6799 }); | |
6800 | |
6801 scriptLoadedCallbacks[url] = undefined; | |
6802 }; | |
6803 | |
6804 queueLoadedCallbacks.push({ | |
6805 func : callback, | |
6806 scope : scope || this | |
6807 }); | |
6808 | |
6809 loadScripts = function() { | |
6810 var loadingScripts = tinymce.grep(scripts); | |
6811 | |
6812 // Current scripts has been handled | |
6813 scripts.length = 0; | |
6814 | |
6815 // Load scripts that needs to be loaded | |
6816 tinymce.each(loadingScripts, function(url) { | |
6817 // Script is already loaded then execute script callbacks directly | |
6818 if (states[url] == LOADED) { | |
6819 execScriptLoadedCallbacks(url); | |
6820 return; | |
6821 } | |
6822 | |
6823 // Is script not loading then start loading it | |
6824 if (states[url] != LOADING) { | |
6825 states[url] = LOADING; | |
6826 loading++; | |
6827 | |
6828 loadScript(url, function() { | |
6829 states[url] = LOADED; | |
6830 loading--; | |
6831 | |
6832 execScriptLoadedCallbacks(url); | |
6833 | |
6834 // Load more scripts if they where added by the recently loaded script | |
6835 loadScripts(); | |
6836 }); | |
6837 } | |
6838 }); | |
6839 | |
6840 // No scripts are currently loading then execute all pending queue loaded callbacks | |
6841 if (!loading) { | |
6842 tinymce.each(queueLoadedCallbacks, function(callback) { | |
6843 callback.func.call(callback.scope); | |
6844 }); | |
6845 | |
6846 queueLoadedCallbacks.length = 0; | |
6847 } | |
6848 }; | |
6849 | |
6850 loadScripts(); | |
6851 }; | |
6852 }; | |
6561 | 6853 |
6562 // Global script loader | 6854 // Global script loader |
6563 tinymce.ScriptLoader = new tinymce.dom.ScriptLoader(); | 6855 tinymce.ScriptLoader = new tinymce.dom.ScriptLoader(); |
6564 })(tinymce); | 6856 })(tinymce); |
6857 | |
6858 tinymce.dom.TreeWalker = function(start_node, root_node) { | |
6859 var node = start_node; | |
6860 | |
6861 function findSibling(node, start_name, sibling_name, shallow) { | |
6862 var sibling, parent; | |
6863 | |
6864 if (node) { | |
6865 // Walk into nodes if it has a start | |
6866 if (!shallow && node[start_name]) | |
6867 return node[start_name]; | |
6868 | |
6869 // Return the sibling if it has one | |
6870 if (node != root_node) { | |
6871 sibling = node[sibling_name]; | |
6872 if (sibling) | |
6873 return sibling; | |
6874 | |
6875 // Walk up the parents to look for siblings | |
6876 for (parent = node.parentNode; parent && parent != root_node; parent = parent.parentNode) { | |
6877 sibling = parent[sibling_name]; | |
6878 if (sibling) | |
6879 return sibling; | |
6880 } | |
6881 } | |
6882 } | |
6883 }; | |
6884 | |
6885 this.current = function() { | |
6886 return node; | |
6887 }; | |
6888 | |
6889 this.next = function(shallow) { | |
6890 return (node = findSibling(node, 'firstChild', 'nextSibling', shallow)); | |
6891 }; | |
6892 | |
6893 this.prev = function(shallow) { | |
6894 return (node = findSibling(node, 'lastChild', 'lastSibling', shallow)); | |
6895 }; | |
6896 }; | |
6897 | |
6898 (function() { | |
6899 var transitional = {}; | |
6900 | |
6901 function unpack(lookup, data) { | |
6902 var key; | |
6903 | |
6904 function replace(value) { | |
6905 return value.replace(/[A-Z]+/g, function(key) { | |
6906 return replace(lookup[key]); | |
6907 }); | |
6908 }; | |
6909 | |
6910 // Unpack lookup | |
6911 for (key in lookup) { | |
6912 if (lookup.hasOwnProperty(key)) | |
6913 lookup[key] = replace(lookup[key]); | |
6914 } | |
6915 | |
6916 // Unpack and parse data into object map | |
6917 replace(data).replace(/#/g, '#text').replace(/(\w+)\[([^\]]+)\]/g, function(str, name, children) { | |
6918 var i, map = {}; | |
6919 | |
6920 children = children.split(/\|/); | |
6921 | |
6922 for (i = children.length - 1; i >= 0; i--) | |
6923 map[children[i]] = 1; | |
6924 | |
6925 transitional[name] = map; | |
6926 }); | |
6927 }; | |
6928 | |
6929 // This is the XHTML 1.0 transitional elements with it's children packed to reduce it's size | |
6930 // we will later include the attributes here and use it as a default for valid elements but it | |
6931 // requires us to rewrite the serializer engine | |
6932 unpack({ | |
6933 Z : '#|H|K|N|O|P', | |
6934 Y : '#|X|form|R|Q', | |
6935 X : 'p|T|div|U|W|isindex|fieldset|table', | |
6936 W : 'pre|hr|blockquote|address|center|noframes', | |
6937 U : 'ul|ol|dl|menu|dir', | |
6938 ZC : '#|p|Y|div|U|W|table|br|span|bdo|object|applet|img|map|K|N|Q', | |
6939 T : 'h1|h2|h3|h4|h5|h6', | |
6940 ZB : '#|X|S|Q', | |
6941 S : 'R|P', | |
6942 ZA : '#|a|G|J|M|O|P', | |
6943 R : '#|a|H|K|N|O', | |
6944 Q : 'noscript|P', | |
6945 P : 'ins|del|script', | |
6946 O : 'input|select|textarea|label|button', | |
6947 N : 'M|L', | |
6948 M : 'em|strong|dfn|code|q|samp|kbd|var|cite|abbr|acronym', | |
6949 L : 'sub|sup', | |
6950 K : 'J|I', | |
6951 J : 'tt|i|b|u|s|strike', | |
6952 I : 'big|small|font|basefont', | |
6953 H : 'G|F', | |
6954 G : 'br|span|bdo', | |
6955 F : 'object|applet|img|map|iframe' | |
6956 }, 'script[]' + | |
6957 'style[]' + | |
6958 'object[#|param|X|form|a|H|K|N|O|Q]' + | |
6959 'param[]' + | |
6960 'p[S]' + | |
6961 'a[Z]' + | |
6962 'br[]' + | |
6963 'span[S]' + | |
6964 'bdo[S]' + | |
6965 'applet[#|param|X|form|a|H|K|N|O|Q]' + | |
6966 'h1[S]' + | |
6967 'img[]' + | |
6968 'map[X|form|Q|area]' + | |
6969 'h2[S]' + | |
6970 'iframe[#|X|form|a|H|K|N|O|Q]' + | |
6971 'h3[S]' + | |
6972 'tt[S]' + | |
6973 'i[S]' + | |
6974 'b[S]' + | |
6975 'u[S]' + | |
6976 's[S]' + | |
6977 'strike[S]' + | |
6978 'big[S]' + | |
6979 'small[S]' + | |
6980 'font[S]' + | |
6981 'basefont[]' + | |
6982 'em[S]' + | |
6983 'strong[S]' + | |
6984 'dfn[S]' + | |
6985 'code[S]' + | |
6986 'q[S]' + | |
6987 'samp[S]' + | |
6988 'kbd[S]' + | |
6989 'var[S]' + | |
6990 'cite[S]' + | |
6991 'abbr[S]' + | |
6992 'acronym[S]' + | |
6993 'sub[S]' + | |
6994 'sup[S]' + | |
6995 'input[]' + | |
6996 'select[optgroup|option]' + | |
6997 'optgroup[option]' + | |
6998 'option[]' + | |
6999 'textarea[]' + | |
7000 'label[S]' + | |
7001 'button[#|p|T|div|U|W|table|G|object|applet|img|map|K|N|Q]' + | |
7002 'h4[S]' + | |
7003 'ins[#|X|form|a|H|K|N|O|Q]' + | |
7004 'h5[S]' + | |
7005 'del[#|X|form|a|H|K|N|O|Q]' + | |
7006 'h6[S]' + | |
7007 'div[#|X|form|a|H|K|N|O|Q]' + | |
7008 'ul[li]' + | |
7009 'li[#|X|form|a|H|K|N|O|Q]' + | |
7010 'ol[li]' + | |
7011 'dl[dt|dd]' + | |
7012 'dt[S]' + | |
7013 'dd[#|X|form|a|H|K|N|O|Q]' + | |
7014 'menu[li]' + | |
7015 'dir[li]' + | |
7016 'pre[ZA]' + | |
7017 'hr[]' + | |
7018 'blockquote[#|X|form|a|H|K|N|O|Q]' + | |
7019 'address[S|p]' + | |
7020 'center[#|X|form|a|H|K|N|O|Q]' + | |
7021 'noframes[#|X|form|a|H|K|N|O|Q]' + | |
7022 'isindex[]' + | |
7023 'fieldset[#|legend|X|form|a|H|K|N|O|Q]' + | |
7024 'legend[S]' + | |
7025 'table[caption|col|colgroup|thead|tfoot|tbody|tr]' + | |
7026 'caption[S]' + | |
7027 'col[]' + | |
7028 'colgroup[col]' + | |
7029 'thead[tr]' + | |
7030 'tr[th|td]' + | |
7031 'th[#|X|form|a|H|K|N|O|Q]' + | |
7032 'form[#|X|a|H|K|N|O|Q]' + | |
7033 'noscript[#|X|form|a|H|K|N|O|Q]' + | |
7034 'td[#|X|form|a|H|K|N|O|Q]' + | |
7035 'tfoot[tr]' + | |
7036 'tbody[tr]' + | |
7037 'area[]' + | |
7038 'base[]' + | |
7039 'body[#|X|form|a|H|K|N|O|Q]' | |
7040 ); | |
7041 | |
7042 tinymce.dom.Schema = function() { | |
7043 var t = this, elements = transitional; | |
7044 | |
7045 t.isValid = function(name, child_name) { | |
7046 var element = elements[name]; | |
7047 | |
7048 return !!(element && (!child_name || element[child_name])); | |
7049 }; | |
7050 }; | |
7051 })(); | |
7052 (function(tinymce) { | |
7053 tinymce.dom.RangeUtils = function(dom) { | |
7054 var INVISIBLE_CHAR = '\uFEFF'; | |
7055 | |
7056 this.walk = function(rng, callback) { | |
7057 var startContainer = rng.startContainer, | |
7058 startOffset = rng.startOffset, | |
7059 endContainer = rng.endContainer, | |
7060 endOffset = rng.endOffset, | |
7061 ancestor, startPoint, | |
7062 endPoint, node, parent, siblings, nodes; | |
7063 | |
7064 // Handle table cell selection the table plugin enables | |
7065 // you to fake select table cells and perform formatting actions on them | |
7066 nodes = dom.select('td.mceSelected,th.mceSelected'); | |
7067 if (nodes.length > 0) { | |
7068 tinymce.each(nodes, function(node) { | |
7069 callback([node]); | |
7070 }); | |
7071 | |
7072 return; | |
7073 } | |
7074 | |
7075 function collectSiblings(node, name, end_node) { | |
7076 var siblings = []; | |
7077 | |
7078 for (; node && node != end_node; node = node[name]) | |
7079 siblings.push(node); | |
7080 | |
7081 return siblings; | |
7082 }; | |
7083 | |
7084 function findEndPoint(node, root) { | |
7085 do { | |
7086 if (node.parentNode == root) | |
7087 return node; | |
7088 | |
7089 node = node.parentNode; | |
7090 } while(node); | |
7091 }; | |
7092 | |
7093 function walkBoundary(start_node, end_node, next) { | |
7094 var siblingName = next ? 'nextSibling' : 'previousSibling'; | |
7095 | |
7096 for (node = start_node, parent = node.parentNode; node && node != end_node; node = parent) { | |
7097 parent = node.parentNode; | |
7098 siblings = collectSiblings(node == start_node ? node : node[siblingName], siblingName); | |
7099 | |
7100 if (siblings.length) { | |
7101 if (!next) | |
7102 siblings.reverse(); | |
7103 | |
7104 callback(siblings); | |
7105 } | |
7106 } | |
7107 }; | |
7108 | |
7109 // If index based start position then resolve it | |
7110 if (startContainer.nodeType == 1 && startContainer.hasChildNodes()) | |
7111 startContainer = startContainer.childNodes[startOffset]; | |
7112 | |
7113 // If index based end position then resolve it | |
7114 if (endContainer.nodeType == 1 && endContainer.hasChildNodes()) | |
7115 endContainer = endContainer.childNodes[Math.min(startOffset == endOffset ? endOffset : endOffset - 1, endContainer.childNodes.length - 1)]; | |
7116 | |
7117 // Find common ancestor and end points | |
7118 ancestor = dom.findCommonAncestor(startContainer, endContainer); | |
7119 | |
7120 // Same container | |
7121 if (startContainer == endContainer) | |
7122 return callback([startContainer]); | |
7123 | |
7124 // Process left side | |
7125 for (node = startContainer; node; node = node.parentNode) { | |
7126 if (node == endContainer) | |
7127 return walkBoundary(startContainer, ancestor, true); | |
7128 | |
7129 if (node == ancestor) | |
7130 break; | |
7131 } | |
7132 | |
7133 // Process right side | |
7134 for (node = endContainer; node; node = node.parentNode) { | |
7135 if (node == startContainer) | |
7136 return walkBoundary(endContainer, ancestor); | |
7137 | |
7138 if (node == ancestor) | |
7139 break; | |
7140 } | |
7141 | |
7142 // Find start/end point | |
7143 startPoint = findEndPoint(startContainer, ancestor) || startContainer; | |
7144 endPoint = findEndPoint(endContainer, ancestor) || endContainer; | |
7145 | |
7146 // Walk left leaf | |
7147 walkBoundary(startContainer, startPoint, true); | |
7148 | |
7149 // Walk the middle from start to end point | |
7150 siblings = collectSiblings( | |
7151 startPoint == startContainer ? startPoint : startPoint.nextSibling, | |
7152 'nextSibling', | |
7153 endPoint == endContainer ? endPoint.nextSibling : endPoint | |
7154 ); | |
7155 | |
7156 if (siblings.length) | |
7157 callback(siblings); | |
7158 | |
7159 // Walk right leaf | |
7160 walkBoundary(endContainer, endPoint); | |
7161 }; | |
7162 | |
7163 /* this.split = function(rng) { | |
7164 var startContainer = rng.startContainer, | |
7165 startOffset = rng.startOffset, | |
7166 endContainer = rng.endContainer, | |
7167 endOffset = rng.endOffset; | |
7168 | |
7169 function splitText(node, offset) { | |
7170 if (offset == node.nodeValue.length) | |
7171 node.appendData(INVISIBLE_CHAR); | |
7172 | |
7173 node = node.splitText(offset); | |
7174 | |
7175 if (node.nodeValue === INVISIBLE_CHAR) | |
7176 node.nodeValue = ''; | |
7177 | |
7178 return node; | |
7179 }; | |
7180 | |
7181 // Handle single text node | |
7182 if (startContainer == endContainer) { | |
7183 if (startContainer.nodeType == 3) { | |
7184 if (startOffset != 0) | |
7185 startContainer = endContainer = splitText(startContainer, startOffset); | |
7186 | |
7187 if (endOffset - startOffset != startContainer.nodeValue.length) | |
7188 splitText(startContainer, endOffset - startOffset); | |
7189 } | |
7190 } else { | |
7191 // Split startContainer text node if needed | |
7192 if (startContainer.nodeType == 3 && startOffset != 0) { | |
7193 startContainer = splitText(startContainer, startOffset); | |
7194 startOffset = 0; | |
7195 } | |
7196 | |
7197 // Split endContainer text node if needed | |
7198 if (endContainer.nodeType == 3 && endOffset != endContainer.nodeValue.length) { | |
7199 endContainer = splitText(endContainer, endOffset).previousSibling; | |
7200 endOffset = endContainer.nodeValue.length; | |
7201 } | |
7202 } | |
7203 | |
7204 return { | |
7205 startContainer : startContainer, | |
7206 startOffset : startOffset, | |
7207 endContainer : endContainer, | |
7208 endOffset : endOffset | |
7209 }; | |
7210 }; | |
7211 */ | |
7212 }; | |
7213 })(tinymce); | |
7214 | |
6565 (function(tinymce) { | 7215 (function(tinymce) { |
6566 // Shorten class names | 7216 // Shorten class names |
6567 var DOM = tinymce.DOM, is = tinymce.is; | 7217 var DOM = tinymce.DOM, is = tinymce.is; |
6568 | 7218 |
6569 tinymce.create('tinymce.ui.Control', { | 7219 tinymce.create('tinymce.ui.Control', { |
6659 }, | 7309 }, |
6660 | 7310 |
6661 destroy : function() { | 7311 destroy : function() { |
6662 tinymce.dom.Event.clear(this.id); | 7312 tinymce.dom.Event.clear(this.id); |
6663 } | 7313 } |
6664 | 7314 }); |
6665 }); | 7315 })(tinymce); |
6666 })(tinymce);tinymce.create('tinymce.ui.Container:tinymce.ui.Control', { | 7316 tinymce.create('tinymce.ui.Container:tinymce.ui.Control', { |
6667 Container : function(id, s) { | 7317 Container : function(id, s) { |
6668 this.parent(id, s); | 7318 this.parent(id, s); |
7319 | |
6669 this.controls = []; | 7320 this.controls = []; |
7321 | |
6670 this.lookup = {}; | 7322 this.lookup = {}; |
6671 }, | 7323 }, |
6672 | 7324 |
6673 add : function(c) { | 7325 add : function(c) { |
6674 this.lookup[c.id] = c; | 7326 this.lookup[c.id] = c; |
6678 }, | 7330 }, |
6679 | 7331 |
6680 get : function(n) { | 7332 get : function(n) { |
6681 return this.lookup[n]; | 7333 return this.lookup[n]; |
6682 } | 7334 } |
6683 | 7335 }); |
6684 }); | 7336 |
6685 | 7337 |
6686 tinymce.create('tinymce.ui.Separator:tinymce.ui.Control', { | 7338 tinymce.create('tinymce.ui.Separator:tinymce.ui.Control', { |
6687 Separator : function(id, s) { | 7339 Separator : function(id, s) { |
6688 this.parent(id, s); | 7340 this.parent(id, s); |
6689 this.classPrefix = 'mceSeparator'; | 7341 this.classPrefix = 'mceSeparator'; |
6690 }, | 7342 }, |
6691 | 7343 |
6692 renderHTML : function() { | 7344 renderHTML : function() { |
6693 return tinymce.DOM.createHTML('span', {'class' : this.classPrefix}); | 7345 return tinymce.DOM.createHTML('span', {'class' : this.classPrefix}); |
6694 } | 7346 } |
6695 | 7347 }); |
6696 }); | 7348 |
6697 (function(tinymce) { | 7349 (function(tinymce) { |
6698 var is = tinymce.is, DOM = tinymce.DOM, each = tinymce.each, walk = tinymce.walk; | 7350 var is = tinymce.is, DOM = tinymce.DOM, each = tinymce.each, walk = tinymce.walk; |
6699 | 7351 |
6700 tinymce.create('tinymce.ui.MenuItem:tinymce.ui.Control', { | 7352 tinymce.create('tinymce.ui.MenuItem:tinymce.ui.Control', { |
6701 MenuItem : function(id, s) { | 7353 MenuItem : function(id, s) { |
6719 | 7371 |
6720 // Set pending state | 7372 // Set pending state |
6721 if (is(t.selected)) | 7373 if (is(t.selected)) |
6722 t.setSelected(t.selected); | 7374 t.setSelected(t.selected); |
6723 } | 7375 } |
6724 | 7376 }); |
6725 }); | |
6726 })(tinymce); | 7377 })(tinymce); |
7378 | |
6727 (function(tinymce) { | 7379 (function(tinymce) { |
6728 var is = tinymce.is, DOM = tinymce.DOM, each = tinymce.each, walk = tinymce.walk; | 7380 var is = tinymce.is, DOM = tinymce.DOM, each = tinymce.each, walk = tinymce.walk; |
6729 | 7381 |
6730 tinymce.create('tinymce.ui.Menu:tinymce.ui.MenuItem', { | 7382 tinymce.create('tinymce.ui.Menu:tinymce.ui.MenuItem', { |
6731 Menu : function(id, s) { | 7383 Menu : function(id, s) { |
6818 | 7470 |
6819 m.onAddItem.add(this.onAddItem.dispatch, this.onAddItem); | 7471 m.onAddItem.add(this.onAddItem.dispatch, this.onAddItem); |
6820 | 7472 |
6821 return m; | 7473 return m; |
6822 } | 7474 } |
6823 | 7475 }); |
6824 }); | 7476 })(tinymce); |
6825 })(tinymce);(function(tinymce) { | 7477 (function(tinymce) { |
6826 var is = tinymce.is, DOM = tinymce.DOM, each = tinymce.each, Event = tinymce.dom.Event, Element = tinymce.dom.Element; | 7478 var is = tinymce.is, DOM = tinymce.DOM, each = tinymce.each, Event = tinymce.dom.Event, Element = tinymce.dom.Element; |
6827 | 7479 |
6828 tinymce.create('tinymce.ui.DropMenu:tinymce.ui.Menu', { | 7480 tinymce.create('tinymce.ui.DropMenu:tinymce.ui.Menu', { |
6829 DropMenu : function(id, s) { | 7481 DropMenu : function(id, s) { |
6830 s = s || {}; | 7482 s = s || {}; |
6933 t.mouseClickFunc = Event.add(co, 'click', function(e) { | 7585 t.mouseClickFunc = Event.add(co, 'click', function(e) { |
6934 var m; | 7586 var m; |
6935 | 7587 |
6936 e = e.target; | 7588 e = e.target; |
6937 | 7589 |
6938 if (e && (e = DOM.getParent(e, 'TR')) && !DOM.hasClass(e, cp + 'ItemSub')) { | 7590 if (e && (e = DOM.getParent(e, 'tr')) && !DOM.hasClass(e, cp + 'ItemSub')) { |
6939 m = t.items[e.id]; | 7591 m = t.items[e.id]; |
6940 | 7592 |
6941 if (m.isDisabled()) | 7593 if (m.isDisabled()) |
6942 return; | 7594 return; |
6943 | 7595 |
6960 if (t.hasMenus()) { | 7612 if (t.hasMenus()) { |
6961 t.mouseOverFunc = Event.add(co, 'mouseover', function(e) { | 7613 t.mouseOverFunc = Event.add(co, 'mouseover', function(e) { |
6962 var m, r, mi; | 7614 var m, r, mi; |
6963 | 7615 |
6964 e = e.target; | 7616 e = e.target; |
6965 if (e && (e = DOM.getParent(e, 'TR'))) { | 7617 if (e && (e = DOM.getParent(e, 'tr'))) { |
6966 m = t.items[e.id]; | 7618 m = t.items[e.id]; |
6967 | 7619 |
6968 if (t.lastMenu) | 7620 if (t.lastMenu) |
6969 t.lastMenu.collapse(1); | 7621 t.lastMenu.collapse(1); |
6970 | 7622 |
7146 if (n = ro.previousSibling) | 7798 if (n = ro.previousSibling) |
7147 DOM.removeClass(n, 'mceLast'); | 7799 DOM.removeClass(n, 'mceLast'); |
7148 | 7800 |
7149 DOM.addClass(ro, 'mceLast'); | 7801 DOM.addClass(ro, 'mceLast'); |
7150 } | 7802 } |
7151 | 7803 }); |
7152 }); | 7804 })(tinymce); |
7153 })(tinymce);(function(tinymce) { | 7805 (function(tinymce) { |
7154 var DOM = tinymce.DOM; | 7806 var DOM = tinymce.DOM; |
7155 | 7807 |
7156 tinymce.create('tinymce.ui.Button:tinymce.ui.Control', { | 7808 tinymce.create('tinymce.ui.Button:tinymce.ui.Control', { |
7157 Button : function(id, s) { | 7809 Button : function(id, s) { |
7158 this.parent(id, s); | 7810 this.parent(id, s); |
7179 tinymce.dom.Event.add(t.id, 'click', function(e) { | 7831 tinymce.dom.Event.add(t.id, 'click', function(e) { |
7180 if (!t.isDisabled()) | 7832 if (!t.isDisabled()) |
7181 return s.onclick.call(s.scope, e); | 7833 return s.onclick.call(s.scope, e); |
7182 }); | 7834 }); |
7183 } | 7835 } |
7184 | 7836 }); |
7185 }); | |
7186 })(tinymce); | 7837 })(tinymce); |
7838 | |
7187 (function(tinymce) { | 7839 (function(tinymce) { |
7188 var DOM = tinymce.DOM, Event = tinymce.dom.Event, each = tinymce.each, Dispatcher = tinymce.util.Dispatcher; | 7840 var DOM = tinymce.DOM, Event = tinymce.dom.Event, each = tinymce.each, Dispatcher = tinymce.util.Dispatcher; |
7189 | 7841 |
7190 tinymce.create('tinymce.ui.ListBox:tinymce.ui.Control', { | 7842 tinymce.create('tinymce.ui.ListBox:tinymce.ui.Control', { |
7191 ListBox : function(id, s) { | 7843 ListBox : function(id, s) { |
7192 var t = this; | 7844 var t = this; |
7193 | 7845 |
7194 t.parent(id, s); | 7846 t.parent(id, s); |
7847 | |
7195 t.items = []; | 7848 t.items = []; |
7849 | |
7196 t.onChange = new Dispatcher(t); | 7850 t.onChange = new Dispatcher(t); |
7851 | |
7197 t.onPostRender = new Dispatcher(t); | 7852 t.onPostRender = new Dispatcher(t); |
7853 | |
7198 t.onAdd = new Dispatcher(t); | 7854 t.onAdd = new Dispatcher(t); |
7855 | |
7199 t.onRenderMenu = new tinymce.util.Dispatcher(this); | 7856 t.onRenderMenu = new tinymce.util.Dispatcher(this); |
7857 | |
7200 t.classPrefix = 'mceListBox'; | 7858 t.classPrefix = 'mceListBox'; |
7201 }, | 7859 }, |
7202 | 7860 |
7203 select : function(va) { | 7861 select : function(va) { |
7204 var t = this, fv, f; | 7862 var t = this, fv, f; |
7323 }, | 7981 }, |
7324 | 7982 |
7325 hideMenu : function(e) { | 7983 hideMenu : function(e) { |
7326 var t = this; | 7984 var t = this; |
7327 | 7985 |
7328 // Prevent double toogles by canceling the mouse click event to the button | 7986 if (t.menu && t.menu.isMenuVisible) { |
7329 if (e && e.type == "mousedown" && (e.target.id == t.id + '_text' || e.target.id == t.id + '_open')) | 7987 // Prevent double toogles by canceling the mouse click event to the button |
7330 return; | 7988 if (e && e.type == "mousedown" && (e.target.id == t.id + '_text' || e.target.id == t.id + '_open')) |
7331 | 7989 return; |
7332 if (!e || !DOM.getParent(e.target, '.mceMenu')) { | 7990 |
7333 DOM.removeClass(t.id, t.classPrefix + 'Selected'); | 7991 if (!e || !DOM.getParent(e.target, '.mceMenu')) { |
7334 Event.remove(DOM.doc, 'mousedown', t.hideMenu, t); | 7992 DOM.removeClass(t.id, t.classPrefix + 'Selected'); |
7335 | 7993 Event.remove(DOM.doc, 'mousedown', t.hideMenu, t); |
7336 if (t.menu) | |
7337 t.menu.hideMenu(); | 7994 t.menu.hideMenu(); |
7995 } | |
7338 } | 7996 } |
7339 }, | 7997 }, |
7340 | 7998 |
7341 renderMenu : function() { | 7999 renderMenu : function() { |
7342 var t = this, m; | 8000 var t = this, m; |
7358 t.select(''); // Must be runned after | 8016 t.select(''); // Must be runned after |
7359 } | 8017 } |
7360 }); | 8018 }); |
7361 | 8019 |
7362 each(t.items, function(o) { | 8020 each(t.items, function(o) { |
7363 o.id = DOM.uniqueId(); | 8021 // No value then treat it as a title |
7364 o.onclick = function() { | 8022 if (o.value === undefined) { |
7365 if (t.settings.onselect(o.value) !== false) | 8023 m.add({ |
7366 t.select(o.value); // Must be runned after | 8024 title : o.title, |
7367 }; | 8025 'class' : 'mceMenuItemTitle', |
7368 | 8026 onclick : function() { |
7369 m.add(o); | 8027 if (t.settings.onselect('') !== false) |
8028 t.select(''); // Must be runned after | |
8029 } | |
8030 }); | |
8031 } else { | |
8032 o.id = DOM.uniqueId(); | |
8033 o.onclick = function() { | |
8034 if (t.settings.onselect(o.value) !== false) | |
8035 t.select(o.value); // Must be runned after | |
8036 }; | |
8037 | |
8038 m.add(o); | |
8039 } | |
7370 }); | 8040 }); |
7371 | 8041 |
7372 t.onRenderMenu.dispatch(t, m); | 8042 t.onRenderMenu.dispatch(t, m); |
7373 t.menu = m; | 8043 t.menu = m; |
7374 }, | 8044 }, |
7430 | 8100 |
7431 destroy : function() { | 8101 destroy : function() { |
7432 this.parent(); | 8102 this.parent(); |
7433 | 8103 |
7434 Event.clear(this.id + '_text'); | 8104 Event.clear(this.id + '_text'); |
8105 Event.clear(this.id + '_open'); | |
7435 } | 8106 } |
7436 | 8107 }); |
7437 }); | 8108 })(tinymce); |
7438 })(tinymce);(function(tinymce) { | 8109 (function(tinymce) { |
7439 var DOM = tinymce.DOM, Event = tinymce.dom.Event, each = tinymce.each, Dispatcher = tinymce.util.Dispatcher; | 8110 var DOM = tinymce.DOM, Event = tinymce.dom.Event, each = tinymce.each, Dispatcher = tinymce.util.Dispatcher; |
7440 | 8111 |
7441 tinymce.create('tinymce.ui.NativeListBox:tinymce.ui.ListBox', { | 8112 tinymce.create('tinymce.ui.NativeListBox:tinymce.ui.ListBox', { |
7442 NativeListBox : function(id, s) { | 8113 NativeListBox : function(id, s) { |
7443 this.parent(id, s); | 8114 this.parent(id, s); |
7506 t.items.push(o); | 8177 t.items.push(o); |
7507 t.onAdd.dispatch(t, o); | 8178 t.onAdd.dispatch(t, o); |
7508 }, | 8179 }, |
7509 | 8180 |
7510 getLength : function() { | 8181 getLength : function() { |
7511 return DOM.get(this.id).options.length - 1; | 8182 return this.items.length; |
7512 }, | 8183 }, |
7513 | 8184 |
7514 renderHTML : function() { | 8185 renderHTML : function() { |
7515 var h, t = this; | 8186 var h, t = this; |
7516 | 8187 |
7560 } | 8231 } |
7561 }); | 8232 }); |
7562 | 8233 |
7563 t.onPostRender.dispatch(t, DOM.get(t.id)); | 8234 t.onPostRender.dispatch(t, DOM.get(t.id)); |
7564 } | 8235 } |
7565 | 8236 }); |
7566 }); | 8237 })(tinymce); |
7567 })(tinymce);(function(tinymce) { | 8238 (function(tinymce) { |
7568 var DOM = tinymce.DOM, Event = tinymce.dom.Event, each = tinymce.each; | 8239 var DOM = tinymce.DOM, Event = tinymce.dom.Event, each = tinymce.each; |
7569 | 8240 |
7570 tinymce.create('tinymce.ui.MenuButton:tinymce.ui.Button', { | 8241 tinymce.create('tinymce.ui.MenuButton:tinymce.ui.Button', { |
7571 MenuButton : function(id, s) { | 8242 MenuButton : function(id, s) { |
7572 this.parent(id, s); | 8243 this.parent(id, s); |
8244 | |
7573 this.onRenderMenu = new tinymce.util.Dispatcher(this); | 8245 this.onRenderMenu = new tinymce.util.Dispatcher(this); |
8246 | |
7574 s.menu_container = s.menu_container || DOM.doc.body; | 8247 s.menu_container = s.menu_container || DOM.doc.body; |
7575 }, | 8248 }, |
7576 | 8249 |
7577 showMenu : function() { | 8250 showMenu : function() { |
7578 var t = this, p1, p2, e = DOM.get(t.id), m; | 8251 var t = this, p1, p2, e = DOM.get(t.id), m; |
7647 | 8320 |
7648 t.showMenu(); | 8321 t.showMenu(); |
7649 } | 8322 } |
7650 }); | 8323 }); |
7651 } | 8324 } |
7652 | 8325 }); |
7653 }); | |
7654 })(tinymce); | 8326 })(tinymce); |
8327 | |
7655 (function(tinymce) { | 8328 (function(tinymce) { |
7656 var DOM = tinymce.DOM, Event = tinymce.dom.Event, each = tinymce.each; | 8329 var DOM = tinymce.DOM, Event = tinymce.dom.Event, each = tinymce.each; |
7657 | 8330 |
7658 tinymce.create('tinymce.ui.SplitButton:tinymce.ui.MenuButton', { | 8331 tinymce.create('tinymce.ui.SplitButton:tinymce.ui.MenuButton', { |
7659 SplitButton : function(id, s) { | 8332 SplitButton : function(id, s) { |
7713 this.parent(); | 8386 this.parent(); |
7714 | 8387 |
7715 Event.clear(this.id + '_action'); | 8388 Event.clear(this.id + '_action'); |
7716 Event.clear(this.id + '_open'); | 8389 Event.clear(this.id + '_open'); |
7717 } | 8390 } |
7718 | 8391 }); |
7719 }); | |
7720 })(tinymce); | 8392 })(tinymce); |
8393 | |
7721 (function(tinymce) { | 8394 (function(tinymce) { |
7722 var DOM = tinymce.DOM, Event = tinymce.dom.Event, is = tinymce.is, each = tinymce.each; | 8395 var DOM = tinymce.DOM, Event = tinymce.dom.Event, is = tinymce.is, each = tinymce.each; |
7723 | 8396 |
7724 tinymce.create('tinymce.ui.ColorSplitButton:tinymce.ui.SplitButton', { | 8397 tinymce.create('tinymce.ui.ColorSplitButton:tinymce.ui.SplitButton', { |
7725 ColorSplitButton : function(id, s) { | 8398 ColorSplitButton : function(id, s) { |
7732 grid_width : 8, | 8405 grid_width : 8, |
7733 default_color : '#888888' | 8406 default_color : '#888888' |
7734 }, t.settings); | 8407 }, t.settings); |
7735 | 8408 |
7736 t.onShowMenu = new tinymce.util.Dispatcher(t); | 8409 t.onShowMenu = new tinymce.util.Dispatcher(t); |
8410 | |
7737 t.onHideMenu = new tinymce.util.Dispatcher(t); | 8411 t.onHideMenu = new tinymce.util.Dispatcher(t); |
7738 | 8412 |
7739 t.value = s.default_color; | 8413 t.value = s.default_color; |
7740 }, | 8414 }, |
7741 | 8415 |
7763 zIndex : 200000 | 8437 zIndex : 200000 |
7764 }); | 8438 }); |
7765 e = 0; | 8439 e = 0; |
7766 | 8440 |
7767 Event.add(DOM.doc, 'mousedown', t.hideMenu, t); | 8441 Event.add(DOM.doc, 'mousedown', t.hideMenu, t); |
8442 t.onShowMenu.dispatch(t); | |
7768 | 8443 |
7769 if (t._focused) { | 8444 if (t._focused) { |
7770 t._keyHandler = Event.add(t.id + '_menu', 'keydown', function(e) { | 8445 t._keyHandler = Event.add(t.id + '_menu', 'keydown', function(e) { |
7771 if (e.keyCode == 27) | 8446 if (e.keyCode == 27) |
7772 t.hideMenu(); | 8447 t.hideMenu(); |
7773 }); | 8448 }); |
7774 | 8449 |
7775 DOM.select('a', t.id + '_menu')[0].focus(); // Select first link | 8450 DOM.select('a', t.id + '_menu')[0].focus(); // Select first link |
7776 } | 8451 } |
7777 | 8452 |
7778 t.onShowMenu.dispatch(t); | |
7779 | |
7780 t.isMenuVisible = 1; | 8453 t.isMenuVisible = 1; |
7781 }, | 8454 }, |
7782 | 8455 |
7783 hideMenu : function(e) { | 8456 hideMenu : function(e) { |
7784 var t = this; | 8457 var t = this; |
7824 n = DOM.add(n, 'a', { | 8497 n = DOM.add(n, 'a', { |
7825 href : 'javascript:;', | 8498 href : 'javascript:;', |
7826 style : { | 8499 style : { |
7827 backgroundColor : '#' + c | 8500 backgroundColor : '#' + c |
7828 }, | 8501 }, |
7829 mce_color : '#' + c | 8502 _mce_color : '#' + c |
7830 }); | 8503 }); |
7831 }); | 8504 }); |
7832 | 8505 |
7833 if (s.more_colors_func) { | 8506 if (s.more_colors_func) { |
7834 n = DOM.add(tb, 'tr'); | 8507 n = DOM.add(tb, 'tr'); |
7846 Event.add(t.id + '_menu', 'click', function(e) { | 8519 Event.add(t.id + '_menu', 'click', function(e) { |
7847 var c; | 8520 var c; |
7848 | 8521 |
7849 e = e.target; | 8522 e = e.target; |
7850 | 8523 |
7851 if (e.nodeName == 'A' && (c = e.getAttribute('mce_color'))) | 8524 if (e.nodeName == 'A' && (c = e.getAttribute('_mce_color'))) |
7852 t.setColor(c); | 8525 t.setColor(c); |
7853 | 8526 |
7854 return Event.cancel(e); // Prevent IE auto save warning | 8527 return Event.cancel(e); // Prevent IE auto save warning |
7855 }); | 8528 }); |
7856 | 8529 |
7880 | 8553 |
7881 Event.clear(this.id + '_menu'); | 8554 Event.clear(this.id + '_menu'); |
7882 Event.clear(this.id + '_more'); | 8555 Event.clear(this.id + '_more'); |
7883 DOM.remove(this.id + '_menu'); | 8556 DOM.remove(this.id + '_menu'); |
7884 } | 8557 } |
7885 | 8558 }); |
7886 }); | |
7887 })(tinymce); | 8559 })(tinymce); |
8560 | |
7888 tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', { | 8561 tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', { |
7889 renderHTML : function() { | 8562 renderHTML : function() { |
7890 var t = this, h = '', c, co, dom = tinymce.DOM, s = t.settings, i, pr, nx, cl; | 8563 var t = this, h = '', c, co, dom = tinymce.DOM, s = t.settings, i, pr, nx, cl; |
7891 | 8564 |
7892 cl = t.controls; | 8565 cl = t.controls; |
7944 | 8617 |
7945 h += dom.createHTML('td', {'class' : c}, dom.createHTML('span', null, '<!-- IE -->')); | 8618 h += dom.createHTML('td', {'class' : c}, dom.createHTML('span', null, '<!-- IE -->')); |
7946 | 8619 |
7947 return dom.createHTML('table', {id : t.id, 'class' : 'mceToolbar' + (s['class'] ? ' ' + s['class'] : ''), cellpadding : '0', cellspacing : '0', align : t.settings.align || ''}, '<tbody><tr>' + h + '</tr></tbody>'); | 8620 return dom.createHTML('table', {id : t.id, 'class' : 'mceToolbar' + (s['class'] ? ' ' + s['class'] : ''), cellpadding : '0', cellspacing : '0', align : t.settings.align || ''}, '<tbody><tr>' + h + '</tr></tbody>'); |
7948 } | 8621 } |
7949 | 8622 }); |
7950 }); | 8623 |
7951 (function(tinymce) { | 8624 (function(tinymce) { |
7952 var Dispatcher = tinymce.util.Dispatcher, each = tinymce.each; | 8625 var Dispatcher = tinymce.util.Dispatcher, each = tinymce.each; |
7953 | 8626 |
7954 tinymce.create('tinymce.AddOnManager', { | 8627 tinymce.create('tinymce.AddOnManager', { |
7955 items : [], | 8628 items : [], |
7956 urls : {}, | 8629 urls : {}, |
7957 lookup : {}, | 8630 lookup : {}, |
8631 | |
7958 onAdd : new Dispatcher(this), | 8632 onAdd : new Dispatcher(this), |
7959 | 8633 |
7960 get : function(n) { | 8634 get : function(n) { |
7961 return this.lookup[n]; | 8635 return this.lookup[n]; |
7962 }, | 8636 }, |
7963 | 8637 |
7964 requireLangPack : function(n) { | 8638 requireLangPack : function(n) { |
7965 var u, s = tinymce.EditorManager.settings; | 8639 var s = tinymce.settings; |
7966 | 8640 |
7967 if (s && s.language) { | 8641 if (s && s.language) |
7968 u = this.urls[n] + '/langs/' + s.language + '.js'; | 8642 tinymce.ScriptLoader.add(this.urls[n] + '/langs/' + s.language + '.js'); |
7969 | |
7970 if (!tinymce.dom.Event.domLoaded && !s.strict_mode) | |
7971 tinymce.ScriptLoader.load(u); | |
7972 else | |
7973 tinymce.ScriptLoader.add(u); | |
7974 } | |
7975 }, | 8643 }, |
7976 | 8644 |
7977 add : function(id, o) { | 8645 add : function(id, o) { |
7978 this.items.push(o); | 8646 this.items.push(o); |
7979 this.lookup[id] = o; | 8647 this.lookup[id] = o; |
7992 u = tinymce.baseURL + '/' + u; | 8660 u = tinymce.baseURL + '/' + u; |
7993 | 8661 |
7994 t.urls[n] = u.substring(0, u.lastIndexOf('/')); | 8662 t.urls[n] = u.substring(0, u.lastIndexOf('/')); |
7995 tinymce.ScriptLoader.add(u, cb, s); | 8663 tinymce.ScriptLoader.add(u, cb, s); |
7996 } | 8664 } |
7997 | 8665 }); |
7998 }); | |
7999 | 8666 |
8000 // Create plugin and theme managers | 8667 // Create plugin and theme managers |
8001 tinymce.PluginManager = new tinymce.AddOnManager(); | 8668 tinymce.PluginManager = new tinymce.AddOnManager(); |
8002 tinymce.ThemeManager = new tinymce.AddOnManager(); | 8669 tinymce.ThemeManager = new tinymce.AddOnManager(); |
8003 }(tinymce));(function(tinymce) { | 8670 }(tinymce)); |
8671 | |
8672 (function(tinymce) { | |
8004 // Shorten names | 8673 // Shorten names |
8005 var each = tinymce.each, extend = tinymce.extend, DOM = tinymce.DOM, Event = tinymce.dom.Event, ThemeManager = tinymce.ThemeManager, PluginManager = tinymce.PluginManager, explode = tinymce.explode; | 8674 var each = tinymce.each, extend = tinymce.extend, |
8006 | 8675 DOM = tinymce.DOM, Event = tinymce.dom.Event, |
8007 tinymce.create('static tinymce.EditorManager', { | 8676 ThemeManager = tinymce.ThemeManager, PluginManager = tinymce.PluginManager, |
8008 editors : {}, | 8677 explode = tinymce.explode, |
8678 Dispatcher = tinymce.util.Dispatcher, undefined, instanceCounter = 0; | |
8679 | |
8680 // Setup some URLs where the editor API is located and where the document is | |
8681 tinymce.documentBaseURL = window.location.href.replace(/[\?#].*$/, '').replace(/[\/\\][^\/]+$/, ''); | |
8682 if (!/[\/\\]$/.test(tinymce.documentBaseURL)) | |
8683 tinymce.documentBaseURL += '/'; | |
8684 | |
8685 tinymce.baseURL = new tinymce.util.URI(tinymce.documentBaseURL).toAbsolute(tinymce.baseURL); | |
8686 | |
8687 tinymce.baseURI = new tinymce.util.URI(tinymce.baseURL); | |
8688 | |
8689 // Add before unload listener | |
8690 // This was required since IE was leaking memory if you added and removed beforeunload listeners | |
8691 // with attachEvent/detatchEvent so this only adds one listener and instances can the attach to the onBeforeUnload event | |
8692 tinymce.onBeforeUnload = new Dispatcher(tinymce); | |
8693 | |
8694 // Must be on window or IE will leak if the editor is placed in frame or iframe | |
8695 Event.add(window, 'beforeunload', function(e) { | |
8696 tinymce.onBeforeUnload.dispatch(tinymce, e); | |
8697 }); | |
8698 | |
8699 tinymce.onAddEditor = new Dispatcher(tinymce); | |
8700 | |
8701 tinymce.onRemoveEditor = new Dispatcher(tinymce); | |
8702 | |
8703 tinymce.EditorManager = extend(tinymce, { | |
8704 editors : [], | |
8705 | |
8009 i18n : {}, | 8706 i18n : {}, |
8707 | |
8010 activeEditor : null, | 8708 activeEditor : null, |
8011 | 8709 |
8012 preInit : function() { | |
8013 var t = this, lo = window.location; | |
8014 | |
8015 // Setup some URLs where the editor API is located and where the document is | |
8016 tinymce.documentBaseURL = lo.href.replace(/[\?#].*$/, '').replace(/[\/\\][^\/]+$/, ''); | |
8017 if (!/[\/\\]$/.test(tinymce.documentBaseURL)) | |
8018 tinymce.documentBaseURL += '/'; | |
8019 | |
8020 tinymce.baseURL = new tinymce.util.URI(tinymce.documentBaseURL).toAbsolute(tinymce.baseURL); | |
8021 tinymce.EditorManager.baseURI = new tinymce.util.URI(tinymce.baseURL); | |
8022 | |
8023 // User specified a document.domain value | |
8024 if (document.domain && lo.hostname != document.domain) | |
8025 tinymce.relaxedDomain = document.domain; | |
8026 | |
8027 // Add before unload listener | |
8028 // This was required since IE was leaking memory if you added and removed beforeunload listeners | |
8029 // with attachEvent/detatchEvent so this only adds one listener and instances can the attach to the onBeforeUnload event | |
8030 t.onBeforeUnload = new tinymce.util.Dispatcher(t); | |
8031 | |
8032 // Must be on window or IE will leak if the editor is placed in frame or iframe | |
8033 Event.add(window, 'beforeunload', function(e) { | |
8034 t.onBeforeUnload.dispatch(t, e); | |
8035 }); | |
8036 }, | |
8037 | |
8038 init : function(s) { | 8710 init : function(s) { |
8039 var t = this, pl, sl = tinymce.ScriptLoader, c, e, el = [], ed; | 8711 var t = this, pl, sl = tinymce.ScriptLoader, e, el = [], ed; |
8040 | 8712 |
8041 function execCallback(se, n, s) { | 8713 function execCallback(se, n, s) { |
8042 var f = se[n]; | 8714 var f = se[n]; |
8043 | 8715 |
8044 if (!f) | 8716 if (!f) |
8053 return f.apply(s || this, Array.prototype.slice.call(arguments, 2)); | 8725 return f.apply(s || this, Array.prototype.slice.call(arguments, 2)); |
8054 }; | 8726 }; |
8055 | 8727 |
8056 s = extend({ | 8728 s = extend({ |
8057 theme : "simple", | 8729 theme : "simple", |
8058 language : "en", | 8730 language : "en" |
8059 strict_loading_mode : document.contentType == 'application/xhtml+xml' | |
8060 }, s); | 8731 }, s); |
8061 | 8732 |
8062 t.settings = s; | 8733 t.settings = s; |
8063 | |
8064 // If page not loaded and strict mode isn't enabled then load them | |
8065 if (!Event.domLoaded && !s.strict_loading_mode) { | |
8066 // Load language | |
8067 if (s.language) | |
8068 sl.add(tinymce.baseURL + '/langs/' + s.language + '.js'); | |
8069 | |
8070 // Load theme | |
8071 if (s.theme && s.theme.charAt(0) != '-' && !ThemeManager.urls[s.theme]) | |
8072 ThemeManager.load(s.theme, 'themes/' + s.theme + '/editor_template' + tinymce.suffix + '.js'); | |
8073 | |
8074 // Load plugins | |
8075 if (s.plugins) { | |
8076 pl = explode(s.plugins); | |
8077 | |
8078 // Load compat2x first | |
8079 if (tinymce.inArray(pl, 'compat2x') != -1) | |
8080 PluginManager.load('compat2x', 'plugins/compat2x/editor_plugin' + tinymce.suffix + '.js'); | |
8081 | |
8082 // Load rest if plugins | |
8083 each(pl, function(v) { | |
8084 if (v && v.charAt(0) != '-' && !PluginManager.urls[v]) { | |
8085 // Skip safari plugin for other browsers | |
8086 if (!tinymce.isWebKit && v == 'safari') | |
8087 return; | |
8088 | |
8089 PluginManager.load(v, 'plugins/' + v + '/editor_plugin' + tinymce.suffix + '.js'); | |
8090 } | |
8091 }); | |
8092 } | |
8093 | |
8094 sl.loadQueue(); | |
8095 } | |
8096 | 8734 |
8097 // Legacy call | 8735 // Legacy call |
8098 Event.add(document, 'init', function() { | 8736 Event.add(document, 'init', function() { |
8099 var l, co; | 8737 var l, co; |
8100 | 8738 |
8101 execCallback(s, 'onpageload'); | 8739 execCallback(s, 'onpageload'); |
8102 | |
8103 // Verify that it's a valid browser | |
8104 if (s.browsers) { | |
8105 l = false; | |
8106 | |
8107 each(explode(s.browsers), function(v) { | |
8108 switch (v) { | |
8109 case 'ie': | |
8110 case 'msie': | |
8111 if (tinymce.isIE) | |
8112 l = true; | |
8113 break; | |
8114 | |
8115 case 'gecko': | |
8116 if (tinymce.isGecko) | |
8117 l = true; | |
8118 break; | |
8119 | |
8120 case 'safari': | |
8121 case 'webkit': | |
8122 if (tinymce.isWebKit) | |
8123 l = true; | |
8124 break; | |
8125 | |
8126 case 'opera': | |
8127 if (tinymce.isOpera) | |
8128 l = true; | |
8129 | |
8130 break; | |
8131 } | |
8132 }); | |
8133 | |
8134 // Not a valid one | |
8135 if (!l) | |
8136 return; | |
8137 } | |
8138 | 8740 |
8139 switch (s.mode) { | 8741 switch (s.mode) { |
8140 case "exact": | 8742 case "exact": |
8141 l = s.elements || ''; | 8743 l = s.elements || ''; |
8142 | 8744 |
8145 if (DOM.get(v)) { | 8747 if (DOM.get(v)) { |
8146 ed = new tinymce.Editor(v, s); | 8748 ed = new tinymce.Editor(v, s); |
8147 el.push(ed); | 8749 el.push(ed); |
8148 ed.render(1); | 8750 ed.render(1); |
8149 } else { | 8751 } else { |
8150 c = 0; | |
8151 | |
8152 each(document.forms, function(f) { | 8752 each(document.forms, function(f) { |
8153 each(f.elements, function(e) { | 8753 each(f.elements, function(e) { |
8154 if (e.name === v) { | 8754 if (e.name === v) { |
8155 v = 'mce_editor_' + c; | 8755 v = 'mce_editor_' + instanceCounter++; |
8156 DOM.setAttrib(e, 'id', v); | 8756 DOM.setAttrib(e, 'id', v); |
8157 | 8757 |
8158 ed = new tinymce.Editor(v, s); | 8758 ed = new tinymce.Editor(v, s); |
8159 el.push(ed); | 8759 el.push(ed); |
8160 ed.render(1); | 8760 ed.render(1); |
8196 | 8796 |
8197 // Call onInit when all editors are initialized | 8797 // Call onInit when all editors are initialized |
8198 if (s.oninit) { | 8798 if (s.oninit) { |
8199 l = co = 0; | 8799 l = co = 0; |
8200 | 8800 |
8201 each (el, function(ed) { | 8801 each(el, function(ed) { |
8202 co++; | 8802 co++; |
8203 | 8803 |
8204 if (!ed.initialized) { | 8804 if (!ed.initialized) { |
8205 // Wait for it | 8805 // Wait for it |
8206 ed.onInit.add(function() { | 8806 ed.onInit.add(function() { |
8220 } | 8820 } |
8221 }); | 8821 }); |
8222 }, | 8822 }, |
8223 | 8823 |
8224 get : function(id) { | 8824 get : function(id) { |
8825 if (id === undefined) | |
8826 return this.editors; | |
8827 | |
8225 return this.editors[id]; | 8828 return this.editors[id]; |
8226 }, | 8829 }, |
8227 | 8830 |
8228 getInstanceById : function(id) { | 8831 getInstanceById : function(id) { |
8229 return this.get(id); | 8832 return this.get(id); |
8230 }, | 8833 }, |
8231 | 8834 |
8232 add : function(e) { | 8835 add : function(editor) { |
8233 this.editors[e.id] = e; | 8836 var self = this, editors = self.editors; |
8234 this._setActive(e); | 8837 |
8235 | 8838 // Add named and index editor instance |
8236 return e; | 8839 editors[editor.id] = editor; |
8237 }, | 8840 editors.push(editor); |
8238 | 8841 |
8239 remove : function(e) { | 8842 self._setActive(editor); |
8240 var t = this; | 8843 self.onAddEditor.dispatch(self, editor); |
8844 | |
8845 | |
8846 return editor; | |
8847 }, | |
8848 | |
8849 remove : function(editor) { | |
8850 var t = this, i, editors = t.editors; | |
8241 | 8851 |
8242 // Not in the collection | 8852 // Not in the collection |
8243 if (!t.editors[e.id]) | 8853 if (!editors[editor.id]) |
8244 return null; | 8854 return null; |
8245 | 8855 |
8246 delete t.editors[e.id]; | 8856 delete editors[editor.id]; |
8857 | |
8858 for (i = 0; i < editors.length; i++) { | |
8859 if (editors[i] == editor) { | |
8860 editors.splice(i, 1); | |
8861 break; | |
8862 } | |
8863 } | |
8247 | 8864 |
8248 // Select another editor since the active one was removed | 8865 // Select another editor since the active one was removed |
8249 if (t.activeEditor == e) { | 8866 if (t.activeEditor == editor) |
8250 each(t.editors, function(e) { | 8867 t._setActive(editors[0]); |
8251 t._setActive(e); | 8868 |
8252 return false; // Break | 8869 editor.destroy(); |
8253 }); | 8870 t.onRemoveEditor.dispatch(t, editor); |
8254 } | 8871 |
8255 | 8872 return editor; |
8256 e.destroy(); | |
8257 | |
8258 return e; | |
8259 }, | 8873 }, |
8260 | 8874 |
8261 execCommand : function(c, u, v) { | 8875 execCommand : function(c, u, v) { |
8262 var t = this, ed = t.get(v), w; | 8876 var t = this, ed = t.get(v), w; |
8263 | 8877 |
8366 } | 8980 } |
8367 }, | 8981 }, |
8368 | 8982 |
8369 // Private methods | 8983 // Private methods |
8370 | 8984 |
8371 _setActive : function(e) { | 8985 _setActive : function(editor) { |
8372 this.selectedInstance = this.activeEditor = e; | 8986 this.selectedInstance = this.activeEditor = editor; |
8373 } | 8987 } |
8374 | 8988 }); |
8375 }); | |
8376 | |
8377 tinymce.EditorManager.preInit(); | |
8378 })(tinymce); | 8989 })(tinymce); |
8379 | 8990 |
8380 // Short for editor manager window.tinyMCE is needed when TinyMCE gets loaded though a XHR call | |
8381 var tinyMCE = window.tinyMCE = tinymce.EditorManager; | |
8382 (function(tinymce) { | 8991 (function(tinymce) { |
8383 var DOM = tinymce.DOM, Event = tinymce.dom.Event, extend = tinymce.extend, Dispatcher = tinymce.util.Dispatcher; | 8992 // Shorten these names |
8384 var each = tinymce.each, isGecko = tinymce.isGecko, isIE = tinymce.isIE, isWebKit = tinymce.isWebKit; | 8993 var DOM = tinymce.DOM, Event = tinymce.dom.Event, extend = tinymce.extend, |
8385 var is = tinymce.is, ThemeManager = tinymce.ThemeManager, PluginManager = tinymce.PluginManager, EditorManager = tinymce.EditorManager; | 8994 Dispatcher = tinymce.util.Dispatcher, each = tinymce.each, isGecko = tinymce.isGecko, |
8386 var inArray = tinymce.inArray, grep = tinymce.grep, explode = tinymce.explode; | 8995 isIE = tinymce.isIE, isWebKit = tinymce.isWebKit, is = tinymce.is, |
8996 ThemeManager = tinymce.ThemeManager, PluginManager = tinymce.PluginManager, | |
8997 inArray = tinymce.inArray, grep = tinymce.grep, explode = tinymce.explode; | |
8387 | 8998 |
8388 tinymce.create('tinymce.Editor', { | 8999 tinymce.create('tinymce.Editor', { |
8389 Editor : function(id, s) { | 9000 Editor : function(id, s) { |
8390 var t = this; | 9001 var t = this; |
8391 | 9002 |
8392 t.id = t.editorId = id; | 9003 t.id = t.editorId = id; |
9004 | |
8393 t.execCommands = {}; | 9005 t.execCommands = {}; |
8394 t.queryStateCommands = {}; | 9006 t.queryStateCommands = {}; |
8395 t.queryValueCommands = {}; | 9007 t.queryValueCommands = {}; |
9008 | |
9009 t.isNotDirty = false; | |
9010 | |
8396 t.plugins = {}; | 9011 t.plugins = {}; |
8397 | 9012 |
8398 // Add events to the editor | 9013 // Add events to the editor |
8399 each([ | 9014 each([ |
8400 'onPreInit', | 9015 'onPreInit', |
9016 | |
8401 'onBeforeRenderUI', | 9017 'onBeforeRenderUI', |
9018 | |
8402 'onPostRender', | 9019 'onPostRender', |
9020 | |
8403 'onInit', | 9021 'onInit', |
9022 | |
8404 'onRemove', | 9023 'onRemove', |
9024 | |
8405 'onActivate', | 9025 'onActivate', |
9026 | |
8406 'onDeactivate', | 9027 'onDeactivate', |
9028 | |
8407 'onClick', | 9029 'onClick', |
9030 | |
8408 'onEvent', | 9031 'onEvent', |
9032 | |
8409 'onMouseUp', | 9033 'onMouseUp', |
9034 | |
8410 'onMouseDown', | 9035 'onMouseDown', |
9036 | |
8411 'onDblClick', | 9037 'onDblClick', |
9038 | |
8412 'onKeyDown', | 9039 'onKeyDown', |
9040 | |
8413 'onKeyUp', | 9041 'onKeyUp', |
9042 | |
8414 'onKeyPress', | 9043 'onKeyPress', |
9044 | |
8415 'onContextMenu', | 9045 'onContextMenu', |
9046 | |
8416 'onSubmit', | 9047 'onSubmit', |
9048 | |
8417 'onReset', | 9049 'onReset', |
9050 | |
8418 'onPaste', | 9051 'onPaste', |
9052 | |
8419 'onPreProcess', | 9053 'onPreProcess', |
9054 | |
8420 'onPostProcess', | 9055 'onPostProcess', |
9056 | |
8421 'onBeforeSetContent', | 9057 'onBeforeSetContent', |
9058 | |
8422 'onBeforeGetContent', | 9059 'onBeforeGetContent', |
9060 | |
8423 'onSetContent', | 9061 'onSetContent', |
9062 | |
8424 'onGetContent', | 9063 'onGetContent', |
9064 | |
8425 'onLoadContent', | 9065 'onLoadContent', |
9066 | |
8426 'onSaveContent', | 9067 'onSaveContent', |
9068 | |
8427 'onNodeChange', | 9069 'onNodeChange', |
9070 | |
8428 'onChange', | 9071 'onChange', |
9072 | |
8429 'onBeforeExecCommand', | 9073 'onBeforeExecCommand', |
9074 | |
8430 'onExecCommand', | 9075 'onExecCommand', |
9076 | |
8431 'onUndo', | 9077 'onUndo', |
9078 | |
8432 'onRedo', | 9079 'onRedo', |
9080 | |
8433 'onVisualAid', | 9081 'onVisualAid', |
9082 | |
8434 'onSetProgressState' | 9083 'onSetProgressState' |
8435 ], function(e) { | 9084 ], function(e) { |
8436 t[e] = new Dispatcher(t); | 9085 t[e] = new Dispatcher(t); |
8437 }); | 9086 }); |
8438 | 9087 |
8439 // Default editor config | |
8440 t.settings = s = extend({ | 9088 t.settings = s = extend({ |
8441 id : id, | 9089 id : id, |
8442 language : 'en', | 9090 language : 'en', |
8443 docs_language : 'en', | 9091 docs_language : 'en', |
8444 theme : 'simple', | 9092 theme : 'simple', |
8460 accessibility_focus : 1, | 9108 accessibility_focus : 1, |
8461 custom_shortcuts : 1, | 9109 custom_shortcuts : 1, |
8462 custom_undo_redo_keyboard_shortcuts : 1, | 9110 custom_undo_redo_keyboard_shortcuts : 1, |
8463 custom_undo_redo_restore_selection : 1, | 9111 custom_undo_redo_restore_selection : 1, |
8464 custom_undo_redo : 1, | 9112 custom_undo_redo : 1, |
8465 doctype : '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">', | 9113 doctype : tinymce.isIE6 ? '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">' : '<!DOCTYPE>', // Use old doctype on IE 6 to avoid horizontal scroll |
8466 visual_table_class : 'mceItemTable', | 9114 visual_table_class : 'mceItemTable', |
8467 visual : 1, | 9115 visual : 1, |
8468 inline_styles : true, | |
8469 convert_fonts_to_spans : true, | |
8470 font_size_style_values : 'xx-small,x-small,small,medium,large,x-large,xx-large', | 9116 font_size_style_values : 'xx-small,x-small,small,medium,large,x-large,xx-large', |
8471 apply_source_formatting : 1, | 9117 apply_source_formatting : 1, |
8472 directionality : 'ltr', | 9118 directionality : 'ltr', |
8473 forced_root_block : 'p', | 9119 forced_root_block : 'p', |
8474 valid_elements : '@[id|class|style|title|dir<ltr?rtl|lang|xml::lang|onclick|ondblclick|onmousedown|onmouseup|onmouseover|onmousemove|onmouseout|onkeypress|onkeydown|onkeyup],a[rel|rev|charset|hreflang|tabindex|accesskey|type|name|href|target|title|class|onfocus|onblur],strong/b,em/i,strike,u,#p[align],-ol[type|compact],-ul[type|compact],-li,br,img[longdesc|usemap|src|border|alt=|title|hspace|vspace|width|height|align],-sub,-sup,-blockquote[cite],-table[border=0|cellspacing|cellpadding|width|frame|rules|height|align|summary|bgcolor|background|bordercolor],-tr[rowspan|width|height|align|valign|bgcolor|background|bordercolor],tbody,thead,tfoot,#td[colspan|rowspan|width|height|align|valign|bgcolor|background|bordercolor|scope],#th[colspan|rowspan|width|height|align|valign|scope],caption,-div,-span,-code,-pre,address,-h1,-h2,-h3,-h4,-h5,-h6,hr[size|noshade],-font[face|size|color],dd,dl,dt,cite,abbr,acronym,del[datetime|cite],ins[datetime|cite],object[classid|width|height|codebase|*],param[name|value],embed[type|width|height|src|*],script[src|type],map[name],area[shape|coords|href|alt|target],bdo,button,col[align|char|charoff|span|valign|width],colgroup[align|char|charoff|span|valign|width],dfn,fieldset,form[action|accept|accept-charset|enctype|method],input[accept|alt|checked|disabled|maxlength|name|readonly|size|src|type|value|tabindex|accesskey],kbd,label[for],legend,noscript,optgroup[label|disabled],option[disabled|label|selected|value],q[cite],samp,select[disabled|multiple|name|size],small,textarea[cols|rows|disabled|name|readonly],tt,var,big', | 9120 valid_elements : '@[id|class|style|title|dir<ltr?rtl|lang|xml::lang|onclick|ondblclick|onmousedown|onmouseup|onmouseover|onmousemove|onmouseout|onkeypress|onkeydown|onkeyup],a[rel|rev|charset|hreflang|tabindex|accesskey|type|name|href|target|title|class|onfocus|onblur],strong/b,em/i,strike,u,#p,-ol[type|compact],-ul[type|compact],-li,br,img[longdesc|usemap|src|border|alt=|title|hspace|vspace|width|height|align],-sub,-sup,-blockquote[cite],-table[border|cellspacing|cellpadding|width|frame|rules|height|align|summary|bgcolor|background|bordercolor],-tr[rowspan|width|height|align|valign|bgcolor|background|bordercolor],tbody,thead,tfoot,#td[colspan|rowspan|width|height|align|valign|bgcolor|background|bordercolor|scope],#th[colspan|rowspan|width|height|align|valign|scope],caption,-div,-span,-code,-pre,address,-h1,-h2,-h3,-h4,-h5,-h6,hr[size|noshade],-font[face|size|color],dd,dl,dt,cite,abbr,acronym,del[datetime|cite],ins[datetime|cite],object[classid|width|height|codebase|*],param[name|value],embed[type|width|height|src|*],script[src|type],map[name],area[shape|coords|href|alt|target],bdo,button,col[align|char|charoff|span|valign|width],colgroup[align|char|charoff|span|valign|width],dfn,fieldset,form[action|accept|accept-charset|enctype|method],input[accept|alt|checked|disabled|maxlength|name|readonly|size|src|type|value|tabindex|accesskey],kbd,label[for],legend,noscript,optgroup[label|disabled],option[disabled|label|selected|value],q[cite],samp,select[disabled|multiple|name|size],small,textarea[cols|rows|disabled|name|readonly],tt,var,big', |
8475 hidden_input : 1, | 9121 hidden_input : 1, |
8476 padd_empty_editor : 1, | 9122 padd_empty_editor : 1, |
8477 render_ui : 1, | 9123 render_ui : 1, |
8478 init_theme : 1, | 9124 init_theme : 1, |
8479 force_p_newlines : 1, | 9125 force_p_newlines : 1, |
8480 indentation : '30px', | 9126 indentation : '30px', |
8481 keep_styles : 1, | 9127 keep_styles : 1, |
8482 fix_table_elements : 1, | 9128 fix_table_elements : 1, |
8483 removeformat_selector : 'span,b,strong,em,i,font,u,strike' | 9129 inline_styles : 1, |
9130 convert_fonts_to_spans : true | |
8484 }, s); | 9131 }, s); |
8485 | 9132 |
8486 // Setup URIs | |
8487 t.documentBaseURI = new tinymce.util.URI(s.document_base_url || tinymce.documentBaseURL, { | 9133 t.documentBaseURI = new tinymce.util.URI(s.document_base_url || tinymce.documentBaseURL, { |
8488 base_uri : tinyMCE.baseURI | 9134 base_uri : tinyMCE.baseURI |
8489 }); | 9135 }); |
8490 t.baseURI = EditorManager.baseURI; | 9136 |
9137 t.baseURI = tinymce.baseURI; | |
8491 | 9138 |
8492 // Call setup | 9139 // Call setup |
8493 t.execCallback('setup', t); | 9140 t.execCallback('setup', t); |
8494 }, | 9141 }, |
8495 | 9142 |
8502 t.render(); | 9149 t.render(); |
8503 }); | 9150 }); |
8504 return; | 9151 return; |
8505 } | 9152 } |
8506 | 9153 |
8507 // Force strict loading mode if render us called by user and not internally | 9154 tinyMCE.settings = s; |
8508 if (!nst) { | |
8509 s.strict_loading_mode = 1; | |
8510 tinyMCE.settings = s; | |
8511 } | |
8512 | 9155 |
8513 // Element not found, then skip initialization | 9156 // Element not found, then skip initialization |
8514 if (!t.getElement()) | 9157 if (!t.getElement()) |
8515 return; | 9158 return; |
8516 | |
8517 if (s.strict_loading_mode) { | |
8518 sl.settings.strict_mode = s.strict_loading_mode; | |
8519 tinymce.DOM.settings.strict = 1; | |
8520 } | |
8521 | 9159 |
8522 // Add hidden input for non input elements inside form elements | 9160 // Add hidden input for non input elements inside form elements |
8523 if (!/TEXTAREA|INPUT/i.test(t.getElement().nodeName) && s.hidden_input && DOM.getParent(id, 'form')) | 9161 if (!/TEXTAREA|INPUT/i.test(t.getElement().nodeName) && s.hidden_input && DOM.getParent(id, 'form')) |
8524 DOM.insertAfter(DOM.create('input', {type : 'hidden', name : id}), id); | 9162 DOM.insertAfter(DOM.create('input', {type : 'hidden', name : id}), id); |
8525 | 9163 |
8566 if (!n.submit.nodeType && !n.submit.length) { | 9204 if (!n.submit.nodeType && !n.submit.length) { |
8567 t.formElement = n; | 9205 t.formElement = n; |
8568 n._mceOldSubmit = n.submit; | 9206 n._mceOldSubmit = n.submit; |
8569 n.submit = function() { | 9207 n.submit = function() { |
8570 // Save all instances | 9208 // Save all instances |
8571 EditorManager.triggerSave(); | 9209 tinymce.triggerSave(); |
8572 t.isNotDirty = 1; | 9210 t.isNotDirty = 1; |
8573 | 9211 |
8574 return this._mceOldSubmit(this); | 9212 return t.formElement._mceOldSubmit(t.formElement); |
8575 }; | 9213 }; |
8576 } | 9214 } |
8577 | 9215 |
8578 n = null; | 9216 n = null; |
8579 }); | 9217 }); |
8587 if (s.theme && s.theme.charAt(0) != '-' && !ThemeManager.urls[s.theme]) | 9225 if (s.theme && s.theme.charAt(0) != '-' && !ThemeManager.urls[s.theme]) |
8588 ThemeManager.load(s.theme, 'themes/' + s.theme + '/editor_template' + tinymce.suffix + '.js'); | 9226 ThemeManager.load(s.theme, 'themes/' + s.theme + '/editor_template' + tinymce.suffix + '.js'); |
8589 | 9227 |
8590 each(explode(s.plugins), function(p) { | 9228 each(explode(s.plugins), function(p) { |
8591 if (p && p.charAt(0) != '-' && !PluginManager.urls[p]) { | 9229 if (p && p.charAt(0) != '-' && !PluginManager.urls[p]) { |
8592 // Skip safari plugin for other browsers | 9230 // Skip safari plugin, since it is removed as of 3.3b1 |
8593 if (!isWebKit && p == 'safari') | 9231 if (p == 'safari') |
8594 return; | 9232 return; |
8595 | 9233 |
8596 PluginManager.load(p, 'plugins/' + p + '/editor_plugin' + tinymce.suffix + '.js'); | 9234 PluginManager.load(p, 'plugins/' + p + '/editor_plugin' + tinymce.suffix + '.js'); |
8597 } | 9235 } |
8598 }); | 9236 }); |
8602 if (!t.removed) | 9240 if (!t.removed) |
8603 t.init(); | 9241 t.init(); |
8604 }); | 9242 }); |
8605 }; | 9243 }; |
8606 | 9244 |
8607 // Load compat2x first | 9245 loadScripts(); |
8608 if (s.plugins.indexOf('compat2x') != -1) { | |
8609 PluginManager.load('compat2x', 'plugins/compat2x/editor_plugin' + tinymce.suffix + '.js'); | |
8610 sl.loadQueue(loadScripts); | |
8611 } else | |
8612 loadScripts(); | |
8613 }, | 9246 }, |
8614 | 9247 |
8615 init : function() { | 9248 init : function() { |
8616 var n, t = this, s = t.settings, w, h, e = t.getElement(), o, ti, u, bi, bc, re; | 9249 var n, t = this, s = t.settings, w, h, e = t.getElement(), o, ti, u, bi, bc, re; |
8617 | 9250 |
8618 EditorManager.add(t); | 9251 tinymce.add(t); |
8619 | 9252 |
8620 // Create theme | |
8621 if (s.theme) { | 9253 if (s.theme) { |
8622 s.theme = s.theme.replace(/-/, ''); | 9254 s.theme = s.theme.replace(/-/, ''); |
8623 o = ThemeManager.get(s.theme); | 9255 o = ThemeManager.get(s.theme); |
8624 t.theme = new o(); | 9256 t.theme = new o(); |
8625 | 9257 |
8650 } | 9282 } |
8651 | 9283 |
8652 if (s.popup_css_add) | 9284 if (s.popup_css_add) |
8653 s.popup_css += ',' + t.documentBaseURI.toAbsolute(s.popup_css_add); | 9285 s.popup_css += ',' + t.documentBaseURI.toAbsolute(s.popup_css_add); |
8654 | 9286 |
8655 // Setup control factory | |
8656 t.controlManager = new tinymce.ControlManager(t); | 9287 t.controlManager = new tinymce.ControlManager(t); |
8657 t.undoManager = new tinymce.UndoManager(t); | |
8658 | |
8659 // Pass through | |
8660 t.undoManager.onAdd.add(function(um, l) { | |
8661 if (!l.initial) | |
8662 return t.onChange.dispatch(t, l, um); | |
8663 }); | |
8664 | |
8665 t.undoManager.onUndo.add(function(um, l) { | |
8666 return t.onUndo.dispatch(t, l, um); | |
8667 }); | |
8668 | |
8669 t.undoManager.onRedo.add(function(um, l) { | |
8670 return t.onRedo.dispatch(t, l, um); | |
8671 }); | |
8672 | 9288 |
8673 if (s.custom_undo_redo) { | 9289 if (s.custom_undo_redo) { |
9290 // Add initial undo level | |
9291 t.onBeforeExecCommand.add(function(ed, cmd, ui, val, a) { | |
9292 if (cmd != 'Undo' && cmd != 'Redo' && cmd != 'mceRepaint' && (!a || !a.skip_undo)) { | |
9293 if (!t.undoManager.hasUndo()) | |
9294 t.undoManager.add(); | |
9295 } | |
9296 }); | |
9297 | |
8674 t.onExecCommand.add(function(ed, cmd, ui, val, a) { | 9298 t.onExecCommand.add(function(ed, cmd, ui, val, a) { |
8675 if (cmd != 'Undo' && cmd != 'Redo' && cmd != 'mceRepaint' && (!a || !a.skip_undo)) | 9299 if (cmd != 'Undo' && cmd != 'Redo' && cmd != 'mceRepaint' && (!a || !a.skip_undo)) |
8676 t.undoManager.add(); | 9300 t.undoManager.add(); |
8677 }); | 9301 }); |
8678 } | 9302 } |
8722 | 9346 |
8723 t.editorContainer = o.editorContainer; | 9347 t.editorContainer = o.editorContainer; |
8724 } | 9348 } |
8725 | 9349 |
8726 | 9350 |
9351 // User specified a document.domain value | |
9352 if (document.domain && location.hostname != document.domain) | |
9353 tinymce.relaxedDomain = document.domain; | |
9354 | |
8727 // Resize editor | 9355 // Resize editor |
8728 DOM.setStyles(o.sizeContainer || o.editorContainer, { | 9356 DOM.setStyles(o.sizeContainer || o.editorContainer, { |
8729 width : w, | 9357 width : w, |
8730 height : h | 9358 height : h |
8731 }); | 9359 }); |
8732 | 9360 |
8733 h = (o.iframeHeight || h) + (typeof(h) == 'number' ? (o.deltaHeight || 0) : ''); | 9361 h = (o.iframeHeight || h) + (typeof(h) == 'number' ? (o.deltaHeight || 0) : ''); |
8734 if (h < 100) | 9362 if (h < 100) |
8735 h = 100; | 9363 h = 100; |
8736 | 9364 |
8737 t.iframeHTML = s.doctype + '<html><head xmlns="http://www.w3.org/1999/xhtml"><base href="' + t.documentBaseURI.getURI() + '" />'; | 9365 t.iframeHTML = s.doctype + '<html><head xmlns="http://www.w3.org/1999/xhtml">'; |
8738 t.iframeHTML += '<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />'; | 9366 |
9367 // We only need to override paths if we have to | |
9368 // IE has a bug where it remove site absolute urls to relative ones if this is specified | |
9369 if (s.document_base_url != tinymce.documentBaseURL) | |
9370 t.iframeHTML += '<base href="' + t.documentBaseURI.getURI() + '" />'; | |
9371 | |
9372 t.iframeHTML += '<meta http-equiv="X-UA-Compatible" content="IE=7" /><meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />'; | |
8739 | 9373 |
8740 if (tinymce.relaxedDomain) | 9374 if (tinymce.relaxedDomain) |
8741 t.iframeHTML += '<script type="text/javascript">document.domain = "' + tinymce.relaxedDomain + '";</script>'; | 9375 t.iframeHTML += '<script type="text/javascript">document.domain = "' + tinymce.relaxedDomain + '";</script>'; |
8742 | 9376 |
8743 bi = s.body_id || 'tinymce'; | 9377 bi = s.body_id || 'tinymce'; |
8815 b.contentEditable = true; | 9449 b.contentEditable = true; |
8816 | 9450 |
8817 DOM.show(b); | 9451 DOM.show(b); |
8818 } | 9452 } |
8819 | 9453 |
8820 // Setup objects | 9454 t.dom = new tinymce.dom.DOMUtils(t.getDoc(), { |
8821 t.dom = new tinymce.DOM.DOMUtils(t.getDoc(), { | |
8822 keep_values : true, | 9455 keep_values : true, |
8823 url_converter : t.convertURL, | 9456 url_converter : t.convertURL, |
8824 url_converter_scope : t, | 9457 url_converter_scope : t, |
8825 hex_colors : s.force_hex_style_colors, | 9458 hex_colors : s.force_hex_style_colors, |
8826 class_filter : s.class_filter, | 9459 class_filter : s.class_filter, |
8827 update_styles : 1, | 9460 update_styles : 1, |
8828 fix_ie_paragraphs : 1 | 9461 fix_ie_paragraphs : 1, |
9462 valid_styles : s.valid_styles | |
8829 }); | 9463 }); |
8830 | 9464 |
8831 t.serializer = new tinymce.dom.Serializer({ | 9465 t.schema = new tinymce.dom.Schema(); |
8832 entity_encoding : s.entity_encoding, | 9466 |
8833 entities : s.entities, | 9467 t.serializer = new tinymce.dom.Serializer(extend(s, { |
8834 valid_elements : s.verify_html === false ? '*[*]' : s.valid_elements, | 9468 valid_elements : s.verify_html === false ? '*[*]' : s.valid_elements, |
8835 extended_valid_elements : s.extended_valid_elements, | 9469 dom : t.dom, |
8836 valid_child_elements : s.valid_child_elements, | 9470 schema : t.schema |
8837 invalid_elements : s.invalid_elements, | 9471 })); |
8838 fix_table_elements : s.fix_table_elements, | 9472 |
8839 fix_list_elements : s.fix_list_elements, | 9473 t.selection = new tinymce.dom.Selection(t.dom, t.getWin(), t.serializer); |
8840 fix_content_duplication : s.fix_content_duplication, | 9474 |
8841 convert_fonts_to_spans : s.convert_fonts_to_spans, | 9475 t.formatter = new tinymce.Formatter(this); |
8842 font_size_classes : s.font_size_classes, | 9476 |
8843 font_size_style_values : s.font_size_style_values, | 9477 // Register default formats |
8844 apply_source_formatting : s.apply_source_formatting, | 9478 t.formatter.register({ |
8845 remove_linebreaks : s.remove_linebreaks, | 9479 alignleft : [ |
8846 element_format : s.element_format, | 9480 {selector : 'p,h1,h2,h3,h4,h5,h6,td,th,div,ul,ol,li', styles : {textAlign : 'left'}}, |
8847 dom : t.dom | 9481 {selector : 'img,table', styles : {'float' : 'left'}} |
9482 ], | |
9483 | |
9484 aligncenter : [ | |
9485 {selector : 'p,h1,h2,h3,h4,h5,h6,td,th,div,ul,ol,li', styles : {textAlign : 'center'}}, | |
9486 {selector : 'img', styles : {display : 'block', marginLeft : 'auto', marginRight : 'auto'}}, | |
9487 {selector : 'table', styles : {marginLeft : 'auto', marginRight : 'auto'}} | |
9488 ], | |
9489 | |
9490 alignright : [ | |
9491 {selector : 'p,h1,h2,h3,h4,h5,h6,td,th,div,ul,ol,li', styles : {textAlign : 'right'}}, | |
9492 {selector : 'img,table', styles : {'float' : 'right'}} | |
9493 ], | |
9494 | |
9495 alignfull : [ | |
9496 {selector : 'p,h1,h2,h3,h4,h5,h6,td,th,div,ul,ol,li', styles : {textAlign : 'justify'}} | |
9497 ], | |
9498 | |
9499 bold : [ | |
9500 {inline : 'strong'}, | |
9501 {inline : 'span', styles : {fontWeight : 'bold'}}, | |
9502 {inline : 'b'} | |
9503 ], | |
9504 | |
9505 italic : [ | |
9506 {inline : 'em'}, | |
9507 {inline : 'span', styles : {fontStyle : 'italic'}}, | |
9508 {inline : 'i'} | |
9509 ], | |
9510 | |
9511 underline : [ | |
9512 {inline : 'span', styles : {textDecoration : 'underline'}, exact : true}, | |
9513 {inline : 'u'} | |
9514 ], | |
9515 | |
9516 strikethrough : [ | |
9517 {inline : 'span', styles : {textDecoration : 'line-through'}, exact : true}, | |
9518 {inline : 'u'} | |
9519 ], | |
9520 | |
9521 forecolor : {inline : 'span', styles : {color : '%value'}}, | |
9522 hilitecolor : {inline : 'span', styles : {backgroundColor : '%value'}}, | |
9523 fontname : {inline : 'span', styles : {fontFamily : '%value'}}, | |
9524 fontsize : {inline : 'span', styles : {fontSize : '%value'}}, | |
9525 blockquote : {block : 'blockquote', wrapper : 1, remove : 'all'}, | |
9526 | |
9527 removeformat : [ | |
9528 {selector : 'b,strong,em,i,font,u,strike', remove : 'all', split : true, expand : false, block_expand : true, deep : true}, | |
9529 {selector : 'span', attributes : ['style', 'class'], remove : 'empty', split : true, expand : false, deep : true}, | |
9530 {selector : '*', attributes : ['style', 'class'], split : false, expand : false, deep : true} | |
9531 ] | |
8848 }); | 9532 }); |
8849 | 9533 |
8850 t.selection = new tinymce.dom.Selection(t.dom, t.getWin(), t.serializer); | 9534 // Register default block formats |
9535 each('p h1 h2 h3 h4 h5 h6 div address pre div code dt dd samp'.split(/\s/), function(name) { | |
9536 t.formatter.register(name, {block : name, remove : 'all'}); | |
9537 }); | |
9538 | |
9539 // Register user defined formats | |
9540 t.formatter.register(t.settings.formats); | |
9541 | |
9542 t.undoManager = new tinymce.UndoManager(t); | |
9543 | |
9544 // Pass through | |
9545 t.undoManager.onAdd.add(function(um, l) { | |
9546 if (!l.initial) | |
9547 return t.onChange.dispatch(t, l, um); | |
9548 }); | |
9549 | |
9550 t.undoManager.onUndo.add(function(um, l) { | |
9551 return t.onUndo.dispatch(t, l, um); | |
9552 }); | |
9553 | |
9554 t.undoManager.onRedo.add(function(um, l) { | |
9555 return t.onRedo.dispatch(t, l, um); | |
9556 }); | |
9557 | |
8851 t.forceBlocks = new tinymce.ForceBlocks(t, { | 9558 t.forceBlocks = new tinymce.ForceBlocks(t, { |
8852 forced_root_block : s.forced_root_block | 9559 forced_root_block : s.forced_root_block |
8853 }); | 9560 }); |
9561 | |
8854 t.editorCommands = new tinymce.EditorCommands(t); | 9562 t.editorCommands = new tinymce.EditorCommands(t); |
8855 | 9563 |
8856 // Pass through | 9564 // Pass through |
8857 t.serializer.onPreProcess.add(function(se, o) { | 9565 t.serializer.onPreProcess.add(function(se, o) { |
8858 return t.onPreProcess.dispatch(t, o, se); | 9566 return t.onPreProcess.dispatch(t, o, se); |
8876 if (s.directionality) | 9584 if (s.directionality) |
8877 t.getBody().dir = s.directionality; | 9585 t.getBody().dir = s.directionality; |
8878 | 9586 |
8879 if (s.nowrap) | 9587 if (s.nowrap) |
8880 t.getBody().style.whiteSpace = "nowrap"; | 9588 t.getBody().style.whiteSpace = "nowrap"; |
8881 | |
8882 if (s.auto_resize) | |
8883 t.onNodeChange.add(t.resizeToContent, t); | |
8884 | 9589 |
8885 if (s.custom_elements) { | 9590 if (s.custom_elements) { |
8886 function handleCustom(ed, o) { | 9591 function handleCustom(ed, o) { |
8887 each(explode(s.custom_elements), function(v) { | 9592 each(explode(s.custom_elements), function(v) { |
8888 var n; | 9593 var n; |
8891 v = v.substring(1); | 9596 v = v.substring(1); |
8892 n = 'span'; | 9597 n = 'span'; |
8893 } else | 9598 } else |
8894 n = 'div'; | 9599 n = 'div'; |
8895 | 9600 |
8896 o.content = o.content.replace(new RegExp('<(' + v + ')([^>]*)>', 'g'), '<' + n + ' mce_name="$1"$2>'); | 9601 o.content = o.content.replace(new RegExp('<(' + v + ')([^>]*)>', 'g'), '<' + n + ' _mce_name="$1"$2>'); |
8897 o.content = o.content.replace(new RegExp('</(' + v + ')>', 'g'), '</' + n + '>'); | 9602 o.content = o.content.replace(new RegExp('</(' + v + ')>', 'g'), '</' + n + '>'); |
8898 }); | 9603 }); |
8899 }; | 9604 }; |
8900 | 9605 |
8901 t.onBeforeSetContent.add(handleCustom); | 9606 t.onBeforeSetContent.add(handleCustom); |
8902 t.onPostProcess.add(function(ed, o) { | 9607 t.onPostProcess.add(function(ed, o) { |
8903 if (o.set) | 9608 if (o.set) |
8904 handleCustom(ed, o) | 9609 handleCustom(ed, o); |
8905 }); | 9610 }); |
8906 } | 9611 } |
8907 | 9612 |
8908 if (s.handle_node_change_callback) { | 9613 if (s.handle_node_change_callback) { |
8909 t.onNodeChange.add(function(ed, cm, n) { | 9614 t.onNodeChange.add(function(ed, cm, n) { |
8974 | 9679 |
8975 return v; | 9680 return v; |
8976 }; | 9681 }; |
8977 } | 9682 } |
8978 | 9683 |
8979 if (s.convert_fonts_to_spans) | |
8980 t._convertFonts(); | |
8981 | |
8982 if (s.inline_styles) | |
8983 t._convertInlineElements(); | |
8984 | |
8985 if (s.cleanup_callback) { | 9684 if (s.cleanup_callback) { |
8986 t.onBeforeSetContent.add(function(ed, o) { | 9685 t.onBeforeSetContent.add(function(ed, o) { |
8987 o.content = t.execCallback('cleanup_callback', 'insert_to_editor', o.content, o); | 9686 o.content = t.execCallback('cleanup_callback', 'insert_to_editor', o.content, o); |
8988 }); | 9687 }); |
8989 | 9688 |
9028 t.onPostProcess.add(function(ed, o) { | 9727 t.onPostProcess.add(function(ed, o) { |
9029 o.content = o.content.replace(/^(<p[^>]*>( | |\s|\u00a0|)<\/p>[\r\n]*|<br \/>[\r\n]*)$/, ''); | 9728 o.content = o.content.replace(/^(<p[^>]*>( | |\s|\u00a0|)<\/p>[\r\n]*|<br \/>[\r\n]*)$/, ''); |
9030 }); | 9729 }); |
9031 } | 9730 } |
9032 | 9731 |
9033 // Fix gecko link bug, when a link is placed at the end of block elements there is | |
9034 // no way to move the caret behind the link. This fix adds a bogus br element after the link | |
9035 if (isGecko) { | 9732 if (isGecko) { |
9733 // Fix gecko link bug, when a link is placed at the end of block elements there is | |
9734 // no way to move the caret behind the link. This fix adds a bogus br element after the link | |
9036 function fixLinks(ed, o) { | 9735 function fixLinks(ed, o) { |
9037 each(ed.dom.select('a'), function(n) { | 9736 each(ed.dom.select('a'), function(n) { |
9038 var pn = n.parentNode; | 9737 var pn = n.parentNode; |
9039 | 9738 |
9040 if (ed.dom.isBlock(pn) && pn.lastChild === n) | 9739 if (ed.dom.isBlock(pn) && pn.lastChild === n) |
9041 ed.dom.add(pn, 'br', {'mce_bogus' : 1}); | 9740 ed.dom.add(pn, 'br', {'_mce_bogus' : 1}); |
9042 }); | 9741 }); |
9043 }; | 9742 }; |
9044 | 9743 |
9045 t.onExecCommand.add(function(ed, cmd) { | 9744 t.onExecCommand.add(function(ed, cmd) { |
9046 if (cmd === 'CreateLink') | 9745 if (cmd === 'CreateLink') |
9047 fixLinks(ed); | 9746 fixLinks(ed); |
9048 }); | 9747 }); |
9049 | 9748 |
9050 t.onSetContent.add(t.selection.onSetContent.add(fixLinks)); | 9749 t.onSetContent.add(t.selection.onSetContent.add(fixLinks)); |
9051 } | 9750 |
9052 | 9751 if (!s.readonly) { |
9053 if (isGecko && !s.readonly) { | 9752 try { |
9054 try { | 9753 // Design mode must be set here once again to fix a bug where |
9055 // Design mode must be set here once again to fix a bug where | 9754 // Ctrl+A/Delete/Backspace didn't work if the editor was added using mceAddControl then removed then added again |
9056 // Ctrl+A/Delete/Backspace didn't work if the editor was added using mceAddControl then removed then added again | 9755 d.designMode = 'Off'; |
9057 d.designMode = 'Off'; | 9756 d.designMode = 'On'; |
9058 d.designMode = 'On'; | 9757 } catch (ex) { |
9059 } catch (ex) { | 9758 // Will fail on Gecko if the editor is placed in an hidden container element |
9060 // Will fail on Gecko if the editor is placed in an hidden container element | 9759 // The design mode will be set ones the editor is focused |
9061 // The design mode will be set ones the editor is focused | 9760 } |
9062 } | 9761 } |
9063 } | 9762 } |
9064 | 9763 |
9065 // A small timeout was needed since firefox will remove. Bug: #1838304 | 9764 // A small timeout was needed since firefox will remove. Bug: #1838304 |
9066 setTimeout(function () { | 9765 setTimeout(function () { |
9067 if (t.removed) | 9766 if (t.removed) |
9068 return; | 9767 return; |
9069 | 9768 |
9070 t.load({initial : true, format : (s.cleanup_on_startup ? 'html' : 'raw')}); | 9769 t.load({initial : true, format : (s.cleanup_on_startup ? 'html' : 'raw')}); |
9071 t.startContent = t.getContent({format : 'raw'}); | 9770 t.startContent = t.getContent({format : 'raw'}); |
9072 t.undoManager.add({initial : true}); | |
9073 t.initialized = true; | 9771 t.initialized = true; |
9074 | 9772 |
9075 t.onInit.dispatch(t); | 9773 t.onInit.dispatch(t); |
9076 t.execCallback('setupcontent_callback', t.id, t.getBody(), t.getDoc()); | 9774 t.execCallback('setupcontent_callback', t.id, t.getBody(), t.getDoc()); |
9077 t.execCallback('init_instance_callback', t); | 9775 t.execCallback('init_instance_callback', t); |
9086 } | 9784 } |
9087 | 9785 |
9088 // Handle auto focus | 9786 // Handle auto focus |
9089 if (s.auto_focus) { | 9787 if (s.auto_focus) { |
9090 setTimeout(function () { | 9788 setTimeout(function () { |
9091 var ed = EditorManager.get(s.auto_focus); | 9789 var ed = tinymce.get(s.auto_focus); |
9092 | 9790 |
9093 ed.selection.select(ed.getBody(), 1); | 9791 ed.selection.select(ed.getBody(), 1); |
9094 ed.selection.collapse(1); | 9792 ed.selection.collapse(1); |
9095 ed.getWin().focus(); | 9793 ed.getWin().focus(); |
9096 }, 100); | 9794 }, 100); |
9103 | 9801 |
9104 focus : function(sf) { | 9802 focus : function(sf) { |
9105 var oed, t = this, ce = t.settings.content_editable; | 9803 var oed, t = this, ce = t.settings.content_editable; |
9106 | 9804 |
9107 if (!sf) { | 9805 if (!sf) { |
9108 // Is not content editable or the selection is outside the area in IE | 9806 // Is not content editable |
9109 // the IE statement is needed to avoid bluring if element selections inside layers since | 9807 if (!ce) |
9110 // the layer is like it's own document in IE | |
9111 if (!ce && (!isIE || t.selection.getNode().ownerDocument != t.getDoc())) | |
9112 t.getWin().focus(); | 9808 t.getWin().focus(); |
9113 | 9809 |
9114 } | 9810 } |
9115 | 9811 |
9116 if (EditorManager.activeEditor != t) { | 9812 if (tinymce.activeEditor != t) { |
9117 if ((oed = EditorManager.activeEditor) != null) | 9813 if ((oed = tinymce.activeEditor) != null) |
9118 oed.onDeactivate.dispatch(oed, t); | 9814 oed.onDeactivate.dispatch(oed, t); |
9119 | 9815 |
9120 t.onActivate.dispatch(t, oed); | 9816 t.onActivate.dispatch(t, oed); |
9121 } | 9817 } |
9122 | 9818 |
9123 EditorManager._setActive(t); | 9819 tinymce._setActive(t); |
9124 }, | 9820 }, |
9125 | 9821 |
9126 execCallback : function(n) { | 9822 execCallback : function(n) { |
9127 var t = this, f = t.settings[n], s; | 9823 var t = this, f = t.settings[n], s; |
9128 | 9824 |
9145 | 9841 |
9146 return f.apply(s || t, Array.prototype.slice.call(arguments, 1)); | 9842 return f.apply(s || t, Array.prototype.slice.call(arguments, 1)); |
9147 }, | 9843 }, |
9148 | 9844 |
9149 translate : function(s) { | 9845 translate : function(s) { |
9150 var c = this.settings.language || 'en', i18n = EditorManager.i18n; | 9846 var c = this.settings.language || 'en', i18n = tinymce.i18n; |
9151 | 9847 |
9152 if (!s) | 9848 if (!s) |
9153 return ''; | 9849 return ''; |
9154 | 9850 |
9155 return i18n[c + '.' + s] || s.replace(/{\#([^}]+)\}/g, function(a, b) { | 9851 return i18n[c + '.' + s] || s.replace(/{\#([^}]+)\}/g, function(a, b) { |
9156 return i18n[c + '.' + b] || '{#' + b + '}'; | 9852 return i18n[c + '.' + b] || '{#' + b + '}'; |
9157 }); | 9853 }); |
9158 }, | 9854 }, |
9159 | 9855 |
9160 getLang : function(n, dv) { | 9856 getLang : function(n, dv) { |
9161 return EditorManager.i18n[(this.settings.language || 'en') + '.' + n] || (is(dv) ? dv : '{#' + n + '}'); | 9857 return tinymce.i18n[(this.settings.language || 'en') + '.' + n] || (is(dv) ? dv : '{#' + n + '}'); |
9162 }, | 9858 }, |
9163 | 9859 |
9164 getParam : function(n, dv, ty) { | 9860 getParam : function(n, dv, ty) { |
9165 var tr = tinymce.trim, v = is(this.settings[n]) ? this.settings[n] : dv, o; | 9861 var tr = tinymce.trim, v = is(this.settings[n]) ? this.settings[n] : dv, o; |
9166 | 9862 |
9184 | 9880 |
9185 return v; | 9881 return v; |
9186 }, | 9882 }, |
9187 | 9883 |
9188 nodeChanged : function(o) { | 9884 nodeChanged : function(o) { |
9189 var t = this, s = t.selection, n = s.getNode() || t.getBody(); | 9885 var t = this, s = t.selection, n = (isIE ? s.getNode() : s.getStart()) || t.getBody(); |
9190 | 9886 |
9191 // Fix for bug #1896577 it seems that this can not be fired while the editor is loading | 9887 // Fix for bug #1896577 it seems that this can not be fired while the editor is loading |
9192 if (t.initialized) { | 9888 if (t.initialized) { |
9889 o = o || {}; | |
9890 n = isIE && n.ownerDocument != t.getDoc() ? t.getBody() : n; // Fix for IE initial state | |
9891 | |
9892 // Get parents and add them to object | |
9893 o.parents = []; | |
9894 t.dom.getParent(n, function(node) { | |
9895 if (node.nodeName == 'BODY') | |
9896 return true; | |
9897 | |
9898 o.parents.push(node); | |
9899 }); | |
9900 | |
9193 t.onNodeChange.dispatch( | 9901 t.onNodeChange.dispatch( |
9194 t, | 9902 t, |
9195 o ? o.controlManager || t.controlManager : t.controlManager, | 9903 o ? o.controlManager || t.controlManager : t.controlManager, |
9196 isIE && n.ownerDocument != t.getDoc() ? t.getBody() : n, // Fix for IE initial state | 9904 n, |
9197 s.isCollapsed(), | 9905 s.isCollapsed(), |
9198 o | 9906 o |
9199 ); | 9907 ); |
9200 } | 9908 } |
9201 }, | 9909 }, |
9334 // Browser commands | 10042 // Browser commands |
9335 t.getDoc().execCommand(cmd, ui, val); | 10043 t.getDoc().execCommand(cmd, ui, val); |
9336 t.onExecCommand.dispatch(t, cmd, ui, val, a); | 10044 t.onExecCommand.dispatch(t, cmd, ui, val, a); |
9337 }, | 10045 }, |
9338 | 10046 |
9339 queryCommandState : function(c) { | 10047 queryCommandState : function(cmd) { |
9340 var t = this, o, s; | 10048 var t = this, o, s; |
9341 | 10049 |
9342 // Is hidden then return undefined | 10050 // Is hidden then return undefined |
9343 if (t._isHidden()) | 10051 if (t._isHidden()) |
9344 return; | 10052 return; |
9345 | 10053 |
9346 // Registred commands | 10054 // Registred commands |
9347 if (o = t.queryStateCommands[c]) { | 10055 if (o = t.queryStateCommands[cmd]) { |
9348 s = o.func.call(o.scope); | 10056 s = o.func.call(o.scope); |
9349 | 10057 |
9350 // Fall though on true | 10058 // Fall though on true |
9351 if (s !== true) | 10059 if (s !== true) |
9352 return s; | 10060 return s; |
9353 } | 10061 } |
9354 | 10062 |
9355 // Registred commands | 10063 // Registred commands |
9356 o = t.editorCommands.queryCommandState(c); | 10064 o = t.editorCommands.queryCommandState(cmd); |
9357 if (o !== -1) | 10065 if (o !== -1) |
9358 return o; | 10066 return o; |
9359 | 10067 |
9360 // Browser commands | 10068 // Browser commands |
9361 try { | 10069 try { |
9362 return this.getDoc().queryCommandState(c); | 10070 return this.getDoc().queryCommandState(cmd); |
9363 } catch (ex) { | 10071 } catch (ex) { |
9364 // Fails sometimes see bug: 1896577 | 10072 // Fails sometimes see bug: 1896577 |
9365 } | 10073 } |
9366 }, | 10074 }, |
9367 | 10075 |
9421 | 10129 |
9422 setProgressState : function(b, ti, o) { | 10130 setProgressState : function(b, ti, o) { |
9423 this.onSetProgressState.dispatch(this, b, ti, o); | 10131 this.onSetProgressState.dispatch(this, b, ti, o); |
9424 | 10132 |
9425 return b; | 10133 return b; |
9426 }, | |
9427 | |
9428 resizeToContent : function() { | |
9429 var t = this; | |
9430 | |
9431 DOM.setStyle(t.id + "_ifr", 'height', t.getBody().scrollHeight); | |
9432 }, | 10134 }, |
9433 | 10135 |
9434 load : function(o) { | 10136 load : function(o) { |
9435 var t = this, e = t.getElement(), h; | 10137 var t = this, e = t.getElement(), h; |
9436 | 10138 |
9506 t.onBeforeSetContent.dispatch(t, o); | 10208 t.onBeforeSetContent.dispatch(t, o); |
9507 | 10209 |
9508 // Padd empty content in Gecko and Safari. Commands will otherwise fail on the content | 10210 // Padd empty content in Gecko and Safari. Commands will otherwise fail on the content |
9509 // It will also be impossible to place the caret in the editor unless there is a BR element present | 10211 // It will also be impossible to place the caret in the editor unless there is a BR element present |
9510 if (!tinymce.isIE && (h.length === 0 || /^\s+$/.test(h))) { | 10212 if (!tinymce.isIE && (h.length === 0 || /^\s+$/.test(h))) { |
9511 o.content = t.dom.setHTML(t.getBody(), '<br mce_bogus="1" />'); | 10213 o.content = t.dom.setHTML(t.getBody(), '<br _mce_bogus="1" />'); |
9512 o.format = 'raw'; | 10214 o.format = 'raw'; |
9513 } | 10215 } |
9514 | 10216 |
9515 o.content = t.dom.setHTML(t.getBody(), tinymce.trim(o.content)); | 10217 o.content = t.dom.setHTML(t.getBody(), tinymce.trim(o.content)); |
9516 | 10218 |
9675 t.onRemove.dispatch(t); | 10377 t.onRemove.dispatch(t); |
9676 | 10378 |
9677 // Clear all execCommand listeners this is required to avoid errors if the editor was removed inside another command | 10379 // Clear all execCommand listeners this is required to avoid errors if the editor was removed inside another command |
9678 t.onExecCommand.listeners = []; | 10380 t.onExecCommand.listeners = []; |
9679 | 10381 |
9680 EditorManager.remove(t); | 10382 tinymce.remove(t); |
9681 DOM.remove(e); | 10383 DOM.remove(e); |
9682 }, | 10384 }, |
9683 | 10385 |
9684 destroy : function(s) { | 10386 destroy : function(s) { |
9685 var t = this; | 10387 var t = this; |
9763 each(lo, function(v, k) { | 10465 each(lo, function(v, k) { |
9764 switch (k) { | 10466 switch (k) { |
9765 case 'contextmenu': | 10467 case 'contextmenu': |
9766 if (tinymce.isOpera) { | 10468 if (tinymce.isOpera) { |
9767 // Fake contextmenu on Opera | 10469 // Fake contextmenu on Opera |
9768 Event.add(t.getBody(), 'mousedown', function(e) { | 10470 t.dom.bind(t.getBody(), 'mousedown', function(e) { |
9769 if (e.ctrlKey) { | 10471 if (e.ctrlKey) { |
9770 e.fakeType = 'contextmenu'; | 10472 e.fakeType = 'contextmenu'; |
9771 eventHandler(e); | 10473 eventHandler(e); |
9772 } | 10474 } |
9773 }); | 10475 }); |
9774 } else | 10476 } else |
9775 Event.add(t.getBody(), k, eventHandler); | 10477 t.dom.bind(t.getBody(), k, eventHandler); |
9776 break; | 10478 break; |
9777 | 10479 |
9778 case 'paste': | 10480 case 'paste': |
9779 Event.add(t.getBody(), k, function(e) { | 10481 t.dom.bind(t.getBody(), k, function(e) { |
9780 var tx, h, el, r; | 10482 eventHandler(e); |
9781 | |
9782 // Get plain text data | |
9783 if (e.clipboardData) | |
9784 tx = e.clipboardData.getData('text/plain'); | |
9785 else if (tinymce.isIE) | |
9786 tx = t.getWin().clipboardData.getData('Text'); | |
9787 | |
9788 // Get HTML data | |
9789 /*if (tinymce.isIE) { | |
9790 el = DOM.add(DOM.doc.body, 'div', {style : 'visibility:hidden;overflow:hidden;position:absolute;width:1px;height:1px'}); | |
9791 r = DOM.doc.body.createTextRange(); | |
9792 r.moveToElementText(el); | |
9793 r.execCommand('Paste'); | |
9794 h = el.innerHTML; | |
9795 DOM.remove(el); | |
9796 }*/ | |
9797 | |
9798 eventHandler(e, {text : tx, html : h}); | |
9799 }); | 10483 }); |
9800 break; | 10484 break; |
9801 | 10485 |
9802 case 'submit': | 10486 case 'submit': |
9803 case 'reset': | 10487 case 'reset': |
9804 Event.add(t.getElement().form || DOM.getParent(t.id, 'form'), k, eventHandler); | 10488 t.dom.bind(t.getElement().form || DOM.getParent(t.id, 'form'), k, eventHandler); |
9805 break; | 10489 break; |
9806 | 10490 |
9807 default: | 10491 default: |
9808 Event.add(s.content_editable ? t.getBody() : t.getDoc(), k, eventHandler); | 10492 t.dom.bind(s.content_editable ? t.getBody() : t.getDoc(), k, eventHandler); |
9809 } | 10493 } |
9810 }); | 10494 }); |
9811 | 10495 |
9812 Event.add(s.content_editable ? t.getBody() : (isGecko ? t.getDoc() : t.getWin()), 'focus', function(e) { | 10496 t.dom.bind(s.content_editable ? t.getBody() : (isGecko ? t.getDoc() : t.getWin()), 'focus', function(e) { |
9813 t.focus(true); | 10497 t.focus(true); |
9814 }); | 10498 }); |
9815 | 10499 |
9816 | 10500 |
9817 // Fixes bug where a specified document_base_uri could result in broken images | 10501 // Fixes bug where a specified document_base_uri could result in broken images |
9820 // Convert all images to absolute URLs | 10504 // Convert all images to absolute URLs |
9821 /* t.onSetContent.add(function(ed, o) { | 10505 /* t.onSetContent.add(function(ed, o) { |
9822 each(ed.dom.select('img'), function(e) { | 10506 each(ed.dom.select('img'), function(e) { |
9823 var v; | 10507 var v; |
9824 | 10508 |
9825 if (v = e.getAttribute('mce_src')) | 10509 if (v = e.getAttribute('_mce_src')) |
9826 e.src = t.documentBaseURI.toAbsolute(v); | 10510 e.src = t.documentBaseURI.toAbsolute(v); |
9827 }) | 10511 }) |
9828 });*/ | 10512 });*/ |
9829 | 10513 |
9830 Event.add(t.getDoc(), 'DOMNodeInserted', function(e) { | 10514 t.dom.bind(t.getDoc(), 'DOMNodeInserted', function(e) { |
9831 var v; | 10515 var v; |
9832 | 10516 |
9833 e = e.target; | 10517 e = e.target; |
9834 | 10518 |
9835 if (e.nodeType === 1 && e.nodeName === 'IMG' && (v = e.getAttribute('mce_src'))) | 10519 if (e.nodeType === 1 && e.nodeName === 'IMG' && (v = e.getAttribute('_mce_src'))) |
9836 e.src = t.documentBaseURI.toAbsolute(v); | 10520 e.src = t.documentBaseURI.toAbsolute(v); |
9837 }); | 10521 }); |
9838 } | 10522 } |
9839 | 10523 |
9840 // Set various midas options in Gecko | 10524 // Set various midas options in Gecko |
9871 | 10555 |
9872 t.onBeforeExecCommand.add(setOpts); | 10556 t.onBeforeExecCommand.add(setOpts); |
9873 t.onMouseDown.add(setOpts); | 10557 t.onMouseDown.add(setOpts); |
9874 } | 10558 } |
9875 | 10559 |
10560 // Workaround for bug, http://bugs.webkit.org/show_bug.cgi?id=12250 | |
10561 // WebKit can't even do simple things like selecting an image | |
10562 // This also fixes so it's possible to select mceItemAnchors | |
10563 if (tinymce.isWebKit) { | |
10564 t.onClick.add(function(ed, e) { | |
10565 e = e.target; | |
10566 | |
10567 // Needs tobe the setBaseAndExtend or it will fail to select floated images | |
10568 if (e.nodeName == 'IMG' || (e.nodeName == 'A' && t.dom.hasClass(e, 'mceItemAnchor'))) | |
10569 t.selection.getSel().setBaseAndExtent(e, 0, e, 1); | |
10570 }); | |
10571 } | |
10572 | |
9876 // Add node change handlers | 10573 // Add node change handlers |
9877 t.onMouseUp.add(t.nodeChanged); | 10574 t.onMouseUp.add(t.nodeChanged); |
9878 t.onClick.add(t.nodeChanged); | 10575 t.onClick.add(t.nodeChanged); |
9879 t.onKeyUp.add(function(ed, e) { | 10576 t.onKeyUp.add(function(ed, e) { |
9880 var c = e.keyCode; | 10577 var c = e.keyCode; |
9902 t.addShortcut('ctrl+u', t.getLang('underline_desc'), 'Underline'); | 10599 t.addShortcut('ctrl+u', t.getLang('underline_desc'), 'Underline'); |
9903 } | 10600 } |
9904 | 10601 |
9905 // BlockFormat shortcuts keys | 10602 // BlockFormat shortcuts keys |
9906 for (i=1; i<=6; i++) | 10603 for (i=1; i<=6; i++) |
9907 t.addShortcut('ctrl+' + i, '', ['FormatBlock', false, '<h' + i + '>']); | 10604 t.addShortcut('ctrl+' + i, '', ['FormatBlock', false, 'h' + i]); |
9908 | 10605 |
9909 t.addShortcut('ctrl+7', '', ['FormatBlock', false, '<p>']); | 10606 t.addShortcut('ctrl+7', '', ['FormatBlock', false, '<p>']); |
9910 t.addShortcut('ctrl+8', '', ['FormatBlock', false, '<div>']); | 10607 t.addShortcut('ctrl+8', '', ['FormatBlock', false, '<div>']); |
9911 t.addShortcut('ctrl+9', '', ['FormatBlock', false, '<address>']); | 10608 t.addShortcut('ctrl+9', '', ['FormatBlock', false, '<address>']); |
9912 | 10609 |
9962 } | 10659 } |
9963 | 10660 |
9964 if (tinymce.isIE) { | 10661 if (tinymce.isIE) { |
9965 // Fix so resize will only update the width and height attributes not the styles of an image | 10662 // Fix so resize will only update the width and height attributes not the styles of an image |
9966 // It will also block mceItemNoResize items | 10663 // It will also block mceItemNoResize items |
9967 Event.add(t.getDoc(), 'controlselect', function(e) { | 10664 t.dom.bind(t.getDoc(), 'controlselect', function(e) { |
9968 var re = t.resizeInfo, cb; | 10665 var re = t.resizeInfo, cb; |
9969 | 10666 |
9970 e = e.target; | 10667 e = e.target; |
9971 | 10668 |
9972 // Don't do this action for non image elements | 10669 // Don't do this action for non image elements |
9973 if (e.nodeName !== 'IMG') | 10670 if (e.nodeName !== 'IMG') |
9974 return; | 10671 return; |
9975 | 10672 |
9976 if (re) | 10673 if (re) |
9977 Event.remove(re.node, re.ev, re.cb); | 10674 t.dom.unbind(re.node, re.ev, re.cb); |
9978 | 10675 |
9979 if (!t.dom.hasClass(e, 'mceItemNoResize')) { | 10676 if (!t.dom.hasClass(e, 'mceItemNoResize')) { |
9980 ev = 'resizeend'; | 10677 ev = 'resizeend'; |
9981 cb = Event.add(e, ev, function(e) { | 10678 cb = t.dom.bind(e, ev, function(e) { |
9982 var v; | 10679 var v; |
9983 | 10680 |
9984 e = e.target; | 10681 e = e.target; |
9985 | 10682 |
9986 if (v = t.dom.getStyle(e, 'width')) { | 10683 if (v = t.dom.getStyle(e, 'width')) { |
9993 t.dom.setStyle(e, 'height', ''); | 10690 t.dom.setStyle(e, 'height', ''); |
9994 } | 10691 } |
9995 }); | 10692 }); |
9996 } else { | 10693 } else { |
9997 ev = 'resizestart'; | 10694 ev = 'resizestart'; |
9998 cb = Event.add(e, 'resizestart', Event.cancel, Event); | 10695 cb = t.dom.bind(e, 'resizestart', Event.cancel, Event); |
9999 } | 10696 } |
10000 | 10697 |
10001 re = t.resizeInfo = { | 10698 re = t.resizeInfo = { |
10002 node : e, | 10699 node : e, |
10003 ev : ev, | 10700 ev : ev, |
10008 t.onKeyDown.add(function(ed, e) { | 10705 t.onKeyDown.add(function(ed, e) { |
10009 switch (e.keyCode) { | 10706 switch (e.keyCode) { |
10010 case 8: | 10707 case 8: |
10011 // Fix IE control + backspace browser bug | 10708 // Fix IE control + backspace browser bug |
10012 if (t.selection.getRng().item) { | 10709 if (t.selection.getRng().item) { |
10013 t.selection.getRng().item(0).removeNode(); | 10710 ed.dom.remove(t.selection.getRng().item(0)); |
10014 return Event.cancel(e); | 10711 return Event.cancel(e); |
10015 } | 10712 } |
10016 } | 10713 } |
10017 }); | 10714 }); |
10715 | |
10716 /*if (t.dom.boxModel) { | |
10717 t.getBody().style.height = '100%'; | |
10718 | |
10719 Event.add(t.getWin(), 'resize', function(e) { | |
10720 var docElm = t.getDoc().documentElement; | |
10721 | |
10722 docElm.style.height = (docElm.offsetHeight - 10) + 'px'; | |
10723 }); | |
10724 }*/ | |
10018 } | 10725 } |
10019 | 10726 |
10020 if (tinymce.isOpera) { | 10727 if (tinymce.isOpera) { |
10021 t.onClick.add(function(ed, e) { | 10728 t.onClick.add(function(ed, e) { |
10022 Event.prevent(e); | 10729 Event.prevent(e); |
10028 function addUndo() { | 10735 function addUndo() { |
10029 t.undoManager.typing = 0; | 10736 t.undoManager.typing = 0; |
10030 t.undoManager.add(); | 10737 t.undoManager.add(); |
10031 }; | 10738 }; |
10032 | 10739 |
10033 // Add undo level on editor blur | 10740 t.dom.bind(t.getDoc(), 'focusout', function(e) { |
10034 if (tinymce.isIE) { | 10741 if (!t.removed && t.undoManager.typing) |
10035 Event.add(t.getWin(), 'blur', function(e) { | 10742 addUndo(); |
10036 var n; | 10743 }); |
10037 | |
10038 // Check added for fullscreen bug | |
10039 if (t.selection) { | |
10040 n = t.selection.getNode(); | |
10041 | |
10042 // Add undo level is selection was lost to another document | |
10043 if (!t.removed && n.ownerDocument && n.ownerDocument != t.getDoc()) | |
10044 addUndo(); | |
10045 } | |
10046 }); | |
10047 } else { | |
10048 Event.add(t.getDoc(), 'blur', function() { | |
10049 if (t.selection && !t.removed) | |
10050 addUndo(); | |
10051 }); | |
10052 } | |
10053 | |
10054 t.onMouseDown.add(addUndo); | |
10055 | 10744 |
10056 t.onKeyUp.add(function(ed, e) { | 10745 t.onKeyUp.add(function(ed, e) { |
10057 if ((e.keyCode >= 33 && e.keyCode <= 36) || (e.keyCode >= 37 && e.keyCode <= 40) || e.keyCode == 13 || e.keyCode == 45 || e.ctrlKey) { | 10746 if ((e.keyCode >= 33 && e.keyCode <= 36) || (e.keyCode >= 37 && e.keyCode <= 40) || e.keyCode == 13 || e.keyCode == 45 || e.ctrlKey) |
10058 t.undoManager.typing = 0; | 10747 addUndo(); |
10059 t.undoManager.add(); | |
10060 } | |
10061 }); | 10748 }); |
10062 | 10749 |
10063 t.onKeyDown.add(function(ed, e) { | 10750 t.onKeyDown.add(function(ed, e) { |
10064 // Is caracter positon keys | 10751 // Is caracter positon keys |
10065 if ((e.keyCode >= 33 && e.keyCode <= 36) || (e.keyCode >= 37 && e.keyCode <= 40) || e.keyCode == 13 || e.keyCode == 45) { | 10752 if ((e.keyCode >= 33 && e.keyCode <= 36) || (e.keyCode >= 37 && e.keyCode <= 40) || e.keyCode == 13 || e.keyCode == 45) { |
10066 if (t.undoManager.typing) { | 10753 if (t.undoManager.typing) |
10067 t.undoManager.add(); | 10754 addUndo(); |
10068 t.undoManager.typing = 0; | |
10069 } | |
10070 | 10755 |
10071 return; | 10756 return; |
10072 } | 10757 } |
10073 | 10758 |
10074 if (!t.undoManager.typing) { | 10759 if (!t.undoManager.typing) { |
10075 t.undoManager.add(); | 10760 t.undoManager.add(); |
10076 t.undoManager.typing = 1; | 10761 t.undoManager.typing = 1; |
10077 } | 10762 } |
10078 }); | 10763 }); |
10079 } | 10764 |
10080 }, | 10765 t.onMouseDown.add(function() { |
10081 | 10766 if (t.undoManager.typing) |
10082 _convertInlineElements : function() { | 10767 addUndo(); |
10083 var t = this, s = t.settings, dom = t.dom, v, e, na, st, sp; | |
10084 | |
10085 function convert(ed, o) { | |
10086 if (!s.inline_styles) | |
10087 return; | |
10088 | |
10089 if (o.get) { | |
10090 each(t.dom.select('table,u,strike', o.node), function(n) { | |
10091 switch (n.nodeName) { | |
10092 case 'TABLE': | |
10093 if (v = dom.getAttrib(n, 'height')) { | |
10094 dom.setStyle(n, 'height', v); | |
10095 dom.setAttrib(n, 'height', ''); | |
10096 } | |
10097 break; | |
10098 | |
10099 case 'U': | |
10100 case 'STRIKE': | |
10101 //sp = dom.create('span', {style : dom.getAttrib(n, 'style')}); | |
10102 n.style.textDecoration = n.nodeName == 'U' ? 'underline' : 'line-through'; | |
10103 dom.setAttrib(n, 'mce_style', ''); | |
10104 dom.setAttrib(n, 'mce_name', 'span'); | |
10105 break; | |
10106 } | |
10107 }); | |
10108 } else if (o.set) { | |
10109 each(t.dom.select('table,span', o.node).reverse(), function(n) { | |
10110 if (n.nodeName == 'TABLE') { | |
10111 if (v = dom.getStyle(n, 'height')) | |
10112 dom.setAttrib(n, 'height', v.replace(/[^0-9%]+/g, '')); | |
10113 } else { | |
10114 // Convert spans to elements | |
10115 if (n.style.textDecoration == 'underline') | |
10116 na = 'u'; | |
10117 else if (n.style.textDecoration == 'line-through') | |
10118 na = 'strike'; | |
10119 else | |
10120 na = ''; | |
10121 | |
10122 if (na) { | |
10123 n.style.textDecoration = ''; | |
10124 dom.setAttrib(n, 'mce_style', ''); | |
10125 | |
10126 e = dom.create(na, { | |
10127 style : dom.getAttrib(n, 'style') | |
10128 }); | |
10129 | |
10130 dom.replace(e, n, 1); | |
10131 } | |
10132 } | |
10133 }); | |
10134 } | |
10135 }; | |
10136 | |
10137 t.onPreProcess.add(convert); | |
10138 | |
10139 if (!s.cleanup_on_startup) { | |
10140 t.onSetContent.add(function(ed, o) { | |
10141 if (o.initial) | |
10142 convert(t, {node : t.getBody(), set : 1}); | |
10143 }); | 10768 }); |
10144 } | 10769 } |
10145 }, | |
10146 | |
10147 _convertFonts : function() { | |
10148 var t = this, s = t.settings, dom = t.dom, fz, fzn, sl, cl; | |
10149 | |
10150 // No need | |
10151 if (!s.inline_styles) | |
10152 return; | |
10153 | |
10154 // Font pt values and font size names | |
10155 fz = [8, 10, 12, 14, 18, 24, 36]; | |
10156 fzn = ['xx-small', 'x-small','small','medium','large','x-large', 'xx-large']; | |
10157 | |
10158 if (sl = s.font_size_style_values) | |
10159 sl = explode(sl); | |
10160 | |
10161 if (cl = s.font_size_classes) | |
10162 cl = explode(cl); | |
10163 | |
10164 function process(no) { | |
10165 var n, sp, nl, x; | |
10166 | |
10167 // Keep unit tests happy | |
10168 if (!s.inline_styles) | |
10169 return; | |
10170 | |
10171 nl = t.dom.select('font', no); | |
10172 for (x = nl.length - 1; x >= 0; x--) { | |
10173 n = nl[x]; | |
10174 | |
10175 sp = dom.create('span', { | |
10176 style : dom.getAttrib(n, 'style'), | |
10177 'class' : dom.getAttrib(n, 'class') | |
10178 }); | |
10179 | |
10180 dom.setStyles(sp, { | |
10181 fontFamily : dom.getAttrib(n, 'face'), | |
10182 color : dom.getAttrib(n, 'color'), | |
10183 backgroundColor : n.style.backgroundColor | |
10184 }); | |
10185 | |
10186 if (n.size) { | |
10187 if (sl) | |
10188 dom.setStyle(sp, 'fontSize', sl[parseInt(n.size) - 1]); | |
10189 else | |
10190 dom.setAttrib(sp, 'class', cl[parseInt(n.size) - 1]); | |
10191 } | |
10192 | |
10193 dom.setAttrib(sp, 'mce_style', ''); | |
10194 dom.replace(sp, n, 1); | |
10195 } | |
10196 }; | |
10197 | |
10198 // Run on cleanup | |
10199 t.onPreProcess.add(function(ed, o) { | |
10200 if (o.get) | |
10201 process(o.node); | |
10202 }); | |
10203 | |
10204 t.onSetContent.add(function(ed, o) { | |
10205 if (o.initial) | |
10206 process(o.node); | |
10207 }); | |
10208 }, | 10770 }, |
10209 | 10771 |
10210 _isHidden : function() { | 10772 _isHidden : function() { |
10211 var s; | 10773 var s; |
10212 | 10774 |
10266 for (i=d.length - 1; i>=0; i--) | 10828 for (i=d.length - 1; i>=0; i--) |
10267 s += '</' + d[i].tag + '>'; | 10829 s += '</' + d[i].tag + '>'; |
10268 | 10830 |
10269 return s; | 10831 return s; |
10270 } | 10832 } |
10271 | 10833 }); |
10834 })(tinymce); | |
10835 | |
10836 (function(tinymce) { | |
10837 // Added for compression purposes | |
10838 var each = tinymce.each, undefined, TRUE = true, FALSE = false; | |
10839 | |
10840 tinymce.EditorCommands = function(editor) { | |
10841 var dom = editor.dom, | |
10842 selection = editor.selection, | |
10843 commands = {state: {}, exec : {}, value : {}}, | |
10844 settings = editor.settings, | |
10845 bookmark; | |
10846 | |
10847 function execCommand(command, ui, value) { | |
10848 var func; | |
10849 | |
10850 command = command.toLowerCase(); | |
10851 if (func = commands.exec[command]) { | |
10852 func(command, ui, value); | |
10853 return TRUE; | |
10854 } | |
10855 | |
10856 return FALSE; | |
10857 }; | |
10858 | |
10859 function queryCommandState(command) { | |
10860 var func; | |
10861 | |
10862 command = command.toLowerCase(); | |
10863 if (func = commands.state[command]) | |
10864 return func(command); | |
10865 | |
10866 return -1; | |
10867 }; | |
10868 | |
10869 function queryCommandValue(command) { | |
10870 var func; | |
10871 | |
10872 command = command.toLowerCase(); | |
10873 if (func = commands.value[command]) | |
10874 return func(command); | |
10875 | |
10876 return FALSE; | |
10877 }; | |
10878 | |
10879 function addCommands(command_list, type) { | |
10880 type = type || 'exec'; | |
10881 | |
10882 each(command_list, function(callback, command) { | |
10883 each(command.toLowerCase().split(','), function(command) { | |
10884 commands[type][command] = callback; | |
10885 }); | |
10886 }); | |
10887 }; | |
10888 | |
10889 // Expose public methods | |
10890 tinymce.extend(this, { | |
10891 execCommand : execCommand, | |
10892 queryCommandState : queryCommandState, | |
10893 queryCommandValue : queryCommandValue, | |
10894 addCommands : addCommands | |
10272 }); | 10895 }); |
10896 | |
10897 // Private methods | |
10898 | |
10899 function execNativeCommand(command, ui, value) { | |
10900 if (ui === undefined) | |
10901 ui = FALSE; | |
10902 | |
10903 if (value === undefined) | |
10904 value = null; | |
10905 | |
10906 return editor.getDoc().execCommand(command, ui, value); | |
10907 }; | |
10908 | |
10909 function isFormatMatch(name) { | |
10910 return editor.formatter.match(name); | |
10911 }; | |
10912 | |
10913 function toggleFormat(name, value) { | |
10914 editor.formatter.toggle(name, value ? {value : value} : undefined); | |
10915 }; | |
10916 | |
10917 function storeSelection(type) { | |
10918 bookmark = selection.getBookmark(type); | |
10919 }; | |
10920 | |
10921 function restoreSelection() { | |
10922 selection.moveToBookmark(bookmark); | |
10923 }; | |
10924 | |
10925 // Add execCommand overrides | |
10926 addCommands({ | |
10927 // Ignore these, added for compatibility | |
10928 'mceResetDesignMode,mceBeginUndoLevel' : function() {}, | |
10929 | |
10930 // Add undo manager logic | |
10931 'mceEndUndoLevel,mceAddUndoLevel' : function() { | |
10932 editor.undoManager.add(); | |
10933 }, | |
10934 | |
10935 'Cut,Copy,Paste' : function(command) { | |
10936 var doc = editor.getDoc(), failed; | |
10937 | |
10938 // Try executing the native command | |
10939 try { | |
10940 execNativeCommand(command); | |
10941 } catch (ex) { | |
10942 // Command failed | |
10943 failed = TRUE; | |
10944 } | |
10945 | |
10946 // Present alert message about clipboard access not being available | |
10947 if (failed || !doc.queryCommandEnabled(command)) { | |
10948 if (tinymce.isGecko) { | |
10949 editor.windowManager.confirm(editor.getLang('clipboard_msg'), function(state) { | |
10950 if (state) | |
10951 open('http://www.mozilla.org/editor/midasdemo/securityprefs.html', '_blank'); | |
10952 }); | |
10953 } else | |
10954 editor.windowManager.alert(editor.getLang('clipboard_no_support')); | |
10955 } | |
10956 }, | |
10957 | |
10958 // Override unlink command | |
10959 unlink : function(command) { | |
10960 if (selection.isCollapsed()) | |
10961 selection.select(selection.getNode()); | |
10962 | |
10963 execNativeCommand(command); | |
10964 selection.collapse(FALSE); | |
10965 }, | |
10966 | |
10967 // Override justify commands to use the text formatter engine | |
10968 'JustifyLeft,JustifyCenter,JustifyRight,JustifyFull' : function(command) { | |
10969 var align = command.substring(7); | |
10970 | |
10971 // Remove all other alignments first | |
10972 each('left,center,right,full'.split(','), function(name) { | |
10973 if (align != name) | |
10974 editor.formatter.remove('align' + name); | |
10975 }); | |
10976 | |
10977 toggleFormat('align' + align); | |
10978 }, | |
10979 | |
10980 // Override list commands to fix WebKit bug | |
10981 'InsertUnorderedList,InsertOrderedList' : function(command) { | |
10982 var listElm, listParent; | |
10983 | |
10984 execNativeCommand(command); | |
10985 | |
10986 // WebKit produces lists within block elements so we need to split them | |
10987 // we will replace the native list creation logic to custom logic later on | |
10988 // TODO: Remove this when the list creation logic is removed | |
10989 listElm = dom.getParent(selection.getNode(), 'ol,ul'); | |
10990 if (listElm) { | |
10991 listParent = listElm.parentNode; | |
10992 | |
10993 // If list is within a text block then split that block | |
10994 if (/^(H[1-6]|P|ADDRESS|PRE)$/.test(listParent.nodeName)) { | |
10995 storeSelection(); | |
10996 dom.split(listParent, listElm); | |
10997 restoreSelection(); | |
10998 } | |
10999 } | |
11000 }, | |
11001 | |
11002 // Override commands to use the text formatter engine | |
11003 'Bold,Italic,Underline,Strikethrough' : function(command) { | |
11004 toggleFormat(command); | |
11005 }, | |
11006 | |
11007 // Override commands to use the text formatter engine | |
11008 'ForeColor,HiliteColor,FontName' : function(command, ui, value) { | |
11009 toggleFormat(command, value); | |
11010 }, | |
11011 | |
11012 FontSize : function(command, ui, value) { | |
11013 var fontClasses, fontSizes; | |
11014 | |
11015 // Convert font size 1-7 to styles | |
11016 if (value >= 1 && value <= 7) { | |
11017 fontSizes = tinymce.explode(settings.font_size_style_values); | |
11018 fontClasses = tinymce.explode(settings.font_size_classes); | |
11019 | |
11020 if (fontClasses) | |
11021 value = fontClasses[value - 1] || value; | |
11022 else | |
11023 value = fontSizes[value - 1] || value; | |
11024 } | |
11025 | |
11026 toggleFormat(command, value); | |
11027 }, | |
11028 | |
11029 RemoveFormat : function(command) { | |
11030 editor.formatter.remove(command); | |
11031 }, | |
11032 | |
11033 mceBlockQuote : function(command) { | |
11034 toggleFormat('blockquote'); | |
11035 }, | |
11036 | |
11037 FormatBlock : function(command, ui, value) { | |
11038 return toggleFormat(value); | |
11039 }, | |
11040 | |
11041 mceCleanup : function() { | |
11042 storeSelection(); | |
11043 editor.setContent(editor.getContent({cleanup : TRUE}), {cleanup : TRUE}); | |
11044 restoreSelection(); | |
11045 }, | |
11046 | |
11047 mceRemoveNode : function(command, ui, value) { | |
11048 var node = value || selection.getNode(); | |
11049 | |
11050 // Make sure that the body node isn't removed | |
11051 if (node != ed.getBody()) { | |
11052 storeSelection(); | |
11053 editor.dom.remove(node, TRUE); | |
11054 restoreSelection(); | |
11055 } | |
11056 }, | |
11057 | |
11058 mceSelectNodeDepth : function(command, ui, value) { | |
11059 var counter = 0; | |
11060 | |
11061 dom.getParent(selection.getNode(), function(node) { | |
11062 if (node.nodeType == 1 && counter++ == value) { | |
11063 selection.select(node); | |
11064 return FALSE; | |
11065 } | |
11066 }, editor.getBody()); | |
11067 }, | |
11068 | |
11069 mceSelectNode : function(command, ui, value) { | |
11070 selection.select(value); | |
11071 }, | |
11072 | |
11073 mceInsertContent : function(command, ui, value) { | |
11074 selection.setContent(value); | |
11075 }, | |
11076 | |
11077 mceInsertRawHTML : function(command, ui, value) { | |
11078 selection.setContent('tiny_mce_marker'); | |
11079 editor.setContent(editor.getContent().replace(/tiny_mce_marker/g, value)); | |
11080 }, | |
11081 | |
11082 mceSetContent : function(command, ui, value) { | |
11083 editor.setContent(value); | |
11084 }, | |
11085 | |
11086 'Indent,Outdent' : function(command) { | |
11087 var intentValue, indentUnit, value; | |
11088 | |
11089 // Setup indent level | |
11090 intentValue = settings.indentation; | |
11091 indentUnit = /[a-z%]+$/i.exec(intentValue); | |
11092 intentValue = parseInt(intentValue); | |
11093 | |
11094 if (!queryCommandState('InsertUnorderedList') && !queryCommandState('InsertOrderedList')) { | |
11095 each(selection.getSelectedBlocks(), function(element) { | |
11096 if (command == 'outdent') { | |
11097 value = Math.max(0, parseInt(element.style.paddingLeft || 0) - intentValue); | |
11098 dom.setStyle(element, 'paddingLeft', value ? value + indentUnit : ''); | |
11099 } else | |
11100 dom.setStyle(element, 'paddingLeft', (parseInt(element.style.paddingLeft || 0) + intentValue) + indentUnit); | |
11101 }); | |
11102 } else | |
11103 execNativeCommand(command); | |
11104 }, | |
11105 | |
11106 mceRepaint : function() { | |
11107 var bookmark; | |
11108 | |
11109 if (tinymce.isGecko) { | |
11110 try { | |
11111 storeSelection(TRUE); | |
11112 | |
11113 if (selection.getSel()) | |
11114 selection.getSel().selectAllChildren(editor.getBody()); | |
11115 | |
11116 selection.collapse(TRUE); | |
11117 restoreSelection(); | |
11118 } catch (ex) { | |
11119 // Ignore | |
11120 } | |
11121 } | |
11122 }, | |
11123 | |
11124 mceToggleFormat : function(command, ui, value) { | |
11125 editor.formatter.toggle(value); | |
11126 }, | |
11127 | |
11128 InsertHorizontalRule : function() { | |
11129 selection.setContent('<hr />'); | |
11130 }, | |
11131 | |
11132 mceToggleVisualAid : function() { | |
11133 editor.hasVisual = !editor.hasVisual; | |
11134 editor.addVisual(); | |
11135 }, | |
11136 | |
11137 mceReplaceContent : function(command, ui, value) { | |
11138 selection.setContent(value.replace(/\{\$selection\}/g, selection.getContent({format : 'text'}))); | |
11139 }, | |
11140 | |
11141 mceInsertLink : function(command, ui, value) { | |
11142 var link = dom.getParent(selection.getNode(), 'a'); | |
11143 | |
11144 if (tinymce.is(value, 'string')) | |
11145 value = {href : value}; | |
11146 | |
11147 if (!link) { | |
11148 execNativeCommand('CreateLink', FALSE, 'javascript:mctmp(0);'); | |
11149 each(dom.select('a[href=javascript:mctmp(0);]'), function(link) { | |
11150 dom.setAttribs(link, value); | |
11151 }); | |
11152 } else { | |
11153 if (value.href) | |
11154 dom.setAttribs(link, value); | |
11155 else | |
11156 ed.dom.remove(link, TRUE); | |
11157 } | |
11158 } | |
11159 }); | |
11160 | |
11161 // Add queryCommandState overrides | |
11162 addCommands({ | |
11163 // Override justify commands | |
11164 'JustifyLeft,JustifyCenter,JustifyRight,JustifyFull' : function(command) { | |
11165 return isFormatMatch('align' + command.substring(7)); | |
11166 }, | |
11167 | |
11168 'Bold,Italic,Underline,Strikethrough' : function(command) { | |
11169 return isFormatMatch(command); | |
11170 }, | |
11171 | |
11172 mceBlockQuote : function() { | |
11173 return isFormatMatch('blockquote'); | |
11174 }, | |
11175 | |
11176 Outdent : function() { | |
11177 var node; | |
11178 | |
11179 if (settings.inline_styles) { | |
11180 if ((node = dom.getParent(selection.getStart(), dom.isBlock)) && parseInt(node.style.paddingLeft) > 0) | |
11181 return TRUE; | |
11182 | |
11183 if ((node = dom.getParent(selection.getEnd(), dom.isBlock)) && parseInt(node.style.paddingLeft) > 0) | |
11184 return TRUE; | |
11185 } | |
11186 | |
11187 return queryCommandState('InsertUnorderedList') || queryCommandState('InsertOrderedList') || (!settings.inline_styles && !!dom.getParent(selection.getNode(), 'BLOCKQUOTE')); | |
11188 }, | |
11189 | |
11190 'InsertUnorderedList,InsertOrderedList' : function(command) { | |
11191 return dom.getParent(selection.getNode(), command == 'insertunorderedlist' ? 'UL' : 'OL'); | |
11192 } | |
11193 }, 'state'); | |
11194 | |
11195 // Add queryCommandValue overrides | |
11196 addCommands({ | |
11197 'FontSize,FontName' : function(command) { | |
11198 var value = 0, parent; | |
11199 | |
11200 if (parent = dom.getParent(selection.getNode(), 'span')) { | |
11201 if (command == 'fontsize') | |
11202 value = parent.style.fontSize; | |
11203 else | |
11204 value = parent.style.fontFamily.replace(/, /g, ',').replace(/[\'\"]/g, '').toLowerCase(); | |
11205 } | |
11206 | |
11207 return value; | |
11208 } | |
11209 }, 'value'); | |
11210 | |
11211 // Add undo manager logic | |
11212 if (settings.custom_undo_redo) { | |
11213 addCommands({ | |
11214 Undo : function() { | |
11215 editor.undoManager.undo(); | |
11216 }, | |
11217 | |
11218 Redo : function() { | |
11219 editor.undoManager.redo(); | |
11220 } | |
11221 }); | |
11222 } | |
11223 }; | |
10273 })(tinymce); | 11224 })(tinymce); |
10274 (function(tinymce) { | 11225 (function(tinymce) { |
10275 var each = tinymce.each, isIE = tinymce.isIE, isGecko = tinymce.isGecko, isOpera = tinymce.isOpera, isWebKit = tinymce.isWebKit; | 11226 var Dispatcher = tinymce.util.Dispatcher; |
10276 | 11227 |
10277 tinymce.create('tinymce.EditorCommands', { | 11228 tinymce.UndoManager = function(editor) { |
10278 EditorCommands : function(ed) { | 11229 var self, index = 0, data = []; |
10279 this.editor = ed; | 11230 |
10280 }, | 11231 function getContent() { |
10281 | 11232 return tinymce.trim(editor.getContent({format : 'raw', no_events : 1})); |
10282 execCommand : function(cmd, ui, val) { | 11233 }; |
10283 var t = this, ed = t.editor, f; | 11234 |
10284 | 11235 return self = { |
10285 switch (cmd) { | 11236 typing : 0, |
10286 // Ignore these | 11237 |
10287 case 'mceResetDesignMode': | 11238 onAdd : new Dispatcher(self), |
10288 case 'mceBeginUndoLevel': | 11239 onUndo : new Dispatcher(self), |
10289 return true; | 11240 onRedo : new Dispatcher(self), |
10290 | 11241 |
10291 // Ignore these | 11242 add : function(level) { |
10292 case 'unlink': | 11243 var i, settings = editor.settings, lastLevel; |
10293 t.UnLink(); | 11244 |
10294 return true; | 11245 level = level || {}; |
10295 | 11246 level.content = getContent(); |
10296 // Bundle these together | 11247 |
10297 case 'JustifyLeft': | 11248 // Add undo level if needed |
10298 case 'JustifyCenter': | 11249 lastLevel = data[index]; |
10299 case 'JustifyRight': | 11250 if (lastLevel && lastLevel.content == level.content) { |
10300 case 'JustifyFull': | 11251 if (index > 0 || data.length == 1) |
10301 t.mceJustify(cmd, cmd.substring(7).toLowerCase()); | 11252 return null; |
10302 return true; | 11253 } |
10303 | 11254 |
10304 default: | 11255 // Time to compress |
10305 f = this[cmd]; | 11256 if (settings.custom_undo_redo_levels) { |
10306 | 11257 if (data.length > settings.custom_undo_redo_levels) { |
10307 if (f) { | 11258 for (i = 0; i < data.length - 1; i++) |
10308 f.call(this, ui, val); | 11259 data[i] = data[i + 1]; |
10309 return true; | 11260 |
11261 data.length--; | |
11262 index = data.length; | |
10310 } | 11263 } |
10311 } | 11264 } |
10312 | 11265 |
10313 return false; | 11266 // Get a non intrusive normalized bookmark |
10314 }, | 11267 level.bookmark = editor.selection.getBookmark(2, true); |
10315 | 11268 |
10316 Indent : function() { | 11269 // Crop array if needed |
10317 var ed = this.editor, d = ed.dom, s = ed.selection, e, iv, iu; | 11270 if (index < data.length - 1) { |
10318 | 11271 // Treat first level as initial |
10319 // Setup indent level | 11272 if (index == 0) |
10320 iv = ed.settings.indentation; | 11273 data = []; |
10321 iu = /[a-z%]+$/i.exec(iv); | 11274 else |
10322 iv = parseInt(iv); | 11275 data.length = index + 1; |
10323 | 11276 } |
10324 if (ed.settings.inline_styles && (!this.queryStateInsertUnorderedList() && !this.queryStateInsertOrderedList())) { | 11277 |
10325 each(s.getSelectedBlocks(), function(e) { | 11278 data.push(level); |
10326 d.setStyle(e, 'paddingLeft', (parseInt(e.style.paddingLeft || 0) + iv) + iu); | 11279 index = data.length - 1; |
10327 }); | 11280 |
10328 | 11281 self.onAdd.dispatch(self, level); |
10329 return; | 11282 editor.isNotDirty = 0; |
10330 } | 11283 |
10331 | 11284 return level; |
10332 ed.getDoc().execCommand('Indent', false, null); | 11285 }, |
10333 | 11286 |
10334 if (isIE) { | 11287 undo : function() { |
10335 d.getParent(s.getNode(), function(n) { | 11288 var level, i; |
10336 if (n.nodeName == 'BLOCKQUOTE') { | 11289 |
10337 n.dir = n.style.cssText = ''; | 11290 if (self.typing) { |
10338 } | 11291 self.add(); |
10339 }); | 11292 self.typing = 0; |
10340 } | 11293 } |
10341 }, | 11294 |
10342 | 11295 if (index > 0) { |
10343 Outdent : function() { | 11296 level = data[--index]; |
10344 var ed = this.editor, d = ed.dom, s = ed.selection, e, v, iv, iu; | 11297 |
10345 | 11298 editor.setContent(level.content, {format : 'raw'}); |
10346 // Setup indent level | 11299 editor.selection.moveToBookmark(level.bookmark); |
10347 iv = ed.settings.indentation; | 11300 |
10348 iu = /[a-z%]+$/i.exec(iv); | 11301 self.onUndo.dispatch(self, level); |
10349 iv = parseInt(iv); | 11302 } |
10350 | 11303 |
10351 if (ed.settings.inline_styles && (!this.queryStateInsertUnorderedList() && !this.queryStateInsertOrderedList())) { | 11304 return level; |
10352 each(s.getSelectedBlocks(), function(e) { | 11305 }, |
10353 v = Math.max(0, parseInt(e.style.paddingLeft || 0) - iv); | 11306 |
10354 d.setStyle(e, 'paddingLeft', v ? v + iu : ''); | 11307 redo : function() { |
10355 }); | 11308 var level; |
10356 | 11309 |
10357 return; | 11310 if (index < data.length - 1) { |
10358 } | 11311 level = data[++index]; |
10359 | 11312 |
10360 ed.getDoc().execCommand('Outdent', false, null); | 11313 editor.setContent(level.content, {format : 'raw'}); |
10361 }, | 11314 editor.selection.moveToBookmark(level.bookmark); |
10362 | 11315 |
10363 /* | 11316 self.onRedo.dispatch(self, level); |
10364 mceSetAttribute : function(u, v) { | 11317 } |
10365 var ed = this.editor, d = ed.dom, e; | 11318 |
10366 | 11319 return level; |
10367 if (e = d.getParent(ed.selection.getNode(), d.isBlock)) | 11320 }, |
10368 d.setAttrib(e, v.name, v.value); | 11321 |
10369 }, | 11322 clear : function() { |
10370 */ | 11323 data = []; |
10371 mceSetContent : function(u, v) { | 11324 index = self.typing = 0; |
10372 this.editor.setContent(v); | 11325 }, |
10373 }, | 11326 |
10374 | 11327 hasUndo : function() { |
10375 mceToggleVisualAid : function() { | 11328 return index > 0 || self.typing; |
10376 var ed = this.editor; | 11329 }, |
10377 | 11330 |
10378 ed.hasVisual = !ed.hasVisual; | 11331 hasRedo : function() { |
10379 ed.addVisual(); | 11332 return index < data.length - 1; |
10380 }, | 11333 } |
10381 | 11334 }; |
10382 mceReplaceContent : function(u, v) { | 11335 }; |
10383 var s = this.editor.selection; | |
10384 | |
10385 s.setContent(v.replace(/\{\$selection\}/g, s.getContent({format : 'text'}))); | |
10386 }, | |
10387 | |
10388 mceInsertLink : function(u, v) { | |
10389 var ed = this.editor, s = ed.selection, e = ed.dom.getParent(s.getNode(), 'A'); | |
10390 | |
10391 if (tinymce.is(v, 'string')) | |
10392 v = {href : v}; | |
10393 | |
10394 function set(e) { | |
10395 each(v, function(v, k) { | |
10396 ed.dom.setAttrib(e, k, v); | |
10397 }); | |
10398 }; | |
10399 | |
10400 if (!e) { | |
10401 ed.execCommand('CreateLink', false, 'javascript:mctmp(0);'); | |
10402 each(ed.dom.select('a[href=javascript:mctmp(0);]'), function(e) { | |
10403 set(e); | |
10404 }); | |
10405 } else { | |
10406 if (v.href) | |
10407 set(e); | |
10408 else | |
10409 ed.dom.remove(e, 1); | |
10410 } | |
10411 }, | |
10412 | |
10413 UnLink : function() { | |
10414 var ed = this.editor, s = ed.selection; | |
10415 | |
10416 if (s.isCollapsed()) | |
10417 s.select(s.getNode()); | |
10418 | |
10419 ed.getDoc().execCommand('unlink', false, null); | |
10420 s.collapse(0); | |
10421 }, | |
10422 | |
10423 FontName : function(u, v) { | |
10424 var t = this, ed = t.editor, s = ed.selection, e; | |
10425 | |
10426 if (!v) { | |
10427 if (s.isCollapsed()) | |
10428 s.select(s.getNode()); | |
10429 } else { | |
10430 if (ed.settings.convert_fonts_to_spans) | |
10431 t._applyInlineStyle('span', {style : {fontFamily : v}}); | |
10432 else | |
10433 ed.getDoc().execCommand('FontName', false, v); | |
10434 } | |
10435 }, | |
10436 | |
10437 FontSize : function(u, v) { | |
10438 var ed = this.editor, s = ed.settings, fc, fs; | |
10439 | |
10440 // Use style options instead | |
10441 if (s.convert_fonts_to_spans && v >= 1 && v <= 7) { | |
10442 fs = tinymce.explode(s.font_size_style_values); | |
10443 fc = tinymce.explode(s.font_size_classes); | |
10444 | |
10445 if (fc) | |
10446 v = fc[v - 1] || v; | |
10447 else | |
10448 v = fs[v - 1] || v; | |
10449 } | |
10450 | |
10451 if (v >= 1 && v <= 7) | |
10452 ed.getDoc().execCommand('FontSize', false, v); | |
10453 else | |
10454 this._applyInlineStyle('span', {style : {fontSize : v}}); | |
10455 }, | |
10456 | |
10457 queryCommandValue : function(c) { | |
10458 var f = this['queryValue' + c]; | |
10459 | |
10460 if (f) | |
10461 return f.call(this, c); | |
10462 | |
10463 return false; | |
10464 }, | |
10465 | |
10466 queryCommandState : function(cmd) { | |
10467 var f; | |
10468 | |
10469 switch (cmd) { | |
10470 // Bundle these together | |
10471 case 'JustifyLeft': | |
10472 case 'JustifyCenter': | |
10473 case 'JustifyRight': | |
10474 case 'JustifyFull': | |
10475 return this.queryStateJustify(cmd, cmd.substring(7).toLowerCase()); | |
10476 | |
10477 default: | |
10478 if (f = this['queryState' + cmd]) | |
10479 return f.call(this, cmd); | |
10480 } | |
10481 | |
10482 return -1; | |
10483 }, | |
10484 | |
10485 _queryState : function(c) { | |
10486 try { | |
10487 return this.editor.getDoc().queryCommandState(c); | |
10488 } catch (ex) { | |
10489 // Ignore exception | |
10490 } | |
10491 }, | |
10492 | |
10493 _queryVal : function(c) { | |
10494 try { | |
10495 return this.editor.getDoc().queryCommandValue(c); | |
10496 } catch (ex) { | |
10497 // Ignore exception | |
10498 } | |
10499 }, | |
10500 | |
10501 queryValueFontSize : function() { | |
10502 var ed = this.editor, v = 0, p; | |
10503 | |
10504 if (p = ed.dom.getParent(ed.selection.getNode(), 'SPAN')) | |
10505 v = p.style.fontSize; | |
10506 | |
10507 if (!v && (isOpera || isWebKit)) { | |
10508 if (p = ed.dom.getParent(ed.selection.getNode(), 'FONT')) | |
10509 v = p.size; | |
10510 | |
10511 return v; | |
10512 } | |
10513 | |
10514 return v || this._queryVal('FontSize'); | |
10515 }, | |
10516 | |
10517 queryValueFontName : function() { | |
10518 var ed = this.editor, v = 0, p; | |
10519 | |
10520 if (p = ed.dom.getParent(ed.selection.getNode(), 'FONT')) | |
10521 v = p.face; | |
10522 | |
10523 if (p = ed.dom.getParent(ed.selection.getNode(), 'SPAN')) | |
10524 v = p.style.fontFamily.replace(/, /g, ',').replace(/[\'\"]/g, '').toLowerCase(); | |
10525 | |
10526 if (!v) | |
10527 v = this._queryVal('FontName'); | |
10528 | |
10529 return v; | |
10530 }, | |
10531 | |
10532 mceJustify : function(c, v) { | |
10533 var ed = this.editor, se = ed.selection, n = se.getNode(), nn = n.nodeName, bl, nb, dom = ed.dom, rm; | |
10534 | |
10535 if (ed.settings.inline_styles && this.queryStateJustify(c, v)) | |
10536 rm = 1; | |
10537 | |
10538 bl = dom.getParent(n, ed.dom.isBlock); | |
10539 | |
10540 if (nn == 'IMG') { | |
10541 if (v == 'full') | |
10542 return; | |
10543 | |
10544 if (rm) { | |
10545 if (v == 'center') | |
10546 dom.setStyle(bl || n.parentNode, 'textAlign', ''); | |
10547 | |
10548 dom.setStyle(n, 'float', ''); | |
10549 this.mceRepaint(); | |
10550 return; | |
10551 } | |
10552 | |
10553 if (v == 'center') { | |
10554 // Do not change table elements | |
10555 if (bl && /^(TD|TH)$/.test(bl.nodeName)) | |
10556 bl = 0; | |
10557 | |
10558 if (!bl || bl.childNodes.length > 1) { | |
10559 nb = dom.create('p'); | |
10560 nb.appendChild(n.cloneNode(false)); | |
10561 | |
10562 if (bl) | |
10563 dom.insertAfter(nb, bl); | |
10564 else | |
10565 dom.insertAfter(nb, n); | |
10566 | |
10567 dom.remove(n); | |
10568 n = nb.firstChild; | |
10569 bl = nb; | |
10570 } | |
10571 | |
10572 dom.setStyle(bl, 'textAlign', v); | |
10573 dom.setStyle(n, 'float', ''); | |
10574 } else { | |
10575 dom.setStyle(n, 'float', v); | |
10576 dom.setStyle(bl || n.parentNode, 'textAlign', ''); | |
10577 } | |
10578 | |
10579 this.mceRepaint(); | |
10580 return; | |
10581 } | |
10582 | |
10583 // Handle the alignment outselfs, less quirks in all browsers | |
10584 if (ed.settings.inline_styles && ed.settings.forced_root_block) { | |
10585 if (rm) | |
10586 v = ''; | |
10587 | |
10588 each(se.getSelectedBlocks(dom.getParent(se.getStart(), dom.isBlock), dom.getParent(se.getEnd(), dom.isBlock)), function(e) { | |
10589 dom.setAttrib(e, 'align', ''); | |
10590 dom.setStyle(e, 'textAlign', v == 'full' ? 'justify' : v); | |
10591 }); | |
10592 | |
10593 return; | |
10594 } else if (!rm) | |
10595 ed.getDoc().execCommand(c, false, null); | |
10596 | |
10597 if (ed.settings.inline_styles) { | |
10598 if (rm) { | |
10599 dom.getParent(ed.selection.getNode(), function(n) { | |
10600 if (n.style && n.style.textAlign) | |
10601 dom.setStyle(n, 'textAlign', ''); | |
10602 }); | |
10603 | |
10604 return; | |
10605 } | |
10606 | |
10607 each(dom.select('*'), function(n) { | |
10608 var v = n.align; | |
10609 | |
10610 if (v) { | |
10611 if (v == 'full') | |
10612 v = 'justify'; | |
10613 | |
10614 dom.setStyle(n, 'textAlign', v); | |
10615 dom.setAttrib(n, 'align', ''); | |
10616 } | |
10617 }); | |
10618 } | |
10619 }, | |
10620 | |
10621 mceSetCSSClass : function(u, v) { | |
10622 this.mceSetStyleInfo(0, {command : 'setattrib', name : 'class', value : v}); | |
10623 }, | |
10624 | |
10625 getSelectedElement : function() { | |
10626 var t = this, ed = t.editor, dom = ed.dom, se = ed.selection, r = se.getRng(), r1, r2, sc, ec, so, eo, e, sp, ep, re; | |
10627 | |
10628 if (se.isCollapsed() || r.item) | |
10629 return se.getNode(); | |
10630 | |
10631 // Setup regexp | |
10632 re = ed.settings.merge_styles_invalid_parents; | |
10633 if (tinymce.is(re, 'string')) | |
10634 re = new RegExp(re, 'i'); | |
10635 | |
10636 if (isIE) { | |
10637 r1 = r.duplicate(); | |
10638 r1.collapse(true); | |
10639 sc = r1.parentElement(); | |
10640 | |
10641 r2 = r.duplicate(); | |
10642 r2.collapse(false); | |
10643 ec = r2.parentElement(); | |
10644 | |
10645 if (sc != ec) { | |
10646 r1.move('character', 1); | |
10647 sc = r1.parentElement(); | |
10648 } | |
10649 | |
10650 if (sc == ec) { | |
10651 r1 = r.duplicate(); | |
10652 r1.moveToElementText(sc); | |
10653 | |
10654 if (r1.compareEndPoints('StartToStart', r) == 0 && r1.compareEndPoints('EndToEnd', r) == 0) | |
10655 return re && re.test(sc.nodeName) ? null : sc; | |
10656 } | |
10657 } else { | |
10658 function getParent(n) { | |
10659 return dom.getParent(n, '*'); | |
10660 }; | |
10661 | |
10662 sc = r.startContainer; | |
10663 ec = r.endContainer; | |
10664 so = r.startOffset; | |
10665 eo = r.endOffset; | |
10666 | |
10667 if (!r.collapsed) { | |
10668 if (sc == ec) { | |
10669 if (so - eo < 2) { | |
10670 if (sc.hasChildNodes()) { | |
10671 sp = sc.childNodes[so]; | |
10672 return re && re.test(sp.nodeName) ? null : sp; | |
10673 } | |
10674 } | |
10675 } | |
10676 } | |
10677 | |
10678 if (sc.nodeType != 3 || ec.nodeType != 3) | |
10679 return null; | |
10680 | |
10681 if (so == 0) { | |
10682 sp = getParent(sc); | |
10683 | |
10684 if (sp && sp.firstChild != sc) | |
10685 sp = null; | |
10686 } | |
10687 | |
10688 if (so == sc.nodeValue.length) { | |
10689 e = sc.nextSibling; | |
10690 | |
10691 if (e && e.nodeType == 1) | |
10692 sp = sc.nextSibling; | |
10693 } | |
10694 | |
10695 if (eo == 0) { | |
10696 e = ec.previousSibling; | |
10697 | |
10698 if (e && e.nodeType == 1) | |
10699 ep = e; | |
10700 } | |
10701 | |
10702 if (eo == ec.nodeValue.length) { | |
10703 ep = getParent(ec); | |
10704 | |
10705 if (ep && ep.lastChild != ec) | |
10706 ep = null; | |
10707 } | |
10708 | |
10709 // Same element | |
10710 if (sp == ep) | |
10711 return re && sp && re.test(sp.nodeName) ? null : sp; | |
10712 } | |
10713 | |
10714 return null; | |
10715 }, | |
10716 | |
10717 mceSetStyleInfo : function(u, v) { | |
10718 var t = this, ed = t.editor, d = ed.getDoc(), dom = ed.dom, e, b, s = ed.selection, nn = v.wrapper || 'span', b = s.getBookmark(), re; | |
10719 | |
10720 function set(n, e) { | |
10721 if (n.nodeType == 1) { | |
10722 switch (v.command) { | |
10723 case 'setattrib': | |
10724 return dom.setAttrib(n, v.name, v.value); | |
10725 | |
10726 case 'setstyle': | |
10727 return dom.setStyle(n, v.name, v.value); | |
10728 | |
10729 case 'removeformat': | |
10730 return dom.setAttrib(n, 'class', ''); | |
10731 } | |
10732 } | |
10733 }; | |
10734 | |
10735 // Setup regexp | |
10736 re = ed.settings.merge_styles_invalid_parents; | |
10737 if (tinymce.is(re, 'string')) | |
10738 re = new RegExp(re, 'i'); | |
10739 | |
10740 // Set style info on selected element | |
10741 if ((e = t.getSelectedElement()) && !ed.settings.force_span_wrappers) | |
10742 set(e, 1); | |
10743 else { | |
10744 // Generate wrappers and set styles on them | |
10745 d.execCommand('FontName', false, '__'); | |
10746 each(dom.select('span,font'), function(n) { | |
10747 var sp, e; | |
10748 | |
10749 if (dom.getAttrib(n, 'face') == '__' || n.style.fontFamily === '__') { | |
10750 sp = dom.create(nn, {mce_new : '1'}); | |
10751 | |
10752 set(sp); | |
10753 | |
10754 each (n.childNodes, function(n) { | |
10755 sp.appendChild(n.cloneNode(true)); | |
10756 }); | |
10757 | |
10758 dom.replace(sp, n); | |
10759 } | |
10760 }); | |
10761 } | |
10762 | |
10763 // Remove wrappers inside new ones | |
10764 each(dom.select(nn).reverse(), function(n) { | |
10765 var p = n.parentNode; | |
10766 | |
10767 // Check if it's an old span in a new wrapper | |
10768 if (!dom.getAttrib(n, 'mce_new')) { | |
10769 // Find new wrapper | |
10770 p = dom.getParent(n, '*[mce_new]'); | |
10771 | |
10772 if (p) | |
10773 dom.remove(n, 1); | |
10774 } | |
10775 }); | |
10776 | |
10777 // Merge wrappers with parent wrappers | |
10778 each(dom.select(nn).reverse(), function(n) { | |
10779 var p = n.parentNode; | |
10780 | |
10781 if (!p || !dom.getAttrib(n, 'mce_new')) | |
10782 return; | |
10783 | |
10784 if (ed.settings.force_span_wrappers && p.nodeName != 'SPAN') | |
10785 return; | |
10786 | |
10787 // Has parent of the same type and only child | |
10788 if (p.nodeName == nn.toUpperCase() && p.childNodes.length == 1) | |
10789 return dom.remove(p, 1); | |
10790 | |
10791 // Has parent that is more suitable to have the class and only child | |
10792 if (n.nodeType == 1 && (!re || !re.test(p.nodeName)) && p.childNodes.length == 1) { | |
10793 set(p); // Set style info on parent instead | |
10794 dom.setAttrib(n, 'class', ''); | |
10795 } | |
10796 }); | |
10797 | |
10798 // Remove empty wrappers | |
10799 each(dom.select(nn).reverse(), function(n) { | |
10800 if (dom.getAttrib(n, 'mce_new') || (dom.getAttribs(n).length <= 1 && n.className === '')) { | |
10801 if (!dom.getAttrib(n, 'class') && !dom.getAttrib(n, 'style')) | |
10802 return dom.remove(n, 1); | |
10803 | |
10804 dom.setAttrib(n, 'mce_new', ''); // Remove mce_new marker | |
10805 } | |
10806 }); | |
10807 | |
10808 s.moveToBookmark(b); | |
10809 }, | |
10810 | |
10811 queryStateJustify : function(c, v) { | |
10812 var ed = this.editor, n = ed.selection.getNode(), dom = ed.dom; | |
10813 | |
10814 if (n && n.nodeName == 'IMG') { | |
10815 if (dom.getStyle(n, 'float') == v) | |
10816 return 1; | |
10817 | |
10818 return n.parentNode.style.textAlign == v; | |
10819 } | |
10820 | |
10821 n = dom.getParent(ed.selection.getStart(), function(n) { | |
10822 return n.nodeType == 1 && n.style.textAlign; | |
10823 }); | |
10824 | |
10825 if (v == 'full') | |
10826 v = 'justify'; | |
10827 | |
10828 if (ed.settings.inline_styles) | |
10829 return (n && n.style.textAlign == v); | |
10830 | |
10831 return this._queryState(c); | |
10832 }, | |
10833 | |
10834 ForeColor : function(ui, v) { | |
10835 var ed = this.editor; | |
10836 | |
10837 if (ed.settings.convert_fonts_to_spans) { | |
10838 this._applyInlineStyle('span', {style : {color : v}}); | |
10839 return; | |
10840 } else | |
10841 ed.getDoc().execCommand('ForeColor', false, v); | |
10842 }, | |
10843 | |
10844 HiliteColor : function(ui, val) { | |
10845 var t = this, ed = t.editor, d = ed.getDoc(); | |
10846 | |
10847 if (ed.settings.convert_fonts_to_spans) { | |
10848 this._applyInlineStyle('span', {style : {backgroundColor : val}}); | |
10849 return; | |
10850 } | |
10851 | |
10852 function set(s) { | |
10853 if (!isGecko) | |
10854 return; | |
10855 | |
10856 try { | |
10857 // Try new Gecko method | |
10858 d.execCommand("styleWithCSS", 0, s); | |
10859 } catch (ex) { | |
10860 // Use old | |
10861 d.execCommand("useCSS", 0, !s); | |
10862 } | |
10863 }; | |
10864 | |
10865 if (isGecko || isOpera) { | |
10866 set(true); | |
10867 d.execCommand('hilitecolor', false, val); | |
10868 set(false); | |
10869 } else | |
10870 d.execCommand('BackColor', false, val); | |
10871 }, | |
10872 | |
10873 FormatBlock : function(ui, val) { | |
10874 var t = this, ed = t.editor, s = ed.selection, dom = ed.dom, bl, nb, b; | |
10875 | |
10876 function isBlock(n) { | |
10877 return /^(P|DIV|H[1-6]|ADDRESS|BLOCKQUOTE|PRE)$/.test(n.nodeName); | |
10878 }; | |
10879 | |
10880 bl = dom.getParent(s.getNode(), function(n) { | |
10881 return isBlock(n); | |
10882 }); | |
10883 | |
10884 // IE has an issue where it removes the parent div if you change format on the paragrah in <div><p>Content</p></div> | |
10885 // FF and Opera doesn't change parent DIV elements if you switch format | |
10886 if (bl) { | |
10887 if ((isIE && isBlock(bl.parentNode)) || bl.nodeName == 'DIV') { | |
10888 // Rename block element | |
10889 nb = ed.dom.create(val); | |
10890 | |
10891 each(dom.getAttribs(bl), function(v) { | |
10892 dom.setAttrib(nb, v.nodeName, dom.getAttrib(bl, v.nodeName)); | |
10893 }); | |
10894 | |
10895 b = s.getBookmark(); | |
10896 dom.replace(nb, bl, 1); | |
10897 s.moveToBookmark(b); | |
10898 ed.nodeChanged(); | |
10899 return; | |
10900 } | |
10901 } | |
10902 | |
10903 val = ed.settings.forced_root_block ? (val || '<p>') : val; | |
10904 | |
10905 if (val.indexOf('<') == -1) | |
10906 val = '<' + val + '>'; | |
10907 | |
10908 if (tinymce.isGecko) | |
10909 val = val.replace(/<(div|blockquote|code|dt|dd|dl|samp)>/gi, '$1'); | |
10910 | |
10911 ed.getDoc().execCommand('FormatBlock', false, val); | |
10912 }, | |
10913 | |
10914 mceCleanup : function() { | |
10915 var ed = this.editor, s = ed.selection, b = s.getBookmark(); | |
10916 ed.setContent(ed.getContent()); | |
10917 s.moveToBookmark(b); | |
10918 }, | |
10919 | |
10920 mceRemoveNode : function(ui, val) { | |
10921 var ed = this.editor, s = ed.selection, b, n = val || s.getNode(); | |
10922 | |
10923 // Make sure that the body node isn't removed | |
10924 if (n == ed.getBody()) | |
10925 return; | |
10926 | |
10927 b = s.getBookmark(); | |
10928 ed.dom.remove(n, 1); | |
10929 s.moveToBookmark(b); | |
10930 ed.nodeChanged(); | |
10931 }, | |
10932 | |
10933 mceSelectNodeDepth : function(ui, val) { | |
10934 var ed = this.editor, s = ed.selection, c = 0; | |
10935 | |
10936 ed.dom.getParent(s.getNode(), function(n) { | |
10937 if (n.nodeType == 1 && c++ == val) { | |
10938 s.select(n); | |
10939 ed.nodeChanged(); | |
10940 return false; | |
10941 } | |
10942 }, ed.getBody()); | |
10943 }, | |
10944 | |
10945 mceSelectNode : function(u, v) { | |
10946 this.editor.selection.select(v); | |
10947 }, | |
10948 | |
10949 mceInsertContent : function(ui, val) { | |
10950 this.editor.selection.setContent(val); | |
10951 }, | |
10952 | |
10953 mceInsertRawHTML : function(ui, val) { | |
10954 var ed = this.editor; | |
10955 | |
10956 ed.selection.setContent('tiny_mce_marker'); | |
10957 ed.setContent(ed.getContent().replace(/tiny_mce_marker/g, val)); | |
10958 }, | |
10959 | |
10960 mceRepaint : function() { | |
10961 var s, b, e = this.editor; | |
10962 | |
10963 if (tinymce.isGecko) { | |
10964 try { | |
10965 s = e.selection; | |
10966 b = s.getBookmark(true); | |
10967 | |
10968 if (s.getSel()) | |
10969 s.getSel().selectAllChildren(e.getBody()); | |
10970 | |
10971 s.collapse(true); | |
10972 s.moveToBookmark(b); | |
10973 } catch (ex) { | |
10974 // Ignore | |
10975 } | |
10976 } | |
10977 }, | |
10978 | |
10979 queryStateUnderline : function() { | |
10980 var ed = this.editor, n = ed.selection.getNode(); | |
10981 | |
10982 if (n && n.nodeName == 'A') | |
10983 return false; | |
10984 | |
10985 return this._queryState('Underline'); | |
10986 }, | |
10987 | |
10988 queryStateOutdent : function() { | |
10989 var ed = this.editor, n; | |
10990 | |
10991 if (ed.settings.inline_styles) { | |
10992 if ((n = ed.dom.getParent(ed.selection.getStart(), ed.dom.isBlock)) && parseInt(n.style.paddingLeft) > 0) | |
10993 return true; | |
10994 | |
10995 if ((n = ed.dom.getParent(ed.selection.getEnd(), ed.dom.isBlock)) && parseInt(n.style.paddingLeft) > 0) | |
10996 return true; | |
10997 } | |
10998 | |
10999 return this.queryStateInsertUnorderedList() || this.queryStateInsertOrderedList() || (!ed.settings.inline_styles && !!ed.dom.getParent(ed.selection.getNode(), 'BLOCKQUOTE')); | |
11000 }, | |
11001 | |
11002 queryStateInsertUnorderedList : function() { | |
11003 return this.editor.dom.getParent(this.editor.selection.getNode(), 'UL'); | |
11004 }, | |
11005 | |
11006 queryStateInsertOrderedList : function() { | |
11007 return this.editor.dom.getParent(this.editor.selection.getNode(), 'OL'); | |
11008 }, | |
11009 | |
11010 queryStatemceBlockQuote : function() { | |
11011 return !!this.editor.dom.getParent(this.editor.selection.getStart(), function(n) {return n.nodeName === 'BLOCKQUOTE';}); | |
11012 }, | |
11013 | |
11014 _applyInlineStyle : function(na, at, op) { | |
11015 var t = this, ed = t.editor, dom = ed.dom, bm, lo = {}, kh, found; | |
11016 | |
11017 na = na.toUpperCase(); | |
11018 | |
11019 if (op && op.check_classes && at['class']) | |
11020 op.check_classes.push(at['class']); | |
11021 | |
11022 function removeEmpty() { | |
11023 each(dom.select(na).reverse(), function(n) { | |
11024 var c = 0; | |
11025 | |
11026 // Check if there is any attributes | |
11027 each(dom.getAttribs(n), function(an) { | |
11028 if (an.nodeName.substring(0, 1) != '_' && dom.getAttrib(n, an.nodeName) != '') { | |
11029 //console.log(dom.getOuterHTML(n), dom.getAttrib(n, an.nodeName)); | |
11030 c++; | |
11031 } | |
11032 }); | |
11033 | |
11034 // No attributes then remove the element and keep the children | |
11035 if (c == 0) | |
11036 dom.remove(n, 1); | |
11037 }); | |
11038 }; | |
11039 | |
11040 function replaceFonts() { | |
11041 var bm; | |
11042 | |
11043 each(dom.select('span,font'), function(n) { | |
11044 if (n.style.fontFamily == 'mceinline' || n.face == 'mceinline') { | |
11045 if (!bm) | |
11046 bm = ed.selection.getBookmark(); | |
11047 | |
11048 at._mce_new = '1'; | |
11049 dom.replace(dom.create(na, at), n, 1); | |
11050 } | |
11051 }); | |
11052 | |
11053 // Remove redundant elements | |
11054 each(dom.select(na + '[_mce_new]'), function(n) { | |
11055 function removeStyle(n) { | |
11056 if (n.nodeType == 1) { | |
11057 each(at.style, function(v, k) { | |
11058 dom.setStyle(n, k, ''); | |
11059 }); | |
11060 | |
11061 // Remove spans with the same class or marked classes | |
11062 if (at['class'] && n.className && op) { | |
11063 each(op.check_classes, function(c) { | |
11064 if (dom.hasClass(n, c)) | |
11065 dom.removeClass(n, c); | |
11066 }); | |
11067 } | |
11068 } | |
11069 }; | |
11070 | |
11071 // Remove specified style information from child elements | |
11072 each(dom.select(na, n), removeStyle); | |
11073 | |
11074 // Remove the specified style information on parent if current node is only child (IE) | |
11075 if (n.parentNode && n.parentNode.nodeType == 1 && n.parentNode.childNodes.length == 1) | |
11076 removeStyle(n.parentNode); | |
11077 | |
11078 // Remove the child elements style info if a parent already has it | |
11079 dom.getParent(n.parentNode, function(pn) { | |
11080 if (pn.nodeType == 1) { | |
11081 if (at.style) { | |
11082 each(at.style, function(v, k) { | |
11083 var sv; | |
11084 | |
11085 if (!lo[k] && (sv = dom.getStyle(pn, k))) { | |
11086 if (sv === v) | |
11087 dom.setStyle(n, k, ''); | |
11088 | |
11089 lo[k] = 1; | |
11090 } | |
11091 }); | |
11092 } | |
11093 | |
11094 // Remove spans with the same class or marked classes | |
11095 if (at['class'] && pn.className && op) { | |
11096 each(op.check_classes, function(c) { | |
11097 if (dom.hasClass(pn, c)) | |
11098 dom.removeClass(n, c); | |
11099 }); | |
11100 } | |
11101 } | |
11102 | |
11103 return false; | |
11104 }); | |
11105 | |
11106 n.removeAttribute('_mce_new'); | |
11107 }); | |
11108 | |
11109 removeEmpty(); | |
11110 ed.selection.moveToBookmark(bm); | |
11111 | |
11112 return !!bm; | |
11113 }; | |
11114 | |
11115 // Create inline elements | |
11116 ed.focus(); | |
11117 ed.getDoc().execCommand('FontName', false, 'mceinline'); | |
11118 replaceFonts(); | |
11119 | |
11120 if (kh = t._applyInlineStyle.keyhandler) { | |
11121 ed.onKeyUp.remove(kh); | |
11122 ed.onKeyPress.remove(kh); | |
11123 ed.onKeyDown.remove(kh); | |
11124 ed.onSetContent.remove(t._applyInlineStyle.chandler); | |
11125 } | |
11126 | |
11127 if (ed.selection.isCollapsed()) { | |
11128 // IE will format the current word so this code can't be executed on that browser | |
11129 if (!isIE) { | |
11130 each(dom.getParents(ed.selection.getNode(), 'span'), function(n) { | |
11131 each(at.style, function(v, k) { | |
11132 var kv; | |
11133 | |
11134 if (kv = dom.getStyle(n, k)) { | |
11135 if (kv == v) { | |
11136 dom.setStyle(n, k, ''); | |
11137 found = 2; | |
11138 return false; | |
11139 } | |
11140 | |
11141 found = 1; | |
11142 return false; | |
11143 } | |
11144 }); | |
11145 | |
11146 if (found) | |
11147 return false; | |
11148 }); | |
11149 | |
11150 if (found == 2) { | |
11151 bm = ed.selection.getBookmark(); | |
11152 | |
11153 removeEmpty(); | |
11154 | |
11155 ed.selection.moveToBookmark(bm); | |
11156 | |
11157 // Node change needs to be detached since the onselect event | |
11158 // for the select box will run the onclick handler after onselect call. Todo: Add a nicer fix! | |
11159 window.setTimeout(function() { | |
11160 ed.nodeChanged(); | |
11161 }, 1); | |
11162 | |
11163 return; | |
11164 } | |
11165 } | |
11166 | |
11167 // Start collecting styles | |
11168 t._pendingStyles = tinymce.extend(t._pendingStyles || {}, at.style); | |
11169 | |
11170 t._applyInlineStyle.chandler = ed.onSetContent.add(function() { | |
11171 delete t._pendingStyles; | |
11172 }); | |
11173 | |
11174 t._applyInlineStyle.keyhandler = kh = function(e) { | |
11175 // Use pending styles | |
11176 if (t._pendingStyles) { | |
11177 at.style = t._pendingStyles; | |
11178 delete t._pendingStyles; | |
11179 } | |
11180 | |
11181 if (replaceFonts()) { | |
11182 ed.onKeyDown.remove(t._applyInlineStyle.keyhandler); | |
11183 ed.onKeyPress.remove(t._applyInlineStyle.keyhandler); | |
11184 } | |
11185 | |
11186 if (e.type == 'keyup') | |
11187 ed.onKeyUp.remove(t._applyInlineStyle.keyhandler); | |
11188 }; | |
11189 | |
11190 ed.onKeyDown.add(kh); | |
11191 ed.onKeyPress.add(kh); | |
11192 ed.onKeyUp.add(kh); | |
11193 } else | |
11194 t._pendingStyles = 0; | |
11195 } | |
11196 }); | |
11197 })(tinymce);(function(tinymce) { | |
11198 tinymce.create('tinymce.UndoManager', { | |
11199 index : 0, | |
11200 data : null, | |
11201 typing : 0, | |
11202 | |
11203 UndoManager : function(ed) { | |
11204 var t = this, Dispatcher = tinymce.util.Dispatcher; | |
11205 | |
11206 t.editor = ed; | |
11207 t.data = []; | |
11208 t.onAdd = new Dispatcher(this); | |
11209 t.onUndo = new Dispatcher(this); | |
11210 t.onRedo = new Dispatcher(this); | |
11211 }, | |
11212 | |
11213 add : function(l) { | |
11214 var t = this, i, ed = t.editor, b, s = ed.settings, la; | |
11215 | |
11216 l = l || {}; | |
11217 l.content = l.content || ed.getContent({format : 'raw', no_events : 1}); | |
11218 | |
11219 // Add undo level if needed | |
11220 l.content = l.content.replace(/^\s*|\s*$/g, ''); | |
11221 la = t.data[t.index > 0 && (t.index == 0 || t.index == t.data.length) ? t.index - 1 : t.index]; | |
11222 if (!l.initial && la && l.content == la.content) | |
11223 return null; | |
11224 | |
11225 // Time to compress | |
11226 if (s.custom_undo_redo_levels) { | |
11227 if (t.data.length > s.custom_undo_redo_levels) { | |
11228 for (i = 0; i < t.data.length - 1; i++) | |
11229 t.data[i] = t.data[i + 1]; | |
11230 | |
11231 t.data.length--; | |
11232 t.index = t.data.length; | |
11233 } | |
11234 } | |
11235 | |
11236 if (s.custom_undo_redo_restore_selection && !l.initial) | |
11237 l.bookmark = b = l.bookmark || ed.selection.getBookmark(); | |
11238 | |
11239 if (t.index < t.data.length) | |
11240 t.index++; | |
11241 | |
11242 // Only initial marked undo levels should be allowed as first item | |
11243 // This to workaround a bug with Firefox and the blur event | |
11244 if (t.data.length === 0 && !l.initial) | |
11245 return null; | |
11246 | |
11247 // Add level | |
11248 t.data.length = t.index + 1; | |
11249 t.data[t.index++] = l; | |
11250 | |
11251 if (l.initial) | |
11252 t.index = 0; | |
11253 | |
11254 // Set initial bookmark use first real undo level | |
11255 if (t.data.length == 2 && t.data[0].initial) | |
11256 t.data[0].bookmark = b; | |
11257 | |
11258 t.onAdd.dispatch(t, l); | |
11259 ed.isNotDirty = 0; | |
11260 | |
11261 //console.dir(t.data); | |
11262 | |
11263 return l; | |
11264 }, | |
11265 | |
11266 undo : function() { | |
11267 var t = this, ed = t.editor, l = l, i; | |
11268 | |
11269 if (t.typing) { | |
11270 t.add(); | |
11271 t.typing = 0; | |
11272 } | |
11273 | |
11274 if (t.index > 0) { | |
11275 // If undo on last index then take snapshot | |
11276 if (t.index == t.data.length && t.index > 1) { | |
11277 i = t.index; | |
11278 t.typing = 0; | |
11279 | |
11280 if (!t.add()) | |
11281 t.index = i; | |
11282 | |
11283 --t.index; | |
11284 } | |
11285 | |
11286 l = t.data[--t.index]; | |
11287 ed.setContent(l.content, {format : 'raw'}); | |
11288 ed.selection.moveToBookmark(l.bookmark); | |
11289 | |
11290 t.onUndo.dispatch(t, l); | |
11291 } | |
11292 | |
11293 return l; | |
11294 }, | |
11295 | |
11296 redo : function() { | |
11297 var t = this, ed = t.editor, l = null; | |
11298 | |
11299 if (t.index < t.data.length - 1) { | |
11300 l = t.data[++t.index]; | |
11301 ed.setContent(l.content, {format : 'raw'}); | |
11302 ed.selection.moveToBookmark(l.bookmark); | |
11303 | |
11304 t.onRedo.dispatch(t, l); | |
11305 } | |
11306 | |
11307 return l; | |
11308 }, | |
11309 | |
11310 clear : function() { | |
11311 var t = this; | |
11312 | |
11313 t.data = []; | |
11314 t.index = 0; | |
11315 t.typing = 0; | |
11316 t.add({initial : true}); | |
11317 }, | |
11318 | |
11319 hasUndo : function() { | |
11320 return this.index != 0 || this.typing; | |
11321 }, | |
11322 | |
11323 hasRedo : function() { | |
11324 return this.index < this.data.length - 1; | |
11325 } | |
11326 | |
11327 }); | |
11328 })(tinymce); | 11336 })(tinymce); |
11337 | |
11329 (function(tinymce) { | 11338 (function(tinymce) { |
11330 // Shorten names | 11339 // Shorten names |
11331 var Event, isIE, isGecko, isOpera, each, extend; | 11340 var Event = tinymce.dom.Event, |
11332 | 11341 isIE = tinymce.isIE, |
11333 Event = tinymce.dom.Event; | 11342 isGecko = tinymce.isGecko, |
11334 isIE = tinymce.isIE; | 11343 isOpera = tinymce.isOpera, |
11335 isGecko = tinymce.isGecko; | 11344 each = tinymce.each, |
11336 isOpera = tinymce.isOpera; | 11345 extend = tinymce.extend, |
11337 each = tinymce.each; | 11346 TRUE = true, |
11338 extend = tinymce.extend; | 11347 FALSE = false; |
11348 | |
11349 // Checks if the selection/caret is at the end of the specified block element | |
11350 function isAtEnd(rng, par) { | |
11351 var rng2 = par.ownerDocument.createRange(); | |
11352 | |
11353 rng2.setStart(rng.endContainer, rng.endOffset); | |
11354 rng2.setEndAfter(par); | |
11355 | |
11356 // Get number of characters to the right of the cursor if it's zero then we are at the end and need to merge the next block element | |
11357 return rng2.cloneContents().textContent.length == 0; | |
11358 }; | |
11359 | |
11360 function isEmpty(n) { | |
11361 n = n.innerHTML; | |
11362 | |
11363 n = n.replace(/<(img|hr|table|input|select|textarea)[ \>]/gi, '-'); // Keep these convert them to - chars | |
11364 n = n.replace(/<[^>]+>/g, ''); // Remove all tags | |
11365 | |
11366 return n.replace(/[ \u00a0\t\r\n]+/g, '') == ''; | |
11367 }; | |
11368 | |
11369 function splitList(selection, dom, li) { | |
11370 var listBlock, block; | |
11371 | |
11372 if (isEmpty(li)) { | |
11373 listBlock = dom.getParent(li, 'ul,ol'); | |
11374 | |
11375 if (!dom.getParent(listBlock.parentNode, 'ul,ol')) { | |
11376 dom.split(listBlock, li); | |
11377 block = dom.create('p', 0, '<br _mce_bogus="1" />'); | |
11378 dom.replace(block, li); | |
11379 selection.select(block, 1); | |
11380 } | |
11381 | |
11382 return FALSE; | |
11383 } | |
11384 | |
11385 return TRUE; | |
11386 }; | |
11339 | 11387 |
11340 tinymce.create('tinymce.ForceBlocks', { | 11388 tinymce.create('tinymce.ForceBlocks', { |
11341 ForceBlocks : function(ed) { | 11389 ForceBlocks : function(ed) { |
11342 var t = this, s = ed.settings, elm; | 11390 var t = this, s = ed.settings, elm; |
11343 | 11391 |
11351 t.reOpera = new RegExp('(\\u00a0| | )<\/' + elm + '>', 'gi'); | 11399 t.reOpera = new RegExp('(\\u00a0| | )<\/' + elm + '>', 'gi'); |
11352 t.rePadd = new RegExp('<p( )([^>]+)><\\\/p>|<p( )([^>]+)\\\/>|<p( )([^>]+)>\\s+<\\\/p>|<p><\\\/p>|<p\\\/>|<p>\\s+<\\\/p>'.replace(/p/g, elm), 'gi'); | 11400 t.rePadd = new RegExp('<p( )([^>]+)><\\\/p>|<p( )([^>]+)\\\/>|<p( )([^>]+)>\\s+<\\\/p>|<p><\\\/p>|<p\\\/>|<p>\\s+<\\\/p>'.replace(/p/g, elm), 'gi'); |
11353 t.reNbsp2BR1 = new RegExp('<p( )([^>]+)>[\\s\\u00a0]+<\\\/p>|<p>[\\s\\u00a0]+<\\\/p>'.replace(/p/g, elm), 'gi'); | 11401 t.reNbsp2BR1 = new RegExp('<p( )([^>]+)>[\\s\\u00a0]+<\\\/p>|<p>[\\s\\u00a0]+<\\\/p>'.replace(/p/g, elm), 'gi'); |
11354 t.reNbsp2BR2 = new RegExp('<%p()([^>]+)>( | )<\\\/%p>|<%p>( | )<\\\/%p>'.replace(/%p/g, elm), 'gi'); | 11402 t.reNbsp2BR2 = new RegExp('<%p()([^>]+)>( | )<\\\/%p>|<%p>( | )<\\\/%p>'.replace(/%p/g, elm), 'gi'); |
11355 t.reBR2Nbsp = new RegExp('<p( )([^>]+)>\\s*<br \\\/>\\s*<\\\/p>|<p>\\s*<br \\\/>\\s*<\\\/p>'.replace(/p/g, elm), 'gi'); | 11403 t.reBR2Nbsp = new RegExp('<p( )([^>]+)>\\s*<br \\\/>\\s*<\\\/p>|<p>\\s*<br \\\/>\\s*<\\\/p>'.replace(/p/g, elm), 'gi'); |
11356 t.reTrailBr = new RegExp('\\s*<br \\/>\\s*<\\\/p>'.replace(/p/g, elm), 'gi'); | |
11357 | 11404 |
11358 function padd(ed, o) { | 11405 function padd(ed, o) { |
11359 if (isOpera) | 11406 if (isOpera) |
11360 o.content = o.content.replace(t.reOpera, '</' + elm + '>'); | 11407 o.content = o.content.replace(t.reOpera, '</' + elm + '>'); |
11361 | 11408 |
11363 | 11410 |
11364 if (!isIE && !isOpera && o.set) { | 11411 if (!isIE && !isOpera && o.set) { |
11365 // Use instead of BR in padded paragraphs | 11412 // Use instead of BR in padded paragraphs |
11366 o.content = o.content.replace(t.reNbsp2BR1, '<' + elm + '$1$2><br /></' + elm + '>'); | 11413 o.content = o.content.replace(t.reNbsp2BR1, '<' + elm + '$1$2><br /></' + elm + '>'); |
11367 o.content = o.content.replace(t.reNbsp2BR2, '<' + elm + '$1$2><br /></' + elm + '>'); | 11414 o.content = o.content.replace(t.reNbsp2BR2, '<' + elm + '$1$2><br /></' + elm + '>'); |
11368 } else { | 11415 } else |
11369 o.content = o.content.replace(t.reBR2Nbsp, '<' + elm + '$1$2>\u00a0</' + elm + '>'); | 11416 o.content = o.content.replace(t.reBR2Nbsp, '<' + elm + '$1$2>\u00a0</' + elm + '>'); |
11370 o.content = o.content.replace(t.reTrailBr, '</' + elm + '>'); | |
11371 } | |
11372 }; | 11417 }; |
11373 | 11418 |
11374 ed.onBeforeSetContent.add(padd); | 11419 ed.onBeforeSetContent.add(padd); |
11375 ed.onPostProcess.add(padd); | 11420 ed.onPostProcess.add(padd); |
11376 | 11421 |
11380 ed.onBeforeGetContent.add(t.forceRoots, t); | 11425 ed.onBeforeGetContent.add(t.forceRoots, t); |
11381 } | 11426 } |
11382 }, | 11427 }, |
11383 | 11428 |
11384 setup : function() { | 11429 setup : function() { |
11385 var t = this, ed = t.editor, s = ed.settings; | 11430 var t = this, ed = t.editor, s = ed.settings, dom = ed.dom, selection = ed.selection; |
11386 | 11431 |
11387 // Force root blocks when typing and when getting output | 11432 // Force root blocks when typing and when getting output |
11388 if (s.forced_root_block) { | 11433 if (s.forced_root_block) { |
11434 ed.onBeforeExecCommand.add(t.forceRoots, t); | |
11389 ed.onKeyUp.add(t.forceRoots, t); | 11435 ed.onKeyUp.add(t.forceRoots, t); |
11390 ed.onPreProcess.add(t.forceRoots, t); | 11436 ed.onPreProcess.add(t.forceRoots, t); |
11391 } | 11437 } |
11392 | 11438 |
11393 if (s.force_br_newlines) { | 11439 if (s.force_br_newlines) { |
11394 // Force IE to produce BRs on enter | 11440 // Force IE to produce BRs on enter |
11395 if (isIE) { | 11441 if (isIE) { |
11396 ed.onKeyPress.add(function(ed, e) { | 11442 ed.onKeyPress.add(function(ed, e) { |
11397 var n, s = ed.selection; | 11443 var n; |
11398 | 11444 |
11399 if (e.keyCode == 13 && s.getNode().nodeName != 'LI') { | 11445 if (e.keyCode == 13 && selection.getNode().nodeName != 'LI') { |
11400 s.setContent('<br id="__" /> ', {format : 'raw'}); | 11446 selection.setContent('<br id="__" /> ', {format : 'raw'}); |
11401 n = ed.dom.get('__'); | 11447 n = dom.get('__'); |
11402 n.removeAttribute('id'); | 11448 n.removeAttribute('id'); |
11403 s.select(n); | 11449 selection.select(n); |
11404 s.collapse(); | 11450 selection.collapse(); |
11405 return Event.cancel(e); | 11451 return Event.cancel(e); |
11406 } | 11452 } |
11407 }); | 11453 }); |
11408 } | 11454 } |
11409 | |
11410 return; | |
11411 } | 11455 } |
11412 | 11456 |
11413 if (!isIE && s.force_p_newlines) { | 11457 if (!isIE && s.force_p_newlines) { |
11414 /* ed.onPreProcess.add(function(ed, o) { | |
11415 each(ed.dom.select('br', o.node), function(n) { | |
11416 var p = n.parentNode; | |
11417 | |
11418 // Replace <p><br /></p> with <p> </p> | |
11419 if (p && p.nodeName == 'p' && (p.childNodes.length == 1 || p.lastChild == n)) { | |
11420 p.replaceChild(ed.getDoc().createTextNode('\u00a0'), n); | |
11421 } | |
11422 }); | |
11423 });*/ | |
11424 | |
11425 ed.onKeyPress.add(function(ed, e) { | 11458 ed.onKeyPress.add(function(ed, e) { |
11426 if (e.keyCode == 13 && !e.shiftKey) { | 11459 if (e.keyCode == 13 && !e.shiftKey && !t.insertPara(e)) |
11427 if (!t.insertPara(e)) | 11460 Event.cancel(e); |
11428 Event.cancel(e); | |
11429 } | |
11430 }); | 11461 }); |
11431 | 11462 |
11432 if (isGecko) { | 11463 if (isGecko) { |
11433 ed.onKeyDown.add(function(ed, e) { | 11464 ed.onKeyDown.add(function(ed, e) { |
11434 if ((e.keyCode == 8 || e.keyCode == 46) && !e.shiftKey) | 11465 if ((e.keyCode == 8 || e.keyCode == 46) && !e.shiftKey) |
11435 t.backspaceDelete(e, e.keyCode == 8); | 11466 t.backspaceDelete(e, e.keyCode == 8); |
11436 }); | 11467 }); |
11437 } | 11468 } |
11438 } | 11469 } |
11439 | 11470 |
11440 function ren(rn, na) { | 11471 // Workaround for missing shift+enter support, http://bugs.webkit.org/show_bug.cgi?id=16973 |
11441 var ne = ed.dom.create(na); | 11472 if (tinymce.isWebKit) { |
11442 | 11473 function insertBr(ed) { |
11443 each(rn.attributes, function(a) { | 11474 var rng = selection.getRng(), br, div = dom.create('div', null, ' '), divYPos, vpHeight = dom.getViewPort(ed.getWin()).h; |
11444 if (a.specified && a.nodeValue) | 11475 |
11445 ne.setAttribute(a.nodeName.toLowerCase(), a.nodeValue); | 11476 // Insert BR element |
11446 }); | 11477 rng.insertNode(br = dom.create('br')); |
11447 | 11478 |
11448 each(rn.childNodes, function(n) { | 11479 // Place caret after BR |
11449 ne.appendChild(n.cloneNode(true)); | 11480 rng.setStartAfter(br); |
11450 }); | 11481 rng.setEndAfter(br); |
11451 | 11482 selection.setRng(rng); |
11452 rn.parentNode.replaceChild(ne, rn); | 11483 |
11453 | 11484 // Could not place caret after BR then insert an nbsp entity and move the caret |
11454 return ne; | 11485 if (selection.getSel().focusNode == br.previousSibling) { |
11455 }; | 11486 selection.select(dom.insertAfter(dom.doc.createTextNode('\u00a0'), br)); |
11456 | 11487 selection.collapse(TRUE); |
11457 // Replaces IE:s auto generated paragraphs with the specified element name | 11488 } |
11458 if (isIE && s.element != 'P') { | 11489 |
11490 // Create a temporary DIV after the BR and get the position as it | |
11491 // seems like getPos() returns 0 for text nodes and BR elements. | |
11492 dom.insertAfter(div, br); | |
11493 divYPos = dom.getPos(div).y; | |
11494 dom.remove(div); | |
11495 | |
11496 // Scroll to new position, scrollIntoView can't be used due to bug: http://bugs.webkit.org/show_bug.cgi?id=16117 | |
11497 if (divYPos > vpHeight) // It is not necessary to scroll if the DIV is inside the view port. | |
11498 ed.getWin().scrollTo(0, divYPos); | |
11499 }; | |
11500 | |
11459 ed.onKeyPress.add(function(ed, e) { | 11501 ed.onKeyPress.add(function(ed, e) { |
11460 t.lastElm = ed.selection.getNode().nodeName; | 11502 if (e.keyCode == 13 && (e.shiftKey || s.force_br_newlines)) { |
11461 }); | 11503 insertBr(ed); |
11462 | 11504 Event.cancel(e); |
11463 ed.onKeyUp.add(function(ed, e) { | |
11464 var bl, sel = ed.selection, n = sel.getNode(), b = ed.getBody(); | |
11465 | |
11466 if (b.childNodes.length === 1 && n.nodeName == 'P') { | |
11467 n = ren(n, s.element); | |
11468 sel.select(n); | |
11469 sel.collapse(); | |
11470 ed.nodeChanged(); | |
11471 } else if (e.keyCode == 13 && !e.shiftKey && t.lastElm != 'P') { | |
11472 bl = ed.dom.getParent(n, 'P'); | |
11473 | |
11474 if (bl) { | |
11475 ren(bl, s.element); | |
11476 ed.nodeChanged(); | |
11477 } | |
11478 } | 11505 } |
11479 }); | 11506 }); |
11480 } | 11507 } |
11508 | |
11509 // Padd empty inline elements within block elements | |
11510 // For example: <p><strong><em></em></strong></p> becomes <p><strong><em> </em></strong></p> | |
11511 ed.onPreProcess.add(function(ed, o) { | |
11512 each(dom.select('p,h1,h2,h3,h4,h5,h6,div', o.node), function(p) { | |
11513 if (isEmpty(p)) { | |
11514 each(dom.select('span,em,strong,b,i', o.node), function(n) { | |
11515 if (!n.hasChildNodes()) { | |
11516 n.appendChild(ed.getDoc().createTextNode('\u00a0')); | |
11517 return FALSE; // Break the loop one padding is enough | |
11518 } | |
11519 }); | |
11520 } | |
11521 }); | |
11522 }); | |
11523 | |
11524 // IE specific fixes | |
11525 if (isIE) { | |
11526 // Replaces IE:s auto generated paragraphs with the specified element name | |
11527 if (s.element != 'P') { | |
11528 ed.onKeyPress.add(function(ed, e) { | |
11529 t.lastElm = selection.getNode().nodeName; | |
11530 }); | |
11531 | |
11532 ed.onKeyUp.add(function(ed, e) { | |
11533 var bl, n = selection.getNode(), b = ed.getBody(); | |
11534 | |
11535 if (b.childNodes.length === 1 && n.nodeName == 'P') { | |
11536 n = dom.rename(n, s.element); | |
11537 selection.select(n); | |
11538 selection.collapse(); | |
11539 ed.nodeChanged(); | |
11540 } else if (e.keyCode == 13 && !e.shiftKey && t.lastElm != 'P') { | |
11541 bl = dom.getParent(n, 'p'); | |
11542 | |
11543 if (bl) { | |
11544 dom.rename(bl, s.element); | |
11545 ed.nodeChanged(); | |
11546 } | |
11547 } | |
11548 }); | |
11549 } | |
11550 } | |
11481 }, | 11551 }, |
11482 | 11552 |
11483 find : function(n, t, s) { | 11553 find : function(n, t, s) { |
11484 var ed = this.editor, w = ed.getDoc().createTreeWalker(n, 4, null, false), c = -1; | 11554 var ed = this.editor, w = ed.getDoc().createTreeWalker(n, 4, null, FALSE), c = -1; |
11485 | 11555 |
11486 while (n = w.nextNode()) { | 11556 while (n = w.nextNode()) { |
11487 c++; | 11557 c++; |
11488 | 11558 |
11489 // Index by node | 11559 // Index by node |
11502 var t = this, ed = t.editor, b = ed.getBody(), d = ed.getDoc(), se = ed.selection, s = se.getSel(), r = se.getRng(), si = -2, ei, so, eo, tr, c = -0xFFFFFF; | 11572 var t = this, ed = t.editor, b = ed.getBody(), d = ed.getDoc(), se = ed.selection, s = se.getSel(), r = se.getRng(), si = -2, ei, so, eo, tr, c = -0xFFFFFF; |
11503 var nx, bl, bp, sp, le, nl = b.childNodes, i, n, eid; | 11573 var nx, bl, bp, sp, le, nl = b.childNodes, i, n, eid; |
11504 | 11574 |
11505 // Fix for bug #1863847 | 11575 // Fix for bug #1863847 |
11506 //if (e && e.keyCode == 13) | 11576 //if (e && e.keyCode == 13) |
11507 // return true; | 11577 // return TRUE; |
11508 | 11578 |
11509 // Wrap non blocks into blocks | 11579 // Wrap non blocks into blocks |
11510 for (i = nl.length - 1; i >= 0; i--) { | 11580 for (i = nl.length - 1; i >= 0; i--) { |
11511 nx = nl[i]; | 11581 nx = nl[i]; |
11512 | 11582 |
11583 // Ignore internal elements | |
11584 if (nx.nodeType === 1 && nx.getAttribute('_mce_type')) { | |
11585 bl = null; | |
11586 continue; | |
11587 } | |
11588 | |
11513 // Is text or non block element | 11589 // Is text or non block element |
11514 if (nx.nodeType == 3 || (!t.dom.isBlock(nx) && nx.nodeType != 8)) { | 11590 if (nx.nodeType === 3 || (!t.dom.isBlock(nx) && nx.nodeType !== 8 && !/^(script|mce:script|style|mce:style)$/i.test(nx.nodeName))) { |
11515 if (!bl) { | 11591 if (!bl) { |
11516 // Create new block but ignore whitespace | 11592 // Create new block but ignore whitespace |
11517 if (nx.nodeType != 3 || /[^\s]/g.test(nx.nodeValue)) { | 11593 if (nx.nodeType != 3 || /[^\s]/g.test(nx.nodeValue)) { |
11518 // Store selection | 11594 // Store selection |
11519 if (si == -2 && r) { | 11595 if (si == -2 && r) { |
11549 si = sp - bp; | 11625 si = sp - bp; |
11550 ei = le; | 11626 ei = le; |
11551 } | 11627 } |
11552 } | 11628 } |
11553 | 11629 |
11630 // Uses replaceChild instead of cloneNode since it removes selected attribute from option elements on IE | |
11631 // See: http://support.microsoft.com/kb/829907 | |
11554 bl = ed.dom.create(ed.settings.forced_root_block); | 11632 bl = ed.dom.create(ed.settings.forced_root_block); |
11555 bl.appendChild(nx.cloneNode(1)); | |
11556 nx.parentNode.replaceChild(bl, nx); | 11633 nx.parentNode.replaceChild(bl, nx); |
11634 bl.appendChild(nx); | |
11557 } | 11635 } |
11558 } else { | 11636 } else { |
11559 if (bl.hasChildNodes()) | 11637 if (bl.hasChildNodes()) |
11560 bl.insertBefore(nx, bl.firstChild); | 11638 bl.insertBefore(nx, bl.firstChild); |
11561 else | 11639 else |
11622 | 11700 |
11623 insertPara : function(e) { | 11701 insertPara : function(e) { |
11624 var t = this, ed = t.editor, dom = ed.dom, d = ed.getDoc(), se = ed.settings, s = ed.selection.getSel(), r = s.getRangeAt(0), b = d.body; | 11702 var t = this, ed = t.editor, dom = ed.dom, d = ed.getDoc(), se = ed.settings, s = ed.selection.getSel(), r = s.getRangeAt(0), b = d.body; |
11625 var rb, ra, dir, sn, so, en, eo, sb, eb, bn, bef, aft, sc, ec, n, vp = dom.getViewPort(ed.getWin()), y, ch, car; | 11703 var rb, ra, dir, sn, so, en, eo, sb, eb, bn, bef, aft, sc, ec, n, vp = dom.getViewPort(ed.getWin()), y, ch, car; |
11626 | 11704 |
11627 function isEmpty(n) { | |
11628 n = n.innerHTML; | |
11629 n = n.replace(/<(img|hr|table)/gi, '-'); // Keep these convert them to - chars | |
11630 n = n.replace(/<[^>]+>/g, ''); // Remove all tags | |
11631 | |
11632 return n.replace(/[ \t\r\n]+/g, '') == ''; | |
11633 }; | |
11634 | |
11635 // If root blocks are forced then use Operas default behavior since it's really good | 11705 // If root blocks are forced then use Operas default behavior since it's really good |
11636 // Removed due to bug: #1853816 | 11706 // Removed due to bug: #1853816 |
11637 // if (se.forced_root_block && isOpera) | 11707 // if (se.forced_root_block && isOpera) |
11638 // return true; | 11708 // return TRUE; |
11639 | 11709 |
11640 // Setup before range | 11710 // Setup before range |
11641 rb = d.createRange(); | 11711 rb = d.createRange(); |
11642 | 11712 |
11643 // If is before the first block element and in body, then move it into first block element | 11713 // If is before the first block element and in body, then move it into first block element |
11644 rb.setStart(s.anchorNode, s.anchorOffset); | 11714 rb.setStart(s.anchorNode, s.anchorOffset); |
11645 rb.collapse(true); | 11715 rb.collapse(TRUE); |
11646 | 11716 |
11647 // Setup after range | 11717 // Setup after range |
11648 ra = d.createRange(); | 11718 ra = d.createRange(); |
11649 | 11719 |
11650 // If is before the first block element and in body, then move it into first block element | 11720 // If is before the first block element and in body, then move it into first block element |
11651 ra.setStart(s.focusNode, s.focusOffset); | 11721 ra.setStart(s.focusNode, s.focusOffset); |
11652 ra.collapse(true); | 11722 ra.collapse(TRUE); |
11653 | 11723 |
11654 // Setup start/end points | 11724 // Setup start/end points |
11655 dir = rb.compareBoundaryPoints(rb.START_TO_END, ra) < 0; | 11725 dir = rb.compareBoundaryPoints(rb.START_TO_END, ra) < 0; |
11656 sn = dir ? s.anchorNode : s.focusNode; | 11726 sn = dir ? s.anchorNode : s.focusNode; |
11657 so = dir ? s.anchorOffset : s.focusOffset; | 11727 so = dir ? s.anchorOffset : s.focusOffset; |
11658 en = dir ? s.focusNode : s.anchorNode; | 11728 en = dir ? s.focusNode : s.anchorNode; |
11659 eo = dir ? s.focusOffset : s.anchorOffset; | 11729 eo = dir ? s.focusOffset : s.anchorOffset; |
11660 | 11730 |
11661 // If selection is in empty table cell | 11731 // If selection is in empty table cell |
11662 if (sn === en && /^(TD|TH)$/.test(sn.nodeName)) { | 11732 if (sn === en && /^(TD|TH)$/.test(sn.nodeName)) { |
11663 dom.remove(sn.firstChild); // Remove BR | 11733 if (sn.firstChild.nodeName == 'BR') |
11734 dom.remove(sn.firstChild); // Remove BR | |
11664 | 11735 |
11665 // Create two new block elements | 11736 // Create two new block elements |
11666 ed.dom.add(sn, se.element, null, '<br />'); | 11737 if (sn.childNodes.length == 0) { |
11667 aft = ed.dom.add(sn, se.element, null, '<br />'); | 11738 ed.dom.add(sn, se.element, null, '<br />'); |
11739 aft = ed.dom.add(sn, se.element, null, '<br />'); | |
11740 } else { | |
11741 n = sn.innerHTML; | |
11742 sn.innerHTML = ''; | |
11743 ed.dom.add(sn, se.element, null, n); | |
11744 aft = ed.dom.add(sn, se.element, null, '<br />'); | |
11745 } | |
11668 | 11746 |
11669 // Move caret into the last one | 11747 // Move caret into the last one |
11670 r = d.createRange(); | 11748 r = d.createRange(); |
11671 r.selectNodeContents(aft); | 11749 r.selectNodeContents(aft); |
11672 r.collapse(1); | 11750 r.collapse(1); |
11673 ed.selection.setRng(r); | 11751 ed.selection.setRng(r); |
11674 | 11752 |
11675 return false; | 11753 return FALSE; |
11676 } | 11754 } |
11677 | 11755 |
11678 // If the caret is in an invalid location in FF we need to move it into the first block | 11756 // If the caret is in an invalid location in FF we need to move it into the first block |
11679 if (sn == b && en == b && b.firstChild && ed.dom.isBlock(b.firstChild)) { | 11757 if (sn == b && en == b && b.firstChild && ed.dom.isBlock(b.firstChild)) { |
11680 sn = en = sn.firstChild; | 11758 sn = en = sn.firstChild; |
11695 sb = t.getParentBlock(sn); | 11773 sb = t.getParentBlock(sn); |
11696 eb = t.getParentBlock(en); | 11774 eb = t.getParentBlock(en); |
11697 bn = sb ? sb.nodeName : se.element; // Get block name to create | 11775 bn = sb ? sb.nodeName : se.element; // Get block name to create |
11698 | 11776 |
11699 // Return inside list use default browser behavior | 11777 // Return inside list use default browser behavior |
11700 if (t.dom.getParent(sb, 'OL,UL,PRE')) | 11778 if (n = t.dom.getParent(sb, 'li,pre')) { |
11701 return true; | 11779 if (n.nodeName == 'LI') |
11780 return splitList(ed.selection, t.dom, n); | |
11781 | |
11782 return TRUE; | |
11783 } | |
11702 | 11784 |
11703 // If caption or absolute layers then always generate new blocks within | 11785 // If caption or absolute layers then always generate new blocks within |
11704 if (sb && (sb.nodeName == 'CAPTION' || /absolute|relative|fixed/gi.test(dom.getStyle(sb, 'position', 1)))) { | 11786 if (sb && (sb.nodeName == 'CAPTION' || /absolute|relative|fixed/gi.test(dom.getStyle(sb, 'position', 1)))) { |
11705 bn = se.element; | 11787 bn = se.element; |
11706 sb = null; | 11788 sb = null; |
11724 | 11806 |
11725 // Remove id from after clone | 11807 // Remove id from after clone |
11726 aft.removeAttribute('id'); | 11808 aft.removeAttribute('id'); |
11727 | 11809 |
11728 // Is header and cursor is at the end, then force paragraph under | 11810 // Is header and cursor is at the end, then force paragraph under |
11729 if (/^(H[1-6])$/.test(bn) && sn.nodeValue && so == sn.nodeValue.length) | 11811 if (/^(H[1-6])$/.test(bn) && isAtEnd(r, sb)) |
11730 aft = ed.dom.create(se.element); | 11812 aft = ed.dom.create(se.element); |
11731 | 11813 |
11732 // Find start chop node | 11814 // Find start chop node |
11733 n = sc = sn; | 11815 n = sc = sn; |
11734 do { | 11816 do { |
11808 if (se.keep_styles) { | 11890 if (se.keep_styles) { |
11809 n = en; | 11891 n = en; |
11810 do { | 11892 do { |
11811 // We only want style specific elements | 11893 // We only want style specific elements |
11812 if (/^(SPAN|STRONG|B|EM|I|FONT|STRIKE|U)$/.test(n.nodeName)) { | 11894 if (/^(SPAN|STRONG|B|EM|I|FONT|STRIKE|U)$/.test(n.nodeName)) { |
11813 nn = n.cloneNode(false); | 11895 nn = n.cloneNode(FALSE); |
11814 dom.setAttrib(nn, 'id', ''); // Remove ID since it needs to be unique | 11896 dom.setAttrib(nn, 'id', ''); // Remove ID since it needs to be unique |
11815 nl.push(nn); | 11897 nl.push(nn); |
11816 } | 11898 } |
11817 } while (n = n.parentNode); | 11899 } while (n = n.parentNode); |
11818 } | 11900 } |
11845 // Normalize | 11927 // Normalize |
11846 aft.normalize(); | 11928 aft.normalize(); |
11847 bef.normalize(); | 11929 bef.normalize(); |
11848 | 11930 |
11849 function first(n) { | 11931 function first(n) { |
11850 return d.createTreeWalker(n, NodeFilter.SHOW_TEXT, null, false).nextNode() || n; | 11932 return d.createTreeWalker(n, NodeFilter.SHOW_TEXT, null, FALSE).nextNode() || n; |
11851 }; | 11933 }; |
11852 | 11934 |
11853 // Move cursor and scroll into view | 11935 // Move cursor and scroll into view |
11854 r = d.createRange(); | 11936 r = d.createRange(); |
11855 r.selectNodeContents(isGecko ? first(car || aft) : car || aft); | 11937 r.selectNodeContents(isGecko ? first(car || aft) : car || aft); |
11865 if (y < vp.y || y + ch > vp.y + vp.h) { | 11947 if (y < vp.y || y + ch > vp.y + vp.h) { |
11866 ed.getWin().scrollTo(0, y < vp.y ? y : y - vp.h + 25); // Needs to be hardcoded to roughly one line of text if a huge text block is broken into two blocks | 11948 ed.getWin().scrollTo(0, y < vp.y ? y : y - vp.h + 25); // Needs to be hardcoded to roughly one line of text if a huge text block is broken into two blocks |
11867 //console.debug('SCROLL!', 'vp.y: ' + vp.y, 'y' + y, 'vp.h' + vp.h, 'clientHeight' + aft.clientHeight, 'yyy: ' + (y < vp.y ? y : y - vp.h + aft.clientHeight)); | 11949 //console.debug('SCROLL!', 'vp.y: ' + vp.y, 'y' + y, 'vp.h' + vp.h, 'clientHeight' + aft.clientHeight, 'yyy: ' + (y < vp.y ? y : y - vp.h + aft.clientHeight)); |
11868 } | 11950 } |
11869 | 11951 |
11870 return false; | 11952 return FALSE; |
11871 }, | 11953 }, |
11872 | 11954 |
11873 backspaceDelete : function(e, bs) { | 11955 backspaceDelete : function(e, bs) { |
11874 var t = this, ed = t.editor, b = ed.getBody(), n, se = ed.selection, r = se.getRng(), sc = r.startContainer, n, w, tn; | 11956 var t = this, ed = t.editor, b = ed.getBody(), dom = ed.dom, n, se = ed.selection, r = se.getRng(), sc = r.startContainer, n, w, tn; |
11875 | 11957 |
11876 // The caret sometimes gets stuck in Gecko if you delete empty paragraphs | 11958 // The caret sometimes gets stuck in Gecko if you delete empty paragraphs |
11877 // This workaround removes the element by hand and moves the caret to the previous element | 11959 // This workaround removes the element by hand and moves the caret to the previous element |
11878 if (sc && ed.dom.isBlock(sc) && !/^(TD|TH)$/.test(sc.nodeName) && bs) { | 11960 if (sc && ed.dom.isBlock(sc) && !/^(TD|TH)$/.test(sc.nodeName) && bs) { |
11879 if (sc.childNodes.length == 0 || (sc.childNodes.length == 1 && sc.firstChild.nodeName == 'BR')) { | 11961 if (sc.childNodes.length == 0 || (sc.childNodes.length == 1 && sc.firstChild.nodeName == 'BR')) { |
11882 while ((n = n.previousSibling) && !ed.dom.isBlock(n)) ; | 11964 while ((n = n.previousSibling) && !ed.dom.isBlock(n)) ; |
11883 | 11965 |
11884 if (n) { | 11966 if (n) { |
11885 if (sc != b.firstChild) { | 11967 if (sc != b.firstChild) { |
11886 // Find last text node | 11968 // Find last text node |
11887 w = ed.dom.doc.createTreeWalker(n, NodeFilter.SHOW_TEXT, null, false); | 11969 w = ed.dom.doc.createTreeWalker(n, NodeFilter.SHOW_TEXT, null, FALSE); |
11888 while (tn = w.nextNode()) | 11970 while (tn = w.nextNode()) |
11889 n = tn; | 11971 n = tn; |
11890 | 11972 |
11891 // Place caret at the end of last text node | 11973 // Place caret at the end of last text node |
11892 r = ed.getDoc().createRange(); | 11974 r = ed.getDoc().createRange(); |
11934 Event._remove(b, 'DOMNodeInserted', handler); | 12016 Event._remove(b, 'DOMNodeInserted', handler); |
11935 }, 1); | 12017 }, 1); |
11936 } | 12018 } |
11937 }); | 12019 }); |
11938 })(tinymce); | 12020 })(tinymce); |
12021 | |
11939 (function(tinymce) { | 12022 (function(tinymce) { |
11940 // Shorten names | 12023 // Shorten names |
11941 var DOM = tinymce.DOM, Event = tinymce.dom.Event, each = tinymce.each, extend = tinymce.extend; | 12024 var DOM = tinymce.DOM, Event = tinymce.dom.Event, each = tinymce.each, extend = tinymce.extend; |
11942 | 12025 |
11943 tinymce.create('tinymce.ControlManager', { | 12026 tinymce.create('tinymce.ControlManager', { |
12036 | 12119 |
12037 s.title = ed.getLang(s.title, s.title); | 12120 s.title = ed.getLang(s.title, s.title); |
12038 | 12121 |
12039 if (!s.onclick) { | 12122 if (!s.onclick) { |
12040 s.onclick = function(v) { | 12123 s.onclick = function(v) { |
12041 ed.execCommand(s.cmd, s.ui || false, s.value); | 12124 if (s.cmd) |
12125 ed.execCommand(s.cmd, s.ui || false, s.value); | |
12042 }; | 12126 }; |
12043 } | 12127 } |
12044 }); | 12128 }); |
12045 | 12129 |
12046 ed.onRemove.add(function() { | 12130 ed.onRemove.add(function() { |
12048 }); | 12132 }); |
12049 | 12133 |
12050 // Fix for bug #1897785, #1898007 | 12134 // Fix for bug #1897785, #1898007 |
12051 if (tinymce.isIE) { | 12135 if (tinymce.isIE) { |
12052 c.onShowMenu.add(function() { | 12136 c.onShowMenu.add(function() { |
12137 // IE 8 needs focus in order to store away a range with the current collapsed caret location | |
12138 ed.focus(); | |
12139 | |
12053 bm = ed.selection.getBookmark(1); | 12140 bm = ed.selection.getBookmark(1); |
12054 }); | 12141 }); |
12055 | 12142 |
12056 c.onHideMenu.add(function() { | 12143 c.onHideMenu.add(function() { |
12057 if (bm) { | 12144 if (bm) { |
12100 // Fix focus problem in Safari | 12187 // Fix focus problem in Safari |
12101 if (tinymce.isWebKit) { | 12188 if (tinymce.isWebKit) { |
12102 c.onPostRender.add(function(c, n) { | 12189 c.onPostRender.add(function(c, n) { |
12103 // Store bookmark on mousedown | 12190 // Store bookmark on mousedown |
12104 Event.add(n, 'mousedown', function() { | 12191 Event.add(n, 'mousedown', function() { |
12105 ed.bookmark = ed.selection.getBookmark('simple'); | 12192 ed.bookmark = ed.selection.getBookmark(1); |
12106 }); | 12193 }); |
12107 | 12194 |
12108 // Restore on focus, since it might be lost | 12195 // Restore on focus, since it might be lost |
12109 Event.add(n, 'focus', function() { | 12196 Event.add(n, 'focus', function() { |
12110 ed.selection.moveToBookmark(ed.bookmark); | 12197 ed.selection.moveToBookmark(ed.bookmark); |
12211 | 12298 |
12212 if (!s.onclick) { | 12299 if (!s.onclick) { |
12213 s.onclick = function(v) { | 12300 s.onclick = function(v) { |
12214 if (tinymce.isIE) | 12301 if (tinymce.isIE) |
12215 bm = ed.selection.getBookmark(1); | 12302 bm = ed.selection.getBookmark(1); |
12216 | 12303 |
12217 ed.execCommand(s.cmd, s.ui || false, v || s.value); | 12304 ed.execCommand(s.cmd, s.ui || false, v || s.value); |
12218 }; | 12305 }; |
12219 } | 12306 } |
12220 | 12307 |
12221 if (!s.onselect) { | 12308 if (!s.onselect) { |
12242 c.destroy(); | 12329 c.destroy(); |
12243 }); | 12330 }); |
12244 | 12331 |
12245 // Fix for bug #1897785, #1898007 | 12332 // Fix for bug #1897785, #1898007 |
12246 if (tinymce.isIE) { | 12333 if (tinymce.isIE) { |
12334 c.onShowMenu.add(function() { | |
12335 // IE 8 needs focus in order to store away a range with the current collapsed caret location | |
12336 ed.focus(); | |
12337 bm = ed.selection.getBookmark(1); | |
12338 }); | |
12339 | |
12247 c.onHideMenu.add(function() { | 12340 c.onHideMenu.add(function() { |
12248 if (bm) { | 12341 if (bm) { |
12249 ed.selection.moveToBookmark(bm); | 12342 ed.selection.moveToBookmark(bm); |
12250 bm = 0; | 12343 bm = 0; |
12251 } | 12344 } |
12275 }, | 12368 }, |
12276 | 12369 |
12277 setControlType : function(n, c) { | 12370 setControlType : function(n, c) { |
12278 return this._cls[n.toLowerCase()] = c; | 12371 return this._cls[n.toLowerCase()] = c; |
12279 }, | 12372 }, |
12280 | 12373 |
12281 destroy : function() { | 12374 destroy : function() { |
12282 each(this.controls, function(c) { | 12375 each(this.controls, function(c) { |
12283 c.destroy(); | 12376 c.destroy(); |
12284 }); | 12377 }); |
12285 | 12378 |
12286 this.controls = null; | 12379 this.controls = null; |
12287 } | 12380 } |
12288 | 12381 }); |
12289 }); | |
12290 })(tinymce); | 12382 })(tinymce); |
12383 | |
12291 (function(tinymce) { | 12384 (function(tinymce) { |
12292 var Dispatcher = tinymce.util.Dispatcher, each = tinymce.each, isIE = tinymce.isIE, isOpera = tinymce.isOpera; | 12385 var Dispatcher = tinymce.util.Dispatcher, each = tinymce.each, isIE = tinymce.isIE, isOpera = tinymce.isOpera; |
12293 | 12386 |
12294 tinymce.create('tinymce.WindowManager', { | 12387 tinymce.create('tinymce.WindowManager', { |
12295 WindowManager : function(ed) { | 12388 WindowManager : function(ed) { |
12390 | 12483 |
12391 if (cb) | 12484 if (cb) |
12392 cb.call(s || t); | 12485 cb.call(s || t); |
12393 }, | 12486 }, |
12394 | 12487 |
12488 resizeBy : function(dw, dh, win) { | |
12489 win.resizeBy(dw, dh); | |
12490 }, | |
12491 | |
12395 // Internal functions | 12492 // Internal functions |
12396 | 12493 |
12397 _decode : function(s) { | 12494 _decode : function(s) { |
12398 return tinymce.DOM.decode(s).replace(/\\n/g, '\n'); | 12495 return tinymce.DOM.decode(s).replace(/\\n/g, '\n'); |
12399 } | 12496 } |
12400 | 12497 }); |
12401 }); | 12498 }(tinymce)); |
12402 }(tinymce));(function(tinymce) { | 12499 (function(tinymce) { |
12403 tinymce.CommandManager = function() { | 12500 function CommandManager() { |
12404 var execCommands = {}, queryStateCommands = {}, queryValueCommands = {}; | 12501 var execCommands = {}, queryStateCommands = {}, queryValueCommands = {}; |
12405 | 12502 |
12406 function add(collection, cmd, func, scope) { | 12503 function add(collection, cmd, func, scope) { |
12407 if (typeof(cmd) == 'string') | 12504 if (typeof(cmd) == 'string') |
12408 cmd = [cmd]; | 12505 cmd = [cmd]; |
12442 return cmd.func.call(scope || cmd.scope, ui, value, args); | 12539 return cmd.func.call(scope || cmd.scope, ui, value, args); |
12443 } | 12540 } |
12444 }); | 12541 }); |
12445 }; | 12542 }; |
12446 | 12543 |
12447 tinymce.GlobalCommands = new tinymce.CommandManager(); | 12544 tinymce.GlobalCommands = new CommandManager(); |
12448 })(tinymce);(function(tinymce) { | |
12449 function processRange(dom, start, end, callback) { | |
12450 var ancestor, n, startPoint, endPoint, sib; | |
12451 | |
12452 function findEndPoint(n, c) { | |
12453 do { | |
12454 if (n.parentNode == c) | |
12455 return n; | |
12456 | |
12457 n = n.parentNode; | |
12458 } while(n); | |
12459 }; | |
12460 | |
12461 function process(n) { | |
12462 callback(n); | |
12463 tinymce.walk(n, callback, 'childNodes'); | |
12464 }; | |
12465 | |
12466 // Find common ancestor and end points | |
12467 ancestor = dom.findCommonAncestor(start, end); | |
12468 startPoint = findEndPoint(start, ancestor) || start; | |
12469 endPoint = findEndPoint(end, ancestor) || end; | |
12470 | |
12471 // Process left leaf | |
12472 for (n = start; n && n != startPoint; n = n.parentNode) { | |
12473 for (sib = n.nextSibling; sib; sib = sib.nextSibling) | |
12474 process(sib); | |
12475 } | |
12476 | |
12477 // Process middle from start to end point | |
12478 if (startPoint != endPoint) { | |
12479 for (n = startPoint.nextSibling; n && n != endPoint; n = n.nextSibling) | |
12480 process(n); | |
12481 } else | |
12482 process(startPoint); | |
12483 | |
12484 // Process right leaf | |
12485 for (n = end; n && n != endPoint; n = n.parentNode) { | |
12486 for (sib = n.previousSibling; sib; sib = sib.previousSibling) | |
12487 process(sib); | |
12488 } | |
12489 }; | |
12490 | |
12491 tinymce.GlobalCommands.add('RemoveFormat', function() { | |
12492 var ed = this, dom = ed.dom, s = ed.selection, r = s.getRng(1), nodes = [], bm, start, end, sc, so, ec, eo, n; | |
12493 | |
12494 function findFormatRoot(n) { | |
12495 var sp; | |
12496 | |
12497 dom.getParent(n, function(n) { | |
12498 if (dom.is(n, ed.getParam('removeformat_selector'))) | |
12499 sp = n; | |
12500 | |
12501 return dom.isBlock(n); | |
12502 }, ed.getBody()) | |
12503 | |
12504 return sp; | |
12505 }; | |
12506 | |
12507 function collect(n) { | |
12508 if (dom.is(n, ed.getParam('removeformat_selector'))) | |
12509 nodes.push(n); | |
12510 }; | |
12511 | |
12512 function walk(n) { | |
12513 collect(n); | |
12514 tinymce.walk(n, collect, 'childNodes'); | |
12515 }; | |
12516 | |
12517 bm = s.getBookmark(); | |
12518 sc = r.startContainer; | |
12519 ec = r.endContainer; | |
12520 so = r.startOffset; | |
12521 eo = r.endOffset; | |
12522 sc = sc.nodeType == 1 ? sc.childNodes[so] : sc; | |
12523 ec = ec.nodeType == 1 ? ec.childNodes[eo - 1] : ec; | |
12524 | |
12525 // Same container | |
12526 if (sc == ec) { // TEXT_NODE | |
12527 start = findFormatRoot(sc); | |
12528 | |
12529 // Handle single text node | |
12530 if (sc.nodeType == 3) { | |
12531 if (start && start.nodeType == 1) { // ELEMENT | |
12532 n = sc.splitText(so); | |
12533 n.splitText(eo - so); | |
12534 dom.split(start, n); | |
12535 | |
12536 s.moveToBookmark(bm); | |
12537 } | |
12538 | |
12539 return; | |
12540 } | |
12541 | |
12542 // Handle single element | |
12543 walk(dom.split(start, sc) || sc); | |
12544 } else { | |
12545 // Find start/end format root | |
12546 start = findFormatRoot(sc); | |
12547 end = findFormatRoot(ec); | |
12548 | |
12549 // Split start text node | |
12550 if (start) { | |
12551 if (sc.nodeType == 3) { // TEXT | |
12552 // Since IE doesn't support white space nodes in the DOM we need to | |
12553 // add this invisible character so that the splitText function can split the contents | |
12554 if (so == sc.nodeValue.length) | |
12555 sc.nodeValue += '\uFEFF'; // Yet another pesky IE fix | |
12556 | |
12557 sc = sc.splitText(so); | |
12558 } | |
12559 } | |
12560 | |
12561 // Split end text node | |
12562 if (end) { | |
12563 if (ec.nodeType == 3) // TEXT | |
12564 ec.splitText(eo); | |
12565 } | |
12566 | |
12567 // If the start and end format root is the same then we need to wrap | |
12568 // the end node in a span since the split calls might change the reference | |
12569 // Example: <p><b><em>x[yz<span>---</span>12]3</em></b></p> | |
12570 if (start && start == end) | |
12571 dom.replace(dom.create('span', {id : '__end'}, ec.cloneNode(true)), ec); | |
12572 | |
12573 // Split all start containers down to the format root | |
12574 if (start) | |
12575 start = dom.split(start, sc); | |
12576 else | |
12577 start = sc; | |
12578 | |
12579 // If there is a span wrapper use that one instead | |
12580 if (n = dom.get('__end')) { | |
12581 ec = n; | |
12582 end = findFormatRoot(ec); | |
12583 } | |
12584 | |
12585 // Split all end containers down to the format root | |
12586 if (end) | |
12587 end = dom.split(end, ec); | |
12588 else | |
12589 end = ec; | |
12590 | |
12591 // Collect nodes in between | |
12592 processRange(dom, start, end, collect); | |
12593 | |
12594 // Remove invisible character for IE workaround if we find it | |
12595 if (sc.nodeValue == '\uFEFF') | |
12596 sc.nodeValue = ''; | |
12597 | |
12598 // Process start/end container elements | |
12599 walk(ec); | |
12600 walk(sc); | |
12601 } | |
12602 | |
12603 // Remove all collected nodes | |
12604 tinymce.each(nodes, function(n) { | |
12605 dom.remove(n, 1); | |
12606 }); | |
12607 | |
12608 // Remove leftover wrapper | |
12609 dom.remove('__end', 1); | |
12610 | |
12611 s.moveToBookmark(bm); | |
12612 }); | |
12613 })(tinymce); | 12545 })(tinymce); |
12614 (function(tinymce) { | 12546 (function(tinymce) { |
12615 tinymce.GlobalCommands.add('mceBlockQuote', function() { | 12547 tinymce.Formatter = function(ed) { |
12616 var ed = this, s = ed.selection, dom = ed.dom, sb, eb, n, bm, bq, r, bq2, i, nl; | 12548 var formats = {}, |
12617 | 12549 each = tinymce.each, |
12618 function getBQ(e) { | 12550 dom = ed.dom, |
12619 return dom.getParent(e, function(n) {return n.nodeName === 'BLOCKQUOTE';}); | 12551 selection = ed.selection, |
12552 TreeWalker = tinymce.dom.TreeWalker, | |
12553 rangeUtils = new tinymce.dom.RangeUtils(dom), | |
12554 isValid = ed.schema.isValid, | |
12555 isBlock = dom.isBlock, | |
12556 forcedRootBlock = ed.settings.forced_root_block, | |
12557 nodeIndex = dom.nodeIndex, | |
12558 INVISIBLE_CHAR = '\uFEFF', | |
12559 MCE_ATTR_RE = /^(src|href|style)$/, | |
12560 FALSE = false, | |
12561 TRUE = true, | |
12562 undefined, | |
12563 pendingFormats = {apply : [], remove : []}; | |
12564 | |
12565 function isArray(obj) { | |
12566 return obj instanceof Array; | |
12620 }; | 12567 }; |
12621 | 12568 |
12622 // Get start/end block | 12569 function getParents(node, selector) { |
12623 sb = dom.getParent(s.getStart(), dom.isBlock); | 12570 return dom.getParents(node, selector, dom.getRoot()); |
12624 eb = dom.getParent(s.getEnd(), dom.isBlock); | 12571 }; |
12625 | 12572 |
12626 // Remove blockquote(s) | 12573 function isCaretNode(node) { |
12627 if (bq = getBQ(sb)) { | 12574 return node.nodeType === 1 && (node.face === 'mceinline' || node.style.fontFamily === 'mceinline'); |
12628 if (sb != eb || sb.childNodes.length > 1 || (sb.childNodes.length == 1 && sb.firstChild.nodeName != 'BR')) | 12575 }; |
12629 bm = s.getBookmark(); | 12576 |
12630 | 12577 // Public functions |
12631 // Move all elements after the end block into new bq | 12578 |
12632 if (getBQ(eb)) { | 12579 function get(name) { |
12633 bq2 = bq.cloneNode(false); | 12580 return name ? formats[name] : formats; |
12634 | 12581 }; |
12635 while (n = eb.nextSibling) | 12582 |
12636 bq2.appendChild(n.parentNode.removeChild(n)); | 12583 function register(name, format) { |
12637 } | 12584 if (name) { |
12638 | 12585 if (typeof(name) !== 'string') { |
12639 // Add new bq after | 12586 each(name, function(format, name) { |
12640 if (bq2) | 12587 register(name, format); |
12641 dom.insertAfter(bq2, bq); | 12588 }); |
12642 | |
12643 // Move all selected blocks after the current bq | |
12644 nl = s.getSelectedBlocks(sb, eb); | |
12645 for (i = nl.length - 1; i >= 0; i--) { | |
12646 dom.insertAfter(nl[i], bq); | |
12647 } | |
12648 | |
12649 // Empty bq, then remove it | |
12650 if (/^\s*$/.test(bq.innerHTML)) | |
12651 dom.remove(bq, 1); // Keep children so boomark restoration works correctly | |
12652 | |
12653 // Empty bq, then remote it | |
12654 if (bq2 && /^\s*$/.test(bq2.innerHTML)) | |
12655 dom.remove(bq2, 1); // Keep children so boomark restoration works correctly | |
12656 | |
12657 if (!bm) { | |
12658 // Move caret inside empty block element | |
12659 if (!tinymce.isIE) { | |
12660 r = ed.getDoc().createRange(); | |
12661 r.setStart(sb, 0); | |
12662 r.setEnd(sb, 0); | |
12663 s.setRng(r); | |
12664 } else { | 12589 } else { |
12665 s.select(sb); | 12590 // Force format into array and add it to internal collection |
12666 s.collapse(0); | 12591 format = format.length ? format : [format]; |
12667 | 12592 |
12668 // IE misses the empty block some times element so we must move back the caret | 12593 each(format, function(format) { |
12669 if (dom.getParent(s.getStart(), dom.isBlock) != sb) { | 12594 // Set deep to false by default on selector formats this to avoid removing |
12670 r = s.getRng(); | 12595 // alignment on images inside paragraphs when alignment is changed on paragraphs |
12671 r.move('character', -1); | 12596 if (format.deep === undefined) |
12672 r.select(); | 12597 format.deep = !format.selector; |
12598 | |
12599 // Default to true | |
12600 if (format.split === undefined) | |
12601 format.split = !format.selector || format.inline; | |
12602 | |
12603 // Default to true | |
12604 if (format.remove === undefined && format.selector && !format.inline) | |
12605 format.remove = 'none'; | |
12606 | |
12607 // Mark format as a mixed format inline + block level | |
12608 if (format.selector && format.inline) { | |
12609 format.mixed = true; | |
12610 format.block_expand = true; | |
12611 } | |
12612 | |
12613 // Split classes if needed | |
12614 if (typeof(format.classes) === 'string') | |
12615 format.classes = format.classes.split(/\s+/); | |
12616 }); | |
12617 | |
12618 formats[name] = format; | |
12619 } | |
12620 } | |
12621 }; | |
12622 | |
12623 function apply(name, vars, node) { | |
12624 var formatList = get(name), format = formatList[0], bookmark, rng, i; | |
12625 | |
12626 function moveStart(rng) { | |
12627 var container = rng.startContainer, | |
12628 offset = rng.startOffset, | |
12629 walker, node; | |
12630 | |
12631 // Move startContainer/startOffset in to a suitable node | |
12632 if (container.nodeType == 1 || container.nodeValue === "") { | |
12633 walker = new TreeWalker(container.childNodes[offset]); | |
12634 for (node = walker.current(); node; node = walker.next()) { | |
12635 if (node.nodeType == 3 && !isBlock(node.parentNode) && !isWhiteSpaceNode(node)) { | |
12636 rng.setStart(node, 0); | |
12637 break; | |
12638 } | |
12673 } | 12639 } |
12674 } | 12640 } |
12641 | |
12642 return rng; | |
12643 }; | |
12644 | |
12645 function setElementFormat(elm, fmt) { | |
12646 fmt = fmt || format; | |
12647 | |
12648 if (elm) { | |
12649 each(fmt.styles, function(value, name) { | |
12650 dom.setStyle(elm, name, replaceVars(value, vars)); | |
12651 }); | |
12652 | |
12653 each(fmt.attributes, function(value, name) { | |
12654 dom.setAttrib(elm, name, replaceVars(value, vars)); | |
12655 }); | |
12656 | |
12657 each(fmt.classes, function(value) { | |
12658 value = replaceVars(value, vars); | |
12659 | |
12660 if (!dom.hasClass(elm, value)) | |
12661 dom.addClass(elm, value); | |
12662 }); | |
12663 } | |
12664 }; | |
12665 | |
12666 function applyRngStyle(rng) { | |
12667 var newWrappers = [], wrapName, wrapElm; | |
12668 | |
12669 // Setup wrapper element | |
12670 wrapName = format.inline || format.block; | |
12671 wrapElm = dom.create(wrapName); | |
12672 setElementFormat(wrapElm); | |
12673 | |
12674 rangeUtils.walk(rng, function(nodes) { | |
12675 var currentWrapElm; | |
12676 | |
12677 function process(node) { | |
12678 var nodeName = node.nodeName.toLowerCase(), parentName = node.parentNode.nodeName.toLowerCase(), found; | |
12679 | |
12680 // Stop wrapping on br elements | |
12681 if (isEq(nodeName, 'br')) { | |
12682 currentWrapElm = 0; | |
12683 | |
12684 // Remove any br elements when we wrap things | |
12685 if (format.block) | |
12686 dom.remove(node); | |
12687 | |
12688 return; | |
12689 } | |
12690 | |
12691 // If node is wrapper type | |
12692 if (format.wrapper && matchNode(node, name, vars)) { | |
12693 currentWrapElm = 0; | |
12694 return; | |
12695 } | |
12696 | |
12697 // Can we rename the block | |
12698 if (format.block && !format.wrapper && isTextBlock(nodeName)) { | |
12699 node = dom.rename(node, wrapName); | |
12700 setElementFormat(node); | |
12701 newWrappers.push(node); | |
12702 currentWrapElm = 0; | |
12703 return; | |
12704 } | |
12705 | |
12706 // Handle selector patterns | |
12707 if (format.selector) { | |
12708 // Look for matching formats | |
12709 each(formatList, function(format) { | |
12710 if (dom.is(node, format.selector) && !isCaretNode(node)) { | |
12711 setElementFormat(node, format); | |
12712 found = true; | |
12713 } | |
12714 }); | |
12715 | |
12716 // Contine processing if a selector match wasn't found and a inline element is defined | |
12717 if (!format.inline || found) { | |
12718 currentWrapElm = 0; | |
12719 return; | |
12720 } | |
12721 } | |
12722 | |
12723 // Is it valid to wrap this item | |
12724 if (isValid(wrapName, nodeName) && isValid(parentName, wrapName)) { | |
12725 // Start wrapping | |
12726 if (!currentWrapElm) { | |
12727 // Wrap the node | |
12728 currentWrapElm = wrapElm.cloneNode(FALSE); | |
12729 node.parentNode.insertBefore(currentWrapElm, node); | |
12730 newWrappers.push(currentWrapElm); | |
12731 } | |
12732 | |
12733 currentWrapElm.appendChild(node); | |
12734 } else { | |
12735 // Start a new wrapper for possible children | |
12736 currentWrapElm = 0; | |
12737 | |
12738 each(tinymce.grep(node.childNodes), process); | |
12739 | |
12740 // End the last wrapper | |
12741 currentWrapElm = 0; | |
12742 } | |
12743 }; | |
12744 | |
12745 // Process siblings from range | |
12746 each(nodes, process); | |
12747 }); | |
12748 | |
12749 // Cleanup | |
12750 each(newWrappers, function(node) { | |
12751 var childCount; | |
12752 | |
12753 function getChildCount(node) { | |
12754 var count = 0; | |
12755 | |
12756 each(node.childNodes, function(node) { | |
12757 if (!isWhiteSpaceNode(node) && !isBookmarkNode(node)) | |
12758 count++; | |
12759 }); | |
12760 | |
12761 return count; | |
12762 }; | |
12763 | |
12764 function mergeStyles(node) { | |
12765 var child, clone; | |
12766 | |
12767 each(node.childNodes, function(node) { | |
12768 if (node.nodeType == 1 && !isBookmarkNode(node) && !isCaretNode(node)) { | |
12769 child = node; | |
12770 return FALSE; // break loop | |
12771 } | |
12772 }); | |
12773 | |
12774 // If child was found and of the same type as the current node | |
12775 if (child && matchName(child, format)) { | |
12776 clone = child.cloneNode(FALSE); | |
12777 setElementFormat(clone); | |
12778 | |
12779 dom.replace(clone, node, TRUE); | |
12780 dom.remove(child, 1); | |
12781 } | |
12782 | |
12783 return clone || node; | |
12784 }; | |
12785 | |
12786 childCount = getChildCount(node); | |
12787 | |
12788 // Remove empty nodes | |
12789 if (childCount === 0) { | |
12790 dom.remove(node, 1); | |
12791 return; | |
12792 } | |
12793 | |
12794 if (format.inline || format.wrapper) { | |
12795 // Merges the current node with it's children of similar type to reduce the number of elements | |
12796 if (!format.exact && childCount === 1) | |
12797 node = mergeStyles(node); | |
12798 | |
12799 // Remove/merge children | |
12800 each(formatList, function(format) { | |
12801 // Merge all children of similar type will move styles from child to parent | |
12802 // this: <span style="color:red"><b><span style="color:red; font-size:10px">text</span></b></span> | |
12803 // will become: <span style="color:red"><b><span style="font-size:10px">text</span></b></span> | |
12804 each(dom.select(format.inline, node), function(child) { | |
12805 removeFormat(format, vars, child, format.exact ? child : null); | |
12806 }); | |
12807 }); | |
12808 | |
12809 // Look for parent with similar style format | |
12810 dom.getParent(node.parentNode, function(parent) { | |
12811 if (matchNode(parent, name, vars)) { | |
12812 dom.remove(node, 1); | |
12813 node = 0; | |
12814 return TRUE; | |
12815 } | |
12816 }); | |
12817 | |
12818 // Merge next and previous siblings if they are similar <b>text</b><b>text</b> becomes <b>texttext</b> | |
12819 if (node) { | |
12820 node = mergeSiblings(getNonWhiteSpaceSibling(node), node); | |
12821 node = mergeSiblings(node, getNonWhiteSpaceSibling(node, TRUE)); | |
12822 } | |
12823 } | |
12824 }); | |
12825 }; | |
12826 | |
12827 if (format) { | |
12828 if (node) { | |
12829 rng = dom.createRng(); | |
12830 | |
12831 rng.setStartBefore(node); | |
12832 rng.setEndAfter(node); | |
12833 | |
12834 applyRngStyle(rng); | |
12835 } else { | |
12836 if (!selection.isCollapsed() || !format.inline) { | |
12837 // Apply formatting to selection | |
12838 bookmark = selection.getBookmark(); | |
12839 applyRngStyle(expandRng(selection.getRng(TRUE), formatList)); | |
12840 | |
12841 selection.moveToBookmark(bookmark); | |
12842 selection.setRng(moveStart(selection.getRng(TRUE))); | |
12843 ed.nodeChanged(); | |
12844 } else | |
12845 performCaretAction('apply', name, vars); | |
12846 } | |
12847 } | |
12848 }; | |
12849 | |
12850 function remove(name, vars, node) { | |
12851 var formatList = get(name), format = formatList[0], bookmark, i, rng; | |
12852 | |
12853 // Merges the styles for each node | |
12854 function process(node) { | |
12855 var children, i, l; | |
12856 | |
12857 // Grab the children first since the nodelist might be changed | |
12858 children = tinymce.grep(node.childNodes); | |
12859 | |
12860 // Process current node | |
12861 for (i = 0, l = formatList.length; i < l; i++) { | |
12862 if (removeFormat(formatList[i], vars, node, node)) | |
12863 break; | |
12864 } | |
12865 | |
12866 // Process the children | |
12867 if (format.deep) { | |
12868 for (i = 0, l = children.length; i < l; i++) | |
12869 process(children[i]); | |
12870 } | |
12871 }; | |
12872 | |
12873 function findFormatRoot(container) { | |
12874 var formatRoot; | |
12875 | |
12876 // Find format root | |
12877 each(getParents(container.parentNode).reverse(), function(parent) { | |
12878 var format; | |
12879 | |
12880 // Find format root element | |
12881 if (!formatRoot && parent.id != '_start' && parent.id != '_end') { | |
12882 // Is the node matching the format we are looking for | |
12883 format = matchNode(parent, name, vars); | |
12884 if (format && format.split !== false) | |
12885 formatRoot = parent; | |
12886 } | |
12887 }); | |
12888 | |
12889 return formatRoot; | |
12890 }; | |
12891 | |
12892 function wrapAndSplit(format_root, container, target, split) { | |
12893 var parent, clone, lastClone, firstClone, i, formatRootParent; | |
12894 | |
12895 // Format root found then clone formats and split it | |
12896 if (format_root) { | |
12897 formatRootParent = format_root.parentNode; | |
12898 | |
12899 for (parent = container.parentNode; parent && parent != formatRootParent; parent = parent.parentNode) { | |
12900 clone = parent.cloneNode(FALSE); | |
12901 | |
12902 for (i = 0; i < formatList.length; i++) { | |
12903 if (removeFormat(formatList[i], vars, clone, clone)) { | |
12904 clone = 0; | |
12905 break; | |
12906 } | |
12907 } | |
12908 | |
12909 // Build wrapper node | |
12910 if (clone) { | |
12911 if (lastClone) | |
12912 clone.appendChild(lastClone); | |
12913 | |
12914 if (!firstClone) | |
12915 firstClone = clone; | |
12916 | |
12917 lastClone = clone; | |
12918 } | |
12919 } | |
12920 | |
12921 // Never split block elements if the format is mixed | |
12922 if (split && (!format.mixed || !isBlock(format_root))) | |
12923 container = dom.split(format_root, container); | |
12924 | |
12925 // Wrap container in cloned formats | |
12926 if (lastClone) { | |
12927 target.parentNode.insertBefore(lastClone, target); | |
12928 firstClone.appendChild(target); | |
12929 } | |
12930 } | |
12931 | |
12932 return container; | |
12933 }; | |
12934 | |
12935 function splitToFormatRoot(container) { | |
12936 return wrapAndSplit(findFormatRoot(container), container, container, true); | |
12937 }; | |
12938 | |
12939 function unwrap(start) { | |
12940 var node = dom.get(start ? '_start' : '_end'), | |
12941 out = node[start ? 'firstChild' : 'lastChild']; | |
12942 | |
12943 dom.remove(node, 1); | |
12944 | |
12945 return out; | |
12946 }; | |
12947 | |
12948 function removeRngStyle(rng) { | |
12949 var startContainer, endContainer; | |
12950 | |
12951 rng = expandRng(rng, formatList, TRUE); | |
12952 | |
12953 if (format.split) { | |
12954 startContainer = getContainer(rng, TRUE); | |
12955 endContainer = getContainer(rng); | |
12956 | |
12957 if (startContainer != endContainer) { | |
12958 // Wrap start/end nodes in span element since these might be cloned/moved | |
12959 startContainer = wrap(startContainer, 'span', {id : '_start', _mce_type : 'bookmark'}); | |
12960 endContainer = wrap(endContainer, 'span', {id : '_end', _mce_type : 'bookmark'}); | |
12961 | |
12962 // Split start/end | |
12963 splitToFormatRoot(startContainer); | |
12964 splitToFormatRoot(endContainer); | |
12965 | |
12966 // Unwrap start/end to get real elements again | |
12967 startContainer = unwrap(TRUE); | |
12968 endContainer = unwrap(); | |
12969 } else | |
12970 startContainer = endContainer = splitToFormatRoot(startContainer); | |
12971 | |
12972 // Update range positions since they might have changed after the split operations | |
12973 rng.startContainer = startContainer.parentNode; | |
12974 rng.startOffset = nodeIndex(startContainer); | |
12975 rng.endContainer = endContainer.parentNode; | |
12976 rng.endOffset = nodeIndex(endContainer) + 1; | |
12977 } | |
12978 | |
12979 // Remove items between start/end | |
12980 rangeUtils.walk(rng, function(nodes) { | |
12981 each(nodes, function(node) { | |
12982 process(node); | |
12983 }); | |
12984 }); | |
12985 }; | |
12986 | |
12987 // Handle node | |
12988 if (node) { | |
12989 rng = dom.createRng(); | |
12990 rng.setStartBefore(node); | |
12991 rng.setEndAfter(node); | |
12992 removeRngStyle(rng); | |
12993 return; | |
12994 } | |
12995 | |
12996 if (!selection.isCollapsed() || !format.inline) { | |
12997 bookmark = selection.getBookmark(); | |
12998 removeRngStyle(selection.getRng(TRUE)); | |
12999 selection.moveToBookmark(bookmark); | |
13000 ed.nodeChanged(); | |
12675 } else | 13001 } else |
12676 ed.selection.moveToBookmark(bm); | 13002 performCaretAction('remove', name, vars); |
12677 | 13003 }; |
12678 return; | 13004 |
13005 function toggle(name, vars, node) { | |
13006 if (match(name, vars, node)) | |
13007 remove(name, vars, node); | |
13008 else | |
13009 apply(name, vars, node); | |
13010 }; | |
13011 | |
13012 function matchNode(node, name, vars) { | |
13013 var formatList = get(name), format, i, classes; | |
13014 | |
13015 function matchItems(node, format, item_name) { | |
13016 var key, value, items = format[item_name], i; | |
13017 | |
13018 // Check all items | |
13019 if (items) { | |
13020 // Non indexed object | |
13021 if (items.length === undefined) { | |
13022 for (key in items) { | |
13023 if (items.hasOwnProperty(key)) { | |
13024 if (item_name === 'attributes') | |
13025 value = dom.getAttrib(node, key); | |
13026 else | |
13027 value = getStyle(node, key); | |
13028 | |
13029 if (!isEq(value, replaceVars(items[key], vars))) | |
13030 return; | |
13031 } | |
13032 } | |
13033 } else { | |
13034 // Only one match needed for indexed arrays | |
13035 for (i = 0; i < items.length; i++) { | |
13036 if (item_name === 'attributes' ? dom.getAttrib(node, items[i]) : getStyle(node, items[i])) | |
13037 return format; | |
13038 } | |
13039 } | |
13040 } | |
13041 | |
13042 return format; | |
13043 }; | |
13044 | |
13045 if (formatList && node) { | |
13046 // Check each format in list | |
13047 for (i = 0; i < formatList.length; i++) { | |
13048 format = formatList[i]; | |
13049 | |
13050 // Name name, attributes, styles and classes | |
13051 if (matchName(node, format) && matchItems(node, format, 'attributes') && matchItems(node, format, 'styles')) { | |
13052 // Match classes | |
13053 if (classes = format.classes) { | |
13054 for (i = 0; i < classes.length; i++) { | |
13055 if (!dom.hasClass(node, classes[i])) | |
13056 return; | |
13057 } | |
13058 } | |
13059 | |
13060 return format; | |
13061 } | |
13062 } | |
13063 } | |
13064 }; | |
13065 | |
13066 function match(name, vars, node) { | |
13067 var startNode, i; | |
13068 | |
13069 function matchParents(node) { | |
13070 // Find first node with similar format settings | |
13071 node = dom.getParent(node, function(node) { | |
13072 return !!matchNode(node, name, vars); | |
13073 }); | |
13074 | |
13075 // Do an exact check on the similar format element | |
13076 return matchNode(node, name, vars); | |
13077 }; | |
13078 | |
13079 // Check specified node | |
13080 if (node) | |
13081 return matchParents(node); | |
13082 | |
13083 // Check pending formats | |
13084 if (selection.isCollapsed()) { | |
13085 for (i = pendingFormats.apply.length - 1; i >= 0; i--) { | |
13086 if (pendingFormats.apply[i].name == name) | |
13087 return true; | |
13088 } | |
13089 | |
13090 for (i = pendingFormats.remove.length - 1; i >= 0; i--) { | |
13091 if (pendingFormats.remove[i].name == name) | |
13092 return false; | |
13093 } | |
13094 | |
13095 return matchParents(selection.getNode()); | |
13096 } | |
13097 | |
13098 // Check selected node | |
13099 node = selection.getNode(); | |
13100 if (matchParents(node)) | |
13101 return TRUE; | |
13102 | |
13103 // Check start node if it's different | |
13104 startNode = selection.getStart(); | |
13105 if (startNode != node) { | |
13106 if (matchParents(startNode)) | |
13107 return TRUE; | |
13108 } | |
13109 | |
13110 return FALSE; | |
13111 }; | |
13112 | |
13113 function matchAll(names, vars) { | |
13114 var startElement, matchedFormatNames = [], checkedMap = {}, i, ni, name; | |
13115 | |
13116 // If the selection is collapsed then check pending formats | |
13117 if (selection.isCollapsed()) { | |
13118 for (ni = 0; ni < names.length; ni++) { | |
13119 // If the name is to be removed, then stop it from being added | |
13120 for (i = pendingFormats.remove.length - 1; i >= 0; i--) { | |
13121 name = names[ni]; | |
13122 | |
13123 if (pendingFormats.remove[i].name == name) { | |
13124 checkedMap[name] = true; | |
13125 break; | |
13126 } | |
13127 } | |
13128 } | |
13129 | |
13130 // If the format is to be applied | |
13131 for (i = pendingFormats.apply.length - 1; i >= 0; i--) { | |
13132 for (ni = 0; ni < names.length; ni++) { | |
13133 name = names[ni]; | |
13134 | |
13135 if (!checkedMap[name] && pendingFormats.apply[i].name == name) { | |
13136 checkedMap[name] = true; | |
13137 matchedFormatNames.push(name); | |
13138 } | |
13139 } | |
13140 } | |
13141 } | |
13142 | |
13143 // Check start of selection for formats | |
13144 startElement = selection.getStart(); | |
13145 dom.getParent(startElement, function(node) { | |
13146 var i, name; | |
13147 | |
13148 for (i = 0; i < names.length; i++) { | |
13149 name = names[i]; | |
13150 | |
13151 if (!checkedMap[name] && matchNode(node, name, vars)) { | |
13152 checkedMap[name] = true; | |
13153 matchedFormatNames.push(name); | |
13154 } | |
13155 } | |
13156 }); | |
13157 | |
13158 return matchedFormatNames; | |
13159 }; | |
13160 | |
13161 function canApply(name) { | |
13162 var formatList = get(name), startNode, parents, i, x, selector; | |
13163 | |
13164 if (formatList) { | |
13165 startNode = selection.getStart(); | |
13166 parents = getParents(startNode); | |
13167 | |
13168 for (x = formatList.length - 1; x >= 0; x--) { | |
13169 selector = formatList[x].selector; | |
13170 | |
13171 // Format is not selector based, then always return TRUE | |
13172 if (!selector) | |
13173 return TRUE; | |
13174 | |
13175 for (i = parents.length - 1; i >= 0; i--) { | |
13176 if (dom.is(parents[i], selector)) | |
13177 return TRUE; | |
13178 } | |
13179 } | |
13180 } | |
13181 | |
13182 return FALSE; | |
13183 }; | |
13184 | |
13185 // Expose to public | |
13186 tinymce.extend(this, { | |
13187 get : get, | |
13188 register : register, | |
13189 apply : apply, | |
13190 remove : remove, | |
13191 toggle : toggle, | |
13192 match : match, | |
13193 matchAll : matchAll, | |
13194 matchNode : matchNode, | |
13195 canApply : canApply | |
13196 }); | |
13197 | |
13198 // Private functions | |
13199 | |
13200 function matchName(node, format) { | |
13201 // Check for inline match | |
13202 if (isEq(node, format.inline)) | |
13203 return TRUE; | |
13204 | |
13205 // Check for block match | |
13206 if (isEq(node, format.block)) | |
13207 return TRUE; | |
13208 | |
13209 // Check for selector match | |
13210 if (format.selector) | |
13211 return dom.is(node, format.selector); | |
13212 }; | |
13213 | |
13214 function isEq(str1, str2) { | |
13215 str1 = str1 || ''; | |
13216 str2 = str2 || ''; | |
13217 | |
13218 str1 = '' + (str1.nodeName || str1); | |
13219 str2 = '' + (str2.nodeName || str2); | |
13220 | |
13221 return str1.toLowerCase() == str2.toLowerCase(); | |
13222 }; | |
13223 | |
13224 function getStyle(node, name) { | |
13225 var styleVal = dom.getStyle(node, name); | |
13226 | |
13227 // Force the format to hex | |
13228 if (name == 'color' || name == 'backgroundColor') | |
13229 styleVal = dom.toHex(styleVal); | |
13230 | |
13231 // Opera will return bold as 700 | |
13232 if (name == 'fontWeight' && styleVal == 700) | |
13233 styleVal = 'bold'; | |
13234 | |
13235 return '' + styleVal; | |
13236 }; | |
13237 | |
13238 function replaceVars(value, vars) { | |
13239 if (typeof(value) != "string") | |
13240 value = value(vars); | |
13241 else if (vars) { | |
13242 value = value.replace(/%(\w+)/g, function(str, name) { | |
13243 return vars[name] || str; | |
13244 }); | |
13245 } | |
13246 | |
13247 return value; | |
13248 }; | |
13249 | |
13250 function isWhiteSpaceNode(node) { | |
13251 return node && node.nodeType === 3 && /^\s*$/.test(node.nodeValue); | |
13252 }; | |
13253 | |
13254 function wrap(node, name, attrs) { | |
13255 var wrapper = dom.create(name, attrs); | |
13256 | |
13257 node.parentNode.insertBefore(wrapper, node); | |
13258 wrapper.appendChild(node); | |
13259 | |
13260 return wrapper; | |
13261 }; | |
13262 | |
13263 function expandRng(rng, format, remove) { | |
13264 var startContainer = rng.startContainer, | |
13265 startOffset = rng.startOffset, | |
13266 endContainer = rng.endContainer, | |
13267 endOffset = rng.endOffset, sibling, lastIdx; | |
13268 | |
13269 // This function walks up the tree if there is no siblings before/after the node | |
13270 function findParentContainer(container, child_name, sibling_name, root) { | |
13271 var parent, child; | |
13272 | |
13273 root = root || dom.getRoot(); | |
13274 | |
13275 for (;;) { | |
13276 // Check if we can move up are we at root level or body level | |
13277 parent = container.parentNode; | |
13278 | |
13279 // Stop expanding on block elements or root depending on format | |
13280 if (parent == root || (!format[0].block_expand && isBlock(parent))) | |
13281 return container; | |
13282 | |
13283 for (sibling = parent[child_name]; sibling && sibling != container; sibling = sibling[sibling_name]) { | |
13284 if (sibling.nodeType == 1 && !isBookmarkNode(sibling)) | |
13285 return container; | |
13286 | |
13287 if (sibling.nodeType == 3 && !isWhiteSpaceNode(sibling)) | |
13288 return container; | |
13289 } | |
13290 | |
13291 container = container.parentNode; | |
13292 } | |
13293 | |
13294 return container; | |
13295 }; | |
13296 | |
13297 // If index based start position then resolve it | |
13298 if (startContainer.nodeType == 1 && startContainer.hasChildNodes()) { | |
13299 lastIdx = startContainer.childNodes.length - 1; | |
13300 startContainer = startContainer.childNodes[startOffset > lastIdx ? lastIdx : startOffset]; | |
13301 | |
13302 if (startContainer.nodeType == 3) | |
13303 startOffset = 0; | |
13304 } | |
13305 | |
13306 // If index based end position then resolve it | |
13307 if (endContainer.nodeType == 1 && endContainer.hasChildNodes()) { | |
13308 lastIdx = endContainer.childNodes.length - 1; | |
13309 endContainer = endContainer.childNodes[endOffset > lastIdx ? lastIdx : endOffset - 1]; | |
13310 | |
13311 if (endContainer.nodeType == 3) | |
13312 endOffset = endContainer.nodeValue.length; | |
13313 } | |
13314 | |
13315 // Exclude bookmark nodes if possible | |
13316 if (isBookmarkNode(startContainer.parentNode)) | |
13317 startContainer = startContainer.parentNode; | |
13318 | |
13319 if (isBookmarkNode(startContainer)) | |
13320 startContainer = startContainer.nextSibling || startContainer; | |
13321 | |
13322 if (isBookmarkNode(endContainer.parentNode)) | |
13323 endContainer = endContainer.parentNode; | |
13324 | |
13325 if (isBookmarkNode(endContainer)) | |
13326 endContainer = endContainer.previousSibling || endContainer; | |
13327 | |
13328 // Move start/end point up the tree if the leaves are sharp and if we are in different containers | |
13329 // Example * becomes !: !<p><b><i>*text</i><i>text*</i></b></p>! | |
13330 // This will reduce the number of wrapper elements that needs to be created | |
13331 // Move start point up the tree | |
13332 if (format[0].inline || format[0].block_expand) { | |
13333 startContainer = findParentContainer(startContainer, 'firstChild', 'nextSibling'); | |
13334 endContainer = findParentContainer(endContainer, 'lastChild', 'previousSibling'); | |
13335 } | |
13336 | |
13337 // Expand start/end container to matching selector | |
13338 if (format[0].selector && format[0].expand !== FALSE && !format[0].inline) { | |
13339 function findSelectorEndPoint(container, sibling_name) { | |
13340 var parents, i, y; | |
13341 | |
13342 if (container.nodeType == 3 && container.nodeValue.length == 0 && container[sibling_name]) | |
13343 container = container[sibling_name]; | |
13344 | |
13345 parents = getParents(container); | |
13346 for (i = 0; i < parents.length; i++) { | |
13347 for (y = 0; y < format.length; y++) { | |
13348 if (dom.is(parents[i], format[y].selector)) | |
13349 return parents[i]; | |
13350 } | |
13351 } | |
13352 | |
13353 return container; | |
13354 }; | |
13355 | |
13356 // Find new startContainer/endContainer if there is better one | |
13357 startContainer = findSelectorEndPoint(startContainer, 'previousSibling'); | |
13358 endContainer = findSelectorEndPoint(endContainer, 'nextSibling'); | |
13359 } | |
13360 | |
13361 // Expand start/end container to matching block element or text node | |
13362 if (format[0].block || format[0].selector) { | |
13363 function findBlockEndPoint(container, sibling_name, sibling_name2) { | |
13364 var node; | |
13365 | |
13366 // Expand to block of similar type | |
13367 if (!format[0].wrapper) | |
13368 node = dom.getParent(container, format[0].block); | |
13369 | |
13370 // Expand to first wrappable block element or any block element | |
13371 if (!node) | |
13372 node = dom.getParent(container.nodeType == 3 ? container.parentNode : container, isBlock); | |
13373 | |
13374 // Exclude inner lists from wrapping | |
13375 if (node && format[0].wrapper) | |
13376 node = getParents(node, 'ul,ol').reverse()[0] || node; | |
13377 | |
13378 // Didn't find a block element look for first/last wrappable element | |
13379 if (!node) { | |
13380 node = container; | |
13381 | |
13382 while (node[sibling_name] && !isBlock(node[sibling_name])) { | |
13383 node = node[sibling_name]; | |
13384 | |
13385 // Break on BR but include it will be removed later on | |
13386 // we can't remove it now since we need to check if it can be wrapped | |
13387 if (isEq(node, 'br')) | |
13388 break; | |
13389 } | |
13390 } | |
13391 | |
13392 return node || container; | |
13393 }; | |
13394 | |
13395 // Find new startContainer/endContainer if there is better one | |
13396 startContainer = findBlockEndPoint(startContainer, 'previousSibling'); | |
13397 endContainer = findBlockEndPoint(endContainer, 'nextSibling'); | |
13398 | |
13399 // Non block element then try to expand up the leaf | |
13400 if (format[0].block) { | |
13401 if (!isBlock(startContainer)) | |
13402 startContainer = findParentContainer(startContainer, 'firstChild', 'nextSibling'); | |
13403 | |
13404 if (!isBlock(endContainer)) | |
13405 endContainer = findParentContainer(endContainer, 'lastChild', 'previousSibling'); | |
13406 } | |
13407 } | |
13408 | |
13409 // Setup index for startContainer | |
13410 if (startContainer.nodeType == 1) { | |
13411 startOffset = nodeIndex(startContainer); | |
13412 startContainer = startContainer.parentNode; | |
13413 } | |
13414 | |
13415 // Setup index for endContainer | |
13416 if (endContainer.nodeType == 1) { | |
13417 endOffset = nodeIndex(endContainer) + 1; | |
13418 endContainer = endContainer.parentNode; | |
13419 } | |
13420 | |
13421 // Return new range like object | |
13422 return { | |
13423 startContainer : startContainer, | |
13424 startOffset : startOffset, | |
13425 endContainer : endContainer, | |
13426 endOffset : endOffset | |
13427 }; | |
12679 } | 13428 } |
12680 | 13429 |
12681 // Since IE can start with a totally empty document we need to add the first bq and paragraph | 13430 function removeFormat(format, vars, node, compare_node) { |
12682 if (tinymce.isIE && !sb && !eb) { | 13431 var i, attrs, stylesModified; |
12683 ed.getDoc().execCommand('Indent'); | 13432 |
12684 n = getBQ(s.getNode()); | 13433 // Check if node matches format |
12685 n.style.margin = n.dir = ''; // IE adds margin and dir to bq | 13434 if (!matchName(node, format)) |
12686 return; | 13435 return FALSE; |
12687 } | 13436 |
12688 | 13437 // Should we compare with format attribs and styles |
12689 if (!sb || !eb) | 13438 if (format.remove != 'all') { |
12690 return; | 13439 // Remove styles |
12691 | 13440 each(format.styles, function(value, name) { |
12692 // If empty paragraph node then do not use bookmark | 13441 value = replaceVars(value, vars); |
12693 if (sb != eb || sb.childNodes.length > 1 || (sb.childNodes.length == 1 && sb.firstChild.nodeName != 'BR')) | 13442 |
12694 bm = s.getBookmark(); | 13443 // Indexed array |
12695 | 13444 if (typeof(name) === 'number') { |
12696 // Move selected block elements into a bq | 13445 name = value; |
12697 tinymce.each(s.getSelectedBlocks(getBQ(s.getStart()), getBQ(s.getEnd())), function(e) { | 13446 compare_node = 0; |
12698 // Found existing BQ add to this one | 13447 } |
12699 if (e.nodeName == 'BLOCKQUOTE' && !bq) { | 13448 |
12700 bq = e; | 13449 if (!compare_node || isEq(getStyle(compare_node, name), value)) |
13450 dom.setStyle(node, name, ''); | |
13451 | |
13452 stylesModified = 1; | |
13453 }); | |
13454 | |
13455 // Remove style attribute if it's empty | |
13456 if (stylesModified && dom.getAttrib(node, 'style') == '') { | |
13457 node.removeAttribute('style'); | |
13458 node.removeAttribute('_mce_style'); | |
13459 } | |
13460 | |
13461 // Remove attributes | |
13462 each(format.attributes, function(value, name) { | |
13463 var valueOut; | |
13464 | |
13465 value = replaceVars(value, vars); | |
13466 | |
13467 // Indexed array | |
13468 if (typeof(name) === 'number') { | |
13469 name = value; | |
13470 compare_node = 0; | |
13471 } | |
13472 | |
13473 if (!compare_node || isEq(dom.getAttrib(compare_node, name), value)) { | |
13474 // Keep internal classes | |
13475 if (name == 'class') { | |
13476 value = dom.getAttrib(node, name); | |
13477 if (value) { | |
13478 // Build new class value where everything is removed except the internal prefixed classes | |
13479 valueOut = ''; | |
13480 each(value.split(/\s+/), function(cls) { | |
13481 if (/mce\w+/.test(cls)) | |
13482 valueOut += (valueOut ? ' ' : '') + cls; | |
13483 }); | |
13484 | |
13485 // We got some internal classes left | |
13486 if (valueOut) { | |
13487 dom.setAttrib(node, name, valueOut); | |
13488 return; | |
13489 } | |
13490 } | |
13491 } | |
13492 | |
13493 // IE6 has a bug where the attribute doesn't get removed correctly | |
13494 if (name == "class") | |
13495 node.removeAttribute('className'); | |
13496 | |
13497 // Remove mce prefixed attributes | |
13498 if (MCE_ATTR_RE.test(name)) | |
13499 node.removeAttribute('_mce_' + name); | |
13500 | |
13501 node.removeAttribute(name); | |
13502 } | |
13503 }); | |
13504 | |
13505 // Remove classes | |
13506 each(format.classes, function(value) { | |
13507 value = replaceVars(value, vars); | |
13508 | |
13509 if (!compare_node || dom.hasClass(compare_node, value)) | |
13510 dom.removeClass(node, value); | |
13511 }); | |
13512 | |
13513 // Check for non internal attributes | |
13514 attrs = dom.getAttribs(node); | |
13515 for (i = 0; i < attrs.length; i++) { | |
13516 if (attrs[i].nodeName.indexOf('_') !== 0) | |
13517 return FALSE; | |
13518 } | |
13519 } | |
13520 | |
13521 // Remove the inline child if it's empty for example <b> or <span> | |
13522 if (format.remove != 'none') { | |
13523 removeNode(node, format); | |
13524 return TRUE; | |
13525 } | |
13526 }; | |
13527 | |
13528 function removeNode(node, format) { | |
13529 var parentNode = node.parentNode, rootBlockElm; | |
13530 | |
13531 if (format.block) { | |
13532 if (!forcedRootBlock) { | |
13533 function find(node, next, inc) { | |
13534 node = getNonWhiteSpaceSibling(node, next, inc); | |
13535 | |
13536 return !node || (node.nodeName == 'BR' || isBlock(node)); | |
13537 }; | |
13538 | |
13539 // Append BR elements if needed before we remove the block | |
13540 if (isBlock(node) && !isBlock(parentNode)) { | |
13541 if (!find(node, FALSE) && !find(node.firstChild, TRUE, 1)) | |
13542 node.insertBefore(dom.create('br'), node.firstChild); | |
13543 | |
13544 if (!find(node, TRUE) && !find(node.lastChild, FALSE, 1)) | |
13545 node.appendChild(dom.create('br')); | |
13546 } | |
13547 } else { | |
13548 // Wrap the block in a forcedRootBlock if we are at the root of document | |
13549 if (parentNode == dom.getRoot()) { | |
13550 if (!format.list_block || !isEq(node, format.list_block)) { | |
13551 each(tinymce.grep(node.childNodes), function(node) { | |
13552 if (isValid(forcedRootBlock, node.nodeName.toLowerCase())) { | |
13553 if (!rootBlockElm) | |
13554 rootBlockElm = wrap(node, forcedRootBlock); | |
13555 else | |
13556 rootBlockElm.appendChild(node); | |
13557 } else | |
13558 rootBlockElm = 0; | |
13559 }); | |
13560 } | |
13561 } | |
13562 } | |
13563 } | |
13564 | |
13565 // Never remove nodes that isn't the specified inline element if a selector is specified too | |
13566 if (format.selector && format.inline && !isEq(format.inline, node)) | |
12701 return; | 13567 return; |
12702 } | 13568 |
12703 | 13569 dom.remove(node, 1); |
12704 // No BQ found, create one | 13570 }; |
12705 if (!bq) { | 13571 |
12706 bq = dom.create('blockquote'); | 13572 function getNonWhiteSpaceSibling(node, next, inc) { |
12707 e.parentNode.insertBefore(bq, e); | 13573 if (node) { |
12708 } | 13574 next = next ? 'nextSibling' : 'previousSibling'; |
12709 | 13575 |
12710 // Add children from existing BQ | 13576 for (node = inc ? node : node[next]; node; node = node[next]) { |
12711 if (e.nodeName == 'BLOCKQUOTE' && bq) { | 13577 if (node.nodeType == 1 || !isWhiteSpaceNode(node)) |
12712 n = e.firstChild; | 13578 return node; |
12713 | 13579 } |
12714 while (n) { | 13580 } |
12715 bq.appendChild(n.cloneNode(true)); | 13581 }; |
12716 n = n.nextSibling; | 13582 |
12717 } | 13583 function isBookmarkNode(node) { |
12718 | 13584 return node && node.nodeType == 1 && node.getAttribute('_mce_type') == 'bookmark'; |
12719 dom.remove(e); | 13585 }; |
12720 return; | 13586 |
12721 } | 13587 function mergeSiblings(prev, next) { |
12722 | 13588 var marker, sibling, tmpSibling; |
12723 // Add non BQ element to BQ | 13589 |
12724 bq.appendChild(dom.remove(e)); | 13590 function compareElements(node1, node2) { |
13591 // Not the same name | |
13592 if (node1.nodeName != node2.nodeName) | |
13593 return FALSE; | |
13594 | |
13595 function getAttribs(node) { | |
13596 var attribs = {}; | |
13597 | |
13598 each(dom.getAttribs(node), function(attr) { | |
13599 var name = attr.nodeName.toLowerCase(); | |
13600 | |
13601 // Don't compare internal attributes or style | |
13602 if (name.indexOf('_') !== 0 && name !== 'style') | |
13603 attribs[name] = dom.getAttrib(node, name); | |
13604 }); | |
13605 | |
13606 return attribs; | |
13607 }; | |
13608 | |
13609 function compareObjects(obj1, obj2) { | |
13610 var value, name; | |
13611 | |
13612 for (name in obj1) { | |
13613 // Obj1 has item obj2 doesn't have | |
13614 if (obj1.hasOwnProperty(name)) { | |
13615 value = obj2[name]; | |
13616 | |
13617 // Obj2 doesn't have obj1 item | |
13618 if (value === undefined) | |
13619 return FALSE; | |
13620 | |
13621 // Obj2 item has a different value | |
13622 if (obj1[name] != value) | |
13623 return FALSE; | |
13624 | |
13625 // Delete similar value | |
13626 delete obj2[name]; | |
13627 } | |
13628 } | |
13629 | |
13630 // Check if obj 2 has something obj 1 doesn't have | |
13631 for (name in obj2) { | |
13632 // Obj2 has item obj1 doesn't have | |
13633 if (obj2.hasOwnProperty(name)) | |
13634 return FALSE; | |
13635 } | |
13636 | |
13637 return TRUE; | |
13638 }; | |
13639 | |
13640 // Attribs are not the same | |
13641 if (!compareObjects(getAttribs(node1), getAttribs(node2))) | |
13642 return FALSE; | |
13643 | |
13644 // Styles are not the same | |
13645 if (!compareObjects(dom.parseStyle(dom.getAttrib(node1, 'style')), dom.parseStyle(dom.getAttrib(node2, 'style')))) | |
13646 return FALSE; | |
13647 | |
13648 return TRUE; | |
13649 }; | |
13650 | |
13651 // Check if next/prev exists and that they are elements | |
13652 if (prev && next) { | |
13653 function findElementSibling(node, sibling_name) { | |
13654 for (sibling = node; sibling; sibling = sibling[sibling_name]) { | |
13655 if (sibling.nodeType == 3 && !isWhiteSpaceNode(sibling)) | |
13656 return node; | |
13657 | |
13658 if (sibling.nodeType == 1 && !isBookmarkNode(sibling)) | |
13659 return sibling; | |
13660 } | |
13661 | |
13662 return node; | |
13663 }; | |
13664 | |
13665 // If previous sibling is empty then jump over it | |
13666 prev = findElementSibling(prev, 'previousSibling'); | |
13667 next = findElementSibling(next, 'nextSibling'); | |
13668 | |
13669 // Compare next and previous nodes | |
13670 if (compareElements(prev, next)) { | |
13671 // Append nodes between | |
13672 for (sibling = prev.nextSibling; sibling && sibling != next;) { | |
13673 tmpSibling = sibling; | |
13674 sibling = sibling.nextSibling; | |
13675 prev.appendChild(tmpSibling); | |
13676 } | |
13677 | |
13678 // Remove next node | |
13679 dom.remove(next); | |
13680 | |
13681 // Move children into prev node | |
13682 each(tinymce.grep(next.childNodes), function(node) { | |
13683 prev.appendChild(node); | |
13684 }); | |
13685 | |
13686 return prev; | |
13687 } | |
13688 } | |
13689 | |
13690 return next; | |
13691 }; | |
13692 | |
13693 function isTextBlock(name) { | |
13694 return /^(h[1-6]|p|div|pre|address)$/.test(name); | |
13695 }; | |
13696 | |
13697 function getContainer(rng, start) { | |
13698 var container, offset, lastIdx; | |
13699 | |
13700 container = rng[start ? 'startContainer' : 'endContainer']; | |
13701 offset = rng[start ? 'startOffset' : 'endOffset']; | |
13702 | |
13703 if (container.nodeType == 1) { | |
13704 lastIdx = container.childNodes.length - 1; | |
13705 | |
13706 if (!start && offset) | |
13707 offset--; | |
13708 | |
13709 container = container.childNodes[offset > lastIdx ? lastIdx : offset]; | |
13710 } | |
13711 | |
13712 return container; | |
13713 }; | |
13714 | |
13715 function performCaretAction(type, name, vars) { | |
13716 var i, currentPendingFormats = pendingFormats[type], | |
13717 otherPendingFormats = pendingFormats[type == 'apply' ? 'remove' : 'apply']; | |
13718 | |
13719 function hasPending() { | |
13720 return pendingFormats.apply.length || pendingFormats.remove.length; | |
13721 }; | |
13722 | |
13723 function resetPending() { | |
13724 pendingFormats.apply = []; | |
13725 pendingFormats.remove = []; | |
13726 }; | |
13727 | |
13728 function perform(caret_node) { | |
13729 // Apply pending formats | |
13730 each(pendingFormats.apply.reverse(), function(item) { | |
13731 apply(item.name, item.vars, caret_node); | |
13732 }); | |
13733 | |
13734 // Remove pending formats | |
13735 each(pendingFormats.remove.reverse(), function(item) { | |
13736 remove(item.name, item.vars, caret_node); | |
13737 }); | |
13738 | |
13739 dom.remove(caret_node, 1); | |
13740 resetPending(); | |
13741 }; | |
13742 | |
13743 // Check if it already exists then ignore it | |
13744 for (i = currentPendingFormats.length - 1; i >= 0; i--) { | |
13745 if (currentPendingFormats[i].name == name) | |
13746 return; | |
13747 } | |
13748 | |
13749 currentPendingFormats.push({name : name, vars : vars}); | |
13750 | |
13751 // Check if it's in the other type, then remove it | |
13752 for (i = otherPendingFormats.length - 1; i >= 0; i--) { | |
13753 if (otherPendingFormats[i].name == name) | |
13754 otherPendingFormats.splice(i, 1); | |
13755 } | |
13756 | |
13757 // Pending apply or remove formats | |
13758 if (hasPending()) { | |
13759 ed.getDoc().execCommand('FontName', false, 'mceinline'); | |
13760 | |
13761 // IE will convert the current word | |
13762 each(dom.select('font,span'), function(node) { | |
13763 var bookmark; | |
13764 | |
13765 if (isCaretNode(node)) { | |
13766 bookmark = selection.getBookmark(); | |
13767 perform(node); | |
13768 selection.moveToBookmark(bookmark); | |
13769 ed.nodeChanged(); | |
13770 } | |
13771 }); | |
13772 | |
13773 // Only register listeners once if we need to | |
13774 if (!pendingFormats.isListening && hasPending()) { | |
13775 pendingFormats.isListening = true; | |
13776 | |
13777 each('onKeyDown,onKeyUp,onKeyPress,onMouseUp'.split(','), function(event) { | |
13778 ed[event].addToTop(function(ed, e) { | |
13779 if (hasPending()) { | |
13780 each(dom.select('font,span'), function(node) { | |
13781 var bookmark, textNode, rng; | |
13782 | |
13783 // Look for marker | |
13784 if (isCaretNode(node)) { | |
13785 textNode = node.firstChild; | |
13786 | |
13787 perform(node); | |
13788 | |
13789 rng = dom.createRng(); | |
13790 rng.setStart(textNode, textNode.nodeValue.length); | |
13791 rng.setEnd(textNode, textNode.nodeValue.length); | |
13792 selection.setRng(rng); | |
13793 ed.nodeChanged(); | |
13794 } | |
13795 }); | |
13796 | |
13797 // Always unbind and clear pending styles on keyup | |
13798 if (e.type == 'keyup' || e.type == 'mouseup') | |
13799 resetPending(); | |
13800 } | |
13801 }); | |
13802 }); | |
13803 } | |
13804 } | |
13805 }; | |
13806 }; | |
13807 })(tinymce); | |
13808 | |
13809 tinymce.onAddEditor.add(function(tinymce, ed) { | |
13810 var filters, fontSizes, dom, settings = ed.settings; | |
13811 | |
13812 if (settings.inline_styles) { | |
13813 fontSizes = tinymce.explode(settings.font_size_style_values); | |
13814 | |
13815 function replaceWithSpan(node, styles) { | |
13816 dom.replace(dom.create('span', { | |
13817 style : styles | |
13818 }), node, 1); | |
13819 }; | |
13820 | |
13821 filters = { | |
13822 font : function(dom, node) { | |
13823 replaceWithSpan(node, { | |
13824 backgroundColor : node.style.backgroundColor, | |
13825 color : node.color, | |
13826 fontFamily : node.face, | |
13827 fontSize : fontSizes[parseInt(node.size) - 1] | |
13828 }); | |
13829 }, | |
13830 | |
13831 u : function(dom, node) { | |
13832 replaceWithSpan(node, { | |
13833 textDecoration : 'underline' | |
13834 }); | |
13835 }, | |
13836 | |
13837 strike : function(dom, node) { | |
13838 replaceWithSpan(node, { | |
13839 textDecoration : 'line-through' | |
13840 }); | |
13841 } | |
13842 }; | |
13843 | |
13844 function convert(editor, params) { | |
13845 dom = editor.dom; | |
13846 | |
13847 if (settings.convert_fonts_to_spans) { | |
13848 tinymce.each(dom.select('font,u,strike', params.node), function(node) { | |
13849 filters[node.nodeName.toLowerCase()](ed.dom, node); | |
13850 }); | |
13851 } | |
13852 }; | |
13853 | |
13854 ed.onPreProcess.add(convert); | |
13855 | |
13856 ed.onInit.add(function() { | |
13857 ed.selection.onSetContent.add(convert); | |
12725 }); | 13858 }); |
12726 | 13859 } |
12727 if (!bm) { | 13860 }); |
12728 // Move caret inside empty block element | 13861 |
12729 if (!tinymce.isIE) { | |
12730 r = ed.getDoc().createRange(); | |
12731 r.setStart(sb, 0); | |
12732 r.setEnd(sb, 0); | |
12733 s.setRng(r); | |
12734 } else { | |
12735 s.select(sb); | |
12736 s.collapse(1); | |
12737 } | |
12738 } else | |
12739 s.moveToBookmark(bm); | |
12740 }); | |
12741 })(tinymce); | |
12742 (function(tinymce) { | |
12743 tinymce.each(['Cut', 'Copy', 'Paste'], function(cmd) { | |
12744 tinymce.GlobalCommands.add(cmd, function() { | |
12745 var ed = this, doc = ed.getDoc(); | |
12746 | |
12747 try { | |
12748 doc.execCommand(cmd, false, null); | |
12749 | |
12750 // On WebKit the command will just be ignored if it's not enabled | |
12751 if (!doc.queryCommandSupported(cmd)) | |
12752 throw 'Error'; | |
12753 } catch (ex) { | |
12754 ed.windowManager.alert(ed.getLang('clipboard_no_support')); | |
12755 } | |
12756 }); | |
12757 }); | |
12758 })(tinymce); | |
12759 (function(tinymce) { | |
12760 tinymce.GlobalCommands.add('InsertHorizontalRule', function() { | |
12761 if (tinymce.isOpera) | |
12762 return this.getDoc().execCommand('InsertHorizontalRule', false, ''); | |
12763 | |
12764 this.selection.setContent('<hr />'); | |
12765 }); | |
12766 })(tinymce); | |
12767 (function() { | |
12768 var cmds = tinymce.GlobalCommands; | |
12769 | |
12770 cmds.add(['mceEndUndoLevel', 'mceAddUndoLevel'], function() { | |
12771 this.undoManager.add(); | |
12772 }); | |
12773 | |
12774 cmds.add('Undo', function() { | |
12775 var ed = this; | |
12776 | |
12777 if (ed.settings.custom_undo_redo) { | |
12778 ed.undoManager.undo(); | |
12779 ed.nodeChanged(); | |
12780 return true; | |
12781 } | |
12782 | |
12783 return false; // Run browser command | |
12784 }); | |
12785 | |
12786 cmds.add('Redo', function() { | |
12787 var ed = this; | |
12788 | |
12789 if (ed.settings.custom_undo_redo) { | |
12790 ed.undoManager.redo(); | |
12791 ed.nodeChanged(); | |
12792 return true; | |
12793 } | |
12794 | |
12795 return false; // Run browser command | |
12796 }); | |
12797 })(); |