Lib_Js_TinyMCE - Version 3.3.7.0

Version Notes

3.3.7.0

Download this release

Release Info

Developer Magento Core Team
Extension Lib_Js_TinyMCE
Version 3.3.7.0
Comparing to
See all releases


Code changes from version 3.3.2.0 to 3.3.7.0

Files changed (47) hide show
  1. js/tiny_mce/classes/Editor.js +76 -7
  2. js/tiny_mce/classes/EditorCommands.js +15 -5
  3. js/tiny_mce/classes/ForceBlocks.js +93 -38
  4. js/tiny_mce/classes/Formatter.js +62 -33
  5. js/tiny_mce/classes/adapter/jquery/jquery.tinymce.js +17 -8
  6. js/tiny_mce/classes/dom/DOMUtils.js +9 -6
  7. js/tiny_mce/classes/dom/RangeUtils.js +28 -0
  8. js/tiny_mce/classes/dom/Selection.js +85 -39
  9. js/tiny_mce/classes/dom/Sizzle.js +234 -137
  10. js/tiny_mce/classes/dom/TridentSelection.js +99 -92
  11. js/tiny_mce/classes/tinymce.js +9 -0
  12. js/tiny_mce/classes/ui/ListBox.js +1 -1
  13. js/tiny_mce/jquery.tinymce.js +1 -1
  14. js/tiny_mce/langs/en.js +17 -16
  15. js/tiny_mce/plugins/autoresize/editor_plugin.js +1 -1
  16. js/tiny_mce/plugins/autoresize/editor_plugin_src.js +23 -21
  17. js/tiny_mce/plugins/contextmenu/editor_plugin.js +1 -1
  18. js/tiny_mce/plugins/contextmenu/editor_plugin_src.js +23 -3
  19. js/tiny_mce/plugins/fullpage/editor_plugin.js +1 -1
  20. js/tiny_mce/plugins/fullpage/editor_plugin_src.js +4 -0
  21. js/tiny_mce/plugins/fullscreen/editor_plugin.js +1 -1
  22. js/tiny_mce/plugins/fullscreen/editor_plugin_src.js +6 -3
  23. js/tiny_mce/plugins/legacyoutput/editor_plugin.js +1 -1
  24. js/tiny_mce/plugins/legacyoutput/editor_plugin_src.js +1 -1
  25. js/tiny_mce/plugins/paste/editor_plugin.js +1 -1
  26. js/tiny_mce/plugins/paste/editor_plugin_src.js +18 -7
  27. js/tiny_mce/plugins/spellchecker/editor_plugin.js +1 -1
  28. js/tiny_mce/plugins/spellchecker/editor_plugin_src.js +100 -24
  29. js/tiny_mce/plugins/style/props.htm +2 -5
  30. js/tiny_mce/plugins/table/editor_plugin.js +1 -1
  31. js/tiny_mce/plugins/table/editor_plugin_src.js +12 -5
  32. js/tiny_mce/plugins/table/js/cell.js +1 -1
  33. js/tiny_mce/plugins/table/js/row.js +1 -1
  34. js/tiny_mce/plugins/table/js/table.js +2 -2
  35. js/tiny_mce/plugins/template/template.htm +0 -1
  36. js/tiny_mce/plugins/wordcount/editor_plugin.js +1 -1
  37. js/tiny_mce/plugins/wordcount/editor_plugin_src.js +1 -1
  38. js/tiny_mce/themes/advanced/charmap.htm +0 -1
  39. js/tiny_mce/themes/advanced/editor_template.js +1 -1
  40. js/tiny_mce/themes/advanced/editor_template_src.js +38 -11
  41. js/tiny_mce/themes/advanced/skins/default/ui.css +1 -1
  42. js/tiny_mce/themes/advanced/skins/o2k7/ui.css +1 -1
  43. js/tiny_mce/themes/advanced/source_editor.htm +0 -1
  44. js/tiny_mce/tiny_mce.js +1 -1
  45. js/tiny_mce/tiny_mce_jquery.js +1 -1
  46. js/tiny_mce/tiny_mce_jquery_src.js +455 -215
  47. js/tiny_mce/tiny_mce_prototype.js +0 -1
js/tiny_mce/classes/Editor.js CHANGED
@@ -480,6 +480,12 @@
480
  if (!t.getElement())
481
  return;
482
 
 
 
 
 
 
 
483
  // Add hidden input for non input elements inside form elements
484
  if (!/TEXTAREA|INPUT/i.test(t.getElement().nodeName) && s.hidden_input && DOM.getParent(id, 'form'))
485
  DOM.insertAfter(DOM.create('input', {type : 'hidden', name : id}), id);
@@ -916,6 +922,7 @@
916
  hilitecolor : {inline : 'span', styles : {backgroundColor : '%value'}},
917
  fontname : {inline : 'span', styles : {fontFamily : '%value'}},
918
  fontsize : {inline : 'span', styles : {fontSize : '%value'}},
 
919
  blockquote : {block : 'blockquote', wrapper : 1, remove : 'all'},
920
 
921
  removeformat : [
@@ -1321,13 +1328,28 @@
1321
  * @param {Boolean} sf Skip DOM focus. Just set is as the active editor.
1322
  */
1323
  focus : function(sf) {
1324
- var oed, t = this, ce = t.settings.content_editable;
1325
 
1326
  if (!sf) {
 
 
 
 
 
 
1327
  // Is not content editable
1328
  if (!ce)
1329
  t.getWin().focus();
1330
 
 
 
 
 
 
 
 
 
 
1331
  // #ifdef contentEditable
1332
 
1333
  // Content editable mode ends here
@@ -2368,7 +2390,7 @@
2368
 
2369
  // Add node change handlers
2370
  t.onMouseUp.add(t.nodeChanged);
2371
- t.onClick.add(t.nodeChanged);
2372
  t.onKeyUp.add(function(ed, e) {
2373
  var c = e.keyCode;
2374
 
@@ -2389,11 +2411,9 @@
2389
  }
2390
 
2391
  // Add default shortcuts for gecko
2392
- if (isGecko) {
2393
- t.addShortcut('ctrl+b', t.getLang('bold_desc'), 'Bold');
2394
- t.addShortcut('ctrl+i', t.getLang('italic_desc'), 'Italic');
2395
- t.addShortcut('ctrl+u', t.getLang('underline_desc'), 'Underline');
2396
- }
2397
 
2398
  // BlockFormat shortcuts keys
2399
  for (i=1; i<=6; i++)
@@ -2544,6 +2564,55 @@
2544
  });
2545
 
2546
  t.onKeyDown.add(function(ed, e) {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2547
  // Is caracter positon keys
2548
  if ((e.keyCode >= 33 && e.keyCode <= 36) || (e.keyCode >= 37 && e.keyCode <= 40) || e.keyCode == 13 || e.keyCode == 45) {
2549
  if (t.undoManager.typing)
480
  if (!t.getElement())
481
  return;
482
 
483
+ // Is a iPad/iPhone, then skip initialization. We need to sniff here since the
484
+ // browser says it has contentEditable support but there is no visible caret
485
+ // We will remove this check ones Apple implements full contentEditable support
486
+ if (tinymce.isIDevice)
487
+ return;
488
+
489
  // Add hidden input for non input elements inside form elements
490
  if (!/TEXTAREA|INPUT/i.test(t.getElement().nodeName) && s.hidden_input && DOM.getParent(id, 'form'))
491
  DOM.insertAfter(DOM.create('input', {type : 'hidden', name : id}), id);
922
  hilitecolor : {inline : 'span', styles : {backgroundColor : '%value'}},
923
  fontname : {inline : 'span', styles : {fontFamily : '%value'}},
924
  fontsize : {inline : 'span', styles : {fontSize : '%value'}},
925
+ fontsize_class : {inline : 'span', attributes : {'class' : '%value'}},
926
  blockquote : {block : 'blockquote', wrapper : 1, remove : 'all'},
927
 
928
  removeformat : [
1328
  * @param {Boolean} sf Skip DOM focus. Just set is as the active editor.
1329
  */
1330
  focus : function(sf) {
1331
+ var oed, t = this, ce = t.settings.content_editable, ieRng, controlElm, doc = t.getDoc();
1332
 
1333
  if (!sf) {
1334
+ // Get selected control element
1335
+ ieRng = t.selection.getRng();
1336
+ if (ieRng.item) {
1337
+ controlElm = ieRng.item(0);
1338
+ }
1339
+
1340
  // Is not content editable
1341
  if (!ce)
1342
  t.getWin().focus();
1343
 
1344
+ // Restore selected control element
1345
+ // This is needed when for example an image is selected within a
1346
+ // layer a call to focus will then remove the control selection
1347
+ if (controlElm && controlElm.ownerDocument == doc) {
1348
+ ieRng = doc.body.createControlRange();
1349
+ ieRng.addElement(controlElm);
1350
+ ieRng.select();
1351
+ }
1352
+
1353
  // #ifdef contentEditable
1354
 
1355
  // Content editable mode ends here
2390
 
2391
  // Add node change handlers
2392
  t.onMouseUp.add(t.nodeChanged);
2393
+ //t.onClick.add(t.nodeChanged);
2394
  t.onKeyUp.add(function(ed, e) {
2395
  var c = e.keyCode;
2396
 
2411
  }
2412
 
2413
  // Add default shortcuts for gecko
2414
+ t.addShortcut('ctrl+b', t.getLang('bold_desc'), 'Bold');
2415
+ t.addShortcut('ctrl+i', t.getLang('italic_desc'), 'Italic');
2416
+ t.addShortcut('ctrl+u', t.getLang('underline_desc'), 'Underline');
 
 
2417
 
2418
  // BlockFormat shortcuts keys
2419
  for (i=1; i<=6; i++)
2564
  });
2565
 
2566
  t.onKeyDown.add(function(ed, e) {
2567
+ var rng, tmpRng, parent, offset;
2568
+
2569
+ // IE has a really odd bug where the DOM might include an node that doesn't have
2570
+ // a proper structure. If you try to access nodeValue it would throw an illegal value exception.
2571
+ // This seems to only happen when you delete contents and it seems to be avoidable if you refresh the element
2572
+ // after you delete contents from it. See: #3008923
2573
+ if (isIE && e.keyCode == 46) {
2574
+ rng = t.selection.getRng();
2575
+
2576
+ if (rng.parentElement) {
2577
+ parent = rng.parentElement();
2578
+
2579
+ // Get the current caret position within the element
2580
+ tmpRng = rng.duplicate();
2581
+ tmpRng.moveToElementText(parent);
2582
+ tmpRng.setEndPoint('EndToEnd', rng);
2583
+ offset = tmpRng.text.length;
2584
+
2585
+ // Select next word when ctrl key is used in combo with delete
2586
+ if (e.ctrlKey) {
2587
+ rng.moveEnd('word', 1);
2588
+ rng.select();
2589
+ }
2590
+
2591
+ // Delete contents
2592
+ t.selection.getSel().clear();
2593
+
2594
+ // Check if we are within the same parent
2595
+ if (rng.parentElement() == parent) {
2596
+ try {
2597
+ // Update the HTML and hopefully it will remove the artifacts
2598
+ parent.innerHTML = parent.innerHTML;
2599
+ } catch (ex) {
2600
+ // And since it's IE it can sometimes produce an unknown runtime error
2601
+ }
2602
+
2603
+ // Restore the caret position
2604
+ tmpRng.moveToElementText(parent);
2605
+ tmpRng.collapse();
2606
+ tmpRng.move('character', offset);
2607
+ tmpRng.select();
2608
+ }
2609
+
2610
+ // Block the default delete behavior since it might be broken
2611
+ e.preventDefault();
2612
+ return;
2613
+ }
2614
+ }
2615
+
2616
  // Is caracter positon keys
2617
  if ((e.keyCode >= 33 && e.keyCode <= 36) || (e.keyCode >= 37 && e.keyCode <= 40) || e.keyCode == 13 || e.keyCode == 45) {
2618
  if (t.undoManager.typing)
js/tiny_mce/classes/EditorCommands.js CHANGED
@@ -155,7 +155,7 @@
155
  }
156
 
157
  // Present alert message about clipboard access not being available
158
- if (failed || !doc.queryCommandEnabled(command)) {
159
  if (tinymce.isGecko) {
160
  editor.windowManager.confirm(editor.getLang('clipboard_msg'), function(state) {
161
  if (state)
@@ -250,16 +250,18 @@
250
  },
251
 
252
  mceCleanup : function() {
253
- storeSelection();
 
254
  editor.setContent(editor.getContent({cleanup : TRUE}), {cleanup : TRUE});
255
- restoreSelection();
 
256
  },
257
 
258
  mceRemoveNode : function(command, ui, value) {
259
  var node = value || selection.getNode();
260
 
261
  // Make sure that the body node isn't removed
262
- if (node != ed.getBody()) {
263
  storeSelection();
264
  editor.dom.remove(node, TRUE);
265
  restoreSelection();
@@ -364,8 +366,16 @@
364
  if (value.href)
365
  dom.setAttribs(link, value);
366
  else
367
- ed.dom.remove(link, TRUE);
368
  }
 
 
 
 
 
 
 
 
369
  }
370
  });
371
 
155
  }
156
 
157
  // Present alert message about clipboard access not being available
158
+ if (failed || !doc.queryCommandSupported(command)) {
159
  if (tinymce.isGecko) {
160
  editor.windowManager.confirm(editor.getLang('clipboard_msg'), function(state) {
161
  if (state)
250
  },
251
 
252
  mceCleanup : function() {
253
+ var bookmark = selection.getBookmark();
254
+
255
  editor.setContent(editor.getContent({cleanup : TRUE}), {cleanup : TRUE});
256
+
257
+ selection.moveToBookmark(bookmark);
258
  },
259
 
260
  mceRemoveNode : function(command, ui, value) {
261
  var node = value || selection.getNode();
262
 
263
  // Make sure that the body node isn't removed
264
+ if (node != editor.getBody()) {
265
  storeSelection();
266
  editor.dom.remove(node, TRUE);
267
  restoreSelection();
366
  if (value.href)
367
  dom.setAttribs(link, value);
368
  else
369
+ editor.dom.remove(link, TRUE);
370
  }
371
+ },
372
+
373
+ selectAll : function() {
374
+ var root = dom.getRoot();
375
+ var rng = dom.createRng();
376
+ rng.setStart(root, 0);
377
+ rng.setEnd(root, root.childNodes.length);
378
+ editor.selection.setRng(rng);
379
  }
380
  });
381
 
js/tiny_mce/classes/ForceBlocks.js CHANGED
@@ -19,6 +19,27 @@
19
  TRUE = true,
20
  FALSE = false;
21
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
22
  // Checks if the selection/caret is at the end of the specified block element
23
  function isAtEnd(rng, par) {
24
  var rng2 = par.ownerDocument.createRange();
@@ -130,11 +151,54 @@
130
  }
131
  }
132
 
133
- if (!isIE && s.force_p_newlines) {
134
- ed.onKeyPress.add(function(ed, e) {
135
- if (e.keyCode == 13 && !e.shiftKey && !t.insertPara(e))
136
- Event.cancel(e);
137
- });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
138
 
139
  if (isGecko) {
140
  ed.onKeyDown.add(function(ed, e) {
@@ -175,7 +239,7 @@
175
  };
176
 
177
  ed.onKeyPress.add(function(ed, e) {
178
- if (e.keyCode == 13 && (e.shiftKey || s.force_br_newlines)) {
179
  insertBr(ed);
180
  Event.cancel(e);
181
  }
@@ -285,6 +349,13 @@
285
  }
286
  }
287
  } else {
 
 
 
 
 
 
 
288
  tr = d.body.createTextRange();
289
  tr.moveToElementText(b);
290
  tr.collapse(1);
@@ -629,7 +700,22 @@
629
  },
630
 
631
  backspaceDelete : function(e, bs) {
632
- 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;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
633
 
634
  // The caret sometimes gets stuck in Gecko if you delete empty paragraphs
635
  // This workaround removes the element by hand and moves the caret to the previous element
@@ -660,37 +746,6 @@
660
  }
661
  }
662
  }
663
-
664
- // Gecko generates BR elements here and there, we don't like those so lets remove them
665
- function handler(e) {
666
- var pr;
667
-
668
- e = e.target;
669
-
670
- // A new BR was created in a block element, remove it
671
- if (e && e.parentNode && e.nodeName == 'BR' && (n = t.getParentBlock(e))) {
672
- pr = e.previousSibling;
673
-
674
- Event.remove(b, 'DOMNodeInserted', handler);
675
-
676
- // Is there whitespace at the end of the node before then we might need the pesky BR
677
- // to place the caret at a correct location see bug: #2013943
678
- if (pr && pr.nodeType == 3 && /\s+$/.test(pr.nodeValue))
679
- return;
680
-
681
- // Only remove BR elements that got inserted in the middle of the text
682
- if (e.previousSibling || e.nextSibling)
683
- ed.dom.remove(e);
684
- }
685
- };
686
-
687
- // Listen for new nodes
688
- Event._add(b, 'DOMNodeInserted', handler);
689
-
690
- // Remove listener
691
- window.setTimeout(function() {
692
- Event._remove(b, 'DOMNodeInserted', handler);
693
- }, 1);
694
  }
695
  });
696
  })(tinymce);
19
  TRUE = true,
20
  FALSE = false;
21
 
22
+ function cloneFormats(node) {
23
+ var clone, temp, inner;
24
+
25
+ do {
26
+ if (/^(SPAN|STRONG|B|EM|I|FONT|STRIKE|U)$/.test(node.nodeName)) {
27
+ if (clone) {
28
+ temp = node.cloneNode(false);
29
+ temp.appendChild(clone);
30
+ clone = temp;
31
+ } else {
32
+ clone = inner = node.cloneNode(false);
33
+ }
34
+
35
+ clone.removeAttribute('id');
36
+ }
37
+ } while (node = node.parentNode);
38
+
39
+ if (clone)
40
+ return {wrapper : clone, inner : inner};
41
+ };
42
+
43
  // Checks if the selection/caret is at the end of the specified block element
44
  function isAtEnd(rng, par) {
45
  var rng2 = par.ownerDocument.createRange();
151
  }
152
  }
153
 
154
+ if (s.force_p_newlines) {
155
+ if (!isIE) {
156
+ ed.onKeyPress.add(function(ed, e) {
157
+ if (e.keyCode == 13 && !e.shiftKey && !t.insertPara(e))
158
+ Event.cancel(e);
159
+ });
160
+ } else {
161
+ // Ungly hack to for IE to preserve the formatting when you press
162
+ // enter at the end of a block element with formatted contents
163
+ // This logic overrides the browsers default logic with
164
+ // custom logic that enables us to control the output
165
+ tinymce.addUnload(function() {
166
+ t._previousFormats = 0; // Fix IE leak
167
+ });
168
+
169
+ ed.onKeyPress.add(function(ed, e) {
170
+ t._previousFormats = 0;
171
+
172
+ // Clone the current formats, this will later be applied to the new block contents
173
+ if (e.keyCode == 13 && !e.shiftKey && ed.selection.isCollapsed() && s.keep_styles)
174
+ t._previousFormats = cloneFormats(ed.selection.getStart());
175
+ });
176
+
177
+ ed.onKeyUp.add(function(ed, e) {
178
+ // Let IE break the element and the wrap the new caret location in the previous formats
179
+ if (e.keyCode == 13 && !e.shiftKey) {
180
+ var parent = ed.selection.getStart(), fmt = t._previousFormats;
181
+
182
+ // Parent is an empty block
183
+ if (!parent.hasChildNodes()) {
184
+ parent = dom.getParent(parent, dom.isBlock);
185
+
186
+ if (parent) {
187
+ parent.innerHTML = '';
188
+
189
+ if (t._previousFormats) {
190
+ parent.appendChild(fmt.wrapper);
191
+ fmt.inner.innerHTML = '\uFEFF';
192
+ } else
193
+ parent.innerHTML = '\uFEFF';
194
+
195
+ selection.select(parent, 1);
196
+ ed.getDoc().execCommand('Delete', false, null);
197
+ }
198
+ }
199
+ }
200
+ });
201
+ }
202
 
203
  if (isGecko) {
204
  ed.onKeyDown.add(function(ed, e) {
239
  };
240
 
241
  ed.onKeyPress.add(function(ed, e) {
242
+ if (e.keyCode == 13 && (e.shiftKey || (s.force_br_newlines && !dom.getParent(selection.getNode(), 'h1,h2,h3,h4,h5,h6,ol,ul')))) {
243
  insertBr(ed);
244
  Event.cancel(e);
245
  }
349
  }
350
  }
351
  } else {
352
+ // Force control range into text range
353
+ if (r.item) {
354
+ tr = d.body.createTextRange();
355
+ tr.moveToElementText(r.item(0));
356
+ r = tr;
357
+ }
358
+
359
  tr = d.body.createTextRange();
360
  tr.moveToElementText(b);
361
  tr.collapse(1);
700
  },
701
 
702
  backspaceDelete : function(e, bs) {
703
+ 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, walker;
704
+
705
+ // Delete when caret is behind a element doesn't work correctly on Gecko see #3011651
706
+ if (!bs && r.collapsed && sc.nodeType == 1 && r.startOffset == sc.childNodes.length) {
707
+ walker = new tinymce.dom.TreeWalker(sc.lastChild, sc);
708
+
709
+ // Walk the dom backwards until we find a text node
710
+ for (n = sc.lastChild; n; n = walker.prev()) {
711
+ if (n.nodeType == 3) {
712
+ r.setStart(n, n.nodeValue.length);
713
+ r.collapse(true);
714
+ se.setRng(r);
715
+ return;
716
+ }
717
+ }
718
+ }
719
 
720
  // The caret sometimes gets stuck in Gecko if you delete empty paragraphs
721
  // This workaround removes the element by hand and moves the caret to the previous element
746
  }
747
  }
748
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
749
  }
750
  });
751
  })(tinymce);
js/tiny_mce/classes/Formatter.js CHANGED
@@ -141,11 +141,16 @@
141
 
142
  // Move startContainer/startOffset in to a suitable node
143
  if (container.nodeType == 1 || container.nodeValue === "") {
144
- walker = new TreeWalker(container.childNodes[offset]);
145
- for (node = walker.current(); node; node = walker.next()) {
146
- if (node.nodeType == 3 && !isBlock(node.parentNode) && !isWhiteSpaceNode(node)) {
147
- rng.setStart(node, 0);
148
- break;
 
 
 
 
 
149
  }
150
  }
151
  }
@@ -227,7 +232,7 @@
227
  }
228
  });
229
 
230
- // Contine processing if a selector match wasn't found and a inline element is defined
231
  if (!format.inline || found) {
232
  currentWrapElm = 0;
233
  return;
@@ -320,14 +325,23 @@
320
  });
321
  });
322
 
 
 
 
 
 
 
 
323
  // Look for parent with similar style format
324
- dom.getParent(node.parentNode, function(parent) {
325
- if (matchNode(parent, name, vars)) {
326
- dom.remove(node, 1);
327
- node = 0;
328
- return TRUE;
329
- }
330
- });
 
 
331
 
332
  // Merge next and previous siblings if they are similar <b>text</b><b>text</b> becomes <b>texttext</b>
333
  if (node) {
@@ -345,7 +359,7 @@
345
  rng.setStartBefore(node);
346
  rng.setEndAfter(node);
347
 
348
- applyRngStyle(rng);
349
  } else {
350
  if (!selection.isCollapsed() || !format.inline) {
351
  // Apply formatting to selection
@@ -462,7 +476,13 @@
462
  var node = dom.get(start ? '_start' : '_end'),
463
  out = node[start ? 'firstChild' : 'lastChild'];
464
 
465
- dom.remove(node, 1);
 
 
 
 
 
 
466
 
467
  return out;
468
  };
@@ -525,7 +545,7 @@
525
  };
526
 
527
  /**
528
- * Toggles the specifed format on/off.
529
  *
530
  * @method toggle
531
  * @param {String} name Name of format to apply/remove.
@@ -546,9 +566,10 @@
546
  * @param {Node} node Node to check the format on.
547
  * @param {String} name Format name to check.
548
  * @param {Object} vars Optional list of variables to replace before checking it.
 
549
  * @return {Object} Returns the format object it matches or undefined if it doesn't match.
550
  */
551
- function matchNode(node, name, vars) {
552
  var formatList = get(name), format, i, classes;
553
 
554
  function matchItems(node, format, item_name) {
@@ -565,7 +586,10 @@
565
  else
566
  value = getStyle(node, key);
567
 
568
- if (!isEq(value, replaceVars(items[key], vars)))
 
 
 
569
  return;
570
  }
571
  }
@@ -603,7 +627,7 @@
603
  };
604
 
605
  /**
606
- * Matches the current selection or specifed node against the specified format name.
607
  *
608
  * @method match
609
  * @param {String} name Name of format to match.
@@ -617,7 +641,7 @@
617
  function matchParents(node) {
618
  // Find first node with similar format settings
619
  node = dom.getParent(node, function(node) {
620
- return !!matchNode(node, name, vars);
621
  });
622
 
623
  // Do an exact check on the similar format element
@@ -764,7 +788,7 @@
764
  * Checks if the specified nodes name matches the format inline/block or selector.
765
  *
766
  * @private
767
- * @param {Node} node Node to match agains the specified format.
768
  * @param {Object} format Format object o match with.
769
  * @return {boolean} true/false if the format matches.
770
  */
@@ -844,7 +868,7 @@
844
  };
845
 
846
  function isWhiteSpaceNode(node) {
847
- return node && node.nodeType === 3 && /^\s*$/.test(node.nodeValue);
848
  };
849
 
850
  function wrap(node, name, attrs) {
@@ -1042,7 +1066,7 @@
1042
  * @param {Object} format Format object with items to remove from node.
1043
  * @param {Object} vars Name/value object with variables to apply to format.
1044
  * @param {Node} node Node to remove the format styles on.
1045
- * @param {Node} compare_node Optional compare node, if specidied the styles will be compared to that node.
1046
  * @return {Boolean} True/false if the node was removed or not.
1047
  */
1048
  function removeFormat(format, vars, node, compare_node) {
@@ -1252,7 +1276,7 @@
1252
  *
1253
  * @private
1254
  * @param {Node} node1 First node to compare with.
1255
- * @param {Node} node2 Secont node to compare with.
1256
  * @return {boolean} True/false if the nodes are the same or not.
1257
  */
1258
  function compareElements(node1, node2) {
@@ -1381,7 +1405,7 @@
1381
  * @return {boolean} True/false if the node is a text block.
1382
  */
1383
  function isTextBlock(name) {
1384
- return /^(h[1-6]|p|div|pre|address)$/.test(name);
1385
  };
1386
 
1387
  function getContainer(rng, start) {
@@ -1447,6 +1471,7 @@
1447
  // Pending apply or remove formats
1448
  if (hasPending()) {
1449
  ed.getDoc().execCommand('FontName', false, 'mceinline');
 
1450
 
1451
  // IE will convert the current word
1452
  each(dom.select('font,span'), function(node) {
@@ -1466,21 +1491,25 @@
1466
 
1467
  each('onKeyDown,onKeyUp,onKeyPress,onMouseUp'.split(','), function(event) {
1468
  ed[event].addToTop(function(ed, e) {
1469
- if (hasPending()) {
 
1470
  each(dom.select('font,span'), function(node) {
1471
- var bookmark, textNode, rng;
1472
 
1473
  // Look for marker
1474
  if (isCaretNode(node)) {
1475
  textNode = node.firstChild;
1476
 
1477
- perform(node);
 
1478
 
1479
- rng = dom.createRng();
1480
- rng.setStart(textNode, textNode.nodeValue.length);
1481
- rng.setEnd(textNode, textNode.nodeValue.length);
1482
- selection.setRng(rng);
1483
- ed.nodeChanged();
 
 
1484
  }
1485
  });
1486
 
141
 
142
  // Move startContainer/startOffset in to a suitable node
143
  if (container.nodeType == 1 || container.nodeValue === "") {
144
+ container = container.nodeType == 1 ? container.childNodes[offset] : container;
145
+
146
+ // Might fail if the offset is behind the last element in it's container
147
+ if (container) {
148
+ walker = new TreeWalker(container, container.parentNode);
149
+ for (node = walker.current(); node; node = walker.next()) {
150
+ if (node.nodeType == 3 && !isWhiteSpaceNode(node)) {
151
+ rng.setStart(node, 0);
152
+ break;
153
+ }
154
  }
155
  }
156
  }
232
  }
233
  });
234
 
235
+ // Continue processing if a selector match wasn't found and a inline element is defined
236
  if (!format.inline || found) {
237
  currentWrapElm = 0;
238
  return;
325
  });
326
  });
327
 
328
+ // Remove child if direct parent is of same type
329
+ if (matchNode(node.parentNode, name, vars)) {
330
+ dom.remove(node, 1);
331
+ node = 0;
332
+ return TRUE;
333
+ }
334
+
335
  // Look for parent with similar style format
336
+ if (format.merge_with_parents) {
337
+ dom.getParent(node.parentNode, function(parent) {
338
+ if (matchNode(parent, name, vars)) {
339
+ dom.remove(node, 1);
340
+ node = 0;
341
+ return TRUE;
342
+ }
343
+ });
344
+ }
345
 
346
  // Merge next and previous siblings if they are similar <b>text</b><b>text</b> becomes <b>texttext</b>
347
  if (node) {
359
  rng.setStartBefore(node);
360
  rng.setEndAfter(node);
361
 
362
+ applyRngStyle(expandRng(rng, formatList));
363
  } else {
364
  if (!selection.isCollapsed() || !format.inline) {
365
  // Apply formatting to selection
476
  var node = dom.get(start ? '_start' : '_end'),
477
  out = node[start ? 'firstChild' : 'lastChild'];
478
 
479
+ // If the end is placed within the start the result will be removed
480
+ // So this checks if the out node is a bookmark node if it is it
481
+ // checks for another more suitable node
482
+ if (isBookmarkNode(out))
483
+ out = out[start ? 'firstChild' : 'lastChild'];
484
+
485
+ dom.remove(node, true);
486
 
487
  return out;
488
  };
545
  };
546
 
547
  /**
548
+ * Toggles the specified format on/off.
549
  *
550
  * @method toggle
551
  * @param {String} name Name of format to apply/remove.
566
  * @param {Node} node Node to check the format on.
567
  * @param {String} name Format name to check.
568
  * @param {Object} vars Optional list of variables to replace before checking it.
569
+ * @param {Boolean} similar Match format that has similar properties.
570
  * @return {Object} Returns the format object it matches or undefined if it doesn't match.
571
  */
572
+ function matchNode(node, name, vars, similar) {
573
  var formatList = get(name), format, i, classes;
574
 
575
  function matchItems(node, format, item_name) {
586
  else
587
  value = getStyle(node, key);
588
 
589
+ if (similar && !value && !format.exact)
590
+ return;
591
+
592
+ if ((!similar || format.exact) && !isEq(value, replaceVars(items[key], vars)))
593
  return;
594
  }
595
  }
627
  };
628
 
629
  /**
630
+ * Matches the current selection or specified node against the specified format name.
631
  *
632
  * @method match
633
  * @param {String} name Name of format to match.
641
  function matchParents(node) {
642
  // Find first node with similar format settings
643
  node = dom.getParent(node, function(node) {
644
+ return !!matchNode(node, name, vars, true);
645
  });
646
 
647
  // Do an exact check on the similar format element
788
  * Checks if the specified nodes name matches the format inline/block or selector.
789
  *
790
  * @private
791
+ * @param {Node} node Node to match against the specified format.
792
  * @param {Object} format Format object o match with.
793
  * @return {boolean} true/false if the format matches.
794
  */
868
  };
869
 
870
  function isWhiteSpaceNode(node) {
871
+ return node && node.nodeType === 3 && /^([\s\r\n]+|)$/.test(node.nodeValue);
872
  };
873
 
874
  function wrap(node, name, attrs) {
1066
  * @param {Object} format Format object with items to remove from node.
1067
  * @param {Object} vars Name/value object with variables to apply to format.
1068
  * @param {Node} node Node to remove the format styles on.
1069
+ * @param {Node} compare_node Optional compare node, if specified the styles will be compared to that node.
1070
  * @return {Boolean} True/false if the node was removed or not.
1071
  */
1072
  function removeFormat(format, vars, node, compare_node) {
1276
  *
1277
  * @private
1278
  * @param {Node} node1 First node to compare with.
1279
+ * @param {Node} node2 Second node to compare with.
1280
  * @return {boolean} True/false if the nodes are the same or not.
1281
  */
1282
  function compareElements(node1, node2) {
1405
  * @return {boolean} True/false if the node is a text block.
1406
  */
1407
  function isTextBlock(name) {
1408
+ return /^(h[1-6]|p|div|pre|address|dl|dt|dd)$/.test(name);
1409
  };
1410
 
1411
  function getContainer(rng, start) {
1471
  // Pending apply or remove formats
1472
  if (hasPending()) {
1473
  ed.getDoc().execCommand('FontName', false, 'mceinline');
1474
+ pendingFormats.lastRng = selection.getRng();
1475
 
1476
  // IE will convert the current word
1477
  each(dom.select('font,span'), function(node) {
1491
 
1492
  each('onKeyDown,onKeyUp,onKeyPress,onMouseUp'.split(','), function(event) {
1493
  ed[event].addToTop(function(ed, e) {
1494
+ // Do we have pending formats and is the selection moved has moved
1495
+ if (hasPending() && !tinymce.dom.RangeUtils.compareRanges(pendingFormats.lastRng, selection.getRng())) {
1496
  each(dom.select('font,span'), function(node) {
1497
+ var textNode, rng;
1498
 
1499
  // Look for marker
1500
  if (isCaretNode(node)) {
1501
  textNode = node.firstChild;
1502
 
1503
+ if (textNode) {
1504
+ perform(node);
1505
 
1506
+ rng = dom.createRng();
1507
+ rng.setStart(textNode, textNode.nodeValue.length);
1508
+ rng.setEnd(textNode, textNode.nodeValue.length);
1509
+ selection.setRng(rng);
1510
+ ed.nodeChanged();
1511
+ } else
1512
+ dom.remove(node);
1513
  }
1514
  });
1515
 
js/tiny_mce/classes/adapter/jquery/jquery.tinymce.js CHANGED
@@ -19,7 +19,7 @@
19
 
20
  // No match then just ignore the call
21
  if (!self.length)
22
- return;
23
 
24
  // Get editor instance
25
  if (!settings)
@@ -91,7 +91,8 @@
91
 
92
  // Setup tinyMCEPreInit object this will later be used by the TinyMCE
93
  // core script to locate other resources like CSS files, dialogs etc
94
- win.tinyMCEPreInit = {
 
95
  base : base,
96
  suffix : suffix,
97
  query : query
@@ -143,6 +144,11 @@
143
  success : function() {
144
  tinymce.dom.Event.domLoaded = 1;
145
  lazyLoading = 2;
 
 
 
 
 
146
  init();
147
 
148
  $.each(delayedInits, function(i, init) {
@@ -157,6 +163,8 @@
157
  else
158
  init();
159
  }
 
 
160
  };
161
 
162
  // Add :tinymce psuedo selector this will select elements that has been converted into editor instances
@@ -242,20 +250,21 @@
242
  var self = this;
243
 
244
  if (!containsTinyMCE(self))
245
- return origFn.call(self, value);
246
 
247
  if (value !== undefined) {
248
  loadOrSave.call(self.filter(":tinymce"), value);
249
- origFn.call(self.not(":tinymce"), value);
250
 
251
  return self; // return original set for chaining
252
  } else {
253
  var ret = "";
254
-
 
255
  (textProc ? self : self.eq(0)).each(function(i, node) {
256
  var ed = tinyMCEInstance(node);
257
 
258
- ret += ed ? (textProc ? ed.getContent().replace(/<(?:"[^"]*"|'[^']*'|[^'">])*>/g, "") : ed.getContent()) : origFn.call($(node), value);
259
  });
260
 
261
  return ret;
@@ -272,7 +281,7 @@
272
  var self = this;
273
 
274
  if (!containsTinyMCE(self))
275
- return origFn.call(self, value);
276
 
277
  if (value !== undefined) {
278
  self.filter(":tinymce").each(function(i, node) {
@@ -281,7 +290,7 @@
281
  ed && ed.setContent(prepend ? value + ed.getContent() : ed.getContent() + value);
282
  });
283
 
284
- origFn.call(self.not(":tinymce"), value);
285
 
286
  return self; // return original set for chaining
287
  }
19
 
20
  // No match then just ignore the call
21
  if (!self.length)
22
+ return self;
23
 
24
  // Get editor instance
25
  if (!settings)
91
 
92
  // Setup tinyMCEPreInit object this will later be used by the TinyMCE
93
  // core script to locate other resources like CSS files, dialogs etc
94
+ // You can also predefined a tinyMCEPreInit object and then it will use that instead
95
+ win.tinyMCEPreInit = win.tinyMCEPreInit || {
96
  base : base,
97
  suffix : suffix,
98
  query : query
144
  success : function() {
145
  tinymce.dom.Event.domLoaded = 1;
146
  lazyLoading = 2;
147
+
148
+ // Execute callback after mainscript has been loaded and before the initialization occurs
149
+ if (settings.script_loaded)
150
+ settings.script_loaded();
151
+
152
  init();
153
 
154
  $.each(delayedInits, function(i, init) {
163
  else
164
  init();
165
  }
166
+
167
+ return self;
168
  };
169
 
170
  // Add :tinymce psuedo selector this will select elements that has been converted into editor instances
250
  var self = this;
251
 
252
  if (!containsTinyMCE(self))
253
+ return origFn.apply(self, arguments);
254
 
255
  if (value !== undefined) {
256
  loadOrSave.call(self.filter(":tinymce"), value);
257
+ origFn.apply(self.not(":tinymce"), arguments);
258
 
259
  return self; // return original set for chaining
260
  } else {
261
  var ret = "";
262
+ var args = arguments;
263
+
264
  (textProc ? self : self.eq(0)).each(function(i, node) {
265
  var ed = tinyMCEInstance(node);
266
 
267
+ ret += ed ? (textProc ? ed.getContent().replace(/<(?:"[^"]*"|'[^']*'|[^'">])*>/g, "") : ed.getContent()) : origFn.apply($(node), args);
268
  });
269
 
270
  return ret;
281
  var self = this;
282
 
283
  if (!containsTinyMCE(self))
284
+ return origFn.apply(self, arguments);
285
 
286
  if (value !== undefined) {
287
  self.filter(":tinymce").each(function(i, node) {
290
  ed && ed.setContent(prepend ? value + ed.getContent() : ed.getContent() + value);
291
  });
292
 
293
+ origFn.apply(self.not(":tinymce"), arguments);
294
 
295
  return self; // return original set for chaining
296
  }
js/tiny_mce/classes/dom/DOMUtils.js CHANGED
@@ -441,7 +441,7 @@
441
  if (keep_children) {
442
  while (child = node.firstChild) {
443
  // IE 8 will crash if you don't remove completely empty text nodes
444
- if (child.nodeType !== 3 || child.nodeValue)
445
  parent.insertBefore(child, node);
446
  else
447
  node.removeChild(child);
@@ -512,7 +512,7 @@
512
  * @method getStyle
513
  * @param {String/Element} n HTML element or element id string to get style from.
514
  * @param {String} na Style name to return.
515
- * @param {String} c Computed style.
516
  * @return {String} Current style or computed style value of a element.
517
  */
518
  getStyle : function(n, na, c) {
@@ -1226,7 +1226,7 @@
1226
  // So if we replace the p elements with divs and mark them and then replace them back to paragraphs
1227
  // after we use innerHTML we can fix the DOM tree
1228
  h = h.replace(/<p ([^>]+)>|<p>/ig, '<div $1 _mce_tmp="1">');
1229
- h = h.replace(/<\/p>/g, '</div>');
1230
 
1231
  // Set the new HTML with DIVs
1232
  set();
@@ -1857,10 +1857,13 @@
1857
  for (lastNodeType = node.nodeType, node = node.previousSibling, lastNode = node; node; node = node.previousSibling) {
1858
  nodeType = node.nodeType;
1859
 
1860
- // Handle normalization of text nodes
1861
- if (!normalized || nodeType != 3 || (lastNodeType != nodeType && node.nodeValue.length))
1862
- idx++;
 
 
1863
 
 
1864
  lastNodeType = nodeType;
1865
  }
1866
  }
441
  if (keep_children) {
442
  while (child = node.firstChild) {
443
  // IE 8 will crash if you don't remove completely empty text nodes
444
+ if (!tinymce.isIE || child.nodeType !== 3 || child.nodeValue)
445
  parent.insertBefore(child, node);
446
  else
447
  node.removeChild(child);
512
  * @method getStyle
513
  * @param {String/Element} n HTML element or element id string to get style from.
514
  * @param {String} na Style name to return.
515
+ * @param {Boolean} c Computed style.
516
  * @return {String} Current style or computed style value of a element.
517
  */
518
  getStyle : function(n, na, c) {
1226
  // So if we replace the p elements with divs and mark them and then replace them back to paragraphs
1227
  // after we use innerHTML we can fix the DOM tree
1228
  h = h.replace(/<p ([^>]+)>|<p>/ig, '<div $1 _mce_tmp="1">');
1229
+ h = h.replace(/<\/p>/gi, '</div>');
1230
 
1231
  // Set the new HTML with DIVs
1232
  set();
1857
  for (lastNodeType = node.nodeType, node = node.previousSibling, lastNode = node; node; node = node.previousSibling) {
1858
  nodeType = node.nodeType;
1859
 
1860
+ // Normalize text nodes
1861
+ if (normalized && nodeType == 3) {
1862
+ if (nodeType == lastNodeType || !node.nodeValue.length)
1863
+ continue;
1864
+ }
1865
 
1866
+ idx++;
1867
  lastNodeType = nodeType;
1868
  }
1869
  }
js/tiny_mce/classes/dom/RangeUtils.js CHANGED
@@ -197,4 +197,32 @@
197
  };
198
  */
199
  };
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
200
  })(tinymce);
197
  };
198
  */
199
  };
200
+
201
+ /**
202
+ * Compares two ranges and checks if they are equal.
203
+ *
204
+ * @static
205
+ * @param {DOMRange} rng1 First range to compare.
206
+ * @param {DOMRange} rng2 First range to compare.
207
+ * @return {Boolean} true/false if the ranges are equal.
208
+ */
209
+ tinymce.dom.RangeUtils.compareRanges = function(rng1, rng2) {
210
+ if (rng1 && rng2) {
211
+ // Compare native IE ranges
212
+ if (rng1.item || rng1.duplicate) {
213
+ // Both are control ranges and the selected element matches
214
+ if (rng1.item && rng2.item && rng1.item(0) === rng2.item(0))
215
+ return true;
216
+
217
+ // Both are text ranges and the range matches
218
+ if (rng1.isEqual && rng2.isEqual && rng2.isEqual(rng1))
219
+ return true;
220
+ } else {
221
+ // Compare w3c ranges
222
+ return rng1.startContainer == rng2.startContainer && rng1.startOffset == rng2.startOffset;
223
+ }
224
+ }
225
+
226
+ return false;
227
+ };
228
  })(tinymce);
js/tiny_mce/classes/dom/Selection.js CHANGED
@@ -125,17 +125,21 @@
125
  h += '<span id="__caret">_</span>';
126
 
127
  // Delete and insert new node
128
- if (r.startContainer == d && r.endContainer == d) {
 
129
  // WebKit will fail if the body is empty since the range is then invalid and it can't insert contents
130
  d.body.innerHTML = h;
131
  } else {
132
  r.deleteContents();
133
- r.insertNode(t.getRng().createContextualFragment(h));
 
 
 
 
134
  }
135
 
136
  // Move to caret marker
137
  c = t.dom.get('__caret');
138
-
139
  // Make sure we wrap it compleatly, Opera fails with a simple select call
140
  r = d.createRange();
141
  r.setStartBefore(c);
@@ -166,30 +170,43 @@
166
  * @return {Element} Start element of selection range.
167
  */
168
  getStart : function() {
169
- var t = this, r = t.getRng(), e;
170
-
171
- if (r.duplicate || r.item) {
172
- if (r.item)
173
- return r.item(0);
174
 
175
- r = r.duplicate();
176
- r.collapse(1);
177
- e = r.parentElement();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
178
 
179
- if (e && e.nodeName == 'BODY')
180
- return e.firstChild || e;
 
181
 
182
- return e;
183
  } else {
184
- e = r.startContainer;
185
 
186
- if (e.nodeType == 1 && e.hasChildNodes())
187
- e = e.childNodes[Math.min(e.childNodes.length - 1, r.startOffset)];
188
 
189
- if (e && e.nodeType == 3)
190
- return e.parentNode;
191
 
192
- return e;
193
  }
194
  },
195
 
@@ -269,10 +286,10 @@
269
  point.push(offset);
270
  } else {
271
  childNodes = container.childNodes;
272
-
273
- if (offset >= childNodes.length) {
274
  after = 1;
275
- offset = childNodes.length - 1;
276
  }
277
 
278
  point.push(t.dom.nodeIndex(childNodes[offset], normalized) + after);
@@ -358,7 +375,7 @@
358
  * @return {Boolean} true/false if it was successful or not.
359
  */
360
  moveToBookmark : function(bookmark) {
361
- var t = this, dom = t.dom, marker1, marker2, rng, root;
362
 
363
  // Clear selection cache
364
  if (t.tridentSel)
@@ -370,12 +387,16 @@
370
  root = dom.getRoot();
371
 
372
  function setEndPoint(start) {
373
- var point = bookmark[start ? 'start' : 'end'], i, node, offset;
374
 
375
  if (point) {
376
  // Find container node
377
- for (node = root, i = point.length - 1; i >= 1; i--)
378
- node = node.childNodes[point[i]];
 
 
 
 
379
 
380
  // Set offset within container node
381
  if (start)
@@ -390,8 +411,6 @@
390
 
391
  t.setRng(rng);
392
  } else if (bookmark.id) {
393
- rng = dom.createRng();
394
-
395
  function restoreEndPoint(suffix) {
396
  var marker = dom.get(bookmark.id + '_' + suffix), node, idx, next, prev, keep = bookmark.keep;
397
 
@@ -402,21 +421,22 @@
402
  if (!keep) {
403
  idx = dom.nodeIndex(marker);
404
  } else {
405
- node = marker;
406
  idx = 1;
407
  }
408
 
409
- rng.setStart(node, idx);
410
- rng.setEnd(node, idx);
411
  } else {
412
  if (!keep) {
413
  idx = dom.nodeIndex(marker);
414
  } else {
415
- node = marker;
416
  idx = 1;
417
  }
418
 
419
- rng.setEnd(node, idx);
 
420
  }
421
 
422
  if (!keep) {
@@ -441,19 +461,33 @@
441
  dom.remove(next);
442
 
443
  if (suffix == 'start') {
444
- rng.setStart(prev, idx);
445
- rng.setEnd(prev, idx);
446
- } else
447
- rng.setEnd(prev, idx);
 
 
448
  }
449
  }
450
  }
451
  };
452
 
 
 
 
 
 
 
 
 
 
453
  // Restore start/end points
454
  restoreEndPoint('start');
455
  restoreEndPoint('end');
456
 
 
 
 
457
  t.setRng(rng);
458
  } else if (bookmark.name) {
459
  t.select(dom.select(bookmark.name)[bookmark.index]);
@@ -591,6 +625,16 @@
591
  if (!r)
592
  r = t.win.document.createRange ? t.win.document.createRange() : t.win.document.body.createTextRange();
593
 
 
 
 
 
 
 
 
 
 
 
594
  return r;
595
  },
596
 
@@ -602,13 +646,15 @@
602
  */
603
  setRng : function(r) {
604
  var s, t = this;
605
-
606
  if (!t.tridentSel) {
607
  s = t.getSel();
608
 
609
  if (s) {
 
610
  s.removeAllRanges();
611
  s.addRange(r);
 
612
  }
613
  } else {
614
  // Is W3C Range
125
  h += '<span id="__caret">_</span>';
126
 
127
  // Delete and insert new node
128
+
129
+ if (r.startContainer == d && r.endContainer == d) {
130
  // WebKit will fail if the body is empty since the range is then invalid and it can't insert contents
131
  d.body.innerHTML = h;
132
  } else {
133
  r.deleteContents();
134
+ if (d.body.childNodes.length == 0) {
135
+ d.body.innerHTML = h;
136
+ } else {
137
+ r.insertNode(r.createContextualFragment(h));
138
+ }
139
  }
140
 
141
  // Move to caret marker
142
  c = t.dom.get('__caret');
 
143
  // Make sure we wrap it compleatly, Opera fails with a simple select call
144
  r = d.createRange();
145
  r.setStartBefore(c);
170
  * @return {Element} Start element of selection range.
171
  */
172
  getStart : function() {
173
+ var rng = this.getRng(), startElement, parentElement, checkRng, node;
 
 
 
 
174
 
175
+ if (rng.duplicate || rng.item) {
176
+ // Control selection, return first item
177
+ if (rng.item)
178
+ return rng.item(0);
179
+
180
+ // Get start element
181
+ checkRng = rng.duplicate();
182
+ checkRng.collapse(1);
183
+ startElement = checkRng.parentElement();
184
+
185
+ // Check if range parent is inside the start element, then return the inner parent element
186
+ // This will fix issues when a single element is selected, IE would otherwise return the wrong start element
187
+ parentElement = node = rng.parentElement();
188
+ while (node = node.parentNode) {
189
+ if (node == startElement) {
190
+ startElement = parentElement;
191
+ break;
192
+ }
193
+ }
194
 
195
+ // If start element is body element try to move to the first child if it exists
196
+ if (startElement && startElement.nodeName == 'BODY')
197
+ return startElement.firstChild || startElement;
198
 
199
+ return startElement;
200
  } else {
201
+ startElement = rng.startContainer;
202
 
203
+ if (startElement.nodeType == 1 && startElement.hasChildNodes())
204
+ startElement = startElement.childNodes[Math.min(startElement.childNodes.length - 1, rng.startOffset)];
205
 
206
+ if (startElement && startElement.nodeType == 3)
207
+ return startElement.parentNode;
208
 
209
+ return startElement;
210
  }
211
  },
212
 
286
  point.push(offset);
287
  } else {
288
  childNodes = container.childNodes;
289
+
290
+ if (offset >= childNodes.length && childNodes.length) {
291
  after = 1;
292
+ offset = Math.max(0, childNodes.length - 1);
293
  }
294
 
295
  point.push(t.dom.nodeIndex(childNodes[offset], normalized) + after);
375
  * @return {Boolean} true/false if it was successful or not.
376
  */
377
  moveToBookmark : function(bookmark) {
378
+ var t = this, dom = t.dom, marker1, marker2, rng, root, startContainer, endContainer, startOffset, endOffset;
379
 
380
  // Clear selection cache
381
  if (t.tridentSel)
387
  root = dom.getRoot();
388
 
389
  function setEndPoint(start) {
390
+ var point = bookmark[start ? 'start' : 'end'], i, node, offset, children;
391
 
392
  if (point) {
393
  // Find container node
394
+ for (node = root, i = point.length - 1; i >= 1; i--) {
395
+ children = node.childNodes;
396
+
397
+ if (children.length)
398
+ node = children[point[i]];
399
+ }
400
 
401
  // Set offset within container node
402
  if (start)
411
 
412
  t.setRng(rng);
413
  } else if (bookmark.id) {
 
 
414
  function restoreEndPoint(suffix) {
415
  var marker = dom.get(bookmark.id + '_' + suffix), node, idx, next, prev, keep = bookmark.keep;
416
 
421
  if (!keep) {
422
  idx = dom.nodeIndex(marker);
423
  } else {
424
+ node = marker.firstChild;
425
  idx = 1;
426
  }
427
 
428
+ startContainer = endContainer = node;
429
+ startOffset = endOffset = idx;
430
  } else {
431
  if (!keep) {
432
  idx = dom.nodeIndex(marker);
433
  } else {
434
+ node = marker.firstChild;
435
  idx = 1;
436
  }
437
 
438
+ endContainer = node;
439
+ endOffset = idx;
440
  }
441
 
442
  if (!keep) {
461
  dom.remove(next);
462
 
463
  if (suffix == 'start') {
464
+ startContainer = endContainer = prev;
465
+ startOffset = endOffset = idx;
466
+ } else {
467
+ endContainer = prev;
468
+ endOffset = idx;
469
+ }
470
  }
471
  }
472
  }
473
  };
474
 
475
+ function addBogus(node) {
476
+ // Adds a bogus BR element for empty block elements
477
+ // on non IE browsers just to have a place to put the caret
478
+ if (!isIE && dom.isBlock(node) && !node.innerHTML)
479
+ node.innerHTML = '<br _mce_bogus="1" />';
480
+
481
+ return node;
482
+ };
483
+
484
  // Restore start/end points
485
  restoreEndPoint('start');
486
  restoreEndPoint('end');
487
 
488
+ rng = dom.createRng();
489
+ rng.setStart(addBogus(startContainer), startOffset);
490
+ rng.setEnd(addBogus(endContainer), endOffset);
491
  t.setRng(rng);
492
  } else if (bookmark.name) {
493
  t.select(dom.select(bookmark.name)[bookmark.index]);
625
  if (!r)
626
  r = t.win.document.createRange ? t.win.document.createRange() : t.win.document.body.createTextRange();
627
 
628
+ if (t.selectedRange && t.explicitRange) {
629
+ if (r.compareBoundaryPoints(r.START_TO_START, t.selectedRange) === 0 && r.compareBoundaryPoints(r.END_TO_END, t.selectedRange) === 0) {
630
+ // Safari, Opera and Chrome only ever select text which causes the range to change.
631
+ // This lets us use the originally set range if the selection hasn't been changed by the user.
632
+ r = t.explicitRange;
633
+ } else {
634
+ t.selectedRange = null;
635
+ t.explicitRange = null;
636
+ }
637
+ }
638
  return r;
639
  },
640
 
646
  */
647
  setRng : function(r) {
648
  var s, t = this;
649
+
650
  if (!t.tridentSel) {
651
  s = t.getSel();
652
 
653
  if (s) {
654
+ t.explicitRange = r;
655
  s.removeAllRanges();
656
  s.addRange(r);
657
+ t.selectedRange = s.getRangeAt(0);
658
  }
659
  } else {
660
  // Is W3C Range
js/tiny_mce/classes/dom/Sizzle.js CHANGED
@@ -8,14 +8,26 @@
8
  */
9
  (function(){
10
 
11
- var chunker = /((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^[\]]*\]|['"][^'"]*['"]|[^[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?/g,
12
  done = 0,
13
  toString = Object.prototype.toString,
14
- hasDuplicate = false;
 
 
 
 
 
 
 
 
 
 
15
 
16
  var Sizzle = function(selector, context, results, seed) {
17
  results = results || [];
18
- var origContext = context = context || document;
 
 
19
 
20
  if ( context.nodeType !== 1 && context.nodeType !== 9 ) {
21
  return [];
@@ -25,19 +37,25 @@ var Sizzle = function(selector, context, results, seed) {
25
  return results;
26
  }
27
 
28
- var parts = [], m, set, checkSet, check, mode, extra, prune = true, contextXML = isXML(context);
 
29
 
30
  // Reset the position of the chunker regexp (start from head)
31
- chunker.lastIndex = 0;
32
-
33
- while ( (m = chunker.exec(selector)) !== null ) {
34
- parts.push( m[1] );
 
 
 
 
35
 
36
- if ( m[2] ) {
37
- extra = RegExp.rightContext;
38
- break;
 
39
  }
40
- }
41
 
42
  if ( parts.length > 1 && origPOS.exec( selector ) ) {
43
  if ( parts.length === 2 && Expr.relative[ parts[0] ] ) {
@@ -50,9 +68,10 @@ var Sizzle = function(selector, context, results, seed) {
50
  while ( parts.length ) {
51
  selector = parts.shift();
52
 
53
- if ( Expr.relative[ selector ] )
54
  selector += parts.shift();
55
-
 
56
  set = posProcess( selector, set );
57
  }
58
  }
@@ -61,12 +80,12 @@ var Sizzle = function(selector, context, results, seed) {
61
  // (but not if it'll be faster if the inner selector is an ID)
62
  if ( !seed && parts.length > 1 && context.nodeType === 9 && !contextXML &&
63
  Expr.match.ID.test(parts[0]) && !Expr.match.ID.test(parts[parts.length - 1]) ) {
64
- var ret = Sizzle.find( parts.shift(), context, contextXML );
65
  context = ret.expr ? Sizzle.filter( ret.expr, ret.set )[0] : ret.set[0];
66
  }
67
 
68
  if ( context ) {
69
- var ret = seed ?
70
  { expr: parts.pop(), set: makeArray(seed) } :
71
  Sizzle.find( parts.pop(), parts.length === 1 && (parts[0] === "~" || parts[0] === "+") && context.parentNode ? context.parentNode : context, contextXML );
72
  set = ret.expr ? Sizzle.filter( ret.expr, ret.set ) : ret.set;
@@ -78,7 +97,8 @@ var Sizzle = function(selector, context, results, seed) {
78
  }
79
 
80
  while ( parts.length ) {
81
- var cur = parts.pop(), pop = cur;
 
82
 
83
  if ( !Expr.relative[ cur ] ) {
84
  cur = "";
@@ -102,20 +122,20 @@ var Sizzle = function(selector, context, results, seed) {
102
  }
103
 
104
  if ( !checkSet ) {
105
- throw "Syntax error, unrecognized expression: " + (cur || selector);
106
  }
107
 
108
  if ( toString.call(checkSet) === "[object Array]" ) {
109
  if ( !prune ) {
110
  results.push.apply( results, checkSet );
111
  } else if ( context && context.nodeType === 1 ) {
112
- for ( var i = 0; checkSet[i] != null; i++ ) {
113
- if ( checkSet[i] && (checkSet[i] === true || checkSet[i].nodeType === 1 && contains(context, checkSet[i])) ) {
114
  results.push( set[i] );
115
  }
116
  }
117
  } else {
118
- for ( var i = 0; checkSet[i] != null; i++ ) {
119
  if ( checkSet[i] && checkSet[i].nodeType === 1 ) {
120
  results.push( set[i] );
121
  }
@@ -135,7 +155,7 @@ var Sizzle = function(selector, context, results, seed) {
135
 
136
  Sizzle.uniqueSort = function(results){
137
  if ( sortOrder ) {
138
- hasDuplicate = false;
139
  results.sort(sortOrder);
140
 
141
  if ( hasDuplicate ) {
@@ -146,6 +166,8 @@ Sizzle.uniqueSort = function(results){
146
  }
147
  }
148
  }
 
 
149
  };
150
 
151
  Sizzle.matches = function(expr, set){
@@ -153,7 +175,7 @@ Sizzle.matches = function(expr, set){
153
  };
154
 
155
  Sizzle.find = function(expr, context, isXML){
156
- var set, match;
157
 
158
  if ( !expr ) {
159
  return [];
@@ -162,8 +184,9 @@ Sizzle.find = function(expr, context, isXML){
162
  for ( var i = 0, l = Expr.order.length; i < l; i++ ) {
163
  var type = Expr.order[i], match;
164
 
165
- if ( (match = Expr.match[ type ].exec( expr )) ) {
166
- var left = RegExp.leftContext;
 
167
 
168
  if ( left.substr( left.length - 1 ) !== "\\" ) {
169
  match[1] = (match[1] || "").replace(/\\/g, "");
@@ -185,15 +208,21 @@ Sizzle.find = function(expr, context, isXML){
185
 
186
  Sizzle.filter = function(expr, set, inplace, not){
187
  var old = expr, result = [], curLoop = set, match, anyFound,
188
- isXMLFilter = set && set[0] && isXML(set[0]);
189
 
190
  while ( expr && set.length ) {
191
  for ( var type in Expr.filter ) {
192
- if ( (match = Expr.match[ type ].exec( expr )) != null ) {
193
- var filter = Expr.filter[ type ], found, item;
194
  anyFound = false;
195
 
196
- if ( curLoop == result ) {
 
 
 
 
 
 
197
  result = [];
198
  }
199
 
@@ -244,9 +273,9 @@ Sizzle.filter = function(expr, set, inplace, not){
244
  }
245
 
246
  // Improper expression
247
- if ( expr == old ) {
248
  if ( anyFound == null ) {
249
- throw "Syntax error, unrecognized expression: " + expr;
250
  } else {
251
  break;
252
  }
@@ -258,18 +287,23 @@ Sizzle.filter = function(expr, set, inplace, not){
258
  return curLoop;
259
  };
260
 
 
 
 
 
261
  var Expr = Sizzle.selectors = {
262
  order: [ "ID", "NAME", "TAG" ],
263
  match: {
264
- ID: /#((?:[\w\u00c0-\uFFFF_-]|\\.)+)/,
265
- CLASS: /\.((?:[\w\u00c0-\uFFFF_-]|\\.)+)/,
266
- NAME: /\[name=['"]*((?:[\w\u00c0-\uFFFF_-]|\\.)+)['"]*\]/,
267
- ATTR: /\[\s*((?:[\w\u00c0-\uFFFF_-]|\\.)+)\s*(?:(\S?=)\s*(['"]*)(.*?)\3|)\s*\]/,
268
- TAG: /^((?:[\w\u00c0-\uFFFF\*_-]|\\.)+)/,
269
- CHILD: /:(only|nth|last|first)-child(?:\((even|odd|[\dn+-]*)\))?/,
270
- POS: /:(nth|eq|gt|lt|first|last|even|odd)(?:\((\d*)\))?(?=[^-]|$)/,
271
- PSEUDO: /:((?:[\w\u00c0-\uFFFF_-]|\\.)+)(?:\((['"]*)((?:\([^\)]+\)|[^\2\(\)]*)+)\2\))?/
272
  },
 
273
  attrMap: {
274
  "class": "className",
275
  "for": "htmlFor"
@@ -280,20 +314,20 @@ var Expr = Sizzle.selectors = {
280
  }
281
  },
282
  relative: {
283
- "+": function(checkSet, part, isXML){
284
  var isPartStr = typeof part === "string",
285
  isTag = isPartStr && !/\W/.test(part),
286
  isPartStrNotTag = isPartStr && !isTag;
287
 
288
- if ( isTag && !isXML ) {
289
- part = part.toUpperCase();
290
  }
291
 
292
  for ( var i = 0, l = checkSet.length, elem; i < l; i++ ) {
293
  if ( (elem = checkSet[i]) ) {
294
  while ( (elem = elem.previousSibling) && elem.nodeType !== 1 ) {}
295
 
296
- checkSet[i] = isPartStrNotTag || elem && elem.nodeName === part ?
297
  elem || false :
298
  elem === part;
299
  }
@@ -303,22 +337,23 @@ var Expr = Sizzle.selectors = {
303
  Sizzle.filter( part, checkSet, true );
304
  }
305
  },
306
- ">": function(checkSet, part, isXML){
307
- var isPartStr = typeof part === "string";
 
308
 
309
  if ( isPartStr && !/\W/.test(part) ) {
310
- part = isXML ? part : part.toUpperCase();
311
 
312
- for ( var i = 0, l = checkSet.length; i < l; i++ ) {
313
- var elem = checkSet[i];
314
  if ( elem ) {
315
  var parent = elem.parentNode;
316
- checkSet[i] = parent.nodeName === part ? parent : false;
317
  }
318
  }
319
  } else {
320
- for ( var i = 0, l = checkSet.length; i < l; i++ ) {
321
- var elem = checkSet[i];
322
  if ( elem ) {
323
  checkSet[i] = isPartStr ?
324
  elem.parentNode :
@@ -332,20 +367,22 @@ var Expr = Sizzle.selectors = {
332
  }
333
  },
334
  "": function(checkSet, part, isXML){
335
- var doneName = done++, checkFn = dirCheck;
336
 
337
- if ( !part.match(/\W/) ) {
338
- var nodeCheck = part = isXML ? part : part.toUpperCase();
 
339
  checkFn = dirNodeCheck;
340
  }
341
 
342
  checkFn("parentNode", part, doneName, checkSet, nodeCheck, isXML);
343
  },
344
  "~": function(checkSet, part, isXML){
345
- var doneName = done++, checkFn = dirCheck;
346
 
347
- if ( typeof part === "string" && !part.match(/\W/) ) {
348
- var nodeCheck = part = isXML ? part : part.toUpperCase();
 
349
  checkFn = dirNodeCheck;
350
  }
351
 
@@ -359,7 +396,7 @@ var Expr = Sizzle.selectors = {
359
  return m ? [m] : [];
360
  }
361
  },
362
- NAME: function(match, context, isXML){
363
  if ( typeof context.getElementsByName !== "undefined" ) {
364
  var ret = [], results = context.getElementsByName(match[1]);
365
 
@@ -386,9 +423,10 @@ var Expr = Sizzle.selectors = {
386
 
387
  for ( var i = 0, elem; (elem = curLoop[i]) != null; i++ ) {
388
  if ( elem ) {
389
- if ( not ^ (elem.className && (" " + elem.className + " ").indexOf(match) >= 0) ) {
390
- if ( !inplace )
391
  result.push( elem );
 
392
  } else if ( inplace ) {
393
  curLoop[i] = false;
394
  }
@@ -401,14 +439,13 @@ var Expr = Sizzle.selectors = {
401
  return match[1].replace(/\\/g, "");
402
  },
403
  TAG: function(match, curLoop){
404
- for ( var i = 0; curLoop[i] === false; i++ ){}
405
- return curLoop[i] && isXML(curLoop[i]) ? match[1] : match[1].toUpperCase();
406
  },
407
  CHILD: function(match){
408
- if ( match[1] == "nth" ) {
409
  // parse equations like 'even', 'odd', '5', '2n', '3n+2', '4n-1', '-n+6'
410
  var test = /(-?)(\d*)n((?:\+|-)?\d*)/.exec(
411
- match[2] == "even" && "2n" || match[2] == "odd" && "2n+1" ||
412
  !/\D/.test( match[2] ) && "0n+" + match[2] || match[2]);
413
 
414
  // calculate the numbers (first)n+(last) including if they are negative
@@ -437,7 +474,7 @@ var Expr = Sizzle.selectors = {
437
  PSEUDO: function(match, curLoop, inplace, result, not){
438
  if ( match[1] === "not" ) {
439
  // If we're dealing with a complex expression, or a simple one
440
- if ( match[3].match(chunker).length > 1 || /^\w/.test(match[3]) ) {
441
  match[3] = Sizzle(match[3], null, null, curLoop);
442
  } else {
443
  var ret = Sizzle.filter(match[3], curLoop, inplace, true ^ not);
@@ -483,7 +520,7 @@ var Expr = Sizzle.selectors = {
483
  return !!Sizzle( match[3], elem ).length;
484
  },
485
  header: function(elem){
486
- return /h\d/i.test( elem.nodeName );
487
  },
488
  text: function(elem){
489
  return "text" === elem.type;
@@ -510,10 +547,10 @@ var Expr = Sizzle.selectors = {
510
  return "reset" === elem.type;
511
  },
512
  button: function(elem){
513
- return "button" === elem.type || elem.nodeName.toUpperCase() === "BUTTON";
514
  },
515
  input: function(elem){
516
- return /input|select|textarea|button/i.test(elem.nodeName);
517
  }
518
  },
519
  setFilters: {
@@ -536,10 +573,10 @@ var Expr = Sizzle.selectors = {
536
  return i > match[3] - 0;
537
  },
538
  nth: function(elem, i, match){
539
- return match[3] - 0 == i;
540
  },
541
  eq: function(elem, i, match){
542
- return match[3] - 0 == i;
543
  }
544
  },
545
  filter: {
@@ -549,17 +586,19 @@ var Expr = Sizzle.selectors = {
549
  if ( filter ) {
550
  return filter( elem, i, match, array );
551
  } else if ( name === "contains" ) {
552
- return (elem.textContent || elem.innerText || "").indexOf(match[3]) >= 0;
553
  } else if ( name === "not" ) {
554
  var not = match[3];
555
 
556
- for ( var i = 0, l = not.length; i < l; i++ ) {
557
- if ( not[i] === elem ) {
558
  return false;
559
  }
560
  }
561
 
562
  return true;
 
 
563
  }
564
  },
565
  CHILD: function(elem, match){
@@ -567,20 +606,26 @@ var Expr = Sizzle.selectors = {
567
  switch (type) {
568
  case 'only':
569
  case 'first':
570
- while (node = node.previousSibling) {
571
- if ( node.nodeType === 1 ) return false;
 
 
 
 
 
572
  }
573
- if ( type == 'first') return true;
574
  node = elem;
575
  case 'last':
576
- while (node = node.nextSibling) {
577
- if ( node.nodeType === 1 ) return false;
 
 
578
  }
579
  return true;
580
  case 'nth':
581
  var first = match[2], last = match[3];
582
 
583
- if ( first == 1 && last == 0 ) {
584
  return true;
585
  }
586
 
@@ -598,10 +643,10 @@ var Expr = Sizzle.selectors = {
598
  }
599
 
600
  var diff = elem.nodeIndex - last;
601
- if ( first == 0 ) {
602
- return diff == 0;
603
  } else {
604
- return ( diff % first == 0 && diff / first >= 0 );
605
  }
606
  }
607
  },
@@ -609,7 +654,7 @@ var Expr = Sizzle.selectors = {
609
  return elem.nodeType === 1 && elem.getAttribute("id") === match;
610
  },
611
  TAG: function(elem, match){
612
- return (match === "*" && elem.nodeType === 1) || elem.nodeName === match;
613
  },
614
  CLASS: function(elem, match){
615
  return (" " + (elem.className || elem.getAttribute("class")) + " ")
@@ -637,7 +682,7 @@ var Expr = Sizzle.selectors = {
637
  !check ?
638
  value && result !== false :
639
  type === "!=" ?
640
- value != check :
641
  type === "^=" ?
642
  value.indexOf(check) === 0 :
643
  type === "$=" ?
@@ -656,14 +701,18 @@ var Expr = Sizzle.selectors = {
656
  }
657
  };
658
 
659
- var origPOS = Expr.match.POS;
 
 
 
660
 
661
  for ( var type in Expr.match ) {
662
- Expr.match[ type ] = new RegExp( Expr.match[ type ].source + /(?![^\[]*\])(?![^\(]*\))/.source );
 
663
  }
664
 
665
  var makeArray = function(array, results) {
666
- array = Array.prototype.slice.call( array );
667
 
668
  if ( results ) {
669
  results.push.apply( results, array );
@@ -675,23 +724,25 @@ var makeArray = function(array, results) {
675
 
676
  // Perform a simple check to determine if the browser is capable of
677
  // converting a NodeList to an array using builtin methods.
 
 
678
  try {
679
- Array.prototype.slice.call( document.documentElement.childNodes );
680
 
681
  // Provide a fallback method if it does not work
682
  } catch(e){
683
  makeArray = function(array, results) {
684
- var ret = results || [];
685
 
686
  if ( toString.call(array) === "[object Array]" ) {
687
  Array.prototype.push.apply( ret, array );
688
  } else {
689
  if ( typeof array.length === "number" ) {
690
- for ( var i = 0, l = array.length; i < l; i++ ) {
691
  ret.push( array[i] );
692
  }
693
  } else {
694
- for ( var i = 0; array[i]; i++ ) {
695
  ret.push( array[i] );
696
  }
697
  }
@@ -705,6 +756,13 @@ var sortOrder;
705
 
706
  if ( document.documentElement.compareDocumentPosition ) {
707
  sortOrder = function( a, b ) {
 
 
 
 
 
 
 
708
  var ret = a.compareDocumentPosition(b) & 4 ? -1 : a === b ? 0 : 1;
709
  if ( ret === 0 ) {
710
  hasDuplicate = true;
@@ -713,6 +771,13 @@ if ( document.documentElement.compareDocumentPosition ) {
713
  };
714
  } else if ( "sourceIndex" in document.documentElement ) {
715
  sortOrder = function( a, b ) {
 
 
 
 
 
 
 
716
  var ret = a.sourceIndex - b.sourceIndex;
717
  if ( ret === 0 ) {
718
  hasDuplicate = true;
@@ -721,6 +786,13 @@ if ( document.documentElement.compareDocumentPosition ) {
721
  };
722
  } else if ( document.createRange ) {
723
  sortOrder = function( a, b ) {
 
 
 
 
 
 
 
724
  var aRange = a.ownerDocument.createRange(), bRange = b.ownerDocument.createRange();
725
  aRange.setStart(a, 0);
726
  aRange.setEnd(a, 0);
@@ -734,12 +806,32 @@ if ( document.documentElement.compareDocumentPosition ) {
734
  };
735
  }
736
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
737
  // Check to see if the browser returns elements by name when
738
  // querying by getElementById (and provide a workaround)
739
  (function(){
740
  // We're going to inject a fake input element with a specified name
741
  var form = document.createElement("div"),
742
- id = "script" + (new Date).getTime();
743
  form.innerHTML = "<a name='" + id + "'/>";
744
 
745
  // Inject it into the root element, check its status, and remove it quickly
@@ -748,7 +840,7 @@ if ( document.documentElement.compareDocumentPosition ) {
748
 
749
  // The workaround has to do additional checks after a getElementById
750
  // Which slows things down for other browsers (hence the branching)
751
- if ( !!document.getElementById( id ) ) {
752
  Expr.find.ID = function(match, context, isXML){
753
  if ( typeof context.getElementById !== "undefined" && !isXML ) {
754
  var m = context.getElementById(match[1]);
@@ -763,6 +855,7 @@ if ( document.documentElement.compareDocumentPosition ) {
763
  }
764
 
765
  root.removeChild( form );
 
766
  })();
767
 
768
  (function(){
@@ -803,68 +896,75 @@ if ( document.documentElement.compareDocumentPosition ) {
803
  return elem.getAttribute("href", 2);
804
  };
805
  }
 
 
806
  })();
807
 
808
- if ( document.querySelectorAll ) (function(){
809
- var oldSizzle = Sizzle, div = document.createElement("div");
810
- div.innerHTML = "<p class='TEST'></p>";
 
811
 
812
- // Safari can't handle uppercase or unicode characters when
813
- // in quirks mode.
814
- if ( div.querySelectorAll && div.querySelectorAll(".TEST").length === 0 ) {
815
- return;
816
- }
817
-
818
- Sizzle = function(query, context, extra, seed){
819
- context = context || document;
820
-
821
- // Only use querySelectorAll on non-XML documents
822
- // (ID selectors don't work in non-HTML documents)
823
- if ( !seed && context.nodeType === 9 && !isXML(context) ) {
824
- try {
825
- return makeArray( context.querySelectorAll(query), extra );
826
- } catch(e){}
827
  }
 
 
 
 
 
 
 
 
 
 
 
828
 
829
- return oldSizzle(query, context, extra, seed);
830
- };
831
 
832
- for ( var prop in oldSizzle ) {
833
- Sizzle[ prop ] = oldSizzle[ prop ];
834
- }
835
- })();
836
 
837
- if ( document.getElementsByClassName && document.documentElement.getElementsByClassName ) (function(){
 
 
 
 
838
  var div = document.createElement("div");
 
839
  div.innerHTML = "<div class='test e'></div><div class='test'></div>";
840
 
841
  // Opera can't find a second classname (in 9.6)
842
- if ( div.getElementsByClassName("e").length === 0 )
 
843
  return;
 
844
 
845
  // Safari caches class attributes, doesn't catch changes (in 3.2)
846
  div.lastChild.className = "e";
847
 
848
- if ( div.getElementsByClassName("e").length === 1 )
849
  return;
850
-
 
851
  Expr.order.splice(1, 0, "CLASS");
852
  Expr.find.CLASS = function(match, context, isXML) {
853
  if ( typeof context.getElementsByClassName !== "undefined" && !isXML ) {
854
  return context.getElementsByClassName(match[1]);
855
  }
856
  };
 
 
857
  })();
858
 
859
  function dirNodeCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) {
860
- var sibDir = dir == "previousSibling" && !isXML;
861
  for ( var i = 0, l = checkSet.length; i < l; i++ ) {
862
  var elem = checkSet[i];
863
  if ( elem ) {
864
- if ( sibDir && elem.nodeType === 1 ){
865
- elem.sizcache = doneName;
866
- elem.sizset = i;
867
- }
868
  elem = elem[dir];
869
  var match = false;
870
 
@@ -879,7 +979,7 @@ function dirNodeCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) {
879
  elem.sizset = i;
880
  }
881
 
882
- if ( elem.nodeName === cur ) {
883
  match = elem;
884
  break;
885
  }
@@ -893,14 +993,9 @@ function dirNodeCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) {
893
  }
894
 
895
  function dirCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) {
896
- var sibDir = dir == "previousSibling" && !isXML;
897
  for ( var i = 0, l = checkSet.length; i < l; i++ ) {
898
  var elem = checkSet[i];
899
  if ( elem ) {
900
- if ( sibDir && elem.nodeType === 1 ) {
901
- elem.sizcache = doneName;
902
- elem.sizset = i;
903
- }
904
  elem = elem[dir];
905
  var match = false;
906
 
@@ -935,15 +1030,17 @@ function dirCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) {
935
  }
936
  }
937
 
938
- var contains = document.compareDocumentPosition ? function(a, b){
939
- return a.compareDocumentPosition(b) & 16;
940
  } : function(a, b){
941
  return a !== b && (a.contains ? a.contains(b) : true);
942
  };
943
 
944
- var isXML = function(elem){
945
- return elem.nodeType === 9 && elem.documentElement.nodeName !== "HTML" ||
946
- !!elem.ownerDocument && elem.ownerDocument.documentElement.nodeName !== "HTML";
 
 
947
  };
948
 
949
  var posProcess = function(selector, context){
8
  */
9
  (function(){
10
 
11
+ var chunker = /((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^\[\]]*\]|['"][^'"]*['"]|[^\[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g,
12
  done = 0,
13
  toString = Object.prototype.toString,
14
+ hasDuplicate = false,
15
+ baseHasDuplicate = true;
16
+
17
+ // Here we check if the JavaScript engine is using some sort of
18
+ // optimization where it does not always call our comparision
19
+ // function. If that is the case, discard the hasDuplicate value.
20
+ // Thus far that includes Google Chrome.
21
+ [0, 0].sort(function(){
22
+ baseHasDuplicate = false;
23
+ return 0;
24
+ });
25
 
26
  var Sizzle = function(selector, context, results, seed) {
27
  results = results || [];
28
+ context = context || document;
29
+
30
+ var origContext = context;
31
 
32
  if ( context.nodeType !== 1 && context.nodeType !== 9 ) {
33
  return [];
37
  return results;
38
  }
39
 
40
+ var parts = [], m, set, checkSet, extra, prune = true, contextXML = Sizzle.isXML(context),
41
+ soFar = selector, ret, cur, pop, i;
42
 
43
  // Reset the position of the chunker regexp (start from head)
44
+ do {
45
+ chunker.exec("");
46
+ m = chunker.exec(soFar);
47
+
48
+ if ( m ) {
49
+ soFar = m[3];
50
+
51
+ parts.push( m[1] );
52
 
53
+ if ( m[2] ) {
54
+ extra = m[3];
55
+ break;
56
+ }
57
  }
58
+ } while ( m );
59
 
60
  if ( parts.length > 1 && origPOS.exec( selector ) ) {
61
  if ( parts.length === 2 && Expr.relative[ parts[0] ] ) {
68
  while ( parts.length ) {
69
  selector = parts.shift();
70
 
71
+ if ( Expr.relative[ selector ] ) {
72
  selector += parts.shift();
73
+ }
74
+
75
  set = posProcess( selector, set );
76
  }
77
  }
80
  // (but not if it'll be faster if the inner selector is an ID)
81
  if ( !seed && parts.length > 1 && context.nodeType === 9 && !contextXML &&
82
  Expr.match.ID.test(parts[0]) && !Expr.match.ID.test(parts[parts.length - 1]) ) {
83
+ ret = Sizzle.find( parts.shift(), context, contextXML );
84
  context = ret.expr ? Sizzle.filter( ret.expr, ret.set )[0] : ret.set[0];
85
  }
86
 
87
  if ( context ) {
88
+ ret = seed ?
89
  { expr: parts.pop(), set: makeArray(seed) } :
90
  Sizzle.find( parts.pop(), parts.length === 1 && (parts[0] === "~" || parts[0] === "+") && context.parentNode ? context.parentNode : context, contextXML );
91
  set = ret.expr ? Sizzle.filter( ret.expr, ret.set ) : ret.set;
97
  }
98
 
99
  while ( parts.length ) {
100
+ cur = parts.pop();
101
+ pop = cur;
102
 
103
  if ( !Expr.relative[ cur ] ) {
104
  cur = "";
122
  }
123
 
124
  if ( !checkSet ) {
125
+ Sizzle.error( cur || selector );
126
  }
127
 
128
  if ( toString.call(checkSet) === "[object Array]" ) {
129
  if ( !prune ) {
130
  results.push.apply( results, checkSet );
131
  } else if ( context && context.nodeType === 1 ) {
132
+ for ( i = 0; checkSet[i] != null; i++ ) {
133
+ if ( checkSet[i] && (checkSet[i] === true || checkSet[i].nodeType === 1 && Sizzle.contains(context, checkSet[i])) ) {
134
  results.push( set[i] );
135
  }
136
  }
137
  } else {
138
+ for ( i = 0; checkSet[i] != null; i++ ) {
139
  if ( checkSet[i] && checkSet[i].nodeType === 1 ) {
140
  results.push( set[i] );
141
  }
155
 
156
  Sizzle.uniqueSort = function(results){
157
  if ( sortOrder ) {
158
+ hasDuplicate = baseHasDuplicate;
159
  results.sort(sortOrder);
160
 
161
  if ( hasDuplicate ) {
166
  }
167
  }
168
  }
169
+
170
+ return results;
171
  };
172
 
173
  Sizzle.matches = function(expr, set){
175
  };
176
 
177
  Sizzle.find = function(expr, context, isXML){
178
+ var set;
179
 
180
  if ( !expr ) {
181
  return [];
184
  for ( var i = 0, l = Expr.order.length; i < l; i++ ) {
185
  var type = Expr.order[i], match;
186
 
187
+ if ( (match = Expr.leftMatch[ type ].exec( expr )) ) {
188
+ var left = match[1];
189
+ match.splice(1,1);
190
 
191
  if ( left.substr( left.length - 1 ) !== "\\" ) {
192
  match[1] = (match[1] || "").replace(/\\/g, "");
208
 
209
  Sizzle.filter = function(expr, set, inplace, not){
210
  var old = expr, result = [], curLoop = set, match, anyFound,
211
+ isXMLFilter = set && set[0] && Sizzle.isXML(set[0]);
212
 
213
  while ( expr && set.length ) {
214
  for ( var type in Expr.filter ) {
215
+ if ( (match = Expr.leftMatch[ type ].exec( expr )) != null && match[2] ) {
216
+ var filter = Expr.filter[ type ], found, item, left = match[1];
217
  anyFound = false;
218
 
219
+ match.splice(1,1);
220
+
221
+ if ( left.substr( left.length - 1 ) === "\\" ) {
222
+ continue;
223
+ }
224
+
225
+ if ( curLoop === result ) {
226
  result = [];
227
  }
228
 
273
  }
274
 
275
  // Improper expression
276
+ if ( expr === old ) {
277
  if ( anyFound == null ) {
278
+ Sizzle.error( expr );
279
  } else {
280
  break;
281
  }
287
  return curLoop;
288
  };
289
 
290
+ Sizzle.error = function( msg ) {
291
+ throw "Syntax error, unrecognized expression: " + msg;
292
+ };
293
+
294
  var Expr = Sizzle.selectors = {
295
  order: [ "ID", "NAME", "TAG" ],
296
  match: {
297
+ ID: /#((?:[\w\u00c0-\uFFFF\-]|\\.)+)/,
298
+ CLASS: /\.((?:[\w\u00c0-\uFFFF\-]|\\.)+)/,
299
+ NAME: /\[name=['"]*((?:[\w\u00c0-\uFFFF\-]|\\.)+)['"]*\]/,
300
+ ATTR: /\[\s*((?:[\w\u00c0-\uFFFF\-]|\\.)+)\s*(?:(\S?=)\s*(['"]*)(.*?)\3|)\s*\]/,
301
+ TAG: /^((?:[\w\u00c0-\uFFFF\*\-]|\\.)+)/,
302
+ CHILD: /:(only|nth|last|first)-child(?:\((even|odd|[\dn+\-]*)\))?/,
303
+ POS: /:(nth|eq|gt|lt|first|last|even|odd)(?:\((\d*)\))?(?=[^\-]|$)/,
304
+ PSEUDO: /:((?:[\w\u00c0-\uFFFF\-]|\\.)+)(?:\((['"]?)((?:\([^\)]+\)|[^\(\)]*)+)\2\))?/
305
  },
306
+ leftMatch: {},
307
  attrMap: {
308
  "class": "className",
309
  "for": "htmlFor"
314
  }
315
  },
316
  relative: {
317
+ "+": function(checkSet, part){
318
  var isPartStr = typeof part === "string",
319
  isTag = isPartStr && !/\W/.test(part),
320
  isPartStrNotTag = isPartStr && !isTag;
321
 
322
+ if ( isTag ) {
323
+ part = part.toLowerCase();
324
  }
325
 
326
  for ( var i = 0, l = checkSet.length, elem; i < l; i++ ) {
327
  if ( (elem = checkSet[i]) ) {
328
  while ( (elem = elem.previousSibling) && elem.nodeType !== 1 ) {}
329
 
330
+ checkSet[i] = isPartStrNotTag || elem && elem.nodeName.toLowerCase() === part ?
331
  elem || false :
332
  elem === part;
333
  }
337
  Sizzle.filter( part, checkSet, true );
338
  }
339
  },
340
+ ">": function(checkSet, part){
341
+ var isPartStr = typeof part === "string",
342
+ elem, i = 0, l = checkSet.length;
343
 
344
  if ( isPartStr && !/\W/.test(part) ) {
345
+ part = part.toLowerCase();
346
 
347
+ for ( ; i < l; i++ ) {
348
+ elem = checkSet[i];
349
  if ( elem ) {
350
  var parent = elem.parentNode;
351
+ checkSet[i] = parent.nodeName.toLowerCase() === part ? parent : false;
352
  }
353
  }
354
  } else {
355
+ for ( ; i < l; i++ ) {
356
+ elem = checkSet[i];
357
  if ( elem ) {
358
  checkSet[i] = isPartStr ?
359
  elem.parentNode :
367
  }
368
  },
369
  "": function(checkSet, part, isXML){
370
+ var doneName = done++, checkFn = dirCheck, nodeCheck;
371
 
372
+ if ( typeof part === "string" && !/\W/.test(part) ) {
373
+ part = part.toLowerCase();
374
+ nodeCheck = part;
375
  checkFn = dirNodeCheck;
376
  }
377
 
378
  checkFn("parentNode", part, doneName, checkSet, nodeCheck, isXML);
379
  },
380
  "~": function(checkSet, part, isXML){
381
+ var doneName = done++, checkFn = dirCheck, nodeCheck;
382
 
383
+ if ( typeof part === "string" && !/\W/.test(part) ) {
384
+ part = part.toLowerCase();
385
+ nodeCheck = part;
386
  checkFn = dirNodeCheck;
387
  }
388
 
396
  return m ? [m] : [];
397
  }
398
  },
399
+ NAME: function(match, context){
400
  if ( typeof context.getElementsByName !== "undefined" ) {
401
  var ret = [], results = context.getElementsByName(match[1]);
402
 
423
 
424
  for ( var i = 0, elem; (elem = curLoop[i]) != null; i++ ) {
425
  if ( elem ) {
426
+ if ( not ^ (elem.className && (" " + elem.className + " ").replace(/[\t\n]/g, " ").indexOf(match) >= 0) ) {
427
+ if ( !inplace ) {
428
  result.push( elem );
429
+ }
430
  } else if ( inplace ) {
431
  curLoop[i] = false;
432
  }
439
  return match[1].replace(/\\/g, "");
440
  },
441
  TAG: function(match, curLoop){
442
+ return match[1].toLowerCase();
 
443
  },
444
  CHILD: function(match){
445
+ if ( match[1] === "nth" ) {
446
  // parse equations like 'even', 'odd', '5', '2n', '3n+2', '4n-1', '-n+6'
447
  var test = /(-?)(\d*)n((?:\+|-)?\d*)/.exec(
448
+ match[2] === "even" && "2n" || match[2] === "odd" && "2n+1" ||
449
  !/\D/.test( match[2] ) && "0n+" + match[2] || match[2]);
450
 
451
  // calculate the numbers (first)n+(last) including if they are negative
474
  PSEUDO: function(match, curLoop, inplace, result, not){
475
  if ( match[1] === "not" ) {
476
  // If we're dealing with a complex expression, or a simple one
477
+ if ( ( chunker.exec(match[3]) || "" ).length > 1 || /^\w/.test(match[3]) ) {
478
  match[3] = Sizzle(match[3], null, null, curLoop);
479
  } else {
480
  var ret = Sizzle.filter(match[3], curLoop, inplace, true ^ not);
520
  return !!Sizzle( match[3], elem ).length;
521
  },
522
  header: function(elem){
523
+ return (/h\d/i).test( elem.nodeName );
524
  },
525
  text: function(elem){
526
  return "text" === elem.type;
547
  return "reset" === elem.type;
548
  },
549
  button: function(elem){
550
+ return "button" === elem.type || elem.nodeName.toLowerCase() === "button";
551
  },
552
  input: function(elem){
553
+ return (/input|select|textarea|button/i).test(elem.nodeName);
554
  }
555
  },
556
  setFilters: {
573
  return i > match[3] - 0;
574
  },
575
  nth: function(elem, i, match){
576
+ return match[3] - 0 === i;
577
  },
578
  eq: function(elem, i, match){
579
+ return match[3] - 0 === i;
580
  }
581
  },
582
  filter: {
586
  if ( filter ) {
587
  return filter( elem, i, match, array );
588
  } else if ( name === "contains" ) {
589
+ return (elem.textContent || elem.innerText || Sizzle.getText([ elem ]) || "").indexOf(match[3]) >= 0;
590
  } else if ( name === "not" ) {
591
  var not = match[3];
592
 
593
+ for ( var j = 0, l = not.length; j < l; j++ ) {
594
+ if ( not[j] === elem ) {
595
  return false;
596
  }
597
  }
598
 
599
  return true;
600
+ } else {
601
+ Sizzle.error( "Syntax error, unrecognized expression: " + name );
602
  }
603
  },
604
  CHILD: function(elem, match){
606
  switch (type) {
607
  case 'only':
608
  case 'first':
609
+ while ( (node = node.previousSibling) ) {
610
+ if ( node.nodeType === 1 ) {
611
+ return false;
612
+ }
613
+ }
614
+ if ( type === "first" ) {
615
+ return true;
616
  }
 
617
  node = elem;
618
  case 'last':
619
+ while ( (node = node.nextSibling) ) {
620
+ if ( node.nodeType === 1 ) {
621
+ return false;
622
+ }
623
  }
624
  return true;
625
  case 'nth':
626
  var first = match[2], last = match[3];
627
 
628
+ if ( first === 1 && last === 0 ) {
629
  return true;
630
  }
631
 
643
  }
644
 
645
  var diff = elem.nodeIndex - last;
646
+ if ( first === 0 ) {
647
+ return diff === 0;
648
  } else {
649
+ return ( diff % first === 0 && diff / first >= 0 );
650
  }
651
  }
652
  },
654
  return elem.nodeType === 1 && elem.getAttribute("id") === match;
655
  },
656
  TAG: function(elem, match){
657
+ return (match === "*" && elem.nodeType === 1) || elem.nodeName.toLowerCase() === match;
658
  },
659
  CLASS: function(elem, match){
660
  return (" " + (elem.className || elem.getAttribute("class")) + " ")
682
  !check ?
683
  value && result !== false :
684
  type === "!=" ?
685
+ value !== check :
686
  type === "^=" ?
687
  value.indexOf(check) === 0 :
688
  type === "$=" ?
701
  }
702
  };
703
 
704
+ var origPOS = Expr.match.POS,
705
+ fescape = function(all, num){
706
+ return "\\" + (num - 0 + 1);
707
+ };
708
 
709
  for ( var type in Expr.match ) {
710
+ Expr.match[ type ] = new RegExp( Expr.match[ type ].source + (/(?![^\[]*\])(?![^\(]*\))/.source) );
711
+ Expr.leftMatch[ type ] = new RegExp( /(^(?:.|\r|\n)*?)/.source + Expr.match[ type ].source.replace(/\\(\d+)/g, fescape) );
712
  }
713
 
714
  var makeArray = function(array, results) {
715
+ array = Array.prototype.slice.call( array, 0 );
716
 
717
  if ( results ) {
718
  results.push.apply( results, array );
724
 
725
  // Perform a simple check to determine if the browser is capable of
726
  // converting a NodeList to an array using builtin methods.
727
+ // Also verifies that the returned array holds DOM nodes
728
+ // (which is not the case in the Blackberry browser)
729
  try {
730
+ Array.prototype.slice.call( document.documentElement.childNodes, 0 )[0].nodeType;
731
 
732
  // Provide a fallback method if it does not work
733
  } catch(e){
734
  makeArray = function(array, results) {
735
+ var ret = results || [], i = 0;
736
 
737
  if ( toString.call(array) === "[object Array]" ) {
738
  Array.prototype.push.apply( ret, array );
739
  } else {
740
  if ( typeof array.length === "number" ) {
741
+ for ( var l = array.length; i < l; i++ ) {
742
  ret.push( array[i] );
743
  }
744
  } else {
745
+ for ( ; array[i]; i++ ) {
746
  ret.push( array[i] );
747
  }
748
  }
756
 
757
  if ( document.documentElement.compareDocumentPosition ) {
758
  sortOrder = function( a, b ) {
759
+ if ( !a.compareDocumentPosition || !b.compareDocumentPosition ) {
760
+ if ( a == b ) {
761
+ hasDuplicate = true;
762
+ }
763
+ return a.compareDocumentPosition ? -1 : 1;
764
+ }
765
+
766
  var ret = a.compareDocumentPosition(b) & 4 ? -1 : a === b ? 0 : 1;
767
  if ( ret === 0 ) {
768
  hasDuplicate = true;
771
  };
772
  } else if ( "sourceIndex" in document.documentElement ) {
773
  sortOrder = function( a, b ) {
774
+ if ( !a.sourceIndex || !b.sourceIndex ) {
775
+ if ( a == b ) {
776
+ hasDuplicate = true;
777
+ }
778
+ return a.sourceIndex ? -1 : 1;
779
+ }
780
+
781
  var ret = a.sourceIndex - b.sourceIndex;
782
  if ( ret === 0 ) {
783
  hasDuplicate = true;
786
  };
787
  } else if ( document.createRange ) {
788
  sortOrder = function( a, b ) {
789
+ if ( !a.ownerDocument || !b.ownerDocument ) {
790
+ if ( a == b ) {
791
+ hasDuplicate = true;
792
+ }
793
+ return a.ownerDocument ? -1 : 1;
794
+ }
795
+
796
  var aRange = a.ownerDocument.createRange(), bRange = b.ownerDocument.createRange();
797
  aRange.setStart(a, 0);
798
  aRange.setEnd(a, 0);
806
  };
807
  }
808
 
809
+ // Utility function for retreiving the text value of an array of DOM nodes
810
+ Sizzle.getText = function( elems ) {
811
+ var ret = "", elem;
812
+
813
+ for ( var i = 0; elems[i]; i++ ) {
814
+ elem = elems[