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 = {'&' : '&amp;', '"' : '&quot;', '<' : '&lt;', '>' : '&gt;'},
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">&nbsp;</p>'); 1885 h = h.replace(/<p><\/p>|<p([^>]+)><\/p>|<p[^\/+]\/>/gi, '<p$1 _mce_keep="true">&nbsp;</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(/&apos;/g, '&#39;'); // IE can't handle apos 1967 h = h.replace(/&apos;/g, '&#39;'); // 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, '&#45;&#45;') + '--></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 '&amp;'; 2166 },
2006 2167
2007 case '"': 2168 insertAfter : function(node, reference_node) {
2008 return '&quot;'; 2169 reference_node = this.get(reference_node);
2009 2170
2010 case '<': 2171 return this.run(node, function(node) {
2011 return '&lt;'; 2172 var parent, nextSibling;
2012 2173
2013 case '>': 2174 parent = reference_node.parentNode;
2014 return '&gt;'; 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]+|&nbsp;|&#160;/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 &nbsp; 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, '&gt;'); 5666 h = h.replace(/\%MCGT%/g, '&gt;');
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>&nbsp;<\/p>|<p([^>]+)>&nbsp;<\/p>/g, '<p$1>\u00a0</p>'); 6310 h = h.replace(/<p>&nbsp;<\/p>|<p([^>]+)>&nbsp;<\/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 &nbsp; 6469 // Padd empty nodes with a &nbsp;
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[^>]*>(&nbsp;|&#160;|\s|\u00a0|)<\/p>[\r\n]*|<br \/>[\r\n]*)$/, ''); 9728 o.content = o.content.replace(/^(<p[^>]*>(&nbsp;|&#160;|\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|&#160;|&nbsp;)<\/' + elm + '>', 'gi'); 11399 t.reOpera = new RegExp('(\\u00a0|&#160;|&nbsp;)<\/' + 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()([^>]+)>(&nbsp;|&#160;)<\\\/%p>|<%p>(&nbsp;|&#160;)<\\\/%p>'.replace(/%p/g, elm), 'gi'); 11402 t.reNbsp2BR2 = new RegExp('<%p()([^>]+)>(&nbsp;|&#160;)<\\\/%p>|<%p>(&nbsp;|&#160;)<\\\/%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 &nbsp; instead of BR in padded paragraphs 11412 // Use &nbsp; 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>&nbsp;</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>&nbsp;</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 })();