From 8552ce581df471ce8912c54931d27a995a988fc8 Mon Sep 17 00:00:00 2001 From: David Badley Date: Tue, 23 Jun 2009 07:41:41 +0800 Subject: [PATCH 001/164] Added selection and selected to commands.js for fonts, fontsizes, colors, backgrounds, and alignment. Added getStyle to iFrame.js to retrieve a style specified within the iFrame. Added an unattach method to the iFrame so you can remove the editor. Tested the above in editor_test.js. Signed-off-by: Joshua Peek --- src/wysihat/editor/commands.js | 157 +++++++++++++++++++++++++++++---- src/wysihat/models/iframe.js | 20 +++++ test/unit/editor_test.js | 85 +++++++++++++++++- 3 files changed, 240 insertions(+), 22 deletions(-) diff --git a/src/wysihat/editor/commands.js b/src/wysihat/editor/commands.js index 5764a25..8162a7e 100644 --- a/src/wysihat/editor/commands.js +++ b/src/wysihat/editor/commands.js @@ -86,6 +86,45 @@ WysiHat.Commands = (function() { this.execCommand('blockquote', false, null); } + /** + * WysiHat.Commands#fontSelection(font) -> undefined + * + * Sets the font for the current selection + **/ + function fontSelection(font) { + this.execCommand('fontname', false, font); + } + + /** + * WysiHat.Commands#fontSelected() -> style string + * + * Gets the font for the current selection + **/ + function fontSelected() { + var node = this.selection.getNode(); + return Element.getStyle(node, 'fontFamily'); + } + + /** + * WysiHat.Commands#fontSizeSelection(fontSize) -> undefined + * - font size (int) : font size for selection + * + * Sets the font size for the current selection + **/ + function fontSizeSelection(fontSize) { + this.execCommand('fontsize', false, fontSize); + } + + /** + * WysiHat.Commands#fontSizeSelected() -> Returns a fontsize, standardized for modern browsers + * + * Gets the font size for the current selection + **/ + function fontSizeSelected() { + var node = this.selection.getNode(); + return standardizeFontSize(Element.getStyle(node, 'fontSize')); + } + /** * WysiHat.Commands#colorSelection(color) -> undefined * - color (String): a color name or hexadecimal value @@ -96,6 +135,54 @@ WysiHat.Commands = (function() { this.execCommand('forecolor', false, color); } + /** + * WysiHat.Commands#colorSelected() -> string (color) + * + * Returns the color of the selected portion + **/ + function colorSelected() { + var node = this.selection.getNode(); + return Element.getStyle(node, 'color'); + } + + /** + * WysiHat.Commands#backgroundColorSelection(color) -> undefined + * - color (string) - a color or hexadecimal value + * + **/ + function backgroundColorSelection(color) { + this.execCommand('backcolor', false, color); + } + + /** + * WysiHat.Commands#backgroundColorSelected() -> color + * + * Returns the background color of the selected text area + **/ + function backgroundColorSelected() { + var node = this.selection.getNode(); + return Element.getStyle(node, 'backgroundColor'); + } + + /** + * WysiHat.Commands#alignSelection(color) -> undefined + * - alignment (string) - how the text should be aligned (left, center, right) + * + **/ + function alignSelection(alignment) { + this.execCommand('justify' + alignment); + } + + /** + * WysiHat.Commands#backgroundColorSelected() -> alignment + * + * Returns the alignment of the selected text area + **/ + function alignSelected() { + var node = this.selection.getNode(); + return Element.getStyle(node, 'textAlign'); + } + /** * WysiHat.Commands#linkSelection(url) -> undefined * - url (String): value for href @@ -216,26 +303,60 @@ WysiHat.Commands = (function() { else return document.queryCommandState(state); } + /** + * fontSizes for Safari, Gecko, and IE are all a different. + **/ + var fontSizeNames = $w('xxx-small xx-small x-small small medium large x-large xx-large'); + var fontSizePixels = $w('9px 10px 13px 16px 18px 24px 32px 48px'); + + if (Prototype.Browser.WebKit) { + fontSizeNames.shift(); + fontSizeNames.push('-webkit-xxx-large'); + } + + /** + * WysiHat.Commands#standardizeFontSize(fontSize) -> int + * + * Returns a standard font size from the three modern browsers. + * + **/ + function standardizeFontSize(fontSize) { + var newSize = fontSizeNames.indexOf(fontSize); + if (newSize >= 0) return newSize; + + newSize = fontSizePixels.indexOf(fontSize); + if (newSize >= 0) return newSize; + return parseInt(fontSize); + } return { - boldSelection: boldSelection, - boldSelected: boldSelected, - underlineSelection: underlineSelection, - underlineSelected: underlineSelected, - italicSelection: italicSelection, - italicSelected: italicSelected, - strikethroughSelection: strikethroughSelection, - blockquoteSelection: blockquoteSelection, - colorSelection: colorSelection, - linkSelection: linkSelection, - unlinkSelection: unlinkSelection, - linkSelected: linkSelected, - insertOrderedList: insertOrderedList, - insertUnorderedList: insertUnorderedList, - insertImage: insertImage, - insertHTML: insertHTML, - execCommand: execCommand, - queryCommandState: queryCommandState, + boldSelection: boldSelection, + boldSelected: boldSelected, + underlineSelection: underlineSelection, + underlineSelected: underlineSelected, + italicSelection: italicSelection, + italicSelected: italicSelected, + strikethroughSelection: strikethroughSelection, + blockquoteSelection: blockquoteSelection, + fontSelection: fontSelection, + fontSelected: fontSelected, + fontSizeSelection: fontSizeSelection, + fontSizeSelected: fontSizeSelected, + colorSelection: colorSelection, + colorSelected: colorSelected, + backgroundColorSelection: backgroundColorSelection, + backgroundColorSelected: backgroundColorSelected, + alignSelection: alignSelection, + alignSelected: alignSelected, + linkSelection: linkSelection, + unlinkSelection: unlinkSelection, + linkSelected: linkSelected, + insertOrderedList: insertOrderedList, + insertUnorderedList: insertUnorderedList, + insertImage: insertImage, + insertHTML: insertHTML, + execCommand: execCommand, + queryCommandState: queryCommandState, commands: $H({}), diff --git a/src/wysihat/models/iframe.js b/src/wysihat/models/iframe.js index 7044aec..ef4f517 100644 --- a/src/wysihat/models/iframe.js +++ b/src/wysihat/models/iframe.js @@ -44,6 +44,15 @@ WysiHat.iFrame.Methods = { }); }, + /** + * WysiHat.iFrame#unattach() -> undefined + * + * Remove the iframe + **/ + unattach: function() { + this.remove(); + }, + /** * WysiHat.iFrame#whenReady(callback) -> Element * - callback (Function): a function that is called if or when the editor is @@ -101,6 +110,17 @@ WysiHat.iFrame.Methods = { return this; }, + /** + * WysiHat.iFrame.Methods#getStyle(style) -> string + * - style specificication (i.e. backgroundColor) + * + * Returns the style from the element based on the given style + */ + getStyle: function(style) { + var document = this.getDocument(); + return Element.getStyle(document.body, style); + }, + rawContent: function() { var document = this.getDocument(); diff --git a/test/unit/editor_test.js b/test/unit/editor_test.js index df08fbf..61f1aa2 100644 --- a/test/unit/editor_test.js +++ b/test/unit/editor_test.js @@ -1,22 +1,24 @@ + new Test.Unit.Runner({ testAttachAndCreateIframe: function() { var runner = this; var editor = WysiHat.Editor.attach('content'); editor.whenReady(function() { + this.editor = editor; runner.assertNotVisible($('content')); runner.assert($('content_editor')); runner.assert($('content_editor').ready); runner.assert($('content_editor').getDocument()); runner.assert($('content_editor').getWindow()); runner.assertEqual('on', $('content_editor').getDocument().designMode); - }); + }); runner.wait(1000, function() {}); }, testIncludedModules: function() { with(this) { - var module = { xyz123: function() {} } + var module = { xyz123: function() {} }; var editor1 = WysiHat.Editor.attach('content'); WysiHat.Editor.include(module); @@ -27,5 +29,80 @@ new Test.Unit.Runner({ // The extension should work on subsequent editors though. assert(Object.isFunction(editor2.xyz123)); - }} -}); + editor1.unattach(); + editor2.unattach(); + }}, + + testBold: function() { with(this) { + testFontFormat('bold', 'b'); + }}, + + testItalic: function() { with(this) { + testFontFormat('italic', 'i'); + }}, + + testUnderline: function() { with(this) { + testFontFormat('underline', 'u'); + }}, + + testFonts: function() { with(this) { + editor.setContent("Times"); + selectFirstNode(); + assertEqual('times', editor.fontSelected()); + }}, + + testFontSizes: function() { with(this) { + editor.setContent("Font Size"); + selectFirstNode(); + assertEqual(7, editor.fontSizeSelected()); + }}, + + testColor: function() { with(this) { + editor.setContent("color"); + selectFirstNode(); + assertEqual('rgb(85, 85, 85)', editor.colorSelected()); + }}, + + testBackgroundColor: function() { with(this) { + editor.setContent("background-color"); + selectFirstNode(); + assertEqual('rgb(85, 85, 85)', editor.backgroundColorSelected()); + }}, + + testNestedAlignment: function() { with(this) { + editor.setContent("
align me
"); + selectFirstNode(); + assertEqual('right', editor.alignSelected()); + }}, + + testStyle: function(){ with(this) { + editor.setContent("set my style"); + selectFirstNode(); + editor.setStyle( {fontFamily: "arial", fontSize: "13px"} ); + assert(editor.getStyle('fontFamily')); + assertEqual('arial', editor.getStyle('fontFamily')); + assertEqual('13px', editor.getStyle('fontSize')); + }}, + + testUnattach: function() { with(this) { + editor.setContent("Unattached"); + editor.unattach(); + var iframe = $('content_editor'); + assert(!iframe); + }}, + + setup: function() { + this.selectFirstNode = function() { + var iframe = $('content_editor'); + var doc = iframe.contentDocument || iframe.contentWindow.document; + var node = doc.body.childNodes[0]; + editor.selection.selectNode(node); + }; + + this.testFontFormat = function(styleType, styleTag) { + editor.setContent('<' + styleTag + '>Boldly going where no man has gone before'); + this.selectFirstNode(); + this.assert(editor[styleType + 'Selected']()); + }; + } +}); \ No newline at end of file From 1b42e625f19f0977ec5f482d5749ed3abc947bfd Mon Sep 17 00:00:00 2001 From: David Badley Date: Thu, 25 Jun 2009 08:58:05 +0800 Subject: [PATCH 002/164] Fixed a bug in firefox with backgroundColorSelection Signed-off-by: Joshua Peek --- src/wysihat/editor/commands.js | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/wysihat/editor/commands.js b/src/wysihat/editor/commands.js index 8162a7e..6e1d8a6 100644 --- a/src/wysihat/editor/commands.js +++ b/src/wysihat/editor/commands.js @@ -149,9 +149,15 @@ WysiHat.Commands = (function() { * WysiHat.Commands#backgroundColorSelection(color) -> undefined * - color (string) - a color or hexadecimal value * + * Sets the background color. Firefox will fill in the background + * color of the entire iframe unless hilitecolor is used. **/ function backgroundColorSelection(color) { - this.execCommand('backcolor', false, color); + if(Prototype.Browser.Gecko) { + this.execCommand('hilitecolor', false, color); + } else { + this.execCommand('backcolor', false, color); + } } /** From 05ff61050c6b1cf51916a3734fd895de78bc71d6 Mon Sep 17 00:00:00 2001 From: David Badley Date: Wed, 1 Jul 2009 05:35:50 +0800 Subject: [PATCH 003/164] Added some bug fi fixes for firefox and for IE. Added tests for formatting Selection() methods, renamed past tests to Nested. The bug fix for firefox deals with backcolor and execCommand being handled differently in firefox than the other browsers (it uses hilitecolor). The bug fix for IE resolves an issue with losing its selection when clicking on the toolbar by saving its selection. Signed-off-by: Joshua Peek --- src/wysihat/dom/selection.js | 20 ++++++-- src/wysihat/editor/commands.js | 55 +++++++++++++++++++++- src/wysihat/editor/events.js | 2 +- test/unit/editor_test.js | 84 ++++++++++++++++++++++++++++++---- test/unit/fixtures/editor.html | 3 ++ 5 files changed, 150 insertions(+), 14 deletions(-) diff --git a/src/wysihat/dom/selection.js b/src/wysihat/dom/selection.js index e4232a9..ca0c2ab 100644 --- a/src/wysihat/dom/selection.js +++ b/src/wysihat/dom/selection.js @@ -9,6 +9,11 @@ WysiHat.Selection = Class.create((function() { function initialize(editor) { this.window = editor.getWindow(); this.document = editor.getDocument(); + + if (Prototype.Browser.IE) { + editor.observe('wysihat:cursormove', saveRange.bind(this)); + editor.observe('wysihat:focus', restoreRange); + } } /** @@ -24,10 +29,9 @@ WysiHat.Selection = Class.create((function() { * Get range for selected text. **/ function getRange() { - var selection = this.getSelection(); + var range = null, selection = this.getSelection(); try { - var range; if (selection.getRangeAt) range = selection.getRangeAt(0); else @@ -190,6 +194,15 @@ WysiHat.Selection = Class.create((function() { bookmark.parentNode.removeChild(bookmark); } + var savedRange = null; + function saveRange() { + savedRange = this.getRange(); + } + + function restoreRange() { + if (savedRange) savedRange.select(); + } + return { initialize: initialize, getSelection: getSelection, @@ -197,6 +210,7 @@ WysiHat.Selection = Class.create((function() { getNode: getNode, selectNode: selectNode, setBookmark: setBookmark, - moveToBookmark: moveToBookmark + moveToBookmark: moveToBookmark, + restore: restoreRange }; })()); diff --git a/src/wysihat/editor/commands.js b/src/wysihat/editor/commands.js index 6e1d8a6..9ce7005 100644 --- a/src/wysihat/editor/commands.js +++ b/src/wysihat/editor/commands.js @@ -142,7 +142,7 @@ WysiHat.Commands = (function() { **/ function colorSelected() { var node = this.selection.getNode(); - return Element.getStyle(node, 'color'); + return standardizeColor(Element.getStyle(node, 'color')); } /** @@ -167,7 +167,7 @@ WysiHat.Commands = (function() { **/ function backgroundColorSelected() { var node = this.selection.getNode(); - return Element.getStyle(node, 'backgroundColor'); + return standardizeColor(Element.getStyle(node, 'backgroundColor')); } /** @@ -281,6 +281,8 @@ WysiHat.Commands = (function() { function execCommand(command, ui, value) { var document = this.getDocument(); + if (Prototype.Browser.IE) this.selection.restore(); + var handler = this.commands.get(command) if (handler) handler.bind(this)(value); @@ -335,6 +337,55 @@ WysiHat.Commands = (function() { return parseInt(fontSize); } + function standardizeColor(color) { + if (!color || color.match(/[0-9a-f]{6}/i)) return color; + var m = color.toLowerCase().match(/^(rgba?|hsla?)\(([\s\.\-,%0-9]+)\)/); + if(m){ + var c = m[2].split(/\s*,\s*/), l = c.length, t = m[1]; + if((t == "rgb" && l == 3) || (t == "rgba" && l == 4)){ + var r = c[0]; + if(r.charAt(r.length - 1) == "%"){ + var a = c.map(function(x){ + return parseFloat(x) * 2.56; + }); + if(l == 4){ a[3] = c[3]; } + return _colorFromArray(a); + } + return _colorFromArray(c); + } + if((t == "hsl" && l == 3) || (t == "hsla" && l == 4)){ + var H = ((parseFloat(c[0]) % 360) + 360) % 360 / 360, + S = parseFloat(c[1]) / 100, + L = parseFloat(c[2]) / 100, + m2 = L <= 0.5 ? L * (S + 1) : L + S - L * S, + m1 = 2 * L - m2, + a = [_hue2rgb(m1, m2, H + 1 / 3) * 256, + _hue2rgb(m1, m2, H) * 256, _hue2rgb(m1, m2, H - 1 / 3) * 256, 1]; + if(l == 4){ a[3] = c[3]; } + return _colorFromArray(a); + } + } + return null; // dojo.Color + } + + function _colorFromArray(a) { + var arr = a.slice(0, 3).map(function(x){ + var s = parseInt(x).toString(16); + return s.length < 2 ? "0" + s : s; + }); + return "#" + arr.join(""); // String + } + + function _hue2rgb(m1, m2, h){ + if(h < 0){ ++h; } + if(h > 1){ --h; } + var h6 = 6 * h; + if(h6 < 1){ return m1 + (m2 - m1) * h6; } + if(2 * h < 1){ return m2; } + if(3 * h < 2){ return m1 + (m2 - m1) * (2 / 3 - h) * 6; } + return m1; + } + return { boldSelection: boldSelection, boldSelected: boldSelected, diff --git a/src/wysihat/editor/events.js b/src/wysihat/editor/events.js index b9a74d8..b47fe04 100644 --- a/src/wysihat/editor/events.js +++ b/src/wysihat/editor/events.js @@ -86,7 +86,7 @@ WysiHat.Events = (function() { var range = editor.selection.getRange(); if (previousRange != range) { editor.fire("wysihat:cursormove"); - editor.previousRange = range; + previousRange = range; } }; diff --git a/test/unit/editor_test.js b/test/unit/editor_test.js index 61f1aa2..480f5fb 100644 --- a/test/unit/editor_test.js +++ b/test/unit/editor_test.js @@ -34,39 +34,77 @@ new Test.Unit.Runner({ }}, testBold: function() { with(this) { - testFontFormat('bold', 'b'); + testFontFormat('bold'); + testNestedFontFormat('bold', 'b'); }}, testItalic: function() { with(this) { - testFontFormat('italic', 'i'); + testFontFormat('italic'); + testNestedFontFormat('italic', 'i'); }}, testUnderline: function() { with(this) { - testFontFormat('underline', 'u'); + testFontFormat('underline'); + testNestedFontFormat('underline', 'u'); }}, testFonts: function() { with(this) { + editor.setContent("Times"); + selectFirstNode(); + editor.fontSelection('times'); + assertEqual('times', editor.fontSelected()); + }}, + + testNestedFonts: function() { with(this) { editor.setContent("Times"); selectFirstNode(); assertEqual('times', editor.fontSelected()); }}, testFontSizes: function() { with(this) { + editor.setContent("Font Size"); + selectFirstNode(); + editor.fontSizeSelection(5); + assertEqual(5, editor.fontSizeSelected()); + }}, + + testNestedFontSizes: function() { with(this) { editor.setContent("Font Size"); selectFirstNode(); assertEqual(7, editor.fontSizeSelected()); }}, - testColor: function() { with(this) { - editor.setContent("color"); + testColor: function() { with (this) { + editor.setContent('color'); selectFirstNode(); - assertEqual('rgb(85, 85, 85)', editor.colorSelected()); + editor.colorSelection("#555555"); + assertEqual('#555555', editor.colorSelected()); }}, + testNestedColor: function() { with(this) { + editor.setContent("color"); + selectFirstNode(); + assertEqual('#555555', editor.colorSelected()); + }}, + testBackgroundColor: function() { with(this) { + editor.setContent("background color"); + selectFirstNode(); + editor.backgroundColorSelection("#555555"); + assertEqual('#555555', editor.backgroundColorSelected()); + }}, + + testNestedBackgroundColor: function() { with(this) { editor.setContent("background-color"); selectFirstNode(); - assertEqual('rgb(85, 85, 85)', editor.backgroundColorSelected()); + assertEqual('#555555', editor.backgroundColorSelected()); + }}, + + testAlignment: function() { with(this) { + editor.setContent("align me"); + selectFirstNode(); + editor.alignSelection('right'); + assertEqual('right', editor.alignSelected()); }}, testNestedAlignment: function() { with(this) { @@ -84,6 +122,29 @@ new Test.Unit.Runner({ assertEqual('13px', editor.getStyle('fontSize')); }}, + testLink: function() { with (this) { + link = 'http://github.com/'; + editor.setContent('linky link'); + selectFirstNode(); + + wait(500, function() { + var textarea = $('link').down('textarea'); + textarea.focus(); // simulate real world case of IE losing focus + textarea.value = link; + editor.linkSelection(link); + assert(editor.linkSelected()); + assertEqual(link, editor.selection.getNode().href); + }); + }}, + + testNestedLink: function(){ with (this) { + link = 'http://github.com/'; + editor.setContent('linky link'); + selectFirstNode(); + assert(editor.linkSelected()); + assertEqual(link, editor.selection.getNode().href); + }}, + testUnattach: function() { with(this) { editor.setContent("Unattached"); editor.unattach(); @@ -99,7 +160,14 @@ new Test.Unit.Runner({ editor.selection.selectNode(node); }; - this.testFontFormat = function(styleType, styleTag) { + this.testFontFormat = function(styleType) { + editor.setContent('Boldly going where no man has gone before'); + this.selectFirstNode(); + editor[styleType + 'Selection'](); + this.assert(editor[styleType + 'Selected']()); + }; + + this.testNestedFontFormat = function(styleType, styleTag) { editor.setContent('<' + styleTag + '>Boldly going where no man has gone before'); this.selectFirstNode(); this.assert(editor[styleType + 'Selected']()); diff --git a/test/unit/fixtures/editor.html b/test/unit/fixtures/editor.html index f757b81..f551114 100644 --- a/test/unit/fixtures/editor.html +++ b/test/unit/fixtures/editor.html @@ -1,3 +1,6 @@
+ \ No newline at end of file From 04521e1f781238dfb52b789569ba9d25628a8126 Mon Sep 17 00:00:00 2001 From: Jeff Kreeftmeijer Date: Mon, 14 Sep 2009 21:03:06 +0800 Subject: [PATCH 004/164] added examples/image.html Signed-off-by: Joshua Peek --- examples/image.html | 94 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 94 insertions(+) create mode 100644 examples/image.html diff --git a/examples/image.html b/examples/image.html new file mode 100644 index 0000000..a8083eb --- /dev/null +++ b/examples/image.html @@ -0,0 +1,94 @@ + + + + WYSIWYG + + + + + + + + + + +

Oops, you need to build the package before running this example. It's easy: just run rake in the project's directory.

+ +

This example shows you how to create a simple UI for linking and unlinking selections

+ +
+  var LinkSelectionHelper = {
+    promptLinkSelection: function() {
+      if (this.linkSelected()) {
+        if (confirm("Remove link?"))
+          this.unlinkSelection();
+      } else {
+        var value = prompt("Enter a URL", "http://www.google.com/");
+        if (value)
+          this.linkSelection(value);
+      }
+    }
+  }
+
+  WysiHat.Editor.include(LinkSelectionHelper);
+
+  Event.observe(window, 'load', function() {
+    var editor = WysiHat.Editor.attach('content');
+    var toolbar = new WysiHat.Toolbar(editor);
+
+    toolbar.addButton({
+      label: "Link",
+      handler: function(editor) { return editor.promptLinkSelection(); }
+    });
+  });
+
+ +
+ +
+ + From f74be190c3a50f68a8ae075be210885646adbb6a Mon Sep 17 00:00:00 2001 From: Jeff Kreeftmeijer Date: Mon, 14 Sep 2009 21:06:05 +0800 Subject: [PATCH 005/164] updated the description on examples/image.html Signed-off-by: Joshua Peek --- examples/image.html | 23 +++++++++-------------- 1 file changed, 9 insertions(+), 14 deletions(-) diff --git a/examples/image.html b/examples/image.html index a8083eb..98479f5 100644 --- a/examples/image.html +++ b/examples/image.html @@ -58,31 +58,26 @@

Oops, you need to build the package before running this example. It's easy: just run rake in the project's directory.

-

This example shows you how to create a simple UI for linking and unlinking selections

+

This example shows you how to create a simple UI for adding images

-  var LinkSelectionHelper = {
-    promptLinkSelection: function() {
-      if (this.linkSelected()) {
-        if (confirm("Remove link?"))
-          this.unlinkSelection();
-      } else {
-        var value = prompt("Enter a URL", "http://www.google.com/");
-        if (value)
-          this.linkSelection(value);
-      }
+  var ImageHelper = {
+    promptImage: function() {
+      var value = prompt("Enter a URL", "http://www.google.com/intl/en_ALL/images/logo.gif")
+      if(value)
+        this.insertImage(value);
     }
   }
 
-  WysiHat.Editor.include(LinkSelectionHelper);
+  WysiHat.Editor.include(ImageHelper);
 
   Event.observe(window, 'load', function() {
     var editor = WysiHat.Editor.attach('content');
     var toolbar = new WysiHat.Toolbar(editor);
 
     toolbar.addButton({
-      label: "Link",
-      handler: function(editor) { return editor.promptLinkSelection(); }
+      label: "Image",
+      handler: function(editor) { return editor.promptImage(); }
     });
   });
 
From 3ed3e7e6b0af834dcb9bab2b5e128be9a3db70db Mon Sep 17 00:00:00 2001 From: Jeff Kreeftmeijer Date: Mon, 14 Sep 2009 21:34:25 +0800 Subject: [PATCH 006/164] added examples/align_selection.html Signed-off-by: Joshua Peek --- examples/align_selection.html | 96 +++++++++++++++++++++++++++++++++++ 1 file changed, 96 insertions(+) create mode 100644 examples/align_selection.html diff --git a/examples/align_selection.html b/examples/align_selection.html new file mode 100644 index 0000000..f939b27 --- /dev/null +++ b/examples/align_selection.html @@ -0,0 +1,96 @@ + + + + WYSIWYG + + + + + + + + + + +

Oops, you need to build the package before running this example. It's easy: just run rake in the project's directory.

+ +

This example shows you how to create a simple UI for aligning text.

+ +
+  Event.observe(window, 'load', function() {
+    var editor = WysiHat.Editor.attach('content');
+    var toolbar = new WysiHat.Toolbar(editor);
+
+    toolbar.addButton({
+      label: "Align left",
+      handler: function(editor) { return editor.alignSelection('left'); }
+    });
+
+    toolbar.addButton({
+      label: "Align center",
+      handler: function(editor) { return editor.alignSelection('center'); }
+    });
+
+    toolbar.addButton({
+      label: "Align right",
+      handler: function(editor) { return editor.alignSelection('right'); }
+    });
+  });
+
+ +
+ + +
+ + From f3ec989605e65212fbf033d72da16f99276e8d8c Mon Sep 17 00:00:00 2001 From: Jeff Kreeftmeijer Date: Mon, 14 Sep 2009 22:50:48 +0800 Subject: [PATCH 007/164] added examples/undo_redo.js Signed-off-by: Joshua Peek --- examples/undo_redo.html | 81 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 81 insertions(+) create mode 100644 examples/undo_redo.html diff --git a/examples/undo_redo.html b/examples/undo_redo.html new file mode 100644 index 0000000..abb8886 --- /dev/null +++ b/examples/undo_redo.html @@ -0,0 +1,81 @@ + + + + WYSIWYG + + + + + + + + + + +

Oops, you need to build the package before running this example. It's easy: just run rake in the project's directory.

+ +

This example shows you how to create a simple UI for using Undo & Redo functionality

+ +
+  Event.observe(window, 'load', function() {
+    var editor = WysiHat.Editor.attach('content');
+    var toolbar = new WysiHat.Toolbar(editor);
+    
+    toolbar.addButton({
+      label: "Undo"
+    });
+    
+    toolbar.addButton({
+      label: "Redo"
+    });
+  });
+
+ +
+ + +
+ + From 96380e4f4d986c9fac4c34b96712c261f579aba0 Mon Sep 17 00:00:00 2001 From: Jeff Kreeftmeijer Date: Mon, 14 Sep 2009 22:56:04 +0800 Subject: [PATCH 008/164] added examples/insert_list.html Signed-off-by: Joshua Peek --- examples/insert_list.html | 82 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 82 insertions(+) create mode 100644 examples/insert_list.html diff --git a/examples/insert_list.html b/examples/insert_list.html new file mode 100644 index 0000000..bdf85d3 --- /dev/null +++ b/examples/insert_list.html @@ -0,0 +1,82 @@ + + + + WYSIWYG + + + + + + + + + + +

Oops, you need to build the package before running this example. It's easy: just run rake in the project's directory.

+ +

This example shows you how to create a simple UI for using Undo & Redo functionality

+ +
+  Event.observe(window, 'load', function() {
+    var editor = WysiHat.Editor.attach('content');
+    var toolbar = new WysiHat.Toolbar(editor);
+    
+    toolbar.addButton({
+      label: "Undo"
+    });
+    
+    toolbar.addButton({
+      label: "Redo"
+    });
+
+  });
+
+ +
+ + +
+ + From 902cc15a0a68cc7b611c43e103853dd9f2fba04b Mon Sep 17 00:00:00 2001 From: Jeff Kreeftmeijer Date: Mon, 14 Sep 2009 23:00:20 +0800 Subject: [PATCH 009/164] renamed examples/insert_list.html to examples/list.html and updated the page Signed-off-by: Joshua Peek --- examples/{insert_list.html => list.html} | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) rename examples/{insert_list.html => list.html} (92%) diff --git a/examples/insert_list.html b/examples/list.html similarity index 92% rename from examples/insert_list.html rename to examples/list.html index bdf85d3..e30c796 100644 --- a/examples/insert_list.html +++ b/examples/list.html @@ -51,7 +51,7 @@

Oops, you need to build the package before running this example. It's easy: just run rake in the project's directory.

-

This example shows you how to create a simple UI for using Undo & Redo functionality

+

This example shows you how to create a simple UI for inserting (un)ordered Lists

   Event.observe(window, 'load', function() {
@@ -59,13 +59,12 @@
     var toolbar = new WysiHat.Toolbar(editor);
     
     toolbar.addButton({
-      label: "Undo"
+      label: "InsertOrderedList"
     });
     
     toolbar.addButton({
-      label: "Redo"
+      label: "InsertUnorderedList"
     });
-
   });
 
From 346ec3eaca288984672ce95eb9534b62f17ca99d Mon Sep 17 00:00:00 2001 From: Jeff Kreeftmeijer Date: Mon, 14 Sep 2009 23:10:02 +0800 Subject: [PATCH 010/164] added examples/html.hml Signed-off-by: Joshua Peek --- examples/html.html | 89 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 89 insertions(+) create mode 100644 examples/html.html diff --git a/examples/html.html b/examples/html.html new file mode 100644 index 0000000..e740e3a --- /dev/null +++ b/examples/html.html @@ -0,0 +1,89 @@ + + + + WYSIWYG + + + + + + + + + + +

Oops, you need to build the package before running this example. It's easy: just run rake in the project's directory.

+ +

This example shows you how to create a simple UI for adding HTML

+ +
+  var HTMLHelper = {
+    promptHTML: function() {
+      var value = prompt("Enter some HTML", '<h1>:)</h1>')
+      if(value)
+        this.insertHTML(value);
+    }
+  }
+
+  WysiHat.Editor.include(HTMLHelper);
+
+  Event.observe(window, 'load', function() {
+    var editor = WysiHat.Editor.attach('content');
+    var toolbar = new WysiHat.Toolbar(editor);
+
+    toolbar.addButton({
+      label: "HTML",
+      handler: function(editor) { return editor.promptHTML(); }
+    });
+  });
+
+ +
+ +
+ + From 1c3870cffe17367dfe6474e7b04a6888c2f91e10 Mon Sep 17 00:00:00 2001 From: Jeff Kreeftmeijer Date: Tue, 15 Sep 2009 18:06:19 +0800 Subject: [PATCH 011/164] Updated examples/align_selection.html. The previous way of doing it was just... wrong. Signed-off-by: Joshua Peek --- examples/align_selection.html | 9 ++-- examples/flash.html | 89 +++++++++++++++++++++++++++++++++++ 2 files changed, 92 insertions(+), 6 deletions(-) create mode 100644 examples/flash.html diff --git a/examples/align_selection.html b/examples/align_selection.html index f939b27..2e0e7ba 100644 --- a/examples/align_selection.html +++ b/examples/align_selection.html @@ -35,18 +35,15 @@ var toolbar = new WysiHat.Toolbar(editor); toolbar.addButton({ - label: "Align left", - handler: function(editor) { return editor.alignSelection('left'); } + label: "JustifyLaft" }); toolbar.addButton({ - label: "Align center", - handler: function(editor) { return editor.alignSelection('center'); } + label: "JustifyCenter" }); toolbar.addButton({ - label: "Align right", - handler: function(editor) { return editor.alignSelection('right'); } + label: "JustifyRight" }); // Hide our error message if the editor loads fine diff --git a/examples/flash.html b/examples/flash.html new file mode 100644 index 0000000..5c1e292 --- /dev/null +++ b/examples/flash.html @@ -0,0 +1,89 @@ + + + + WYSIWYG + + + + + + + + + + +

Oops, you need to build the package before running this example. It's easy: just run rake in the project's directory.

+ +

This example shows you how to create a simple UI for adding images

+ +
+  var ImageHelper = {
+    promptImage: function() {
+      var value = prompt("Enter a URL", "http://www.google.com/intl/en_ALL/images/logo.gif")
+      if(value)
+        this.insertImage(value);
+    }
+  }
+
+  WysiHat.Editor.include(ImageHelper);
+
+  Event.observe(window, 'load', function() {
+    var editor = WysiHat.Editor.attach('content');
+    var toolbar = new WysiHat.Toolbar(editor);
+
+    toolbar.addButton({
+      label: "Image",
+      handler: function(editor) { return editor.promptImage(); }
+    });
+  });
+
+ +
+ +
+ + From e55892489dfe9ee54a7ccdf91ec594c284389138 Mon Sep 17 00:00:00 2001 From: Jeff Kreeftmeijer Date: Tue, 15 Sep 2009 18:06:46 +0800 Subject: [PATCH 012/164] Updated examples/align_selection.html. The previous way of doing it was just... wrong. Signed-off-by: Joshua Peek --- examples/flash.html | 89 --------------------------------------------- 1 file changed, 89 deletions(-) delete mode 100644 examples/flash.html diff --git a/examples/flash.html b/examples/flash.html deleted file mode 100644 index 5c1e292..0000000 --- a/examples/flash.html +++ /dev/null @@ -1,89 +0,0 @@ - - - - WYSIWYG - - - - - - - - - - -

Oops, you need to build the package before running this example. It's easy: just run rake in the project's directory.

- -

This example shows you how to create a simple UI for adding images

- -
-  var ImageHelper = {
-    promptImage: function() {
-      var value = prompt("Enter a URL", "http://www.google.com/intl/en_ALL/images/logo.gif")
-      if(value)
-        this.insertImage(value);
-    }
-  }
-
-  WysiHat.Editor.include(ImageHelper);
-
-  Event.observe(window, 'load', function() {
-    var editor = WysiHat.Editor.attach('content');
-    var toolbar = new WysiHat.Toolbar(editor);
-
-    toolbar.addButton({
-      label: "Image",
-      handler: function(editor) { return editor.promptImage(); }
-    });
-  });
-
- -
- -
- - From 87697bfe14b7101ad6d0c37efd29a47b3720931f Mon Sep 17 00:00:00 2001 From: Jeff Kreeftmeijer Date: Tue, 15 Sep 2009 18:23:56 +0800 Subject: [PATCH 013/164] added examples/flash.html Signed-off-by: Joshua Peek --- examples/flash.html | 93 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 93 insertions(+) create mode 100644 examples/flash.html diff --git a/examples/flash.html b/examples/flash.html new file mode 100644 index 0000000..99188fc --- /dev/null +++ b/examples/flash.html @@ -0,0 +1,93 @@ + + + + WYSIWYG + + + + + + + + + + +

Oops, you need to build the package before running this example. It's easy: just run rake in the project's directory.

+ +

This example shows you how to create a simple UI for adding Flash movies

+ +
+  var FlashHelper = {
+    promptFlash: function() {
+      var value = prompt("Enter a Url", "http://wwwimages.adobe.com/www.adobe.com/homepage/en_us/fma_rotation/fma2/maxintro.swf")
+      if(value)
+        this.insertHTML(
+          '<object><param name="movie" value="' + value + '"><embed src="' + value + '"></embed></object>'
+        );
+    }
+  }
+
+  WysiHat.Editor.include(FlashHelper);
+
+  Event.observe(window, 'load', function() {
+    var editor = WysiHat.Editor.attach('content');
+    var toolbar = new WysiHat.Toolbar(editor);
+
+    toolbar.addButton({
+      label: "Flash",
+      handler: function(editor) { return editor.promptFlash(); }
+    });
+  });
+
+ +
+ +
+ + From 976d9e01a8e1cc5d52c5b06401faed8e2c1cfd0b Mon Sep 17 00:00:00 2001 From: Jeff Kreeftmeijer Date: Wed, 23 Sep 2009 17:50:15 +0800 Subject: [PATCH 014/164] changed JustifyLaft to JusfityLeft in examples/html.html Signed-off-by: Joshua Peek --- examples/align_selection.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/align_selection.html b/examples/align_selection.html index 2e0e7ba..1a5560d 100644 --- a/examples/align_selection.html +++ b/examples/align_selection.html @@ -35,7 +35,7 @@ var toolbar = new WysiHat.Toolbar(editor); toolbar.addButton({ - label: "JustifyLaft" + label: "JustifyLeft" }); toolbar.addButton({ From 6004a6b0040fb6f795fce08e360c37be2642bccd Mon Sep 17 00:00:00 2001 From: Jeff Kreeftmeijer Date: Wed, 23 Sep 2009 18:37:29 +0800 Subject: [PATCH 015/164] added examples/font-selection.html Signed-off-by: Joshua Peek --- examples/font_selection.html | 119 +++++++++++++++++++++++++++++++++++ 1 file changed, 119 insertions(+) create mode 100644 examples/font_selection.html diff --git a/examples/font_selection.html b/examples/font_selection.html new file mode 100644 index 0000000..0ea6db2 --- /dev/null +++ b/examples/font_selection.html @@ -0,0 +1,119 @@ + + + + WYSIWYG + + + + + + + + + + +

Oops, you need to build the package before running this example. It's easy: just run rake in the project's directory.

+ +

This example shows you a very simple way to create buttons for selecting fonts.

+ +
+  Event.observe(window, 'load', function() {
+    var editor = WysiHat.Editor.attach('content');
+    var toolbar = new WysiHat.Toolbar(editor);
+    
+    toolbar.addButton({
+      label: "Georgia",
+      handler: function(editor) { return editor.fontSelection('Georgia'); }
+    });
+    
+    toolbar.addButton({
+      label: "Arial",
+      handler: function(editor) { return editor.fontSelection('Arial'); }
+    });
+    
+    var georgiaButton = $$('.editor_toolbar .georgia').first();
+    editor.observe('wysihat:cursormove', function(event) {
+      if (editor.fontSelected() == 'Georgia')
+        georgiaButton.addClassName('selected')
+      else
+        georgiaButton.removeClassName('selected');
+    });
+    
+    var arialButton = $$('.editor_toolbar .arial').first();
+    editor.observe('wysihat:cursormove', function(event) {
+      if (editor.fontSelected() == 'Arial')
+        arialButton.addClassName('selected')
+      else
+        arialButton.removeClassName('selected');
+    });
+  });
+
+ +
+ + + + +
+ + From 8572988e3c1c2e8ecf96f247d2dbe78065ee4b82 Mon Sep 17 00:00:00 2001 From: Andrei Bocan Date: Mon, 2 Nov 2009 23:46:37 +0800 Subject: [PATCH 016/164] Minor fixes to make wysihat play nice with IE8 --- src/wysihat/editor/window.js | 2 +- src/wysihat/toolbar.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/wysihat/editor/window.js b/src/wysihat/editor/window.js index 2394ff5..8a7ae97 100644 --- a/src/wysihat/editor/window.js +++ b/src/wysihat/editor/window.js @@ -22,7 +22,7 @@ WysiHat.Window = (function() { * You should not need to access this directly, and this API is not final. **/ function getWindow() { - if (this.contentDocument) + if (this.contentDocument && this.contentDocument.defaultView) return this.contentDocument.defaultView; else if (this.contentWindow.document) return this.contentWindow; diff --git a/src/wysihat/toolbar.js b/src/wysihat/toolbar.js index 9d11365..b9556f3 100644 --- a/src/wysihat/toolbar.js +++ b/src/wysihat/toolbar.js @@ -103,7 +103,7 @@ WysiHat.Toolbar = Class.create((function() { * inserted. **/ function createButtonElement(toolbar, options) { - var button = Element('a', { + var button = new Element('a', { 'class': 'button', 'href': '#' }); button.update('' + options.get('label') + ''); From e27d554907848449d4da704af0411de0a344a47e Mon Sep 17 00:00:00 2001 From: Jeff Kreeftmeijer Date: Sun, 8 Nov 2009 23:30:52 +0800 Subject: [PATCH 017/164] added some forgotten semicolons and parseInt() radixes --- src/wysihat/dom/range.js | 4 ++-- src/wysihat/editor/commands.js | 8 ++++---- src/wysihat/toolbar.js | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/wysihat/dom/range.js b/src/wysihat/dom/range.js index 8e97666..4da5add 100644 --- a/src/wysihat/dom/range.js +++ b/src/wysihat/dom/range.js @@ -27,7 +27,7 @@ if (typeof Range == 'undefined') { this.START_TO_END = 1; this.END_TO_END = 2; this.END_TO_START = 3; - } + }; Range.CLONE_CONTENTS = 0; Range.DELETE_CONTENTS = 1; @@ -835,7 +835,7 @@ if (!window.getSelection) { this.isCollapsed = true; this.rangeCount = 0; this.ranges = []; - } + }; Object.extend(SelectionImpl.prototype, (function() { function addRange(r) { diff --git a/src/wysihat/editor/commands.js b/src/wysihat/editor/commands.js index 9ce7005..9b47bc6 100644 --- a/src/wysihat/editor/commands.js +++ b/src/wysihat/editor/commands.js @@ -283,7 +283,7 @@ WysiHat.Commands = (function() { if (Prototype.Browser.IE) this.selection.restore(); - var handler = this.commands.get(command) + var handler = this.commands.get(command); if (handler) handler.bind(this)(value); else @@ -305,7 +305,7 @@ WysiHat.Commands = (function() { function queryCommandState(state) { var document = this.getDocument(); - var handler = this.queryCommands.get(state) + var handler = this.queryCommands.get(state); if (handler) return handler.bind(this)(); else @@ -334,7 +334,7 @@ WysiHat.Commands = (function() { newSize = fontSizePixels.indexOf(fontSize); if (newSize >= 0) return newSize; - return parseInt(fontSize); + return parseInt(fontSize, 10); } function standardizeColor(color) { @@ -370,7 +370,7 @@ WysiHat.Commands = (function() { function _colorFromArray(a) { var arr = a.slice(0, 3).map(function(x){ - var s = parseInt(x).toString(16); + var s = parseInt(x, 10).toString(16); return s.length < 2 ? "0" + s : s; }); return "#" + arr.join(""); // String diff --git a/src/wysihat/toolbar.js b/src/wysihat/toolbar.js index b9556f3..4b4bb63 100644 --- a/src/wysihat/toolbar.js +++ b/src/wysihat/toolbar.js @@ -87,7 +87,7 @@ WysiHat.Toolbar = Class.create((function() { this.observeButtonClick(button, handler); var handler = this.buttonStateHandler(name, options); - this.observeStateChanges(button, name, handler) + this.observeStateChanges(button, name, handler); } /** From 1a3363bb6e9d9599fb6957fba8ce17ad850ad5b9 Mon Sep 17 00:00:00 2001 From: jeffkreeftmeijer Date: Wed, 11 Nov 2009 07:22:00 +0800 Subject: [PATCH 018/164] added WysiHat.Commands#queryValueCommandState --- src/wysihat/editor/commands.js | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/src/wysihat/editor/commands.js b/src/wysihat/editor/commands.js index 9b47bc6..25bd25d 100644 --- a/src/wysihat/editor/commands.js +++ b/src/wysihat/editor/commands.js @@ -310,7 +310,21 @@ WysiHat.Commands = (function() { return handler.bind(this)(); else return document.queryCommandState(state); + } + + /** + * WysiHat.Commands#queryValueCommandState(state) -> String + * - state (String): fontname, fontsize, forecolor, hilitecolor or + * backcolor + * + * A delegation method to one of the ...Selected methods (fontSelected, + * fontSizeSelected, etc.) + **/ + function queryValueCommandState(state) { + var handler = this.queryValueCommands.get(state); + return handler.bind(this)(); } + /** * fontSizes for Safari, Gecko, and IE are all a different. **/ @@ -414,11 +428,20 @@ WysiHat.Commands = (function() { insertHTML: insertHTML, execCommand: execCommand, queryCommandState: queryCommandState, + queryValueCommandState: queryValueCommandState, commands: $H({}), queryCommands: $H({ link: linkSelected + }), + + queryValueCommands: $H({ + fontname: fontSelected, + fontsize: fontSizeSelected, + forecolor: colorSelected, + hilitecolor: backgroundColorSelected, + backcolor: backgroundColorSelected }) }; })(); From 0d611cfccf01813086f4e19f666a217afcbb685f Mon Sep 17 00:00:00 2001 From: Jeff Kreeftmeijer Date: Wed, 11 Nov 2009 18:04:52 +0800 Subject: [PATCH 019/164] added WysiHat.Commands#getSelectedStyles() --- src/wysihat/editor/commands.js | 41 ++++++++++++++++++---------------- 1 file changed, 22 insertions(+), 19 deletions(-) diff --git a/src/wysihat/editor/commands.js b/src/wysihat/editor/commands.js index 25bd25d..4d0eafa 100644 --- a/src/wysihat/editor/commands.js +++ b/src/wysihat/editor/commands.js @@ -313,17 +313,20 @@ WysiHat.Commands = (function() { } /** - * WysiHat.Commands#queryValueCommandState(state) -> String - * - state (String): fontname, fontsize, forecolor, hilitecolor or - * backcolor - * - * A delegation method to one of the ...Selected methods (fontSelected, - * fontSizeSelected, etc.) + * WysiHat.Commands#getSelectedStyles() -> Hash + * + * Fetches the styles (from the styleSelectors hash) from the current + * selection and returns it as a hash **/ - function queryValueCommandState(state) { - var handler = this.queryValueCommands.get(state); - return handler.bind(this)(); - } + function getSelectedStyles() { + var styles = $H({}); + var editor = this; + editor.styleSelectors.each(function(style){ + var node = editor.selection.getNode(); + styles.set(style.first(), Element.getStyle(node, style.last())); + }); + return styles; + } /** * fontSizes for Safari, Gecko, and IE are all a different. @@ -428,20 +431,20 @@ WysiHat.Commands = (function() { insertHTML: insertHTML, execCommand: execCommand, queryCommandState: queryCommandState, - queryValueCommandState: queryValueCommandState, - + getSelectedStyles: getSelectedStyles, + commands: $H({}), queryCommands: $H({ link: linkSelected }), - - queryValueCommands: $H({ - fontname: fontSelected, - fontsize: fontSizeSelected, - forecolor: colorSelected, - hilitecolor: backgroundColorSelected, - backcolor: backgroundColorSelected + + styleSelectors: $H({ + fontname: 'fontFamily', + fontsize: 'fontSize', + forecolor: 'color', + hilitecolor: 'backgroundColor', + backcolor: 'backgroundColor' }) }; })(); From ddb95f8e8881189b2956723ee7aba205d692f0a7 Mon Sep 17 00:00:00 2001 From: jeffkreeftmeijer Date: Thu, 12 Nov 2009 05:39:16 +0800 Subject: [PATCH 020/164] removed the ...Selected methods and updated the font_selection example. --- examples/font_selection.html | 4 +- src/wysihat/editor/commands.js | 119 +-------------------------------- 2 files changed, 4 insertions(+), 119 deletions(-) diff --git a/examples/font_selection.html b/examples/font_selection.html index 0ea6db2..9c4fd5a 100644 --- a/examples/font_selection.html +++ b/examples/font_selection.html @@ -46,7 +46,7 @@ var georgiaButton = $$('.editor_toolbar .georgia').first(); editor.observe('wysihat:cursormove', function(event) { - if (editor.fontSelected() == 'Georgia') + if (editor.getSelectedStyles().get('fontname') == 'Georgia') georgiaButton.addClassName('selected') else georgiaButton.removeClassName('selected'); @@ -54,7 +54,7 @@ var arialButton = $$('.editor_toolbar .arial').first(); editor.observe('wysihat:cursormove', function(event) { - if (editor.fontSelected() == 'Arial') + if (editor.getSelectedStyles().get('fontname') == 'Arial') arialButton.addClassName('selected') else arialButton.removeClassName('selected'); diff --git a/src/wysihat/editor/commands.js b/src/wysihat/editor/commands.js index 4d0eafa..e5ebe35 100644 --- a/src/wysihat/editor/commands.js +++ b/src/wysihat/editor/commands.js @@ -94,17 +94,7 @@ WysiHat.Commands = (function() { function fontSelection(font) { this.execCommand('fontname', false, font); } - - /** - * WysiHat.Commands#fontSelected() -> style string - * - * Gets the font for the current selection - **/ - function fontSelected() { - var node = this.selection.getNode(); - return Element.getStyle(node, 'fontFamily'); - } - + /** * WysiHat.Commands#fontSizeSelection(fontSize) -> undefined * - font size (int) : font size for selection @@ -115,16 +105,6 @@ WysiHat.Commands = (function() { this.execCommand('fontsize', false, fontSize); } - /** - * WysiHat.Commands#fontSizeSelected() -> Returns a fontsize, standardized for modern browsers - * - * Gets the font size for the current selection - **/ - function fontSizeSelected() { - var node = this.selection.getNode(); - return standardizeFontSize(Element.getStyle(node, 'fontSize')); - } - /** * WysiHat.Commands#colorSelection(color) -> undefined * - color (String): a color name or hexadecimal value @@ -135,16 +115,6 @@ WysiHat.Commands = (function() { this.execCommand('forecolor', false, color); } - /** - * WysiHat.Commands#colorSelected() -> string (color) - * - * Returns the color of the selected portion - **/ - function colorSelected() { - var node = this.selection.getNode(); - return standardizeColor(Element.getStyle(node, 'color')); - } - /** * WysiHat.Commands#backgroundColorSelection(color) -> undefined * - color (string) - a color or hexadecimal value @@ -159,17 +129,7 @@ WysiHat.Commands = (function() { this.execCommand('backcolor', false, color); } } - - /** - * WysiHat.Commands#backgroundColorSelected() -> color - * - * Returns the background color of the selected text area - **/ - function backgroundColorSelected() { - var node = this.selection.getNode(); - return standardizeColor(Element.getStyle(node, 'backgroundColor')); - } - + /** * WysiHat.Commands#alignSelection(color) -> undefined * - alignment (string) - how the text should be aligned (left, center, right) @@ -328,81 +288,6 @@ WysiHat.Commands = (function() { return styles; } - /** - * fontSizes for Safari, Gecko, and IE are all a different. - **/ - var fontSizeNames = $w('xxx-small xx-small x-small small medium large x-large xx-large'); - var fontSizePixels = $w('9px 10px 13px 16px 18px 24px 32px 48px'); - - if (Prototype.Browser.WebKit) { - fontSizeNames.shift(); - fontSizeNames.push('-webkit-xxx-large'); - } - - /** - * WysiHat.Commands#standardizeFontSize(fontSize) -> int - * - * Returns a standard font size from the three modern browsers. - * - **/ - function standardizeFontSize(fontSize) { - var newSize = fontSizeNames.indexOf(fontSize); - if (newSize >= 0) return newSize; - - newSize = fontSizePixels.indexOf(fontSize); - if (newSize >= 0) return newSize; - return parseInt(fontSize, 10); - } - - function standardizeColor(color) { - if (!color || color.match(/[0-9a-f]{6}/i)) return color; - var m = color.toLowerCase().match(/^(rgba?|hsla?)\(([\s\.\-,%0-9]+)\)/); - if(m){ - var c = m[2].split(/\s*,\s*/), l = c.length, t = m[1]; - if((t == "rgb" && l == 3) || (t == "rgba" && l == 4)){ - var r = c[0]; - if(r.charAt(r.length - 1) == "%"){ - var a = c.map(function(x){ - return parseFloat(x) * 2.56; - }); - if(l == 4){ a[3] = c[3]; } - return _colorFromArray(a); - } - return _colorFromArray(c); - } - if((t == "hsl" && l == 3) || (t == "hsla" && l == 4)){ - var H = ((parseFloat(c[0]) % 360) + 360) % 360 / 360, - S = parseFloat(c[1]) / 100, - L = parseFloat(c[2]) / 100, - m2 = L <= 0.5 ? L * (S + 1) : L + S - L * S, - m1 = 2 * L - m2, - a = [_hue2rgb(m1, m2, H + 1 / 3) * 256, - _hue2rgb(m1, m2, H) * 256, _hue2rgb(m1, m2, H - 1 / 3) * 256, 1]; - if(l == 4){ a[3] = c[3]; } - return _colorFromArray(a); - } - } - return null; // dojo.Color - } - - function _colorFromArray(a) { - var arr = a.slice(0, 3).map(function(x){ - var s = parseInt(x, 10).toString(16); - return s.length < 2 ? "0" + s : s; - }); - return "#" + arr.join(""); // String - } - - function _hue2rgb(m1, m2, h){ - if(h < 0){ ++h; } - if(h > 1){ --h; } - var h6 = 6 * h; - if(h6 < 1){ return m1 + (m2 - m1) * h6; } - if(2 * h < 1){ return m2; } - if(3 * h < 2){ return m1 + (m2 - m1) * (2 / 3 - h) * 6; } - return m1; - } - return { boldSelection: boldSelection, boldSelected: boldSelected, From 1dc0598ee118d44c1861c307953ab7e8e6af0147 Mon Sep 17 00:00:00 2001 From: jeffkreeftmeijer Date: Thu, 12 Nov 2009 06:28:20 +0800 Subject: [PATCH 021/164] removed deleted methods from the return --- src/wysihat/editor/commands.js | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/wysihat/editor/commands.js b/src/wysihat/editor/commands.js index e5ebe35..62712d9 100644 --- a/src/wysihat/editor/commands.js +++ b/src/wysihat/editor/commands.js @@ -298,13 +298,9 @@ WysiHat.Commands = (function() { strikethroughSelection: strikethroughSelection, blockquoteSelection: blockquoteSelection, fontSelection: fontSelection, - fontSelected: fontSelected, fontSizeSelection: fontSizeSelection, - fontSizeSelected: fontSizeSelected, colorSelection: colorSelection, - colorSelected: colorSelected, backgroundColorSelection: backgroundColorSelection, - backgroundColorSelected: backgroundColorSelected, alignSelection: alignSelection, alignSelected: alignSelected, linkSelection: linkSelection, From 0a175f7e3ec40d9dcf14d06c206ee40c25465e28 Mon Sep 17 00:00:00 2001 From: Jeff Kreeftmeijer Date: Fri, 13 Nov 2009 17:46:49 +0800 Subject: [PATCH 022/164] updated the block in examples/font_selection.html --- examples/font_selection.html | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/examples/font_selection.html b/examples/font_selection.html index 9c4fd5a..8e973d6 100644 --- a/examples/font_selection.html +++ b/examples/font_selection.html @@ -88,7 +88,7 @@ var georgiaButton = $$('.editor_toolbar .georgia').first(); editor.observe('wysihat:cursormove', function(event) { - if (editor.fontSelected() == 'Georgia') + if (editor.getSelectedStyles().get('fontname') == 'Georgia') georgiaButton.addClassName('selected') else georgiaButton.removeClassName('selected'); @@ -96,12 +96,11 @@ var arialButton = $$('.editor_toolbar .arial').first(); editor.observe('wysihat:cursormove', function(event) { - if (editor.fontSelected() == 'Arial') + if (editor.getSelectedStyles().get('fontname') == 'Arial') arialButton.addClassName('selected') else arialButton.removeClassName('selected'); }); - });
From dc6d377bd838264c6fe59762b1ec8636e29aaf53 Mon Sep 17 00:00:00 2001 From: Jeff Kreeftmeijer Date: Mon, 11 Jan 2010 20:29:08 +0800 Subject: [PATCH 023/164] added WysiHat.Commands#formatblockSelection and examples/heading_selection.html --- examples/heading_selection.html | 115 ++++++++++++++++++++++++++++++++ src/wysihat/editor/commands.js | 32 ++++++--- 2 files changed, 137 insertions(+), 10 deletions(-) create mode 100644 examples/heading_selection.html diff --git a/examples/heading_selection.html b/examples/heading_selection.html new file mode 100644 index 0000000..d9b3860 --- /dev/null +++ b/examples/heading_selection.html @@ -0,0 +1,115 @@ + + + + WYSIWYG + + + + + + + + + + +

Oops, you need to build the package before running this example. It's easy: just run rake in the project's directory.

+ +

This example shows you how to create a simple UI for using html headings.

+ +
+  Event.observe(window, 'load', function() {
+    var editor = WysiHat.Editor.attach('content');
+    var toolbar = new WysiHat.Toolbar(editor);
+
+    toolbar.addButton({
+      label: "H1",
+      handler: function(editor) { return editor.formatblockSelection('h1'); }
+    });
+
+    toolbar.addButton({
+      label: "H2",
+      handler: function(editor) { return editor.formatblockSelection('h2'); }
+    });
+
+    toolbar.addButton({
+      label: "H3",
+      handler: function(editor) { return editor.formatblockSelection('h3'); }
+    });
+
+    toolbar.addButton({
+      label: "default",
+      handler: function(editor) { return editor.formatblockSelection('p'); }
+    });
+  });
+
+ + + + + + + diff --git a/src/wysihat/editor/commands.js b/src/wysihat/editor/commands.js index 62712d9..583390e 100644 --- a/src/wysihat/editor/commands.js +++ b/src/wysihat/editor/commands.js @@ -94,7 +94,7 @@ WysiHat.Commands = (function() { function fontSelection(font) { this.execCommand('fontname', false, font); } - + /** * WysiHat.Commands#fontSizeSelection(fontSize) -> undefined * - font size (int) : font size for selection @@ -118,7 +118,7 @@ WysiHat.Commands = (function() { /** * WysiHat.Commands#backgroundColorSelection(color) -> undefined * - color (string) - a color or hexadecimal value - * + * * Sets the background color. Firefox will fill in the background * color of the entire iframe unless hilitecolor is used. **/ @@ -129,11 +129,11 @@ WysiHat.Commands = (function() { this.execCommand('backcolor', false, color); } } - + /** * WysiHat.Commands#alignSelection(color) -> undefined * - alignment (string) - how the text should be aligned (left, center, right) - * + * **/ function alignSelection(alignment) { this.execCommand('justify' + alignment); @@ -182,6 +182,17 @@ WysiHat.Commands = (function() { return node ? node.tagName.toUpperCase() == 'A' : false; } + /** + * WysiHat.Commands#formatblockSelection(element) -> undefined + * - element (String): the type of element you want to wrap your selection + * with (like 'h1' or 'p'). + * + * Wraps the current selection in a header or paragraph. + **/ + function formatblockSelection(element){ + this.execCommand('formatblock', false, element); + } + /** * WysiHat.Commands#insertOrderedList() -> undefined * @@ -270,12 +281,12 @@ WysiHat.Commands = (function() { return handler.bind(this)(); else return document.queryCommandState(state); - } + } /** * WysiHat.Commands#getSelectedStyles() -> Hash - * - * Fetches the styles (from the styleSelectors hash) from the current + * + * Fetches the styles (from the styleSelectors hash) from the current * selection and returns it as a hash **/ function getSelectedStyles() { @@ -287,7 +298,7 @@ WysiHat.Commands = (function() { }); return styles; } - + return { boldSelection: boldSelection, boldSelected: boldSelected, @@ -306,6 +317,7 @@ WysiHat.Commands = (function() { linkSelection: linkSelection, unlinkSelection: unlinkSelection, linkSelected: linkSelected, + formatblockSelection: formatblockSelection, insertOrderedList: insertOrderedList, insertUnorderedList: insertUnorderedList, insertImage: insertImage, @@ -313,13 +325,13 @@ WysiHat.Commands = (function() { execCommand: execCommand, queryCommandState: queryCommandState, getSelectedStyles: getSelectedStyles, - + commands: $H({}), queryCommands: $H({ link: linkSelected }), - + styleSelectors: $H({ fontname: 'fontFamily', fontsize: 'fontSize', From 1a29cfa71fbf29ab0e63813e3ec97eba478fe8d6 Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Wed, 3 Feb 2010 14:12:43 -0600 Subject: [PATCH 024/164] rename license filename --- MIT-LICENSE => LICENSE | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename MIT-LICENSE => LICENSE (100%) diff --git a/MIT-LICENSE b/LICENSE similarity index 100% rename from MIT-LICENSE rename to LICENSE From d03ebf34ed8cd02bed5f3597ec864b7866466d41 Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Wed, 3 Feb 2010 14:13:12 -0600 Subject: [PATCH 025/164] bump copyright --- LICENSE | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/LICENSE b/LICENSE index 118f33a..4eb22c4 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2008 Joshua Peek +Copyright (c) 2010 Joshua Peek Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the From aab2d89e3402dd9c44b098b0a5edaf3247829a38 Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Wed, 3 Feb 2010 14:15:02 -0600 Subject: [PATCH 026/164] trash website --- Rakefile | 15 +- website/assets/images/class.png | Bin 1562 -> 0 bytes website/assets/images/class_deprecated.png | Bin 1245 -> 0 bytes website/assets/images/constant.png | Bin 981 -> 0 bytes website/assets/images/constructor.png | Bin 584 -> 0 bytes website/assets/images/header-logo-small.png | Bin 4123 -> 0 bytes website/assets/images/header-logo.png | Bin 18451 -> 0 bytes website/assets/images/header-stripe-small.png | Bin 1645 -> 0 bytes website/assets/images/header-stripe.png | Bin 4022 -> 0 bytes website/assets/images/logo.png | Bin 6835 -> 0 bytes website/assets/images/method.png | Bin 1680 -> 0 bytes website/assets/images/method_deprecated.png | Bin 1342 -> 0 bytes website/assets/images/mixin.png | Bin 1130 -> 0 bytes website/assets/images/namespace.png | Bin 970 -> 0 bytes website/assets/images/property.png | Bin 599 -> 0 bytes website/assets/images/section.png | Bin 946 -> 0 bytes website/assets/images/tagline.png | Bin 1962 -> 0 bytes website/assets/javascripts/application.js | 231 - .../assets/javascripts/code_highlighter.js | 251 - website/assets/javascripts/controls.js | 963 ---- website/assets/javascripts/effects.js | 1120 ----- .../assets/javascripts/mouse_enter_leave.js | 36 - website/assets/javascripts/prototype.js | 4226 ----------------- website/assets/stylesheets/api.css | 116 - website/assets/stylesheets/grid.css | 13 - website/assets/stylesheets/highlighter.css | 116 - website/assets/stylesheets/main.css | 443 -- website/assets/stylesheets/screen.css | 316 -- website/helpers.rb | 133 - website/index.erb | 5 - website/item_index.js.erb | 6 - website/layout.erb | 105 - website/namespace.erb | 102 - website/partials/short_description.erb | 28 - website/section.erb | 4 - website/utility.erb | 13 - 36 files changed, 1 insertion(+), 8241 deletions(-) delete mode 100644 website/assets/images/class.png delete mode 100644 website/assets/images/class_deprecated.png delete mode 100644 website/assets/images/constant.png delete mode 100755 website/assets/images/constructor.png delete mode 100644 website/assets/images/header-logo-small.png delete mode 100644 website/assets/images/header-logo.png delete mode 100644 website/assets/images/header-stripe-small.png delete mode 100644 website/assets/images/header-stripe.png delete mode 100644 website/assets/images/logo.png delete mode 100644 website/assets/images/method.png delete mode 100644 website/assets/images/method_deprecated.png delete mode 100644 website/assets/images/mixin.png delete mode 100644 website/assets/images/namespace.png delete mode 100755 website/assets/images/property.png delete mode 100644 website/assets/images/section.png delete mode 100644 website/assets/images/tagline.png delete mode 100644 website/assets/javascripts/application.js delete mode 100644 website/assets/javascripts/code_highlighter.js delete mode 100644 website/assets/javascripts/controls.js delete mode 100644 website/assets/javascripts/effects.js delete mode 100644 website/assets/javascripts/mouse_enter_leave.js delete mode 100644 website/assets/javascripts/prototype.js delete mode 100644 website/assets/stylesheets/api.css delete mode 100644 website/assets/stylesheets/grid.css delete mode 100644 website/assets/stylesheets/highlighter.css delete mode 100644 website/assets/stylesheets/main.css delete mode 100644 website/assets/stylesheets/screen.css delete mode 100644 website/helpers.rb delete mode 100644 website/index.erb delete mode 100644 website/item_index.js.erb delete mode 100644 website/layout.erb delete mode 100644 website/namespace.erb delete mode 100644 website/partials/short_description.erb delete mode 100644 website/section.erb delete mode 100644 website/utility.erb diff --git a/Rakefile b/Rakefile index 2258128..2118aaf 100644 --- a/Rakefile +++ b/Rakefile @@ -6,7 +6,6 @@ WYSIHAT_ROOT = File.expand_path(File.dirname(__FILE__)) WYSIHAT_SRC_DIR = File.join(WYSIHAT_ROOT, 'src') WYSIHAT_DIST_DIR = File.join(WYSIHAT_ROOT, 'dist') WYSIHAT_DOC_DIR = File.join(WYSIHAT_ROOT, 'doc') -WYSIHAT_WEBSITE_DIR = File.join(WYSIHAT_ROOT, 'website') WYSIHAT_TEST_DIR = File.join(WYSIHAT_ROOT, 'test') WYSIHAT_TEST_UNIT_DIR = File.join(WYSIHAT_TEST_DIR, 'unit') WYSIHAT_TMP_DIR = File.join(WYSIHAT_TEST_UNIT_DIR, 'tmp') @@ -85,23 +84,11 @@ namespace :doc do secretary.concatenation.save_to(temp.path) PDoc::Runner.new(temp.path, - :output => WYSIHAT_DOC_DIR, - :templates => WYSIHAT_WEBSITE_DIR + :output => WYSIHAT_DOC_DIR ).run end end - task :publish => :build do - Dir.chdir(WYSIHAT_DOC_DIR) do - system "git init" - system "git add ." - system "git commit -m \"import docs\"" - system "git remote add origin git@github.com:josh/wysihat.git" - system "git checkout -b gh-pages" - system "git push -f origin gh-pages" - end - end - desc "Empties documentation directory" task :clean do rm_rf WYSIHAT_DOC_DIR diff --git a/website/assets/images/class.png b/website/assets/images/class.png deleted file mode 100644 index e1510c1694072501a6244da127c7726dde669d07..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1562 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 Hu0mjfmT^=9 diff --git a/website/assets/images/constant.png b/website/assets/images/constant.png deleted file mode 100644 index c4295ec0df9bb1921471c5f6625f77eb2084cea3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 981 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 ZzyQC?_H;||rKJD>002ovPDHLkV1nUx@F@TQ diff --git a/website/assets/images/header-logo.png b/website/assets/images/header-logo.png deleted file mode 100644 index 4f6c7c18615bb6e0ba033f681a7ef90f2009e312..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 18451 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 diff --git a/website/assets/images/header-stripe.png b/website/assets/images/header-stripe.png deleted file mode 100644 index 2254f7c83ac38960f30066a8c68bee5bae95692d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4022 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 diff --git a/website/assets/images/logo.png b/website/assets/images/logo.png deleted file mode 100644 index 6ff25884e1b2dabdb93b7bf47fefbd156855b2ff..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6835 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 diff --git a/website/assets/images/method.png b/website/assets/images/method.png deleted file mode 100644 index 35efa75949d7c79947a61c38a1b54381b122fd90..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1680 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 AS^xk5 diff --git a/website/assets/images/mixin.png b/website/assets/images/mixin.png deleted file mode 100644 index 11a7f8fed4227c4902f8acb9d3a1f4089d37ccbe..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1130 zcmV-w1eN=VP)X+uL$Nkc;*P;zf(X>4Tx0C=30k-tkDVHn3h?`a7X&C=7>f^dVF3{iqv zA{4=7u%bg61d|NHzBK!xR*f>o*3ti&0;^aFtHL~^=Rn&-~=^Bn>3ZOoR^ zKWe3pe-FLg{jxN+dAf6Tes>)JePN|q17QMYLvtE1>zZEx=4&Mth!S8rE4B;51v246 zxd>t#NH#R@0?E4O10cEXG!%$7kPX~s0OA*rcfH5~BMm4=h6;@Lz(}Q9v-D_c4yJ~o z=cMb^BG~&3Pe;36`WXx?!eOrKbzUI=F?bR!=OzFUeFb=Q8ONPbNW6pV?KuAXF^;c4 zLC*!W17|H<|92N*Ou~Iy@20i>0qDNBv)&(oEPQwgi;rP$3wC}$Y9F3_g;GB(Ok(7J z{!e`?0G=0ZzgZ7$<+)aQreGCYek+Wm?RfV*`k|q`HwvKGk{e;Ov7)SLDSawM1loO7=O*nQ&43XAEwK?B@I!!QHP$iXIDB@)Zw z_Yba~=>VtzXpDz(uCw0V&d|3Xn2jevhAE~n{Np!c-cYKGXG*WyU0=GU208CZUXij|ray9|S)-$fu9z z=~#Z7`t~7qovNSyy9G0nDA7lJl-!OOL6jtlj14e<-fPx$t!KfkW%Rwe!K0TC&#pV# zHnSL?K}t#t84!{(7KIQ6pOn#`_gLMz3MqtDZ95Sc*-+mEj$=hupoAnOpYTyBqY$HJ z`Ce%J`byPJXHVC008Z|>#FDu^IB1!efrueN@G*NC6W%K!DBnjKy!t%Ixw{7eICtkD z?}naZ;PX2$gQ8&V`X0n|iiz^F=Q;S7G4UeK0OMfUO5ETsAnA wf8IauAA{zZ*89-%6kNdI7iPVWd%v9e1Ax=SeX(cYBme*a07*qoM6N<$f*KS8aR2}S diff --git a/website/assets/images/namespace.png b/website/assets/images/namespace.png deleted file mode 100644 index f2f7b73ddb76a58b4ee1378396b9de776b6617a7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 970 zcmV;*12z1KP)X+uL$Nkc;*P;zf(X>4Tx0C=30k-tkDVHn3h?`a7X&C=7>f^dVF3{iqv zA{4=7u%bg61d|NHzBK!xR*f>o*3ti&0;^aFtHL~^=Rn&-~=^Bn>3ZOoR^ zKWe3pe-FLg{jxN+dAf6Tes>)JePN|q17QMYLvtE1>zZEx=4&Mth!S8rE4B;51v246 zxd>t#NH#R@0?E4O10cEXG!%$7kPX~s0OA*rcfH5~BMm4=h6;@Lz(}Q9v-D_c4yJ~o z=cMb^BG~&3Pe;36`WXx?!eOrKbzUI=F?bR!=OzFUeFb=Q8ONPbNW6pV?KuAXF^;c4 zLC*!W17|H<|92N*Ou~Iy@20i>0qDNBv)&(oEPQwgi;rP$3wC}$Y9F3_g;GB(Ok(7J z{!e`?0G=0ZzgZ7$<+)aQreGCYek+Wm?RfV*`k|q`HwvKGk{e;Ov7)SLDSaP!CJ7jvau0F&`N9+R7es0 zfrNmXd+(XO*Rq(C7$jB}4s6cM=B&Lwm^JXfR|9bW=IQZfu=Bu;E}~}4fDOXfVAro~ zZ2vIayY*;e4WM~=eQ~N&`*CXS_~i855;P6;BAI<x$%k3Z|iqfAAi2xb~^NG zdSPjDdVZNN?_W|zKT$#&QYHjpBS8r>F?)iE*=0hku3h0wYnWL%cb?bJo-p{nRmHt1 zWHKRSNd*KVNE5Gzn5&Lkso6cmBV?mkfo#*WVM z=7ni(aEQV7JJju<5GtxeN)kn=WM4w14jVtRhzxDoq=ZY7!>kUGs7o@0x?bG(6e6Sc z?4Cd+(J07Kpc3SsTlQz+vMcQAA}fKZjT{!rupEe+ML31JKxv$nWRJj|h*6M(@)ulH z$Ebsxu4QOtSC>JM!|q_*6iVR`-c44Y($!-#=Tuqg?a7ZyD3vy*mFC snhVmtytAA8Ki>hkDlx|5`9HtLU+ISQ!ulZ{g#Z8m07*qoM6N<$f^1^Gh5!Hn diff --git a/website/assets/images/property.png b/website/assets/images/property.png deleted file mode 100755 index ebaf0e8743fc33cf152cb7694b865e8d8adbfa90..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 599 zcmV-d0;v6oP)GWQRM3$+Tx>2G~D3qqtDdzLJ$P+M| z%_5OV?1V_vbzR8Cf4JqIVHo)K^%pXcv3TotyGW%{h{xkw5Q#dkEiV=ekof!9z?%~d z=g(E7!z)Oq(;*j#L?RmyiObN>-n-ERY$OQs{fC-WE z`TR3JBO8xbMN0KSbiO^m!^f-pEr_bBgH6ZV(+;jrKl87%5H3QY5S&iu(~XVt z`Q%tE#s-6d3Aucs;QCb?w-;^voEneC7ITEh5sgOWa5&8962;=`54d^#1;35CDRwv< z7ITTajw2Wh%6`9}(Iu`wF^PG8L%bJ2;q&>deAA2$Oi`4b;)4JRkH=$mxm=9IcDvoO l^OXPhyYjA9E`GA_{V$1@FaBct0}KEF002ovPDHLkV1kGx8r%Q? diff --git a/website/assets/images/section.png b/website/assets/images/section.png deleted file mode 100644 index d9d598569175e9d73005163366a19ab960f7d0b3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 946 zcmV;j15NyiP)X+uL$Nkc;*P;zf(X>4Tx0C=30k-tkDVHn3h?`a7X&C=7>f^dVF3{iqv zA{4=7u%bg61d|NHzBK!xR*f>o*3ti&0;^aFtHL~^=Rn&-~=^Bn>3ZOoR^ zKWe3pe-FLg{jxN+dAf6Tes>)JePN|q17QMYLvtE1>zZEx=4&Mth!S8rE4B;51v246 zxd>t#NH#R@0?E4O10cEXG!%$7kPX~s0OA*rcfH5~BMm4=h6;@Lz(}Q9v-D_c4yJ~o z=cMb^BG~&3Pe;36`WXx?!eOrKbzUI=F?bR!=OzFUeFb=Q8ONPbNW6pV?KuAXF^;c4 zLC*!W17|H<|92N*Ou~Iy@20i>0qDNBv)&(oEPQwgi;rP$3wC}$Y9F3_g;GB(Ok(7J z{!e`?0G=0ZzgZ7$<+)aQreGCYek+Wm?RfV*`k|q`HwvKGk{e;Ov7)SLDSaF&>9#@#i!P_UA2s_XmJS4G2r9|rL8)wTC|cWcWmS$PuG;M1&07*qoM6N<$g085ny8r+H diff --git a/website/assets/images/tagline.png b/website/assets/images/tagline.png deleted file mode 100644 index 85b366caabcc0f5751848242f79a6dd9c23252a6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1962 zcmV;b2UYlqP)MV*mgE5Oh*bQ~&?}|NsC0|NsC0 z|NsC00N^8C82|tV^hrcPRCwC#TG@8PC=A6Lcme$Xe>-Ra1jd=faXX!J%tO)|v|TPj z;HbUbCgc5R_s`nuyZxIskKV6eY3RM%f1~}DoqN%Fuc?#gff3#hA1dp-|Hc;$Hv4Zh zSpOv{|CpV7(fN!L#`k4P2#jiM-&B!Rd^FsJb#e2>mYlnCp|k3UEW z{Z_`gn-XM|9=Tf=*i9S9(WyT}iD~phY8F{S-TTM<#m>CG3{AP9EzH z!xnUe7k*NWeypUyfwPXMY8Zv^M3Jn~`IB%$5?`Q0C12>Y5D`bNY zZYwE>Ezd1LJg_t=j~yLvRm3}+cUF|ChmAfj51~4{(6f_f7jChzrARt<{w$p2j!P@j zF%CE_u<1`6B-DSUom9-)^sPr8Jf*-uqor`Vlxs>PBU}v;U!zk7G)a-d`ZY>ua7TG@ z)SexX%Q%5vHCD9(_N}2 z7aXmg1!M!1mWF3xOOYsY{xlpNJNk6sTln0!5nDGz-$BI7UfF3-!o=_z!?Sdr^L*vBfa4gBGg6swSEk3WA<>tajo_ijizD!Wn2ixU?!36kWOM4uQ>z z79He@xw6xsM30-D?s`nLj%c+cxr!1ANrs>)azKY$(DwND%InS2JgcaCB}>y=8ZXHFDX$EbH!yzR&9w2IA4_9JH%*`!UZMH!wF$5 zwyNbE?}sy1-=Iu!0x^}H1|^pCJXNB>Pi?)ZiyBB01Z|NL9VY{-tO)BDDKV1*n{#-i zM4UI^psgv9|A}aUKiN55Q6k0;4BtusB_$5S^s&y@(uHW9DM6P%Gi3Cd66fLUMR7?M zI{0v)2bC@Hln&Xnw9}x(oc6mpPET0CLz3IuND>5Xk&-RC2&fbh)-O`RFylMPU^H#w zd;#n)DPg16nORds2^~8ytW!#~hh+$p>Jv(Ad*xTA#CbTeXvydB1^ts0K zm7NA9cGXicmC8P>e9C2aMv1maq=bMVqf>|o>(?k@g|^{92`w|&IVCi|_VRfF*&`*) zo>K2CN;HN=jpm4DlM?!b5+~tcbvt^IZ{b|NgnR|GfPF(!Vyx`6C=nK}!Q;v8lrZ}d z_?i;Q`ZY>8p=~KCu||vn9#iy29|Bn;m%`Gb16#E@B1}`fARTg2Z&0E=EUpZp`h*gF zt_5mJoQIR_r(zNIFBm@I_e%8}x4E(-->8Vxx@(YBa1!7)y+?_*NJK|KkOC@2g!OBb z2<0j3TExbUK+fy{*0FJ}aC@zbvwly2qORvOR?GvQnNxeA1bjYtU^ZM(xEG zLWx|p(qXHhgpNCk;Uo~ZmogCLM{!)0%lsFA%GG26~%OAe& z=V|8>Zc%$>Cz7bkH97%8-L^|t|8G*FDN>?i>VSlZwBCJwg{hQyLCc#EU1{^*CUS8 zhyJ#urGa0u53TTp9r-ps{R%*d?)-cxY{)PqTuqSz9aRLR&aK this.resultsElement.offsetHeight + this.resultsElement.scrollTop) { - // item is too low - this.resultsElement.scrollTop = distanceToBottom - this.resultsElement.offsetHeight; - } else if (highlighted.offsetTop < this.resultsElement.scrollTop) { - // item is too high - this.resultsElement.scrollTop = highlighted.offsetTop; - } - - return highlighted; - }, - - scrollList: function(direction) { - this.moveHighlight(direction); - this._timer = window.setTimeout(this.scrollList.bind(this, direction), 100); - }, - - buildResults: function(urls) { - this.resultsElement.update(); - var ul = this.resultsElement; - urls.each( function(url) { - var a = new Element('a', { - 'class': url.type.gsub(/\s/, '_'), - href: PDoc.pathPrefix + url.path - }).update(url.name); - var li = new Element('li'); - li.appendChild(a); - ul.appendChild(li); - }); - this.showResults(); - }, - - - findURLs: function(str) { - var results = []; - for (var i in PDoc.elements) { - if (i.toLowerCase().include(str)) results.push(PDoc.elements[i]); - } - return results; - }, - - onEmpty: function() { - this.hideResults(); - }, - - showResults: function() { - this.resultsElement.show(); - document.observe("keydown", this.events.keydown); - }, - - hideResults: function() { - this.resultsElement.hide(); - document.stopObserving("keydown", this.events.keydown); - } -}); - -document.observe('dom:loaded', function() { - new Filterer($('search'), { menu: $('api_menu'), resultsElement: $('search_results') }); -}); - -document.observe('click', function(event) { - var element = event.findElement('a'); - if (!element) return; - var href = element.readAttribute('href'); - if (!href.include('#')) return; - if (element = $(href.split('#').last())) { - PDoc.highlightSelected(element); - } -}); - -document.observe('dom:loaded', function() { PDoc.highlightSelected() }); - -Event.observe(window, 'load', function() { - var menu = $('menu'); - var OFFSET = menu.viewportOffset().top; - - Event.observe(window, 'scroll', function() { - var sOffset = document.viewport.getScrollOffsets(); - if (sOffset.top > OFFSET) { - menu.addClassName('fixed'); - } else menu.removeClassName('fixed'); - }) -}); \ No newline at end of file diff --git a/website/assets/javascripts/code_highlighter.js b/website/assets/javascripts/code_highlighter.js deleted file mode 100644 index e3550ce..0000000 --- a/website/assets/javascripts/code_highlighter.js +++ /dev/null @@ -1,251 +0,0 @@ -/* Unobtrustive Code Highlighter By Dan Webb 11/2005 - Version: 0.4 - - Usage: - Add a script tag for this script and any stylesets you need to use - to the page in question, add correct class names to CODE elements, - define CSS styles for elements. That's it! - - Known to work on: - IE 5.5+ PC - Firefox/Mozilla PC/Mac - Opera 7.23 + PC - Safari 2 - - Known to degrade gracefully on: - IE5.0 PC - - Note: IE5.0 fails due to the use of lookahead in some stylesets. To avoid script errors - in older browsers use expressions that use lookahead in string format when defining stylesets. - - This script is inspired by star-light by entirely cunning Dean Edwards - http://dean.edwards.name/star-light/. -*/ - -// replace callback support for safari. -if ("a".replace(/a/, function() {return "b"}) != "b") (function(){ - var default_replace = String.prototype.replace; - String.prototype.replace = function(search,replace){ - // replace is not function - if(typeof replace != "function"){ - return default_replace.apply(this,arguments) - } - var str = "" + this; - var callback = replace; - // search string is not RegExp - if(!(search instanceof RegExp)){ - var idx = str.indexOf(search); - return ( - idx == -1 ? str : - default_replace.apply(str,[search,callback(search, idx, str)]) - ) - } - var reg = search; - var result = []; - var lastidx = reg.lastIndex; - var re; - while((re = reg.exec(str)) != null){ - var idx = re.index; - var args = re.concat(idx, str); - result.push( - str.slice(lastidx,idx), - callback.apply(null,args).toString() - ); - if(!reg.global){ - lastidx += RegExp.lastMatch.length; - break - }else{ - lastidx = reg.lastIndex; - } - } - result.push(str.slice(lastidx)); - return result.join("") - } -})(); - -var CodeHighlighter = { styleSets : new Array }; - -CodeHighlighter.addStyle = function(name, rules) { - // using push test to disallow older browsers from adding styleSets - if ([].push) this.styleSets.push({ - name : name, - rules : rules, - ignoreCase : arguments[2] || false - }) - - function setEvent() { - // set highlighter to run on load (use LowPro if present) - if (typeof Event != 'undefined' && typeof Event.onReady == 'function') - return Event.onReady(CodeHighlighter.init.bind(CodeHighlighter)); - - var old = window.onload; - - if (typeof window.onload != 'function') { - window.onload = function() { CodeHighlighter.init() }; - } else { - window.onload = function() { - old(); - CodeHighlighter.init(); - } - } - } - - // only set the event when the first style is added - if (this.styleSets.length==1) setEvent(); -} - -CodeHighlighter.init = function() { - if (!document.getElementsByTagName) return; - if ("a".replace(/a/, function() {return "b"}) != "b") return; // throw out Safari versions that don't support replace function - // throw out older browsers - - var codeEls = document.getElementsByTagName("CODE"); - // collect array of all pre elements - codeEls.filter = function(f) { - var a = new Array; - for (var i = 0; i < this.length; i++) if (f(this[i])) a[a.length] = this[i]; - return a; - } - - var rules = new Array; - rules.toString = function() { - // joins regexes into one big parallel regex - var exps = new Array; - for (var i = 0; i < this.length; i++) exps.push(this[i].exp); - return exps.join("|"); - } - - function addRule(className, rule) { - // add a replace rule - var exp = (typeof rule.exp != "string")?String(rule.exp).substr(1, String(rule.exp).length-2):rule.exp; - // converts regex rules to strings and chops of the slashes - rules.push({ - className : className, - exp : "(" + exp + ")", - length : (exp.match(/(^|[^\\])\([^?]/g) || "").length + 1, // number of subexps in rule - replacement : rule.replacement || null - }); - } - - function parse(text, ignoreCase) { - // main text parsing and replacement - return text.replace(new RegExp(rules, (ignoreCase)?"gi":"g"), function() { - var i = 0, j = 1, rule; - while (rule = rules[i++]) { - if (arguments[j]) { - // if no custom replacement defined do the simple replacement - if (!rule.replacement) return "" + arguments[0] + ""; - else { - // replace $0 with the className then do normal replaces - var str = rule.replacement.replace("$0", rule.className); - for (var k = 1; k <= rule.length - 1; k++) str = str.replace("$" + k, arguments[j + k]); - return str; - } - } else j+= rule.length; - } - }); - } - - function highlightCode(styleSet) { - // clear rules array - var parsed; - rules.length = 0; - - // get stylable elements by filtering out all code elements without the correct className - var stylableEls = codeEls.filter(function(item) {return (item.className.indexOf(styleSet.name)>=0)}); - - // add style rules to parser - for (var className in styleSet.rules) addRule(className, styleSet.rules[className]); - - - // replace for all elements - for (var i = 0; i < stylableEls.length; i++) { - // EVIL hack to fix IE whitespace badness if it's inside a
-			if (/MSIE/.test(navigator.appVersion) && stylableEls[i].parentNode.nodeName == 'PRE') {
-				stylableEls[i] = stylableEls[i].parentNode;
-				
-				parsed = stylableEls[i].innerHTML.replace(/(]*>)([^<]*)<\/code>/i, function() {
-					return arguments[1] + parse(arguments[2], styleSet.ignoreCase) + ""
-				});
-				parsed = parsed.replace(/\n( *)/g, function() { 
-					var spaces = "";
-					for (var i = 0; i < arguments[1].length; i++) spaces+= " ";
-					return "\n" + spaces;  
-				});
-				parsed = parsed.replace(/\t/g, "    ");
-				parsed = parsed.replace(/\n(<\/\w+>)?/g, "
$1").replace(/
[\n\r\s]*
/g, "


"); - - } else parsed = parse(stylableEls[i].innerHTML, styleSet.ignoreCase); - - stylableEls[i].innerHTML = parsed; - } - } - - // run highlighter on all stylesets - for (var i=0; i < this.styleSets.length; i++) { - highlightCode(this.styleSets[i]); - } -}; - -CodeHighlighter.addStyle("css", { - comment : { - exp : /\/\*[^*]*\*+([^\/][^*]*\*+)*\// - }, - keywords : { - exp : /@\w[\w\s]*/ - }, - selectors : { - exp : "([\\w-:\\[.#][^{};>]*)(?={)" - }, - properties : { - exp : "([\\w-]+)(?=\\s*:)" - }, - units : { - exp : /([0-9])(em|en|px|%|pt)\b/, - replacement : "$1$2" - }, - urls : { - exp : /url\([^\)]*\)/ - } -}); - -CodeHighlighter.addStyle("html", { - comment : { - exp: /<!\s*(--([^-]|[\r\n]|-[^-])*--\s*)>/ - }, - tag : { - exp: /(<\/?)([a-zA-Z]+\s?)/, - replacement: "$1$2" - }, - string : { - exp : /'[^']*'|"[^"]*"/ - }, - attribute : { - exp: /\b([a-zA-Z-:]+)(=)/, - replacement: "$1$2" - }, - doctype : { - exp: /<!DOCTYPE([^&]|&[^g]|&g[^t])*>/ - } -}); - -CodeHighlighter.addStyle("javascript",{ - comment : { - exp : /(\/\/[^\n]*(\n|$))|(\/\*[^*]*\*+([^\/][^*]*\*+)*\/)/ - }, - brackets : { - exp : /\(|\)/ - }, - regex : { - exp : /\/(.*?)[g|s|m]?\/[;|\n]/ - }, - string : { - exp : /'(?:\.|(\\\')|[^\''])*'|"(?:\.|(\\\")|[^\""])*"/ - }, - keywords : { - exp : /\b(arguments|break|case|continue|default|delete|do|else|false|for|function|if|in|instanceof|new|null|return|switch|this|true|typeof|var|void|while|with)\b/ - }, - global : { - exp : /\b(toString|valueOf|window|element|prototype|constructor|document|escape|unescape|parseInt|parseFloat|setTimeout|clearTimeout|setInterval|clearInterval|NaN|isNaN|Infinity|alert|prompt|confirm)\b/ - } -}); diff --git a/website/assets/javascripts/controls.js b/website/assets/javascripts/controls.js deleted file mode 100644 index fbc4418..0000000 --- a/website/assets/javascripts/controls.js +++ /dev/null @@ -1,963 +0,0 @@ -// Copyright (c) 2005-2007 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us) -// (c) 2005-2007 Ivan Krstic (http://blogs.law.harvard.edu/ivan) -// (c) 2005-2007 Jon Tirsen (http://www.tirsen.com) -// Contributors: -// Richard Livsey -// Rahul Bhargava -// Rob Wills -// -// script.aculo.us is freely distributable under the terms of an MIT-style license. -// For details, see the script.aculo.us web site: http://script.aculo.us/ - -// Autocompleter.Base handles all the autocompletion functionality -// that's independent of the data source for autocompletion. This -// includes drawing the autocompletion menu, observing keyboard -// and mouse events, and similar. -// -// Specific autocompleters need to provide, at the very least, -// a getUpdatedChoices function that will be invoked every time -// the text inside the monitored textbox changes. This method -// should get the text for which to provide autocompletion by -// invoking this.getToken(), NOT by directly accessing -// this.element.value. This is to allow incremental tokenized -// autocompletion. Specific auto-completion logic (AJAX, etc) -// belongs in getUpdatedChoices. -// -// Tokenized incremental autocompletion is enabled automatically -// when an autocompleter is instantiated with the 'tokens' option -// in the options parameter, e.g.: -// new Ajax.Autocompleter('id','upd', '/url/', { tokens: ',' }); -// will incrementally autocomplete with a comma as the token. -// Additionally, ',' in the above example can be replaced with -// a token array, e.g. { tokens: [',', '\n'] } which -// enables autocompletion on multiple tokens. This is most -// useful when one of the tokens is \n (a newline), as it -// allows smart autocompletion after linebreaks. - -if(typeof Effect == 'undefined') - throw("controls.js requires including script.aculo.us' effects.js library"); - -var Autocompleter = { } -Autocompleter.Base = Class.create({ - baseInitialize: function(element, update, options) { - element = $(element) - this.element = element; - this.update = $(update); - this.hasFocus = false; - this.changed = false; - this.active = false; - this.index = 0; - this.entryCount = 0; - this.oldElementValue = this.element.value; - - if(this.setOptions) - this.setOptions(options); - else - this.options = options || { }; - - this.options.paramName = this.options.paramName || this.element.name; - this.options.tokens = this.options.tokens || []; - this.options.frequency = this.options.frequency || 0.4; - this.options.minChars = this.options.minChars || 1; - this.options.onShow = this.options.onShow || - function(element, update){ - if(!update.style.position || update.style.position=='absolute') { - update.style.position = 'absolute'; - Position.clone(element, update, { - setHeight: false, - offsetTop: element.offsetHeight - }); - } - Effect.Appear(update,{duration:0.15}); - }; - this.options.onHide = this.options.onHide || - function(element, update){ new Effect.Fade(update,{duration:0.15}) }; - - if(typeof(this.options.tokens) == 'string') - this.options.tokens = new Array(this.options.tokens); - // Force carriage returns as token delimiters anyway - if (!this.options.tokens.include('\n')) - this.options.tokens.push('\n'); - - this.observer = null; - - this.element.setAttribute('autocomplete','off'); - - Element.hide(this.update); - - Event.observe(this.element, 'blur', this.onBlur.bindAsEventListener(this)); - Event.observe(this.element, 'keydown', this.onKeyPress.bindAsEventListener(this)); - }, - - show: function() { - if(Element.getStyle(this.update, 'display')=='none') this.options.onShow(this.element, this.update); - if(!this.iefix && - (Prototype.Browser.IE) && - (Element.getStyle(this.update, 'position')=='absolute')) { - new Insertion.After(this.update, - ''); - this.iefix = $(this.update.id+'_iefix'); - } - if(this.iefix) setTimeout(this.fixIEOverlapping.bind(this), 50); - }, - - fixIEOverlapping: function() { - Position.clone(this.update, this.iefix, {setTop:(!this.update.style.height)}); - this.iefix.style.zIndex = 1; - this.update.style.zIndex = 2; - Element.show(this.iefix); - }, - - hide: function() { - this.stopIndicator(); - if(Element.getStyle(this.update, 'display')!='none') this.options.onHide(this.element, this.update); - if(this.iefix) Element.hide(this.iefix); - }, - - startIndicator: function() { - if(this.options.indicator) Element.show(this.options.indicator); - }, - - stopIndicator: function() { - if(this.options.indicator) Element.hide(this.options.indicator); - }, - - onKeyPress: function(event) { - if(this.active) - switch(event.keyCode) { - case Event.KEY_TAB: - case Event.KEY_RETURN: - this.selectEntry(); - Event.stop(event); - case Event.KEY_ESC: - this.hide(); - this.active = false; - Event.stop(event); - return; - case Event.KEY_LEFT: - case Event.KEY_RIGHT: - return; - case Event.KEY_UP: - this.markPrevious(); - this.render(); - Event.stop(event); - return; - case Event.KEY_DOWN: - this.markNext(); - this.render(); - Event.stop(event); - return; - } - else - if(event.keyCode==Event.KEY_TAB || event.keyCode==Event.KEY_RETURN || - (Prototype.Browser.WebKit > 0 && event.keyCode == 0)) return; - - this.changed = true; - this.hasFocus = true; - - if(this.observer) clearTimeout(this.observer); - this.observer = - setTimeout(this.onObserverEvent.bind(this), this.options.frequency*1000); - }, - - activate: function() { - this.changed = false; - this.hasFocus = true; - this.getUpdatedChoices(); - }, - - onHover: function(event) { - var element = Event.findElement(event, 'LI'); - if(this.index != element.autocompleteIndex) - { - this.index = element.autocompleteIndex; - this.render(); - } - Event.stop(event); - }, - - onClick: function(event) { - var element = Event.findElement(event, 'LI'); - this.index = element.autocompleteIndex; - this.selectEntry(); - this.hide(); - }, - - onBlur: function(event) { - // needed to make click events working - setTimeout(this.hide.bind(this), 250); - this.hasFocus = false; - this.active = false; - }, - - render: function() { - if(this.entryCount > 0) { - for (var i = 0; i < this.entryCount; i++) - this.index==i ? - Element.addClassName(this.getEntry(i),"selected") : - Element.removeClassName(this.getEntry(i),"selected"); - if(this.hasFocus) { - this.show(); - this.active = true; - } - } else { - this.active = false; - this.hide(); - } - }, - - markPrevious: function() { - if(this.index > 0) this.index-- - else this.index = this.entryCount-1; - this.getEntry(this.index).scrollIntoView(true); - }, - - markNext: function() { - if(this.index < this.entryCount-1) this.index++ - else this.index = 0; - this.getEntry(this.index).scrollIntoView(false); - }, - - getEntry: function(index) { - return this.update.firstChild.childNodes[index]; - }, - - getCurrentEntry: function() { - return this.getEntry(this.index); - }, - - selectEntry: function() { - this.active = false; - this.updateElement(this.getCurrentEntry()); - }, - - updateElement: function(selectedElement) { - if (this.options.updateElement) { - this.options.updateElement(selectedElement); - return; - } - var value = ''; - if (this.options.select) { - var nodes = $(selectedElement).select('.' + this.options.select) || []; - if(nodes.length>0) value = Element.collectTextNodes(nodes[0], this.options.select); - } else - value = Element.collectTextNodesIgnoreClass(selectedElement, 'informal'); - - var bounds = this.getTokenBounds(); - if (bounds[0] != -1) { - var newValue = this.element.value.substr(0, bounds[0]); - var whitespace = this.element.value.substr(bounds[0]).match(/^\s+/); - if (whitespace) - newValue += whitespace[0]; - this.element.value = newValue + value + this.element.value.substr(bounds[1]); - } else { - this.element.value = value; - } - this.oldElementValue = this.element.value; - this.element.focus(); - - if (this.options.afterUpdateElement) - this.options.afterUpdateElement(this.element, selectedElement); - }, - - updateChoices: function(choices) { - if(!this.changed && this.hasFocus) { - this.update.innerHTML = choices; - Element.cleanWhitespace(this.update); - Element.cleanWhitespace(this.update.down()); - - if(this.update.firstChild && this.update.down().childNodes) { - this.entryCount = - this.update.down().childNodes.length; - for (var i = 0; i < this.entryCount; i++) { - var entry = this.getEntry(i); - entry.autocompleteIndex = i; - this.addObservers(entry); - } - } else { - this.entryCount = 0; - } - - this.stopIndicator(); - this.index = 0; - - if(this.entryCount==1 && this.options.autoSelect) { - this.selectEntry(); - this.hide(); - } else { - this.render(); - } - } - }, - - addObservers: function(element) { - Event.observe(element, "mouseover", this.onHover.bindAsEventListener(this)); - Event.observe(element, "click", this.onClick.bindAsEventListener(this)); - }, - - onObserverEvent: function() { - this.changed = false; - this.tokenBounds = null; - if(this.getToken().length>=this.options.minChars) { - this.getUpdatedChoices(); - } else { - this.active = false; - this.hide(); - } - this.oldElementValue = this.element.value; - }, - - getToken: function() { - var bounds = this.getTokenBounds(); - return this.element.value.substring(bounds[0], bounds[1]).strip(); - }, - - getTokenBounds: function() { - if (null != this.tokenBounds) return this.tokenBounds; - var value = this.element.value; - if (value.strip().empty()) return [-1, 0]; - var diff = arguments.callee.getFirstDifferencePos(value, this.oldElementValue); - var offset = (diff == this.oldElementValue.length ? 1 : 0); - var prevTokenPos = -1, nextTokenPos = value.length; - var tp; - for (var index = 0, l = this.options.tokens.length; index < l; ++index) { - tp = value.lastIndexOf(this.options.tokens[index], diff + offset - 1); - if (tp > prevTokenPos) prevTokenPos = tp; - tp = value.indexOf(this.options.tokens[index], diff + offset); - if (-1 != tp && tp < nextTokenPos) nextTokenPos = tp; - } - return (this.tokenBounds = [prevTokenPos + 1, nextTokenPos]); - } -}); - -Autocompleter.Base.prototype.getTokenBounds.getFirstDifferencePos = function(newS, oldS) { - var boundary = Math.min(newS.length, oldS.length); - for (var index = 0; index < boundary; ++index) - if (newS[index] != oldS[index]) - return index; - return boundary; -}; - -Ajax.Autocompleter = Class.create(Autocompleter.Base, { - initialize: function(element, update, url, options) { - this.baseInitialize(element, update, options); - this.options.asynchronous = true; - this.options.onComplete = this.onComplete.bind(this); - this.options.defaultParams = this.options.parameters || null; - this.url = url; - }, - - getUpdatedChoices: function() { - this.startIndicator(); - - var entry = encodeURIComponent(this.options.paramName) + '=' + - encodeURIComponent(this.getToken()); - - this.options.parameters = this.options.callback ? - this.options.callback(this.element, entry) : entry; - - if(this.options.defaultParams) - this.options.parameters += '&' + this.options.defaultParams; - - new Ajax.Request(this.url, this.options); - }, - - onComplete: function(request) { - this.updateChoices(request.responseText); - } -}); - -// The local array autocompleter. Used when you'd prefer to -// inject an array of autocompletion options into the page, rather -// than sending out Ajax queries, which can be quite slow sometimes. -// -// The constructor takes four parameters. The first two are, as usual, -// the id of the monitored textbox, and id of the autocompletion menu. -// The third is the array you want to autocomplete from, and the fourth -// is the options block. -// -// Extra local autocompletion options: -// - choices - How many autocompletion choices to offer -// -// - partialSearch - If false, the autocompleter will match entered -// text only at the beginning of strings in the -// autocomplete array. Defaults to true, which will -// match text at the beginning of any *word* in the -// strings in the autocomplete array. If you want to -// search anywhere in the string, additionally set -// the option fullSearch to true (default: off). -// -// - fullSsearch - Search anywhere in autocomplete array strings. -// -// - partialChars - How many characters to enter before triggering -// a partial match (unlike minChars, which defines -// how many characters are required to do any match -// at all). Defaults to 2. -// -// - ignoreCase - Whether to ignore case when autocompleting. -// Defaults to true. -// -// It's possible to pass in a custom function as the 'selector' -// option, if you prefer to write your own autocompletion logic. -// In that case, the other options above will not apply unless -// you support them. - -Autocompleter.Local = Class.create(Autocompleter.Base, { - initialize: function(element, update, array, options) { - this.baseInitialize(element, update, options); - this.options.array = array; - }, - - getUpdatedChoices: function() { - this.updateChoices(this.options.selector(this)); - }, - - setOptions: function(options) { - this.options = Object.extend({ - choices: 10, - partialSearch: true, - partialChars: 2, - ignoreCase: true, - fullSearch: false, - selector: function(instance) { - var ret = []; // Beginning matches - var partial = []; // Inside matches - var entry = instance.getToken(); - var count = 0; - - for (var i = 0; i < instance.options.array.length && - ret.length < instance.options.choices ; i++) { - - var elem = instance.options.array[i]; - var foundPos = instance.options.ignoreCase ? - elem.toLowerCase().indexOf(entry.toLowerCase()) : - elem.indexOf(entry); - - while (foundPos != -1) { - if (foundPos == 0 && elem.length != entry.length) { - ret.push("
  • " + elem.substr(0, entry.length) + "" + - elem.substr(entry.length) + "
  • "); - break; - } else if (entry.length >= instance.options.partialChars && - instance.options.partialSearch && foundPos != -1) { - if (instance.options.fullSearch || /\s/.test(elem.substr(foundPos-1,1))) { - partial.push("
  • " + elem.substr(0, foundPos) + "" + - elem.substr(foundPos, entry.length) + "" + elem.substr( - foundPos + entry.length) + "
  • "); - break; - } - } - - foundPos = instance.options.ignoreCase ? - elem.toLowerCase().indexOf(entry.toLowerCase(), foundPos + 1) : - elem.indexOf(entry, foundPos + 1); - - } - } - if (partial.length) - ret = ret.concat(partial.slice(0, instance.options.choices - ret.length)) - return "
      " + ret.join('') + "
    "; - } - }, options || { }); - } -}); - -// AJAX in-place editor and collection editor -// Full rewrite by Christophe Porteneuve (April 2007). - -// Use this if you notice weird scrolling problems on some browsers, -// the DOM might be a bit confused when this gets called so do this -// waits 1 ms (with setTimeout) until it does the activation -Field.scrollFreeActivate = function(field) { - setTimeout(function() { - Field.activate(field); - }, 1); -} - -Ajax.InPlaceEditor = Class.create({ - initialize: function(element, url, options) { - this.url = url; - this.element = element = $(element); - this.prepareOptions(); - this._controls = { }; - arguments.callee.dealWithDeprecatedOptions(options); // DEPRECATION LAYER!!! - Object.extend(this.options, options || { }); - if (!this.options.formId && this.element.id) { - this.options.formId = this.element.id + '-inplaceeditor'; - if ($(this.options.formId)) - this.options.formId = ''; - } - if (this.options.externalControl) - this.options.externalControl = $(this.options.externalControl); - if (!this.options.externalControl) - this.options.externalControlOnly = false; - this._originalBackground = this.element.getStyle('background-color') || 'transparent'; - this.element.title = this.options.clickToEditText; - this._boundCancelHandler = this.handleFormCancellation.bind(this); - this._boundComplete = (this.options.onComplete || Prototype.emptyFunction).bind(this); - this._boundFailureHandler = this.handleAJAXFailure.bind(this); - this._boundSubmitHandler = this.handleFormSubmission.bind(this); - this._boundWrapperHandler = this.wrapUp.bind(this); - this.registerListeners(); - }, - checkForEscapeOrReturn: function(e) { - if (!this._editing || e.ctrlKey || e.altKey || e.shiftKey) return; - if (Event.KEY_ESC == e.keyCode) - this.handleFormCancellation(e); - else if (Event.KEY_RETURN == e.keyCode) - this.handleFormSubmission(e); - }, - createControl: function(mode, handler, extraClasses) { - var control = this.options[mode + 'Control']; - var text = this.options[mode + 'Text']; - if ('button' == control) { - var btn = document.createElement('input'); - btn.type = 'submit'; - btn.value = text; - btn.className = 'editor_' + mode + '_button'; - if ('cancel' == mode) - btn.onclick = this._boundCancelHandler; - this._form.appendChild(btn); - this._controls[mode] = btn; - } else if ('link' == control) { - var link = document.createElement('a'); - link.href = '#'; - link.appendChild(document.createTextNode(text)); - link.onclick = 'cancel' == mode ? this._boundCancelHandler : this._boundSubmitHandler; - link.className = 'editor_' + mode + '_link'; - if (extraClasses) - link.className += ' ' + extraClasses; - this._form.appendChild(link); - this._controls[mode] = link; - } - }, - createEditField: function() { - var text = (this.options.loadTextURL ? this.options.loadingText : this.getText()); - var fld; - if (1 >= this.options.rows && !/\r|\n/.test(this.getText())) { - fld = document.createElement('input'); - fld.type = 'text'; - var size = this.options.size || this.options.cols || 0; - if (0 < size) fld.size = size; - } else { - fld = document.createElement('textarea'); - fld.rows = (1 >= this.options.rows ? this.options.autoRows : this.options.rows); - fld.cols = this.options.cols || 40; - } - fld.name = this.options.paramName; - fld.value = text; // No HTML breaks conversion anymore - fld.className = 'editor_field'; - if (this.options.submitOnBlur) - fld.onblur = this._boundSubmitHandler; - this._controls.editor = fld; - if (this.options.loadTextURL) - this.loadExternalText(); - this._form.appendChild(this._controls.editor); - }, - createForm: function() { - var ipe = this; - function addText(mode, condition) { - var text = ipe.options['text' + mode + 'Controls']; - if (!text || condition === false) return; - ipe._form.appendChild(document.createTextNode(text)); - }; - this._form = $(document.createElement('form')); - this._form.id = this.options.formId; - this._form.addClassName(this.options.formClassName); - this._form.onsubmit = this._boundSubmitHandler; - this.createEditField(); - if ('textarea' == this._controls.editor.tagName.toLowerCase()) - this._form.appendChild(document.createElement('br')); - if (this.options.onFormCustomization) - this.options.onFormCustomization(this, this._form); - addText('Before', this.options.okControl || this.options.cancelControl); - this.createControl('ok', this._boundSubmitHandler); - addText('Between', this.options.okControl && this.options.cancelControl); - this.createControl('cancel', this._boundCancelHandler, 'editor_cancel'); - addText('After', this.options.okControl || this.options.cancelControl); - }, - destroy: function() { - if (this._oldInnerHTML) - this.element.innerHTML = this._oldInnerHTML; - this.leaveEditMode(); - this.unregisterListeners(); - }, - enterEditMode: function(e) { - if (this._saving || this._editing) return; - this._editing = true; - this.triggerCallback('onEnterEditMode'); - if (this.options.externalControl) - this.options.externalControl.hide(); - this.element.hide(); - this.createForm(); - this.element.parentNode.insertBefore(this._form, this.element); - if (!this.options.loadTextURL) - this.postProcessEditField(); - if (e) Event.stop(e); - }, - enterHover: function(e) { - if (this.options.hoverClassName) - this.element.addClassName(this.options.hoverClassName); - if (this._saving) return; - this.triggerCallback('onEnterHover'); - }, - getText: function() { - return this.element.innerHTML; - }, - handleAJAXFailure: function(transport) { - this.triggerCallback('onFailure', transport); - if (this._oldInnerHTML) { - this.element.innerHTML = this._oldInnerHTML; - this._oldInnerHTML = null; - } - }, - handleFormCancellation: function(e) { - this.wrapUp(); - if (e) Event.stop(e); - }, - handleFormSubmission: function(e) { - var form = this._form; - var value = $F(this._controls.editor); - this.prepareSubmission(); - var params = this.options.callback(form, value) || ''; - if (Object.isString(params)) - params = params.toQueryParams(); - params.editorId = this.element.id; - if (this.options.htmlResponse) { - var options = Object.extend({ evalScripts: true }, this.options.ajaxOptions); - Object.extend(options, { - parameters: params, - onComplete: this._boundWrapperHandler, - onFailure: this._boundFailureHandler - }); - new Ajax.Updater({ success: this.element }, this.url, options); - } else { - var options = Object.extend({ method: 'get' }, this.options.ajaxOptions); - Object.extend(options, { - parameters: params, - onComplete: this._boundWrapperHandler, - onFailure: this._boundFailureHandler - }); - new Ajax.Request(this.url, options); - } - if (e) Event.stop(e); - }, - leaveEditMode: function() { - this.element.removeClassName(this.options.savingClassName); - this.removeForm(); - this.leaveHover(); - this.element.style.backgroundColor = this._originalBackground; - this.element.show(); - if (this.options.externalControl) - this.options.externalControl.show(); - this._saving = false; - this._editing = false; - this._oldInnerHTML = null; - this.triggerCallback('onLeaveEditMode'); - }, - leaveHover: function(e) { - if (this.options.hoverClassName) - this.element.removeClassName(this.options.hoverClassName); - if (this._saving) return; - this.triggerCallback('onLeaveHover'); - }, - loadExternalText: function() { - this._form.addClassName(this.options.loadingClassName); - this._controls.editor.disabled = true; - var options = Object.extend({ method: 'get' }, this.options.ajaxOptions); - Object.extend(options, { - parameters: 'editorId=' + encodeURIComponent(this.element.id), - onComplete: Prototype.emptyFunction, - onSuccess: function(transport) { - this._form.removeClassName(this.options.loadingClassName); - var text = transport.responseText; - if (this.options.stripLoadedTextTags) - text = text.stripTags(); - this._controls.editor.value = text; - this._controls.editor.disabled = false; - this.postProcessEditField(); - }.bind(this), - onFailure: this._boundFailureHandler - }); - new Ajax.Request(this.options.loadTextURL, options); - }, - postProcessEditField: function() { - var fpc = this.options.fieldPostCreation; - if (fpc) - $(this._controls.editor)['focus' == fpc ? 'focus' : 'activate'](); - }, - prepareOptions: function() { - this.options = Object.clone(Ajax.InPlaceEditor.DefaultOptions); - Object.extend(this.options, Ajax.InPlaceEditor.DefaultCallbacks); - [this._extraDefaultOptions].flatten().compact().each(function(defs) { - Object.extend(this.options, defs); - }.bind(this)); - }, - prepareSubmission: function() { - this._saving = true; - this.removeForm(); - this.leaveHover(); - this.showSaving(); - }, - registerListeners: function() { - this._listeners = { }; - var listener; - $H(Ajax.InPlaceEditor.Listeners).each(function(pair) { - listener = this[pair.value].bind(this); - this._listeners[pair.key] = listener; - if (!this.options.externalControlOnly) - this.element.observe(pair.key, listener); - if (this.options.externalControl) - this.options.externalControl.observe(pair.key, listener); - }.bind(this)); - }, - removeForm: function() { - if (!this._form) return; - this._form.remove(); - this._form = null; - this._controls = { }; - }, - showSaving: function() { - this._oldInnerHTML = this.element.innerHTML; - this.element.innerHTML = this.options.savingText; - this.element.addClassName(this.options.savingClassName); - this.element.style.backgroundColor = this._originalBackground; - this.element.show(); - }, - triggerCallback: function(cbName, arg) { - if ('function' == typeof this.options[cbName]) { - this.options[cbName](this, arg); - } - }, - unregisterListeners: function() { - $H(this._listeners).each(function(pair) { - if (!this.options.externalControlOnly) - this.element.stopObserving(pair.key, pair.value); - if (this.options.externalControl) - this.options.externalControl.stopObserving(pair.key, pair.value); - }.bind(this)); - }, - wrapUp: function(transport) { - this.leaveEditMode(); - // Can't use triggerCallback due to backward compatibility: requires - // binding + direct element - this._boundComplete(transport, this.element); - } -}); - -Object.extend(Ajax.InPlaceEditor.prototype, { - dispose: Ajax.InPlaceEditor.prototype.destroy -}); - -Ajax.InPlaceCollectionEditor = Class.create(Ajax.InPlaceEditor, { - initialize: function($super, element, url, options) { - this._extraDefaultOptions = Ajax.InPlaceCollectionEditor.DefaultOptions; - $super(element, url, options); - }, - - createEditField: function() { - var list = document.createElement('select'); - list.name = this.options.paramName; - list.size = 1; - this._controls.editor = list; - this._collection = this.options.collection || []; - if (this.options.loadCollectionURL) - this.loadCollection(); - else - this.checkForExternalText(); - this._form.appendChild(this._controls.editor); - }, - - loadCollection: function() { - this._form.addClassName(this.options.loadingClassName); - this.showLoadingText(this.options.loadingCollectionText); - var options = Object.extend({ method: 'get' }, this.options.ajaxOptions); - Object.extend(options, { - parameters: 'editorId=' + encodeURIComponent(this.element.id), - onComplete: Prototype.emptyFunction, - onSuccess: function(transport) { - var js = transport.responseText.strip(); - if (!/^\[.*\]$/.test(js)) // TODO: improve sanity check - throw 'Server returned an invalid collection representation.'; - this._collection = eval(js); - this.checkForExternalText(); - }.bind(this), - onFailure: this.onFailure - }); - new Ajax.Request(this.options.loadCollectionURL, options); - }, - - showLoadingText: function(text) { - this._controls.editor.disabled = true; - var tempOption = this._controls.editor.firstChild; - if (!tempOption) { - tempOption = document.createElement('option'); - tempOption.value = ''; - this._controls.editor.appendChild(tempOption); - tempOption.selected = true; - } - tempOption.update((text || '').stripScripts().stripTags()); - }, - - checkForExternalText: function() { - this._text = this.getText(); - if (this.options.loadTextURL) - this.loadExternalText(); - else - this.buildOptionList(); - }, - - loadExternalText: function() { - this.showLoadingText(this.options.loadingText); - var options = Object.extend({ method: 'get' }, this.options.ajaxOptions); - Object.extend(options, { - parameters: 'editorId=' + encodeURIComponent(this.element.id), - onComplete: Prototype.emptyFunction, - onSuccess: function(transport) { - this._text = transport.responseText.strip(); - this.buildOptionList(); - }.bind(this), - onFailure: this.onFailure - }); - new Ajax.Request(this.options.loadTextURL, options); - }, - - buildOptionList: function() { - this._form.removeClassName(this.options.loadingClassName); - this._collection = this._collection.map(function(entry) { - return 2 === entry.length ? entry : [entry, entry].flatten(); - }); - var marker = ('value' in this.options) ? this.options.value : this._text; - var textFound = this._collection.any(function(entry) { - return entry[0] == marker; - }.bind(this)); - this._controls.editor.update(''); - var option; - this._collection.each(function(entry, index) { - option = document.createElement('option'); - option.value = entry[0]; - option.selected = textFound ? entry[0] == marker : 0 == index; - option.appendChild(document.createTextNode(entry[1])); - this._controls.editor.appendChild(option); - }.bind(this)); - this._controls.editor.disabled = false; - Field.scrollFreeActivate(this._controls.editor); - } -}); - -//**** DEPRECATION LAYER FOR InPlace[Collection]Editor! **** -//**** This only exists for a while, in order to let **** -//**** users adapt to the new API. Read up on the new **** -//**** API and convert your code to it ASAP! **** - -Ajax.InPlaceEditor.prototype.initialize.dealWithDeprecatedOptions = function(options) { - if (!options) return; - function fallback(name, expr) { - if (name in options || expr === undefined) return; - options[name] = expr; - }; - fallback('cancelControl', (options.cancelLink ? 'link' : (options.cancelButton ? 'button' : - options.cancelLink == options.cancelButton == false ? false : undefined))); - fallback('okControl', (options.okLink ? 'link' : (options.okButton ? 'button' : - options.okLink == options.okButton == false ? false : undefined))); - fallback('highlightColor', options.highlightcolor); - fallback('highlightEndColor', options.highlightendcolor); -}; - -Object.extend(Ajax.InPlaceEditor, { - DefaultOptions: { - ajaxOptions: { }, - autoRows: 3, // Use when multi-line w/ rows == 1 - cancelControl: 'link', // 'link'|'button'|false - cancelText: 'cancel', - clickToEditText: 'Click to edit', - externalControl: null, // id|elt - externalControlOnly: false, - fieldPostCreation: 'activate', // 'activate'|'focus'|false - formClassName: 'inplaceeditor-form', - formId: null, // id|elt - highlightColor: '#ffff99', - highlightEndColor: '#ffffff', - hoverClassName: '', - htmlResponse: true, - loadingClassName: 'inplaceeditor-loading', - loadingText: 'Loading...', - okControl: 'button', // 'link'|'button'|false - okText: 'ok', - paramName: 'value', - rows: 1, // If 1 and multi-line, uses autoRows - savingClassName: 'inplaceeditor-saving', - savingText: 'Saving...', - size: 0, - stripLoadedTextTags: false, - submitOnBlur: false, - textAfterControls: '', - textBeforeControls: '', - textBetweenControls: '' - }, - DefaultCallbacks: { - callback: function(form) { - return Form.serialize(form); - }, - onComplete: function(transport, element) { - // For backward compatibility, this one is bound to the IPE, and passes - // the element directly. It was too often customized, so we don't break it. - new Effect.Highlight(element, { - startcolor: this.options.highlightColor, keepBackgroundImage: true }); - }, - onEnterEditMode: null, - onEnterHover: function(ipe) { - ipe.element.style.backgroundColor = ipe.options.highlightColor; - if (ipe._effect) - ipe._effect.cancel(); - }, - onFailure: function(transport, ipe) { - alert('Error communication with the server: ' + transport.responseText.stripTags()); - }, - onFormCustomization: null, // Takes the IPE and its generated form, after editor, before controls. - onLeaveEditMode: null, - onLeaveHover: function(ipe) { - ipe._effect = new Effect.Highlight(ipe.element, { - startcolor: ipe.options.highlightColor, endcolor: ipe.options.highlightEndColor, - restorecolor: ipe._originalBackground, keepBackgroundImage: true - }); - } - }, - Listeners: { - click: 'enterEditMode', - keydown: 'checkForEscapeOrReturn', - mouseover: 'enterHover', - mouseout: 'leaveHover' - } -}); - -Ajax.InPlaceCollectionEditor.DefaultOptions = { - loadingCollectionText: 'Loading options...' -}; - -// Delayed observer, like Form.Element.Observer, -// but waits for delay after last key input -// Ideal for live-search fields - -Form.Element.DelayedObserver = Class.create({ - initialize: function(element, delay, callback) { - this.delay = delay || 0.5; - this.element = $(element); - this.callback = callback; - this.timer = null; - this.lastValue = $F(this.element); - Event.observe(this.element,'keyup',this.delayedListener.bindAsEventListener(this)); - }, - delayedListener: function(event) { - if(this.lastValue == $F(this.element)) return; - if(this.timer) clearTimeout(this.timer); - this.timer = setTimeout(this.onTimerEvent.bind(this), this.delay * 1000); - this.lastValue = $F(this.element); - }, - onTimerEvent: function() { - this.timer = null; - this.callback(this.element, $F(this.element)); - } -}); diff --git a/website/assets/javascripts/effects.js b/website/assets/javascripts/effects.js deleted file mode 100644 index 718a88d..0000000 --- a/website/assets/javascripts/effects.js +++ /dev/null @@ -1,1120 +0,0 @@ -// Copyright (c) 2005-2007 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us) -// Contributors: -// Justin Palmer (http://encytemedia.com/) -// Mark Pilgrim (http://diveintomark.org/) -// Martin Bialasinki -// -// script.aculo.us is freely distributable under the terms of an MIT-style license. -// For details, see the script.aculo.us web site: http://script.aculo.us/ - -// converts rgb() and #xxx to #xxxxxx format, -// returns self (or first argument) if not convertable -String.prototype.parseColor = function() { - var color = '#'; - if (this.slice(0,4) == 'rgb(') { - var cols = this.slice(4,this.length-1).split(','); - var i=0; do { color += parseInt(cols[i]).toColorPart() } while (++i<3); - } else { - if (this.slice(0,1) == '#') { - if (this.length==4) for(var i=1;i<4;i++) color += (this.charAt(i) + this.charAt(i)).toLowerCase(); - if (this.length==7) color = this.toLowerCase(); - } - } - return (color.length==7 ? color : (arguments[0] || this)); -}; - -/*--------------------------------------------------------------------------*/ - -Element.collectTextNodes = function(element) { - return $A($(element).childNodes).collect( function(node) { - return (node.nodeType==3 ? node.nodeValue : - (node.hasChildNodes() ? Element.collectTextNodes(node) : '')); - }).flatten().join(''); -}; - -Element.collectTextNodesIgnoreClass = function(element, className) { - return $A($(element).childNodes).collect( function(node) { - return (node.nodeType==3 ? node.nodeValue : - ((node.hasChildNodes() && !Element.hasClassName(node,className)) ? - Element.collectTextNodesIgnoreClass(node, className) : '')); - }).flatten().join(''); -}; - -Element.setContentZoom = function(element, percent) { - element = $(element); - element.setStyle({fontSize: (percent/100) + 'em'}); - if (Prototype.Browser.WebKit) window.scrollBy(0,0); - return element; -}; - -Element.getInlineOpacity = function(element){ - return $(element).style.opacity || ''; -}; - -Element.forceRerendering = function(element) { - try { - element = $(element); - var n = document.createTextNode(' '); - element.appendChild(n); - element.removeChild(n); - } catch(e) { } -}; - -/*--------------------------------------------------------------------------*/ - -var Effect = { - _elementDoesNotExistError: { - name: 'ElementDoesNotExistError', - message: 'The specified DOM element does not exist, but is required for this effect to operate' - }, - Transitions: { - linear: Prototype.K, - sinoidal: function(pos) { - return (-Math.cos(pos*Math.PI)/2) + 0.5; - }, - reverse: function(pos) { - return 1-pos; - }, - flicker: function(pos) { - var pos = ((-Math.cos(pos*Math.PI)/4) + 0.75) + Math.random()/4; - return pos > 1 ? 1 : pos; - }, - wobble: function(pos) { - return (-Math.cos(pos*Math.PI*(9*pos))/2) + 0.5; - }, - pulse: function(pos, pulses) { - pulses = pulses || 5; - return ( - ((pos % (1/pulses)) * pulses).round() == 0 ? - ((pos * pulses * 2) - (pos * pulses * 2).floor()) : - 1 - ((pos * pulses * 2) - (pos * pulses * 2).floor()) - ); - }, - spring: function(pos) { - return 1 - (Math.cos(pos * 4.5 * Math.PI) * Math.exp(-pos * 6)); - }, - none: function(pos) { - return 0; - }, - full: function(pos) { - return 1; - } - }, - DefaultOptions: { - duration: 1.0, // seconds - fps: 100, // 100= assume 66fps max. - sync: false, // true for combining - from: 0.0, - to: 1.0, - delay: 0.0, - queue: 'parallel' - }, - tagifyText: function(element) { - var tagifyStyle = 'position:relative'; - if (Prototype.Browser.IE) tagifyStyle += ';zoom:1'; - - element = $(element); - $A(element.childNodes).each( function(child) { - if (child.nodeType==3) { - child.nodeValue.toArray().each( function(character) { - element.insertBefore( - new Element('span', {style: tagifyStyle}).update( - character == ' ' ? String.fromCharCode(160) : character), - child); - }); - Element.remove(child); - } - }); - }, - multiple: function(element, effect) { - var elements; - if (((typeof element == 'object') || - Object.isFunction(element)) && - (element.length)) - elements = element; - else - elements = $(element).childNodes; - - var options = Object.extend({ - speed: 0.1, - delay: 0.0 - }, arguments[2] || { }); - var masterDelay = options.delay; - - $A(elements).each( function(element, index) { - new effect(element, Object.extend(options, { delay: index * options.speed + masterDelay })); - }); - }, - PAIRS: { - 'slide': ['SlideDown','SlideUp'], - 'blind': ['BlindDown','BlindUp'], - 'appear': ['Appear','Fade'] - }, - toggle: function(element, effect) { - element = $(element); - effect = (effect || 'appear').toLowerCase(); - var options = Object.extend({ - queue: { position:'end', scope:(element.id || 'global'), limit: 1 } - }, arguments[2] || { }); - Effect[element.visible() ? - Effect.PAIRS[effect][1] : Effect.PAIRS[effect][0]](element, options); - } -}; - -Effect.DefaultOptions.transition = Effect.Transitions.sinoidal; - -/* ------------- core effects ------------- */ - -Effect.ScopedQueue = Class.create(Enumerable, { - initialize: function() { - this.effects = []; - this.interval = null; - }, - _each: function(iterator) { - this.effects._each(iterator); - }, - add: function(effect) { - var timestamp = new Date().getTime(); - - var position = Object.isString(effect.options.queue) ? - effect.options.queue : effect.options.queue.position; - - switch(position) { - case 'front': - // move unstarted effects after this effect - this.effects.findAll(function(e){ return e.state=='idle' }).each( function(e) { - e.startOn += effect.finishOn; - e.finishOn += effect.finishOn; - }); - break; - case 'with-last': - timestamp = this.effects.pluck('startOn').max() || timestamp; - break; - case 'end': - // start effect after last queued effect has finished - timestamp = this.effects.pluck('finishOn').max() || timestamp; - break; - } - - effect.startOn += timestamp; - effect.finishOn += timestamp; - - if (!effect.options.queue.limit || (this.effects.length < effect.options.queue.limit)) - this.effects.push(effect); - - if (!this.interval) - this.interval = setInterval(this.loop.bind(this), 15); - }, - remove: function(effect) { - this.effects = this.effects.reject(function(e) { return e==effect }); - if (this.effects.length == 0) { - clearInterval(this.interval); - this.interval = null; - } - }, - loop: function() { - var timePos = new Date().getTime(); - for(var i=0, len=this.effects.length;i= this.startOn) { - if (timePos >= this.finishOn) { - this.render(1.0); - this.cancel(); - this.event('beforeFinish'); - if (this.finish) this.finish(); - this.event('afterFinish'); - return; - } - var pos = (timePos - this.startOn) / this.totalTime, - frame = (pos * this.totalFrames).round(); - if (frame > this.currentFrame) { - this.render(pos); - this.currentFrame = frame; - } - } - }, - cancel: function() { - if (!this.options.sync) - Effect.Queues.get(Object.isString(this.options.queue) ? - 'global' : this.options.queue.scope).remove(this); - this.state = 'finished'; - }, - event: function(eventName) { - if (this.options[eventName + 'Internal']) this.options[eventName + 'Internal'](this); - if (this.options[eventName]) this.options[eventName](this); - }, - inspect: function() { - var data = $H(); - for(property in this) - if (!Object.isFunction(this[property])) data.set(property, this[property]); - return '#'; - } -}); - -Effect.Parallel = Class.create(Effect.Base, { - initialize: function(effects) { - this.effects = effects || []; - this.start(arguments[1]); - }, - update: function(position) { - this.effects.invoke('render', position); - }, - finish: function(position) { - this.effects.each( function(effect) { - effect.render(1.0); - effect.cancel(); - effect.event('beforeFinish'); - if (effect.finish) effect.finish(position); - effect.event('afterFinish'); - }); - } -}); - -Effect.Tween = Class.create(Effect.Base, { - initialize: function(object, from, to) { - object = Object.isString(object) ? $(object) : object; - var args = $A(arguments), method = args.last(), - options = args.length == 5 ? args[3] : null; - this.method = Object.isFunction(method) ? method.bind(object) : - Object.isFunction(object[method]) ? object[method].bind(object) : - function(value) { object[method] = value }; - this.start(Object.extend({ from: from, to: to }, options || { })); - }, - update: function(position) { - this.method(position); - } -}); - -Effect.Event = Class.create(Effect.Base, { - initialize: function() { - this.start(Object.extend({ duration: 0 }, arguments[0] || { })); - }, - update: Prototype.emptyFunction -}); - -Effect.Opacity = Class.create(Effect.Base, { - initialize: function(element) { - this.element = $(element); - if (!this.element) throw(Effect._elementDoesNotExistError); - // make this work on IE on elements without 'layout' - if (Prototype.Browser.IE && (!this.element.currentStyle.hasLayout)) - this.element.setStyle({zoom: 1}); - var options = Object.extend({ - from: this.element.getOpacity() || 0.0, - to: 1.0 - }, arguments[1] || { }); - this.start(options); - }, - update: function(position) { - this.element.setOpacity(position); - } -}); - -Effect.Move = Class.create(Effect.Base, { - initialize: function(element) { - this.element = $(element); - if (!this.element) throw(Effect._elementDoesNotExistError); - var options = Object.extend({ - x: 0, - y: 0, - mode: 'relative' - }, arguments[1] || { }); - this.start(options); - }, - setup: function() { - this.element.makePositioned(); - this.originalLeft = parseFloat(this.element.getStyle('left') || '0'); - this.originalTop = parseFloat(this.element.getStyle('top') || '0'); - if (this.options.mode == 'absolute') { - this.options.x = this.options.x - this.originalLeft; - this.options.y = this.options.y - this.originalTop; - } - }, - update: function(position) { - this.element.setStyle({ - left: (this.options.x * position + this.originalLeft).round() + 'px', - top: (this.options.y * position + this.originalTop).round() + 'px' - }); - } -}); - -// for backwards compatibility -Effect.MoveBy = function(element, toTop, toLeft) { - return new Effect.Move(element, - Object.extend({ x: toLeft, y: toTop }, arguments[3] || { })); -}; - -Effect.Scale = Class.create(Effect.Base, { - initialize: function(element, percent) { - this.element = $(element); - if (!this.element) throw(Effect._elementDoesNotExistError); - var options = Object.extend({ - scaleX: true, - scaleY: true, - scaleContent: true, - scaleFromCenter: false, - scaleMode: 'box', // 'box' or 'contents' or { } with provided values - scaleFrom: 100.0, - scaleTo: percent - }, arguments[2] || { }); - this.start(options); - }, - setup: function() { - this.restoreAfterFinish = this.options.restoreAfterFinish || false; - this.elementPositioning = this.element.getStyle('position'); - - this.originalStyle = { }; - ['top','left','width','height','fontSize'].each( function(k) { - this.originalStyle[k] = this.element.style[k]; - }.bind(this)); - - this.originalTop = this.element.offsetTop; - this.originalLeft = this.element.offsetLeft; - - var fontSize = this.element.getStyle('font-size') || '100%'; - ['em','px','%','pt'].each( function(fontSizeType) { - if (fontSize.indexOf(fontSizeType)>0) { - this.fontSize = parseFloat(fontSize); - this.fontSizeType = fontSizeType; - } - }.bind(this)); - - this.factor = (this.options.scaleTo - this.options.scaleFrom)/100; - - this.dims = null; - if (this.options.scaleMode=='box') - this.dims = [this.element.offsetHeight, this.element.offsetWidth]; - if (/^content/.test(this.options.scaleMode)) - this.dims = [this.element.scrollHeight, this.element.scrollWidth]; - if (!this.dims) - this.dims = [this.options.scaleMode.originalHeight, - this.options.scaleMode.originalWidth]; - }, - update: function(position) { - var currentScale = (this.options.scaleFrom/100.0) + (this.factor * position); - if (this.options.scaleContent && this.fontSize) - this.element.setStyle({fontSize: this.fontSize * currentScale + this.fontSizeType }); - this.setDimensions(this.dims[0] * currentScale, this.dims[1] * currentScale); - }, - finish: function(position) { - if (this.restoreAfterFinish) this.element.setStyle(this.originalStyle); - }, - setDimensions: function(height, width) { - var d = { }; - if (this.options.scaleX) d.width = width.round() + 'px'; - if (this.options.scaleY) d.height = height.round() + 'px'; - if (this.options.scaleFromCenter) { - var topd = (height - this.dims[0])/2; - var leftd = (width - this.dims[1])/2; - if (this.elementPositioning == 'absolute') { - if (this.options.scaleY) d.top = this.originalTop-topd + 'px'; - if (this.options.scaleX) d.left = this.originalLeft-leftd + 'px'; - } else { - if (this.options.scaleY) d.top = -topd + 'px'; - if (this.options.scaleX) d.left = -leftd + 'px'; - } - } - this.element.setStyle(d); - } -}); - -Effect.Highlight = Class.create(Effect.Base, { - initialize: function(element) { - this.element = $(element); - if (!this.element) throw(Effect._elementDoesNotExistError); - var options = Object.extend({ startcolor: '#ffff99' }, arguments[1] || { }); - this.start(options); - }, - setup: function() { - // Prevent executing on elements not in the layout flow - if (this.element.getStyle('display')=='none') { this.cancel(); return; } - // Disable background image during the effect - this.oldStyle = { }; - if (!this.options.keepBackgroundImage) { - this.oldStyle.backgroundImage = this.element.getStyle('background-image'); - this.element.setStyle({backgroundImage: 'none'}); - } - if (!this.options.endcolor) - this.options.endcolor = this.element.getStyle('background-color').parseColor('#ffffff'); - if (!this.options.restorecolor) - this.options.restorecolor = this.element.getStyle('background-color'); - // init color calculations - this._base = $R(0,2).map(function(i){ return parseInt(this.options.startcolor.slice(i*2+1,i*2+3),16) }.bind(this)); - this._delta = $R(0,2).map(function(i){ return parseInt(this.options.endcolor.slice(i*2+1,i*2+3),16)-this._base[i] }.bind(this)); - }, - update: function(position) { - this.element.setStyle({backgroundColor: $R(0,2).inject('#',function(m,v,i){ - return m+((this._base[i]+(this._delta[i]*position)).round().toColorPart()); }.bind(this)) }); - }, - finish: function() { - this.element.setStyle(Object.extend(this.oldStyle, { - backgroundColor: this.options.restorecolor - })); - } -}); - -Effect.ScrollTo = function(element) { - var options = arguments[1] || { }, - scrollOffsets = document.viewport.getScrollOffsets(), - elementOffsets = $(element).cumulativeOffset(), - max = document.viewport.getScrollOffsets[0] - document.viewport.getHeight(); - - if (options.offset) elementOffsets[1] += options.offset; - - return new Effect.Tween(null, - scrollOffsets.top, - elementOffsets[1] > max ? max : elementOffsets[1], - options, - function(p){ scrollTo(scrollOffsets.left, p.round()) } - ); -}; - -/* ------------- combination effects ------------- */ - -Effect.Fade = function(element) { - element = $(element); - var oldOpacity = element.getInlineOpacity(); - var options = Object.extend({ - from: element.getOpacity() || 1.0, - to: 0.0, - afterFinishInternal: function(effect) { - if (effect.options.to!=0) return; - effect.element.hide().setStyle({opacity: oldOpacity}); - } - }, arguments[1] || { }); - return new Effect.Opacity(element,options); -}; - -Effect.Appear = function(element) { - element = $(element); - var options = Object.extend({ - from: (element.getStyle('display') == 'none' ? 0.0 : element.getOpacity() || 0.0), - to: 1.0, - // force Safari to render floated elements properly - afterFinishInternal: function(effect) { - effect.element.forceRerendering(); - }, - beforeSetup: function(effect) { - effect.element.setOpacity(effect.options.from).show(); - }}, arguments[1] || { }); - return new Effect.Opacity(element,options); -}; - -Effect.Puff = function(element) { - element = $(element); - var oldStyle = { - opacity: element.getInlineOpacity(), - position: element.getStyle('position'), - top: element.style.top, - left: element.style.left, - width: element.style.width, - height: element.style.height - }; - return new Effect.Parallel( - [ new Effect.Scale(element, 200, - { sync: true, scaleFromCenter: true, scaleContent: true, restoreAfterFinish: true }), - new Effect.Opacity(element, { sync: true, to: 0.0 } ) ], - Object.extend({ duration: 1.0, - beforeSetupInternal: function(effect) { - Position.absolutize(effect.effects[0].element) - }, - afterFinishInternal: function(effect) { - effect.effects[0].element.hide().setStyle(oldStyle); } - }, arguments[1] || { }) - ); -}; - -Effect.BlindUp = function(element) { - element = $(element); - element.makeClipping(); - return new Effect.Scale(element, 0, - Object.extend({ scaleContent: false, - scaleX: false, - restoreAfterFinish: true, - afterFinishInternal: function(effect) { - effect.element.hide().undoClipping(); - } - }, arguments[1] || { }) - ); -}; - -Effect.BlindDown = function(element) { - element = $(element); - var elementDimensions = element.getDimensions(); - return new Effect.Scale(element, 100, Object.extend({ - scaleContent: false, - scaleX: false, - scaleFrom: 0, - scaleMode: {originalHeight: elementDimensions.height, originalWidth: elementDimensions.width}, - restoreAfterFinish: true, - afterSetup: function(effect) { - effect.element.makeClipping().setStyle({height: '0px'}).show(); - }, - afterFinishInternal: function(effect) { - effect.element.undoClipping(); - } - }, arguments[1] || { })); -}; - -Effect.SwitchOff = function(element) { - element = $(element); - var oldOpacity = element.getInlineOpacity(); - return new Effect.Appear(element, Object.extend({ - duration: 0.4, - from: 0, - transition: Effect.Transitions.flicker, - afterFinishInternal: function(effect) { - new Effect.Scale(effect.element, 1, { - duration: 0.3, scaleFromCenter: true, - scaleX: false, scaleContent: false, restoreAfterFinish: true, - beforeSetup: function(effect) { - effect.element.makePositioned().makeClipping(); - }, - afterFinishInternal: function(effect) { - effect.element.hide().undoClipping().undoPositioned().setStyle({opacity: oldOpacity}); - } - }) - } - }, arguments[1] || { })); -}; - -Effect.DropOut = function(element) { - element = $(element); - var oldStyle = { - top: element.getStyle('top'), - left: element.getStyle('left'), - opacity: element.getInlineOpacity() }; - return new Effect.Parallel( - [ new Effect.Move(element, {x: 0, y: 100, sync: true }), - new Effect.Opacity(element, { sync: true, to: 0.0 }) ], - Object.extend( - { duration: 0.5, - beforeSetup: function(effect) { - effect.effects[0].element.makePositioned(); - }, - afterFinishInternal: function(effect) { - effect.effects[0].element.hide().undoPositioned().setStyle(oldStyle); - } - }, arguments[1] || { })); -}; - -Effect.Shake = function(element) { - element = $(element); - var options = Object.extend({ - distance: 20, - duration: 0.5 - }, arguments[1] || {}); - var distance = parseFloat(options.distance); - var split = parseFloat(options.duration) / 10.0; - var oldStyle = { - top: element.getStyle('top'), - left: element.getStyle('left') }; - return new Effect.Move(element, - { x: distance, y: 0, duration: split, afterFinishInternal: function(effect) { - new Effect.Move(effect.element, - { x: -distance*2, y: 0, duration: split*2, afterFinishInternal: function(effect) { - new Effect.Move(effect.element, - { x: distance*2, y: 0, duration: split*2, afterFinishInternal: function(effect) { - new Effect.Move(effect.element, - { x: -distance*2, y: 0, duration: split*2, afterFinishInternal: function(effect) { - new Effect.Move(effect.element, - { x: distance*2, y: 0, duration: split*2, afterFinishInternal: function(effect) { - new Effect.Move(effect.element, - { x: -distance, y: 0, duration: split, afterFinishInternal: function(effect) { - effect.element.undoPositioned().setStyle(oldStyle); - }}) }}) }}) }}) }}) }}); -}; - -Effect.SlideDown = function(element) { - element = $(element).cleanWhitespace(); - // SlideDown need to have the content of the element wrapped in a container element with fixed height! - var oldInnerBottom = element.down().getStyle('bottom'); - var elementDimensions = element.getDimensions(); - return new Effect.Scale(element, 100, Object.extend({ - scaleContent: false, - scaleX: false, - scaleFrom: window.opera ? 0 : 1, - scaleMode: {originalHeight: elementDimensions.height, originalWidth: elementDimensions.width}, - restoreAfterFinish: true, - afterSetup: function(effect) { - effect.element.makePositioned(); - effect.element.down().makePositioned(); - if (window.opera) effect.element.setStyle({top: ''}); - effect.element.makeClipping().setStyle({height: '0px'}).show(); - }, - afterUpdateInternal: function(effect) { - effect.element.down().setStyle({bottom: - (effect.dims[0] - effect.element.clientHeight) + 'px' }); - }, - afterFinishInternal: function(effect) { - effect.element.undoClipping().undoPositioned(); - effect.element.down().undoPositioned().setStyle({bottom: oldInnerBottom}); } - }, arguments[1] || { }) - ); -}; - -Effect.SlideUp = function(element) { - element = $(element).cleanWhitespace(); - var oldInnerBottom = element.down().getStyle('bottom'); - var elementDimensions = element.getDimensions(); - return new Effect.Scale(element, window.opera ? 0 : 1, - Object.extend({ scaleContent: false, - scaleX: false, - scaleMode: 'box', - scaleFrom: 100, - scaleMode: {originalHeight: elementDimensions.height, originalWidth: elementDimensions.width}, - restoreAfterFinish: true, - afterSetup: function(effect) { - effect.element.makePositioned(); - effect.element.down().makePositioned(); - if (window.opera) effect.element.setStyle({top: ''}); - effect.element.makeClipping().show(); - }, - afterUpdateInternal: function(effect) { - effect.element.down().setStyle({bottom: - (effect.dims[0] - effect.element.clientHeight) + 'px' }); - }, - afterFinishInternal: function(effect) { - effect.element.hide().undoClipping().undoPositioned(); - effect.element.down().undoPositioned().setStyle({bottom: oldInnerBottom}); - } - }, arguments[1] || { }) - ); -}; - -// Bug in opera makes the TD containing this element expand for a instance after finish -Effect.Squish = function(element) { - return new Effect.Scale(element, window.opera ? 1 : 0, { - restoreAfterFinish: true, - beforeSetup: function(effect) { - effect.element.makeClipping(); - }, - afterFinishInternal: function(effect) { - effect.element.hide().undoClipping(); - } - }); -}; - -Effect.Grow = function(element) { - element = $(element); - var options = Object.extend({ - direction: 'center', - moveTransition: Effect.Transitions.sinoidal, - scaleTransition: Effect.Transitions.sinoidal, - opacityTransition: Effect.Transitions.full - }, arguments[1] || { }); - var oldStyle = { - top: element.style.top, - left: element.style.left, - height: element.style.height, - width: element.style.width, - opacity: element.getInlineOpacity() }; - - var dims = element.getDimensions(); - var initialMoveX, initialMoveY; - var moveX, moveY; - - switch (options.direction) { - case 'top-left': - initialMoveX = initialMoveY = moveX = moveY = 0; - break; - case 'top-right': - initialMoveX = dims.width; - initialMoveY = moveY = 0; - moveX = -dims.width; - break; - case 'bottom-left': - initialMoveX = moveX = 0; - initialMoveY = dims.height; - moveY = -dims.height; - break; - case 'bottom-right': - initialMoveX = dims.width; - initialMoveY = dims.height; - moveX = -dims.width; - moveY = -dims.height; - break; - case 'center': - initialMoveX = dims.width / 2; - initialMoveY = dims.height / 2; - moveX = -dims.width / 2; - moveY = -dims.height / 2; - break; - } - - return new Effect.Move(element, { - x: initialMoveX, - y: initialMoveY, - duration: 0.01, - beforeSetup: function(effect) { - effect.element.hide().makeClipping().makePositioned(); - }, - afterFinishInternal: function(effect) { - new Effect.Parallel( - [ new Effect.Opacity(effect.element, { sync: true, to: 1.0, from: 0.0, transition: options.opacityTransition }), - new Effect.Move(effect.element, { x: moveX, y: moveY, sync: true, transition: options.moveTransition }), - new Effect.Scale(effect.element, 100, { - scaleMode: { originalHeight: dims.height, originalWidth: dims.width }, - sync: true, scaleFrom: window.opera ? 1 : 0, transition: options.scaleTransition, restoreAfterFinish: true}) - ], Object.extend({ - beforeSetup: function(effect) { - effect.effects[0].element.setStyle({height: '0px'}).show(); - }, - afterFinishInternal: function(effect) { - effect.effects[0].element.undoClipping().undoPositioned().setStyle(oldStyle); - } - }, options) - ) - } - }); -}; - -Effect.Shrink = function(element) { - element = $(element); - var options = Object.extend({ - direction: 'center', - moveTransition: Effect.Transitions.sinoidal, - scaleTransition: Effect.Transitions.sinoidal, - opacityTransition: Effect.Transitions.none - }, arguments[1] || { }); - var oldStyle = { - top: element.style.top, - left: element.style.left, - height: element.style.height, - width: element.style.width, - opacity: element.getInlineOpacity() }; - - var dims = element.getDimensions(); - var moveX, moveY; - - switch (options.direction) { - case 'top-left': - moveX = moveY = 0; - break; - case 'top-right': - moveX = dims.width; - moveY = 0; - break; - case 'bottom-left': - moveX = 0; - moveY = dims.height; - break; - case 'bottom-right': - moveX = dims.width; - moveY = dims.height; - break; - case 'center': - moveX = dims.width / 2; - moveY = dims.height / 2; - break; - } - - return new Effect.Parallel( - [ new Effect.Opacity(element, { sync: true, to: 0.0, from: 1.0, transition: options.opacityTransition }), - new Effect.Scale(element, window.opera ? 1 : 0, { sync: true, transition: options.scaleTransition, restoreAfterFinish: true}), - new Effect.Move(element, { x: moveX, y: moveY, sync: true, transition: options.moveTransition }) - ], Object.extend({ - beforeStartInternal: function(effect) { - effect.effects[0].element.makePositioned().makeClipping(); - }, - afterFinishInternal: function(effect) { - effect.effects[0].element.hide().undoClipping().undoPositioned().setStyle(oldStyle); } - }, options) - ); -}; - -Effect.Pulsate = function(element) { - element = $(element); - var options = arguments[1] || { }; - var oldOpacity = element.getInlineOpacity(); - var transition = options.transition || Effect.Transitions.sinoidal; - var reverser = function(pos){ return transition(1-Effect.Transitions.pulse(pos, options.pulses)) }; - reverser.bind(transition); - return new Effect.Opacity(element, - Object.extend(Object.extend({ duration: 2.0, from: 0, - afterFinishInternal: function(effect) { effect.element.setStyle({opacity: oldOpacity}); } - }, options), {transition: reverser})); -}; - -Effect.Fold = function(element) { - element = $(element); - var oldStyle = { - top: element.style.top, - left: element.style.left, - width: element.style.width, - height: element.style.height }; - element.makeClipping(); - return new Effect.Scale(element, 5, Object.extend({ - scaleContent: false, - scaleX: false, - afterFinishInternal: function(effect) { - new Effect.Scale(element, 1, { - scaleContent: false, - scaleY: false, - afterFinishInternal: function(effect) { - effect.element.hide().undoClipping().setStyle(oldStyle); - } }); - }}, arguments[1] || { })); -}; - -Effect.Morph = Class.create(Effect.Base, { - initialize: function(element) { - this.element = $(element); - if (!this.element) throw(Effect._elementDoesNotExistError); - var options = Object.extend({ - style: { } - }, arguments[1] || { }); - - if (!Object.isString(options.style)) this.style = $H(options.style); - else { - if (options.style.include(':')) - this.style = options.style.parseStyle(); - else { - this.element.addClassName(options.style); - this.style = $H(this.element.getStyles()); - this.element.removeClassName(options.style); - var css = this.element.getStyles(); - this.style = this.style.reject(function(style) { - return style.value == css[style.key]; - }); - options.afterFinishInternal = function(effect) { - effect.element.addClassName(effect.options.style); - effect.transforms.each(function(transform) { - effect.element.style[transform.style] = ''; - }); - } - } - } - this.start(options); - }, - - setup: function(){ - function parseColor(color){ - if (!color || ['rgba(0, 0, 0, 0)','transparent'].include(color)) color = '#ffffff'; - color = color.parseColor(); - return $R(0,2).map(function(i){ - return parseInt( color.slice(i*2+1,i*2+3), 16 ) - }); - } - this.transforms = this.style.map(function(pair){ - var property = pair[0], value = pair[1], unit = null; - - if (value.parseColor('#zzzzzz') != '#zzzzzz') { - value = value.parseColor(); - unit = 'color'; - } else if (property == 'opacity') { - value = parseFloat(value); - if (Prototype.Browser.IE && (!this.element.currentStyle.hasLayout)) - this.element.setStyle({zoom: 1}); - } else if (Element.CSS_LENGTH.test(value)) { - var components = value.match(/^([\+\-]?[0-9\.]+)(.*)$/); - value = parseFloat(components[1]); - unit = (components.length == 3) ? components[2] : null; - } - - var originalValue = this.element.getStyle(property); - return { - style: property.camelize(), - originalValue: unit=='color' ? parseColor(originalValue) : parseFloat(originalValue || 0), - targetValue: unit=='color' ? parseColor(value) : value, - unit: unit - }; - }.bind(this)).reject(function(transform){ - return ( - (transform.originalValue == transform.targetValue) || - ( - transform.unit != 'color' && - (isNaN(transform.originalValue) || isNaN(transform.targetValue)) - ) - ) - }); - }, - update: function(position) { - var style = { }, transform, i = this.transforms.length; - while(i--) - style[(transform = this.transforms[i]).style] = - transform.unit=='color' ? '#'+ - (Math.round(transform.originalValue[0]+ - (transform.targetValue[0]-transform.originalValue[0])*position)).toColorPart() + - (Math.round(transform.originalValue[1]+ - (transform.targetValue[1]-transform.originalValue[1])*position)).toColorPart() + - (Math.round(transform.originalValue[2]+ - (transform.targetValue[2]-transform.originalValue[2])*position)).toColorPart() : - (transform.originalValue + - (transform.targetValue - transform.originalValue) * position).toFixed(3) + - (transform.unit === null ? '' : transform.unit); - this.element.setStyle(style, true); - } -}); - -Effect.Transform = Class.create({ - initialize: function(tracks){ - this.tracks = []; - this.options = arguments[1] || { }; - this.addTracks(tracks); - }, - addTracks: function(tracks){ - tracks.each(function(track){ - track = $H(track); - var data = track.values().first(); - this.tracks.push($H({ - ids: track.keys().first(), - effect: Effect.Morph, - options: { style: data } - })); - }.bind(this)); - return this; - }, - play: function(){ - return new Effect.Parallel( - this.tracks.map(function(track){ - var ids = track.get('ids'), effect = track.get('effect'), options = track.get('options'); - var elements = [$(ids) || $$(ids)].flatten(); - return elements.map(function(e){ return new effect(e, Object.extend({ sync:true }, options)) }); - }).flatten(), - this.options - ); - } -}); - -Element.CSS_PROPERTIES = $w( - 'backgroundColor backgroundPosition borderBottomColor borderBottomStyle ' + - 'borderBottomWidth borderLeftColor borderLeftStyle borderLeftWidth ' + - 'borderRightColor borderRightStyle borderRightWidth borderSpacing ' + - 'borderTopColor borderTopStyle borderTopWidth bottom clip color ' + - 'fontSize fontWeight height left letterSpacing lineHeight ' + - 'marginBottom marginLeft marginRight marginTop markerOffset maxHeight '+ - 'maxWidth minHeight minWidth opacity outlineColor outlineOffset ' + - 'outlineWidth paddingBottom paddingLeft paddingRight paddingTop ' + - 'right textIndent top width wordSpacing zIndex'); - -Element.CSS_LENGTH = /^(([\+\-]?[0-9\.]+)(em|ex|px|in|cm|mm|pt|pc|\%))|0$/; - -String.__parseStyleElement = document.createElement('div'); -String.prototype.parseStyle = function(){ - var style, styleRules = $H(); - if (Prototype.Browser.WebKit) - style = new Element('div',{style:this}).style; - else { - String.__parseStyleElement.innerHTML = '
    '; - style = String.__parseStyleElement.childNodes[0].style; - } - - Element.CSS_PROPERTIES.each(function(property){ - if (style[property]) styleRules.set(property, style[property]); - }); - - if (Prototype.Browser.IE && this.include('opacity')) - styleRules.set('opacity', this.match(/opacity:\s*((?:0|1)?(?:\.\d*)?)/)[1]); - - return styleRules; -}; - -if (document.defaultView && document.defaultView.getComputedStyle) { - Element.getStyles = function(element) { - var css = document.defaultView.getComputedStyle($(element), null); - return Element.CSS_PROPERTIES.inject({ }, function(styles, property) { - styles[property] = css[property]; - return styles; - }); - }; -} else { - Element.getStyles = function(element) { - element = $(element); - var css = element.currentStyle, styles; - styles = Element.CSS_PROPERTIES.inject({ }, function(results, property) { - results[property] = css[property]; - return results; - }); - if (!styles.opacity) styles.opacity = element.getOpacity(); - return styles; - }; -}; - -Effect.Methods = { - morph: function(element, style) { - element = $(element); - new Effect.Morph(element, Object.extend({ style: style }, arguments[2] || { })); - return element; - }, - visualEffect: function(element, effect, options) { - element = $(element) - var s = effect.dasherize().camelize(), klass = s.charAt(0).toUpperCase() + s.substring(1); - new Effect[klass](element, options); - return element; - }, - highlight: function(element, options) { - element = $(element); - new Effect.Highlight(element, options); - return element; - } -}; - -$w('fade appear grow shrink fold blindUp blindDown slideUp slideDown '+ - 'pulsate shake puff squish switchOff dropOut').each( - function(effect) { - Effect.Methods[effect] = function(element, options){ - element = $(element); - Effect[effect.charAt(0).toUpperCase() + effect.substring(1)](element, options); - return element; - } - } -); - -$w('getInlineOpacity forceRerendering setContentZoom collectTextNodes collectTextNodesIgnoreClass getStyles').each( - function(f) { Effect.Methods[f] = Element[f]; } -); - -Element.addMethods(Effect.Methods); diff --git a/website/assets/javascripts/mouse_enter_leave.js b/website/assets/javascripts/mouse_enter_leave.js deleted file mode 100644 index 884d17a..0000000 --- a/website/assets/javascripts/mouse_enter_leave.js +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Fires "mouse:enter" and "mouse:leave" events as a substitute for the - * "mouseenter" and "mouseleave" events. Simulates, in effect, the behavior - * of the CSS ":hover" pseudoclass. - */ - - -(function() { - function respondToMouseOver(event) { - var target = event.originalTarget; - // console.log("mouse:enter", "target:", target, - // "relatedTarget:", event.relatedTarget); - if (event.relatedTarget && !event.relatedTarget.descendantOf(target)) - target.fire("mouse:enter", { relatedTarget: event.relatedTarget }); - } - - function respondToMouseOut(event) { - var target = event.originalTarget; - if (event.relatedTarget && !event.relatedTarget.descendantOf(target)) - target.fire("mouse:leave", { relatedTarget: event.relatedTarget }); - } - - - if (Prototype.Browser.IE) { - document.observe("mouseenter", function(event) { - event.element().fire("mouse:enter", { relatedTarget: event.relatedTarget }); - }); - document.observe("mouseleave", function(event) { - event.element().fire("mouse:leave", { relatedTarget: event.relatedTarget }); - }); - } else { - document.observe("mouseover", respondToMouseOver); - document.observe("mouseout", respondToMouseOut); - } - -})(); \ No newline at end of file diff --git a/website/assets/javascripts/prototype.js b/website/assets/javascripts/prototype.js deleted file mode 100644 index 95b2259..0000000 --- a/website/assets/javascripts/prototype.js +++ /dev/null @@ -1,4226 +0,0 @@ -/* Prototype JavaScript framework, version 1.6.0.2 - * (c) 2005-2008 Sam Stephenson - * - * Prototype is freely distributable under the terms of an MIT-style license. - * For details, see the Prototype web site: http://www.prototypejs.org/ - * - *--------------------------------------------------------------------------*/ - -var Prototype = { - Version: '1.6.0.2', - - Browser: { - IE: !!(window.attachEvent && !window.opera), - Opera: !!window.opera, - WebKit: navigator.userAgent.indexOf('AppleWebKit/') > -1, - Gecko: navigator.userAgent.indexOf('Gecko') > -1 && navigator.userAgent.indexOf('KHTML') == -1, - MobileSafari: !!navigator.userAgent.match(/Apple.*Mobile.*Safari/) - }, - - BrowserFeatures: { - XPath: !!document.evaluate, - ElementExtensions: !!window.HTMLElement, - SpecificElementExtensions: - document.createElement('div').__proto__ && - document.createElement('div').__proto__ !== - document.createElement('form').__proto__ - }, - - ScriptFragment: ']*>([\\S\\s]*?)<\/script>', - JSONFilter: /^\/\*-secure-([\s\S]*)\*\/\s*$/, - - emptyFunction: function() { }, - K: function(x) { return x } -}; - -if (Prototype.Browser.MobileSafari) - Prototype.BrowserFeatures.SpecificElementExtensions = false; - - -/* Based on Alex Arnell's inheritance implementation. */ -var Class = { - create: function() { - var parent = null, properties = $A(arguments); - if (Object.isFunction(properties[0])) - parent = properties.shift(); - - function klass() { - this.initialize.apply(this, arguments); - } - - Object.extend(klass, Class.Methods); - klass.superclass = parent; - klass.subclasses = []; - - if (parent) { - var subclass = function() { }; - subclass.prototype = parent.prototype; - klass.prototype = new subclass; - parent.subclasses.push(klass); - } - - for (var i = 0; i < properties.length; i++) - klass.addMethods(properties[i]); - - if (!klass.prototype.initialize) - klass.prototype.initialize = Prototype.emptyFunction; - - klass.prototype.constructor = klass; - - return klass; - } -}; - -Class.Methods = { - addMethods: function(source) { - var ancestor = this.superclass && this.superclass.prototype; - var properties = Object.keys(source); - - if (!Object.keys({ toString: true }).length) - properties.push("toString", "valueOf"); - - for (var i = 0, length = properties.length; i < length; i++) { - var property = properties[i], value = source[property]; - if (ancestor && Object.isFunction(value) && - value.argumentNames().first() == "$super") { - var method = value, value = Object.extend((function(m) { - return function() { return ancestor[m].apply(this, arguments) }; - })(property).wrap(method), { - valueOf: function() { return method }, - toString: function() { return method.toString() } - }); - } - this.prototype[property] = value; - } - - return this; - } -}; - -var Abstract = { }; - -Object.extend = function(destination, source) { - for (var property in source) - destination[property] = source[property]; - return destination; -}; - -Object.extend(Object, { - inspect: function(object) { - try { - if (Object.isUndefined(object)) return 'undefined'; - if (object === null) return 'null'; - return object.inspect ? object.inspect() : String(object); - } catch (e) { - if (e instanceof RangeError) return '...'; - throw e; - } - }, - - toJSON: function(object) { - var type = typeof object; - switch (type) { - case 'undefined': - case 'function': - case 'unknown': return; - case 'boolean': return object.toString(); - } - - if (object === null) return 'null'; - if (object.toJSON) return object.toJSON(); - if (Object.isElement(object)) return; - - var results = []; - for (var property in object) { - var value = Object.toJSON(object[property]); - if (!Object.isUndefined(value)) - results.push(property.toJSON() + ': ' + value); - } - - return '{' + results.join(', ') + '}'; - }, - - toQueryString: function(object) { - return $H(object).toQueryString(); - }, - - toHTML: function(object) { - return object && object.toHTML ? object.toHTML() : String.interpret(object); - }, - - keys: function(object) { - var keys = []; - for (var property in object) - keys.push(property); - return keys; - }, - - values: function(object) { - var values = []; - for (var property in object) - values.push(object[property]); - return values; - }, - - clone: function(object) { - return Object.extend({ }, object); - }, - - isElement: function(object) { - return object && object.nodeType == 1; - }, - - isArray: function(object) { - return object != null && typeof object == "object" && - 'splice' in object && 'join' in object; - }, - - isHash: function(object) { - return object instanceof Hash; - }, - - isFunction: function(object) { - return typeof object == "function"; - }, - - isString: function(object) { - return typeof object == "string"; - }, - - isNumber: function(object) { - return typeof object == "number"; - }, - - isUndefined: function(object) { - return typeof object == "undefined"; - } -}); - -Object.extend(Function.prototype, { - argumentNames: function() { - var names = this.toString().match(/^[\s\(]*function[^(]*\((.*?)\)/)[1].split(",").invoke("strip"); - return names.length == 1 && !names[0] ? [] : names; - }, - - bind: function() { - if (arguments.length < 2 && Object.isUndefined(arguments[0])) return this; - var __method = this, args = $A(arguments), object = args.shift(); - return function() { - return __method.apply(object, args.concat($A(arguments))); - } - }, - - bindAsEventListener: function() { - var __method = this, args = $A(arguments), object = args.shift(); - return function(event) { - return __method.apply(object, [event || window.event].concat(args)); - } - }, - - curry: function() { - if (!arguments.length) return this; - var __method = this, args = $A(arguments); - return function() { - return __method.apply(this, args.concat($A(arguments))); - } - }, - - delay: function() { - var __method = this, args = $A(arguments), timeout = args.shift() * 1000; - return window.setTimeout(function() { - return __method.apply(__method, args); - }, timeout); - }, - - wrap: function(wrapper) { - var __method = this; - return function() { - return wrapper.apply(this, [__method.bind(this)].concat($A(arguments))); - } - }, - - methodize: function() { - if (this._methodized) return this._methodized; - var __method = this; - return this._methodized = function() { - return __method.apply(null, [this].concat($A(arguments))); - }; - } -}); - -Function.prototype.defer = Function.prototype.delay.curry(0.01); - -Date.prototype.toJSON = function() { - return '"' + this.getUTCFullYear() + '-' + - (this.getUTCMonth() + 1).toPaddedString(2) + '-' + - this.getUTCDate().toPaddedString(2) + 'T' + - this.getUTCHours().toPaddedString(2) + ':' + - this.getUTCMinutes().toPaddedString(2) + ':' + - this.getUTCSeconds().toPaddedString(2) + 'Z"'; -}; - -var Try = { - these: function() { - var returnValue; - - for (var i = 0, length = arguments.length; i < length; i++) { - var lambda = arguments[i]; - try { - returnValue = lambda(); - break; - } catch (e) { } - } - - return returnValue; - } -}; - -RegExp.prototype.match = RegExp.prototype.test; - -RegExp.escape = function(str) { - return String(str).replace(/([.*+?^=!:${}()|[\]\/\\])/g, '\\$1'); -}; - -/*--------------------------------------------------------------------------*/ - -var PeriodicalExecuter = Class.create({ - initialize: function(callback, frequency) { - this.callback = callback; - this.frequency = frequency; - this.currentlyExecuting = false; - - this.registerCallback(); - }, - - registerCallback: function() { - this.timer = setInterval(this.onTimerEvent.bind(this), this.frequency * 1000); - }, - - execute: function() { - this.callback(this); - }, - - stop: function() { - if (!this.timer) return; - clearInterval(this.timer); - this.timer = null; - }, - - onTimerEvent: function() { - if (!this.currentlyExecuting) { - try { - this.currentlyExecuting = true; - this.execute(); - } finally { - this.currentlyExecuting = false; - } - } - } -}); -Object.extend(String, { - interpret: function(value) { - return value == null ? '' : String(value); - }, - specialChar: { - '\b': '\\b', - '\t': '\\t', - '\n': '\\n', - '\f': '\\f', - '\r': '\\r', - '\\': '\\\\' - } -}); - -Object.extend(String.prototype, { - gsub: function(pattern, replacement) { - var result = '', source = this, match; - replacement = arguments.callee.prepareReplacement(replacement); - - while (source.length > 0) { - if (match = source.match(pattern)) { - result += source.slice(0, match.index); - result += String.interpret(replacement(match)); - source = source.slice(match.index + match[0].length); - } else { - result += source, source = ''; - } - } - return result; - }, - - sub: function(pattern, replacement, count) { - replacement = this.gsub.prepareReplacement(replacement); - count = Object.isUndefined(count) ? 1 : count; - - return this.gsub(pattern, function(match) { - if (--count < 0) return match[0]; - return replacement(match); - }); - }, - - scan: function(pattern, iterator) { - this.gsub(pattern, iterator); - return String(this); - }, - - truncate: function(length, truncation) { - length = length || 30; - truncation = Object.isUndefined(truncation) ? '...' : truncation; - return this.length > length ? - this.slice(0, length - truncation.length) + truncation : String(this); - }, - - strip: function() { - return this.replace(/^\s+/, '').replace(/\s+$/, ''); - }, - - stripTags: function() { - return this.replace(/<\/?[^>]+>/gi, ''); - }, - - stripScripts: function() { - return this.replace(new RegExp(Prototype.ScriptFragment, 'img'), ''); - }, - - extractScripts: function() { - var matchAll = new RegExp(Prototype.ScriptFragment, 'img'); - var matchOne = new RegExp(Prototype.ScriptFragment, 'im'); - return (this.match(matchAll) || []).map(function(scriptTag) { - return (scriptTag.match(matchOne) || ['', ''])[1]; - }); - }, - - evalScripts: function() { - return this.extractScripts().map(function(script) { return eval(script) }); - }, - - escapeHTML: function() { - var self = arguments.callee; - self.text.data = this; - return self.div.innerHTML; - }, - - unescapeHTML: function() { - var div = new Element('div'); - div.innerHTML = this.stripTags(); - return div.childNodes[0] ? (div.childNodes.length > 1 ? - $A(div.childNodes).inject('', function(memo, node) { return memo+node.nodeValue }) : - div.childNodes[0].nodeValue) : ''; - }, - - toQueryParams: function(separator) { - var match = this.strip().match(/([^?#]*)(#.*)?$/); - if (!match) return { }; - - return match[1].split(separator || '&').inject({ }, function(hash, pair) { - if ((pair = pair.split('='))[0]) { - var key = decodeURIComponent(pair.shift()); - var value = pair.length > 1 ? pair.join('=') : pair[0]; - if (value != undefined) value = decodeURIComponent(value); - - if (key in hash) { - if (!Object.isArray(hash[key])) hash[key] = [hash[key]]; - hash[key].push(value); - } - else hash[key] = value; - } - return hash; - }); - }, - - toArray: function() { - return this.split(''); - }, - - succ: function() { - return this.slice(0, this.length - 1) + - String.fromCharCode(this.charCodeAt(this.length - 1) + 1); - }, - - times: function(count) { - return count < 1 ? '' : new Array(count + 1).join(this); - }, - - camelize: function() { - var parts = this.split('-'), len = parts.length; - if (len == 1) return parts[0]; - - var camelized = this.charAt(0) == '-' - ? parts[0].charAt(0).toUpperCase() + parts[0].substring(1) - : parts[0]; - - for (var i = 1; i < len; i++) - camelized += parts[i].charAt(0).toUpperCase() + parts[i].substring(1); - - return camelized; - }, - - capitalize: function() { - return this.charAt(0).toUpperCase() + this.substring(1).toLowerCase(); - }, - - underscore: function() { - return this.gsub(/::/, '/').gsub(/([A-Z]+)([A-Z][a-z])/,'#{1}_#{2}').gsub(/([a-z\d])([A-Z])/,'#{1}_#{2}').gsub(/-/,'_').toLowerCase(); - }, - - dasherize: function() { - return this.gsub(/_/,'-'); - }, - - inspect: function(useDoubleQuotes) { - var escapedString = this.gsub(/[\x00-\x1f\\]/, function(match) { - var character = String.specialChar[match[0]]; - return character ? character : '\\u00' + match[0].charCodeAt().toPaddedString(2, 16); - }); - if (useDoubleQuotes) return '"' + escapedString.replace(/"/g, '\\"') + '"'; - return "'" + escapedString.replace(/'/g, '\\\'') + "'"; - }, - - toJSON: function() { - return this.inspect(true); - }, - - unfilterJSON: function(filter) { - return this.sub(filter || Prototype.JSONFilter, '#{1}'); - }, - - isJSON: function() { - var str = this; - if (str.blank()) return false; - str = this.replace(/\\./g, '@').replace(/"[^"\\\n\r]*"/g, ''); - return (/^[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t]*$/).test(str); - }, - - evalJSON: function(sanitize) { - var json = this.unfilterJSON(); - try { - if (!sanitize || json.isJSON()) return eval('(' + json + ')'); - } catch (e) { } - throw new SyntaxError('Badly formed JSON string: ' + this.inspect()); - }, - - include: function(pattern) { - return this.indexOf(pattern) > -1; - }, - - startsWith: function(pattern) { - return this.indexOf(pattern) === 0; - }, - - endsWith: function(pattern) { - var d = this.length - pattern.length; - return d >= 0 && this.lastIndexOf(pattern) === d; - }, - - empty: function() { - return this == ''; - }, - - blank: function() { - return /^\s*$/.test(this); - }, - - interpolate: function(object, pattern) { - return new Template(this, pattern).evaluate(object); - } -}); - -if (Prototype.Browser.WebKit || Prototype.Browser.IE) Object.extend(String.prototype, { - escapeHTML: function() { - return this.replace(/&/g,'&').replace(//g,'>'); - }, - unescapeHTML: function() { - return this.replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>'); - } -}); - -String.prototype.gsub.prepareReplacement = function(replacement) { - if (Object.isFunction(replacement)) return replacement; - var template = new Template(replacement); - return function(match) { return template.evaluate(match) }; -}; - -String.prototype.parseQuery = String.prototype.toQueryParams; - -Object.extend(String.prototype.escapeHTML, { - div: document.createElement('div'), - text: document.createTextNode('') -}); - -with (String.prototype.escapeHTML) div.appendChild(text); - -var Template = Class.create({ - initialize: function(template, pattern) { - this.template = template.toString(); - this.pattern = pattern || Template.Pattern; - }, - - evaluate: function(object) { - if (Object.isFunction(object.toTemplateReplacements)) - object = object.toTemplateReplacements(); - - return this.template.gsub(this.pattern, function(match) { - if (object == null) return ''; - - var before = match[1] || ''; - if (before == '\\') return match[2]; - - var ctx = object, expr = match[3]; - var pattern = /^([^.[]+|\[((?:.*?[^\\])?)\])(\.|\[|$)/; - match = pattern.exec(expr); - if (match == null) return before; - - while (match != null) { - var comp = match[1].startsWith('[') ? match[2].gsub('\\\\]', ']') : match[1]; - ctx = ctx[comp]; - if (null == ctx || '' == match[3]) break; - expr = expr.substring('[' == match[3] ? match[1].length : match[0].length); - match = pattern.exec(expr); - } - - return before + String.interpret(ctx); - }); - } -}); -Template.Pattern = /(^|.|\r|\n)(#\{(.*?)\})/; - -var $break = { }; - -var Enumerable = { - each: function(iterator, context) { - var index = 0; - iterator = iterator.bind(context); - try { - this._each(function(value) { - iterator(value, index++); - }); - } catch (e) { - if (e != $break) throw e; - } - return this; - }, - - eachSlice: function(number, iterator, context) { - iterator = iterator ? iterator.bind(context) : Prototype.K; - var index = -number, slices = [], array = this.toArray(); - while ((index += number) < array.length) - slices.push(array.slice(index, index+number)); - return slices.collect(iterator, context); - }, - - all: function(iterator, context) { - iterator = iterator ? iterator.bind(context) : Prototype.K; - var result = true; - this.each(function(value, index) { - result = result && !!iterator(value, index); - if (!result) throw $break; - }); - return result; - }, - - any: function(iterator, context) { - iterator = iterator ? iterator.bind(context) : Prototype.K; - var result = false; - this.each(function(value, index) { - if (result = !!iterator(value, index)) - throw $break; - }); - return result; - }, - - collect: function(iterator, context) { - iterator = iterator ? iterator.bind(context) : Prototype.K; - var results = []; - this.each(function(value, index) { - results.push(iterator(value, index)); - }); - return results; - }, - - detect: function(iterator, context) { - iterator = iterator.bind(context); - var result; - this.each(function(value, index) { - if (iterator(value, index)) { - result = value; - throw $break; - } - }); - return result; - }, - - findAll: function(iterator, context) { - iterator = iterator.bind(context); - var results = []; - this.each(function(value, index) { - if (iterator(value, index)) - results.push(value); - }); - return results; - }, - - grep: function(filter, iterator, context) { - iterator = iterator ? iterator.bind(context) : Prototype.K; - var results = []; - - if (Object.isString(filter)) - filter = new RegExp(filter); - - this.each(function(value, index) { - if (filter.match(value)) - results.push(iterator(value, index)); - }); - return results; - }, - - include: function(object) { - if (Object.isFunction(this.indexOf)) - if (this.indexOf(object) != -1) return true; - - var found = false; - this.each(function(value) { - if (value == object) { - found = true; - throw $break; - } - }); - return found; - }, - - inGroupsOf: function(number, fillWith) { - fillWith = Object.isUndefined(fillWith) ? null : fillWith; - return this.eachSlice(number, function(slice) { - while(slice.length < number) slice.push(fillWith); - return slice; - }); - }, - - inject: function(memo, iterator, context) { - iterator = iterator.bind(context); - this.each(function(value, index) { - memo = iterator(memo, value, index); - }); - return memo; - }, - - invoke: function(method) { - var args = $A(arguments).slice(1); - return this.map(function(value) { - return value[method].apply(value, args); - }); - }, - - max: function(iterator, context) { - iterator = iterator ? iterator.bind(context) : Prototype.K; - var result; - this.each(function(value, index) { - value = iterator(value, index); - if (result == null || value >= result) - result = value; - }); - return result; - }, - - min: function(iterator, context) { - iterator = iterator ? iterator.bind(context) : Prototype.K; - var result; - this.each(function(value, index) { - value = iterator(value, index); - if (result == null || value < result) - result = value; - }); - return result; - }, - - partition: function(iterator, context) { - iterator = iterator ? iterator.bind(context) : Prototype.K; - var trues = [], falses = []; - this.each(function(value, index) { - (iterator(value, index) ? - trues : falses).push(value); - }); - return [trues, falses]; - }, - - pluck: function(property) { - var results = []; - this.each(function(value) { - results.push(value[property]); - }); - return results; - }, - - reject: function(iterator, context) { - iterator = iterator.bind(context); - var results = []; - this.each(function(value, index) { - if (!iterator(value, index)) - results.push(value); - }); - return results; - }, - - sortBy: function(iterator, context) { - iterator = iterator.bind(context); - return this.map(function(value, index) { - return {value: value, criteria: iterator(value, index)}; - }).sort(function(left, right) { - var a = left.criteria, b = right.criteria; - return a < b ? -1 : a > b ? 1 : 0; - }).pluck('value'); - }, - - toArray: function() { - return this.map(); - }, - - zip: function() { - var iterator = Prototype.K, args = $A(arguments); - if (Object.isFunction(args.last())) - iterator = args.pop(); - - var collections = [this].concat(args).map($A); - return this.map(function(value, index) { - return iterator(collections.pluck(index)); - }); - }, - - size: function() { - return this.toArray().length; - }, - - inspect: function() { - return '#'; - } -}; - -Object.extend(Enumerable, { - map: Enumerable.collect, - find: Enumerable.detect, - select: Enumerable.findAll, - filter: Enumerable.findAll, - member: Enumerable.include, - entries: Enumerable.toArray, - every: Enumerable.all, - some: Enumerable.any -}); -function $A(iterable) { - if (!iterable) return []; - if (iterable.toArray) return iterable.toArray(); - var length = iterable.length || 0, results = new Array(length); - while (length--) results[length] = iterable[length]; - return results; -} - -if (Prototype.Browser.WebKit) { - $A = function(iterable) { - if (!iterable) return []; - if (!(Object.isFunction(iterable) && iterable == '[object NodeList]') && - iterable.toArray) return iterable.toArray(); - var length = iterable.length || 0, results = new Array(length); - while (length--) results[length] = iterable[length]; - return results; - }; -} - -Array.from = $A; - -Object.extend(Array.prototype, Enumerable); - -if (!Array.prototype._reverse) Array.prototype._reverse = Array.prototype.reverse; - -Object.extend(Array.prototype, { - _each: function(iterator) { - for (var i = 0, length = this.length; i < length; i++) - iterator(this[i]); - }, - - clear: function() { - this.length = 0; - return this; - }, - - first: function() { - return this[0]; - }, - - last: function() { - return this[this.length - 1]; - }, - - compact: function() { - return this.select(function(value) { - return value != null; - }); - }, - - flatten: function() { - return this.inject([], function(array, value) { - return array.concat(Object.isArray(value) ? - value.flatten() : [value]); - }); - }, - - without: function() { - var values = $A(arguments); - return this.select(function(value) { - return !values.include(value); - }); - }, - - reverse: function(inline) { - return (inline !== false ? this : this.toArray())._reverse(); - }, - - reduce: function() { - return this.length > 1 ? this : this[0]; - }, - - uniq: function(sorted) { - return this.inject([], function(array, value, index) { - if (0 == index || (sorted ? array.last() != value : !array.include(value))) - array.push(value); - return array; - }); - }, - - intersect: function(array) { - return this.uniq().findAll(function(item) { - return array.detect(function(value) { return item === value }); - }); - }, - - clone: function() { - return [].concat(this); - }, - - size: function() { - return this.length; - }, - - inspect: function() { - return '[' + this.map(Object.inspect).join(', ') + ']'; - }, - - toJSON: function() { - var results = []; - this.each(function(object) { - var value = Object.toJSON(object); - if (!Object.isUndefined(value)) results.push(value); - }); - return '[' + results.join(', ') + ']'; - } -}); - -// use native browser JS 1.6 implementation if available -if (Object.isFunction(Array.prototype.forEach)) - Array.prototype._each = Array.prototype.forEach; - -if (!Array.prototype.indexOf) Array.prototype.indexOf = function(item, i) { - i || (i = 0); - var length = this.length; - if (i < 0) i = length + i; - for (; i < length; i++) - if (this[i] === item) return i; - return -1; -}; - -if (!Array.prototype.lastIndexOf) Array.prototype.lastIndexOf = function(item, i) { - i = isNaN(i) ? this.length : (i < 0 ? this.length + i : i) + 1; - var n = this.slice(0, i).reverse().indexOf(item); - return (n < 0) ? n : i - n - 1; -}; - -Array.prototype.toArray = Array.prototype.clone; - -function $w(string) { - if (!Object.isString(string)) return []; - string = string.strip(); - return string ? string.split(/\s+/) : []; -} - -if (Prototype.Browser.Opera){ - Array.prototype.concat = function() { - var array = []; - for (var i = 0, length = this.length; i < length; i++) array.push(this[i]); - for (var i = 0, length = arguments.length; i < length; i++) { - if (Object.isArray(arguments[i])) { - for (var j = 0, arrayLength = arguments[i].length; j < arrayLength; j++) - array.push(arguments[i][j]); - } else { - array.push(arguments[i]); - } - } - return array; - }; -} -Object.extend(Number.prototype, { - toColorPart: function() { - return this.toPaddedString(2, 16); - }, - - succ: function() { - return this + 1; - }, - - times: function(iterator) { - $R(0, this, true).each(iterator); - return this; - }, - - toPaddedString: function(length, radix) { - var string = this.toString(radix || 10); - return '0'.times(length - string.length) + string; - }, - - toJSON: function() { - return isFinite(this) ? this.toString() : 'null'; - } -}); - -$w('abs round ceil floor').each(function(method){ - Number.prototype[method] = Math[method].methodize(); -}); -function $H(object) { - return new Hash(object); -}; - -var Hash = Class.create(Enumerable, (function() { - - function toQueryPair(key, value) { - if (Object.isUndefined(value)) return key; - return key + '=' + encodeURIComponent(String.interpret(value)); - } - - return { - initialize: function(object) { - this._object = Object.isHash(object) ? object.toObject() : Object.clone(object); - }, - - _each: function(iterator) { - for (var key in this._object) { - var value = this._object[key], pair = [key, value]; - pair.key = key; - pair.value = value; - iterator(pair); - } - }, - - set: function(key, value) { - return this._object[key] = value; - }, - - get: function(key) { - return this._object[key]; - }, - - unset: function(key) { - var value = this._object[key]; - delete this._object[key]; - return value; - }, - - toObject: function() { - return Object.clone(this._object); - }, - - keys: function() { - return this.pluck('key'); - }, - - values: function() { - return this.pluck('value'); - }, - - index: function(value) { - var match = this.detect(function(pair) { - return pair.value === value; - }); - return match && match.key; - }, - - merge: function(object) { - return this.clone().update(object); - }, - - update: function(object) { - return new Hash(object).inject(this, function(result, pair) { - result.set(pair.key, pair.value); - return result; - }); - }, - - toQueryString: function() { - return this.map(function(pair) { - var key = encodeURIComponent(pair.key), values = pair.value; - - if (values && typeof values == 'object') { - if (Object.isArray(values)) - return values.map(toQueryPair.curry(key)).join('&'); - } - return toQueryPair(key, values); - }).join('&'); - }, - - inspect: function() { - return '#'; - }, - - toJSON: function() { - return Object.toJSON(this.toObject()); - }, - - clone: function() { - return new Hash(this); - } - } -})()); - -Hash.prototype.toTemplateReplacements = Hash.prototype.toObject; -Hash.from = $H; -var ObjectRange = Class.create(Enumerable, { - initialize: function(start, end, exclusive) { - this.start = start; - this.end = end; - this.exclusive = exclusive; - }, - - _each: function(iterator) { - var value = this.start; - while (this.include(value)) { - iterator(value); - value = value.succ(); - } - }, - - include: function(value) { - if (value < this.start) - return false; - if (this.exclusive) - return value < this.end; - return value <= this.end; - } -}); - -var $R = function(start, end, exclusive) { - return new ObjectRange(start, end, exclusive); -}; - -var Ajax = { - getTransport: function() { - return Try.these( - function() {return new XMLHttpRequest()}, - function() {return new ActiveXObject('Msxml2.XMLHTTP')}, - function() {return new ActiveXObject('Microsoft.XMLHTTP')} - ) || false; - }, - - activeRequestCount: 0 -}; - -Ajax.Responders = { - responders: [], - - _each: function(iterator) { - this.responders._each(iterator); - }, - - register: function(responder) { - if (!this.include(responder)) - this.responders.push(responder); - }, - - unregister: function(responder) { - this.responders = this.responders.without(responder); - }, - - dispatch: function(callback, request, transport, json) { - this.each(function(responder) { - if (Object.isFunction(responder[callback])) { - try { - responder[callback].apply(responder, [request, transport, json]); - } catch (e) { } - } - }); - } -}; - -Object.extend(Ajax.Responders, Enumerable); - -Ajax.Responders.register({ - onCreate: function() { Ajax.activeRequestCount++ }, - onComplete: function() { Ajax.activeRequestCount-- } -}); - -Ajax.Base = Class.create({ - initialize: function(options) { - this.options = { - method: 'post', - asynchronous: true, - contentType: 'application/x-www-form-urlencoded', - encoding: 'UTF-8', - parameters: '', - evalJSON: true, - evalJS: true - }; - Object.extend(this.options, options || { }); - - this.options.method = this.options.method.toLowerCase(); - - if (Object.isString(this.options.parameters)) - this.options.parameters = this.options.parameters.toQueryParams(); - else if (Object.isHash(this.options.parameters)) - this.options.parameters = this.options.parameters.toObject(); - } -}); - -Ajax.Request = Class.create(Ajax.Base, { - _complete: false, - - initialize: function($super, url, options) { - $super(options); - this.transport = Ajax.getTransport(); - this.request(url); - }, - - request: function(url) { - this.url = url; - this.method = this.options.method; - var params = Object.clone(this.options.parameters); - - if (!['get', 'post'].include(this.method)) { - // simulate other verbs over post - params['_method'] = this.method; - this.method = 'post'; - } - - this.parameters = params; - - if (params = Object.toQueryString(params)) { - // when GET, append parameters to URL - if (this.method == 'get') - this.url += (this.url.include('?') ? '&' : '?') + params; - else if (/Konqueror|Safari|KHTML/.test(navigator.userAgent)) - params += '&_='; - } - - try { - var response = new Ajax.Response(this); - if (this.options.onCreate) this.options.onCreate(response); - Ajax.Responders.dispatch('onCreate', this, response); - - this.transport.open(this.method.toUpperCase(), this.url, - this.options.asynchronous); - - if (this.options.asynchronous) this.respondToReadyState.bind(this).defer(1); - - this.transport.onreadystatechange = this.onStateChange.bind(this); - this.setRequestHeaders(); - - this.body = this.method == 'post' ? (this.options.postBody || params) : null; - this.transport.send(this.body); - - /* Force Firefox to handle ready state 4 for synchronous requests */ - if (!this.options.asynchronous && this.transport.overrideMimeType) - this.onStateChange(); - - } - catch (e) { - this.dispatchException(e); - } - }, - - onStateChange: function() { - var readyState = this.transport.readyState; - if (readyState > 1 && !((readyState == 4) && this._complete)) - this.respondToReadyState(this.transport.readyState); - }, - - setRequestHeaders: function() { - var headers = { - 'X-Requested-With': 'XMLHttpRequest', - 'X-Prototype-Version': Prototype.Version, - 'Accept': 'text/javascript, text/html, application/xml, text/xml, */*' - }; - - if (this.method == 'post') { - headers['Content-type'] = this.options.contentType + - (this.options.encoding ? '; charset=' + this.options.encoding : ''); - - /* Force "Connection: close" for older Mozilla browsers to work - * around a bug where XMLHttpRequest sends an incorrect - * Content-length header. See Mozilla Bugzilla #246651. - */ - if (this.transport.overrideMimeType && - (navigator.userAgent.match(/Gecko\/(\d{4})/) || [0,2005])[1] < 2005) - headers['Connection'] = 'close'; - } - - // user-defined headers - if (typeof this.options.requestHeaders == 'object') { - var extras = this.options.requestHeaders; - - if (Object.isFunction(extras.push)) - for (var i = 0, length = extras.length; i < length; i += 2) - headers[extras[i]] = extras[i+1]; - else - $H(extras).each(function(pair) { headers[pair.key] = pair.value }); - } - - for (var name in headers) - this.transport.setRequestHeader(name, headers[name]); - }, - - success: function() { - var status = this.getStatus(); - return !status || (status >= 200 && status < 300); - }, - - getStatus: function() { - try { - return this.transport.status || 0; - } catch (e) { return 0 } - }, - - respondToReadyState: function(readyState) { - var state = Ajax.Request.Events[readyState], response = new Ajax.Response(this); - - if (state == 'Complete') { - try { - this._complete = true; - (this.options['on' + response.status] - || this.options['on' + (this.success() ? 'Success' : 'Failure')] - || Prototype.emptyFunction)(response, response.headerJSON); - } catch (e) { - this.dispatchException(e); - } - - var contentType = response.getHeader('Content-type'); - if (this.options.evalJS == 'force' - || (this.options.evalJS && this.isSameOrigin() && contentType - && contentType.match(/^\s*(text|application)\/(x-)?(java|ecma)script(;.*)?\s*$/i))) - this.evalResponse(); - } - - try { - (this.options['on' + state] || Prototype.emptyFunction)(response, response.headerJSON); - Ajax.Responders.dispatch('on' + state, this, response, response.headerJSON); - } catch (e) { - this.dispatchException(e); - } - - if (state == 'Complete') { - // avoid memory leak in MSIE: clean up - this.transport.onreadystatechange = Prototype.emptyFunction; - } - }, - - isSameOrigin: function() { - var m = this.url.match(/^\s*https?:\/\/[^\/]*/); - return !m || (m[0] == '#{protocol}//#{domain}#{port}'.interpolate({ - protocol: location.protocol, - domain: document.domain, - port: location.port ? ':' + location.port : '' - })); - }, - - getHeader: function(name) { - try { - return this.transport.getResponseHeader(name) || null; - } catch (e) { return null } - }, - - evalResponse: function() { - try { - return eval((this.transport.responseText || '').unfilterJSON()); - } catch (e) { - this.dispatchException(e); - } - }, - - dispatchException: function(exception) { - (this.options.onException || Prototype.emptyFunction)(this, exception); - Ajax.Responders.dispatch('onException', this, exception); - } -}); - -Ajax.Request.Events = - ['Uninitialized', 'Loading', 'Loaded', 'Interactive', 'Complete']; - -Ajax.Response = Class.create({ - initialize: function(request){ - this.request = request; - var transport = this.transport = request.transport, - readyState = this.readyState = transport.readyState; - - if((readyState > 2 && !Prototype.Browser.IE) || readyState == 4) { - this.status = this.getStatus(); - this.statusText = this.getStatusText(); - this.responseText = String.interpret(transport.responseText); - this.headerJSON = this._getHeaderJSON(); - } - - if(readyState == 4) { - var xml = transport.responseXML; - this.responseXML = Object.isUndefined(xml) ? null : xml; - this.responseJSON = this._getResponseJSON(); - } - }, - - status: 0, - statusText: '', - - getStatus: Ajax.Request.prototype.getStatus, - - getStatusText: function() { - try { - return this.transport.statusText || ''; - } catch (e) { return '' } - }, - - getHeader: Ajax.Request.prototype.getHeader, - - getAllHeaders: function() { - try { - return this.getAllResponseHeaders(); - } catch (e) { return null } - }, - - getResponseHeader: function(name) { - return this.transport.getResponseHeader(name); - }, - - getAllResponseHeaders: function() { - return this.transport.getAllResponseHeaders(); - }, - - _getHeaderJSON: function() { - var json = this.getHeader('X-JSON'); - if (!json) return null; - json = decodeURIComponent(escape(json)); - try { - return json.evalJSON(this.request.options.sanitizeJSON || - !this.request.isSameOrigin()); - } catch (e) { - this.request.dispatchException(e); - } - }, - - _getResponseJSON: function() { - var options = this.request.options; - if (!options.evalJSON || (options.evalJSON != 'force' && - !(this.getHeader('Content-type') || '').include('application/json')) || - this.responseText.blank()) - return null; - try { - return this.responseText.evalJSON(options.sanitizeJSON || - !this.request.isSameOrigin()); - } catch (e) { - this.request.dispatchException(e); - } - } -}); - -Ajax.Updater = Class.create(Ajax.Request, { - initialize: function($super, container, url, options) { - this.container = { - success: (container.success || container), - failure: (container.failure || (container.success ? null : container)) - }; - - options = Object.clone(options); - var onComplete = options.onComplete; - options.onComplete = (function(response, json) { - this.updateContent(response.responseText); - if (Object.isFunction(onComplete)) onComplete(response, json); - }).bind(this); - - $super(url, options); - }, - - updateContent: function(responseText) { - var receiver = this.container[this.success() ? 'success' : 'failure'], - options = this.options; - - if (!options.evalScripts) responseText = responseText.stripScripts(); - - if (receiver = $(receiver)) { - if (options.insertion) { - if (Object.isString(options.insertion)) { - var insertion = { }; insertion[options.insertion] = responseText; - receiver.insert(insertion); - } - else options.insertion(receiver, responseText); - } - else receiver.update(responseText); - } - } -}); - -Ajax.PeriodicalUpdater = Class.create(Ajax.Base, { - initialize: function($super, container, url, options) { - $super(options); - this.onComplete = this.options.onComplete; - - this.frequency = (this.options.frequency || 2); - this.decay = (this.options.decay || 1); - - this.updater = { }; - this.container = container; - this.url = url; - - this.start(); - }, - - start: function() { - this.options.onComplete = this.updateComplete.bind(this); - this.onTimerEvent(); - }, - - stop: function() { - this.updater.options.onComplete = undefined; - clearTimeout(this.timer); - (this.onComplete || Prototype.emptyFunction).apply(this, arguments); - }, - - updateComplete: function(response) { - if (this.options.decay) { - this.decay = (response.responseText == this.lastText ? - this.decay * this.options.decay : 1); - - this.lastText = response.responseText; - } - this.timer = this.onTimerEvent.bind(this).delay(this.decay * this.frequency); - }, - - onTimerEvent: function() { - this.updater = new Ajax.Updater(this.container, this.url, this.options); - } -}); -function $(element) { - if (arguments.length > 1) { - for (var i = 0, elements = [], length = arguments.length; i < length; i++) - elements.push($(arguments[i])); - return elements; - } - if (Object.isString(element)) - element = document.getElementById(element); - return Element.extend(element); -} - -if (Prototype.BrowserFeatures.XPath) { - document._getElementsByXPath = function(expression, parentElement) { - var results = []; - var query = document.evaluate(expression, $(parentElement) || document, - null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null); - for (var i = 0, length = query.snapshotLength; i < length; i++) - results.push(Element.extend(query.snapshotItem(i))); - return results; - }; -} - -/*--------------------------------------------------------------------------*/ - -if (!window.Node) var Node = { }; - -if (!Node.ELEMENT_NODE) { - // DOM level 2 ECMAScript Language Binding - Object.extend(Node, { - ELEMENT_NODE: 1, - ATTRIBUTE_NODE: 2, - TEXT_NODE: 3, - CDATA_SECTION_NODE: 4, - ENTITY_REFERENCE_NODE: 5, - ENTITY_NODE: 6, - PROCESSING_INSTRUCTION_NODE: 7, - COMMENT_NODE: 8, - DOCUMENT_NODE: 9, - DOCUMENT_TYPE_NODE: 10, - DOCUMENT_FRAGMENT_NODE: 11, - NOTATION_NODE: 12 - }); -} - -(function() { - var element = this.Element; - this.Element = function(tagName, attributes) { - attributes = attributes || { }; - tagName = tagName.toLowerCase(); - var cache = Element.cache; - if (Prototype.Browser.IE && attributes.name) { - tagName = '<' + tagName + ' name="' + attributes.name + '">'; - delete attributes.name; - return Element.writeAttribute(document.createElement(tagName), attributes); - } - if (!cache[tagName]) cache[tagName] = Element.extend(document.createElement(tagName)); - return Element.writeAttribute(cache[tagName].cloneNode(false), attributes); - }; - Object.extend(this.Element, element || { }); -}).call(window); - -Element.cache = { }; - -Element.Methods = { - visible: function(element) { - return $(element).style.display != 'none'; - }, - - toggle: function(element) { - element = $(element); - Element[Element.visible(element) ? 'hide' : 'show'](element); - return element; - }, - - hide: function(element) { - $(element).style.display = 'none'; - return element; - }, - - show: function(element) { - $(element).style.display = ''; - return element; - }, - - remove: function(element) { - element = $(element); - element.parentNode.removeChild(element); - return element; - }, - - update: function(element, content) { - element = $(element); - if (content && content.toElement) content = content.toElement(); - if (Object.isElement(content)) return element.update().insert(content); - content = Object.toHTML(content); - element.innerHTML = content.stripScripts(); - content.evalScripts.bind(content).defer(); - return element; - }, - - replace: function(element, content) { - element = $(element); - if (content && content.toElement) content = content.toElement(); - else if (!Object.isElement(content)) { - content = Object.toHTML(content); - var range = element.ownerDocument.createRange(); - range.selectNode(element); - content.evalScripts.bind(content).defer(); - content = range.createContextualFragment(content.stripScripts()); - } - element.parentNode.replaceChild(content, element); - return element; - }, - - insert: function(element, insertions) { - element = $(element); - - if (Object.isString(insertions) || Object.isNumber(insertions) || - Object.isElement(insertions) || (insertions && (insertions.toElement || insertions.toHTML))) - insertions = {bottom:insertions}; - - var content, insert, tagName, childNodes; - - for (var position in insertions) { - content = insertions[position]; - position = position.toLowerCase(); - insert = Element._insertionTranslations[position]; - - if (content && content.toElement) content = content.toElement(); - if (Object.isElement(content)) { - insert(element, content); - continue; - } - - content = Object.toHTML(content); - - tagName = ((position == 'before' || position == 'after') - ? element.parentNode : element).tagName.toUpperCase(); - - childNodes = Element._getContentFromAnonymousElement(tagName, content.stripScripts()); - - if (position == 'top' || position == 'after') childNodes.reverse(); - childNodes.each(insert.curry(element)); - - content.evalScripts.bind(content).defer(); - } - - return element; - }, - - wrap: function(element, wrapper, attributes) { - element = $(element); - if (Object.isElement(wrapper)) - $(wrapper).writeAttribute(attributes || { }); - else if (Object.isString(wrapper)) wrapper = new Element(wrapper, attributes); - else wrapper = new Element('div', wrapper); - if (element.parentNode) - element.parentNode.replaceChild(wrapper, element); - wrapper.appendChild(element); - return wrapper; - }, - - inspect: function(element) { - element = $(element); - var result = '<' + element.tagName.toLowerCase(); - $H({'id': 'id', 'className': 'class'}).each(function(pair) { - var property = pair.first(), attribute = pair.last(); - var value = (element[property] || '').toString(); - if (value) result += ' ' + attribute + '=' + value.inspect(true); - }); - return result + '>'; - }, - - recursivelyCollect: function(element, property) { - element = $(element); - var elements = []; - while (element = element[property]) - if (element.nodeType == 1) - elements.push(Element.extend(element)); - return elements; - }, - - ancestors: function(element) { - return $(element).recursivelyCollect('parentNode'); - }, - - descendants: function(element) { - return $(element).select("*"); - }, - - firstDescendant: function(element) { - element = $(element).firstChild; - while (element && element.nodeType != 1) element = element.nextSibling; - return $(element); - }, - - immediateDescendants: function(element) { - if (!(element = $(element).firstChild)) return []; - while (element && element.nodeType != 1) element = element.nextSibling; - if (element) return [element].concat($(element).nextSiblings()); - return []; - }, - - previousSiblings: function(element) { - return $(element).recursivelyCollect('previousSibling'); - }, - - nextSiblings: function(element) { - return $(element).recursivelyCollect('nextSibling'); - }, - - siblings: function(element) { - element = $(element); - return element.previousSiblings().reverse().concat(element.nextSiblings()); - }, - - match: function(element, selector) { - if (Object.isString(selector)) - selector = new Selector(selector); - return selector.match($(element)); - }, - - up: function(element, expression, index) { - element = $(element); - if (arguments.length == 1) return $(element.parentNode); - var ancestors = element.ancestors(); - return Object.isNumber(expression) ? ancestors[expression] : - Selector.findElement(ancestors, expression, index); - }, - - down: function(element, expression, index) { - element = $(element); - if (arguments.length == 1) return element.firstDescendant(); - return Object.isNumber(expression) ? element.descendants()[expression] : - element.select(expression)[index || 0]; - }, - - previous: function(element, expression, index) { - element = $(element); - if (arguments.length == 1) return $(Selector.handlers.previousElementSibling(element)); - var previousSiblings = element.previousSiblings(); - return Object.isNumber(expression) ? previousSiblings[expression] : - Selector.findElement(previousSiblings, expression, index); - }, - - next: function(element, expression, index) { - element = $(element); - if (arguments.length == 1) return $(Selector.handlers.nextElementSibling(element)); - var nextSiblings = element.nextSiblings(); - return Object.isNumber(expression) ? nextSiblings[expression] : - Selector.findElement(nextSiblings, expression, index); - }, - - select: function() { - var args = $A(arguments), element = $(args.shift()); - return Selector.findChildElements(element, args); - }, - - adjacent: function() { - var args = $A(arguments), element = $(args.shift()); - return Selector.findChildElements(element.parentNode, args).without(element); - }, - - identify: function(element) { - element = $(element); - var id = element.readAttribute('id'), self = arguments.callee; - if (id) return id; - do { id = 'anonymous_element_' + self.counter++ } while ($(id)); - element.writeAttribute('id', id); - return id; - }, - - readAttribute: function(element, name) { - element = $(element); - if (Prototype.Browser.IE) { - var t = Element._attributeTranslations.read; - if (t.values[name]) return t.values[name](element, name); - if (t.names[name]) name = t.names[name]; - if (name.include(':')) { - return (!element.attributes || !element.attributes[name]) ? null : - element.attributes[name].value; - } - } - return element.getAttribute(name); - }, - - writeAttribute: function(element, name, value) { - element = $(element); - var attributes = { }, t = Element._attributeTranslations.write; - - if (typeof name == 'object') attributes = name; - else attributes[name] = Object.isUndefined(value) ? true : value; - - for (var attr in attributes) { - name = t.names[attr] || attr; - value = attributes[attr]; - if (t.values[attr]) name = t.values[attr](element, value); - if (value === false || value === null) - element.removeAttribute(name); - else if (value === true) - element.setAttribute(name, name); - else element.setAttribute(name, value); - } - return element; - }, - - getHeight: function(element) { - return $(element).getDimensions().height; - }, - - getWidth: function(element) { - return $(element).getDimensions().width; - }, - - classNames: function(element) { - return new Element.ClassNames(element); - }, - - hasClassName: function(element, className) { - if (!(element = $(element))) return; - var elementClassName = element.className; - return (elementClassName.length > 0 && (elementClassName == className || - new RegExp("(^|\\s)" + className + "(\\s|$)").test(elementClassName))); - }, - - addClassName: function(element, className) { - if (!(element = $(element))) return; - if (!element.hasClassName(className)) - element.className += (element.className ? ' ' : '') + className; - return element; - }, - - removeClassName: function(element, className) { - if (!(element = $(element))) return; - element.className = element.className.replace( - new RegExp("(^|\\s+)" + className + "(\\s+|$)"), ' ').strip(); - return element; - }, - - toggleClassName: function(element, className) { - if (!(element = $(element))) return; - return element[element.hasClassName(className) ? - 'removeClassName' : 'addClassName'](className); - }, - - // removes whitespace-only text node children - cleanWhitespace: function(element) { - element = $(element); - var node = element.firstChild; - while (node) { - var nextNode = node.nextSibling; - if (node.nodeType == 3 && !/\S/.test(node.nodeValue)) - element.removeChild(node); - node = nextNode; - } - return element; - }, - - empty: function(element) { - return $(element).innerHTML.blank(); - }, - - descendantOf: function(element, ancestor) { - element = $(element), ancestor = $(ancestor); - var originalAncestor = ancestor; - - if (element.compareDocumentPosition) { - try { - return (element.compareDocumentPosition(ancestor) & 8) === 8; - } catch(e) { - if (!e.startsWith('Permission denied')) throw e; - } - } - - if (element.sourceIndex && !Prototype.Browser.Opera) { - var e = element.sourceIndex, a = ancestor.sourceIndex, - nextAncestor = ancestor.nextSibling; - if (!nextAncestor) { - do { ancestor = ancestor.parentNode; } - while (!(nextAncestor = ancestor.nextSibling) && ancestor.parentNode); - } - if (nextAncestor && nextAncestor.sourceIndex) - return (e > a && e < nextAncestor.sourceIndex); - } - - while (element = element.parentNode) - if (element == originalAncestor) return true; - return false; - }, - - scrollTo: function(element) { - element = $(element); - var pos = element.cumulativeOffset(); - window.scrollTo(pos[0], pos[1]); - return element; - }, - - getStyle: function(element, style) { - element = $(element); - style = style == 'float' ? 'cssFloat' : style.camelize(); - var value = element.style[style]; - if (!value) { - var css = document.defaultView.getComputedStyle(element, null); - value = css ? css[style] : null; - } - if (style == 'opacity') return value ? parseFloat(value) : 1.0; - return value == 'auto' ? null : value; - }, - - getOpacity: function(element) { - return $(element).getStyle('opacity'); - }, - - setStyle: function(element, styles) { - element = $(element); - var elementStyle = element.style, match; - if (Object.isString(styles)) { - element.style.cssText += ';' + styles; - return styles.include('opacity') ? - element.setOpacity(styles.match(/opacity:\s*(\d?\.?\d*)/)[1]) : element; - } - for (var property in styles) - if (property == 'opacity') element.setOpacity(styles[property]); - else - elementStyle[(property == 'float' || property == 'cssFloat') ? - (Object.isUndefined(elementStyle.styleFloat) ? 'cssFloat' : 'styleFloat') : - property] = styles[property]; - - return element; - }, - - setOpacity: function(element, value) { - element = $(element); - element.style.opacity = (value == 1 || value === '') ? '' : - (value < 0.00001) ? 0 : value; - return element; - }, - - getDimensions: function(element) { - element = $(element); - var display = $(element).getStyle('display'); - if (display != 'none' && display != null) // Safari bug - return {width: element.offsetWidth, height: element.offsetHeight}; - - // All *Width and *Height properties give 0 on elements with display none, - // so enable the element temporarily - var els = element.style; - var originalVisibility = els.visibility; - var originalPosition = els.position; - var originalDisplay = els.display; - els.visibility = 'hidden'; - els.position = 'absolute'; - els.display = 'block'; - var originalWidth = element.clientWidth; - var originalHeight = element.clientHeight; - els.display = originalDisplay; - els.position = originalPosition; - els.visibility = originalVisibility; - return {width: originalWidth, height: originalHeight}; - }, - - makePositioned: function(element) { - element = $(element); - var pos = Element.getStyle(element, 'position'); - if (pos == 'static' || !pos) { - element._madePositioned = true; - element.style.position = 'relative'; - // Opera returns the offset relative to the positioning context, when an - // element is position relative but top and left have not been defined - if (window.opera) { - element.style.top = 0; - element.style.left = 0; - } - } - return element; - }, - - undoPositioned: function(element) { - element = $(element); - if (element._madePositioned) { - element._madePositioned = undefined; - element.style.position = - element.style.top = - element.style.left = - element.style.bottom = - element.style.right = ''; - } - return element; - }, - - makeClipping: function(element) { - element = $(element); - if (element._overflow) return element; - element._overflow = Element.getStyle(element, 'overflow') || 'auto'; - if (element._overflow !== 'hidden') - element.style.overflow = 'hidden'; - return element; - }, - - undoClipping: function(element) { - element = $(element); - if (!element._overflow) return element; - element.style.overflow = element._overflow == 'auto' ? '' : element._overflow; - element._overflow = null; - return element; - }, - - cumulativeOffset: function(element) { - var valueT = 0, valueL = 0; - do { - valueT += element.offsetTop || 0; - valueL += element.offsetLeft || 0; - element = element.offsetParent; - } while (element); - return Element._returnOffset(valueL, valueT); - }, - - positionedOffset: function(element) { - var valueT = 0, valueL = 0; - do { - valueT += element.offsetTop || 0; - valueL += element.offsetLeft || 0; - element = element.offsetParent; - if (element) { - if (element.tagName == 'BODY') break; - var p = Element.getStyle(element, 'position'); - if (p !== 'static') break; - } - } while (element); - return Element._returnOffset(valueL, valueT); - }, - - absolutize: function(element) { - element = $(element); - if (element.getStyle('position') == 'absolute') return; - // Position.prepare(); // To be done manually by Scripty when it needs it. - - var offsets = element.positionedOffset(); - var top = offsets[1]; - var left = offsets[0]; - var width = element.clientWidth; - var height = element.clientHeight; - - element._originalLeft = left - parseFloat(element.style.left || 0); - element._originalTop = top - parseFloat(element.style.top || 0); - element._originalWidth = element.style.width; - element._originalHeight = element.style.height; - - element.style.position = 'absolute'; - element.style.top = top + 'px'; - element.style.left = left + 'px'; - element.style.width = width + 'px'; - element.style.height = height + 'px'; - return element; - }, - - relativize: function(element) { - element = $(element); - if (element.getStyle('position') == 'relative') return; - // Position.prepare(); // To be done manually by Scripty when it needs it. - - element.style.position = 'relative'; - var top = parseFloat(element.style.top || 0) - (element._originalTop || 0); - var left = parseFloat(element.style.left || 0) - (element._originalLeft || 0); - - element.style.top = top + 'px'; - element.style.left = left + 'px'; - element.style.height = element._originalHeight; - element.style.width = element._originalWidth; - return element; - }, - - cumulativeScrollOffset: function(element) { - var valueT = 0, valueL = 0; - do { - valueT += element.scrollTop || 0; - valueL += element.scrollLeft || 0; - element = element.parentNode; - } while (element); - return Element._returnOffset(valueL, valueT); - }, - - getOffsetParent: function(element) { - if (element.offsetParent) return $(element.offsetParent); - if (element == document.body) return $(element); - - while ((element = element.parentNode) && element != document.body) - if (Element.getStyle(element, 'position') != 'static') - return $(element); - - return $(document.body); - }, - - viewportOffset: function(forElement) { - var valueT = 0, valueL = 0; - - var element = forElement; - do { - valueT += element.offsetTop || 0; - valueL += element.offsetLeft || 0; - - // Safari fix - if (element.offsetParent == document.body && - Element.getStyle(element, 'position') == 'absolute') break; - - } while (element = element.offsetParent); - - element = forElement; - do { - if (!Prototype.Browser.Opera || element.tagName == 'BODY') { - valueT -= element.scrollTop || 0; - valueL -= element.scrollLeft || 0; - } - } while (element = element.parentNode); - - return Element._returnOffset(valueL, valueT); - }, - - clonePosition: function(element, source) { - var options = Object.extend({ - setLeft: true, - setTop: true, - setWidth: true, - setHeight: true, - offsetTop: 0, - offsetLeft: 0 - }, arguments[2] || { }); - - // find page position of source - source = $(source); - var p = source.viewportOffset(); - - // find coordinate system to use - element = $(element); - var delta = [0, 0]; - var parent = null; - // delta [0,0] will do fine with position: fixed elements, - // position:absolute needs offsetParent deltas - if (Element.getStyle(element, 'position') == 'absolute') { - parent = element.getOffsetParent(); - delta = parent.viewportOffset(); - } - - // correct by body offsets (fixes Safari) - if (parent == document.body) { - delta[0] -= document.body.offsetLeft; - delta[1] -= document.body.offsetTop; - } - - // set position - if (options.setLeft) element.style.left = (p[0] - delta[0] + options.offsetLeft) + 'px'; - if (options.setTop) element.style.top = (p[1] - delta[1] + options.offsetTop) + 'px'; - if (options.setWidth) element.style.width = source.offsetWidth + 'px'; - if (options.setHeight) element.style.height = source.offsetHeight + 'px'; - return element; - } -}; - -Element.Methods.identify.counter = 1; - -Object.extend(Element.Methods, { - getElementsBySelector: Element.Methods.select, - childElements: Element.Methods.immediateDescendants -}); - -Element._attributeTranslations = { - write: { - names: { - className: 'class', - htmlFor: 'for' - }, - values: { } - } -}; - -if (Prototype.Browser.Opera) { - Element.Methods.getStyle = Element.Methods.getStyle.wrap( - function(proceed, element, style) { - switch (style) { - case 'left': case 'top': case 'right': case 'bottom': - if (proceed(element, 'position') === 'static') return null; - case 'height': case 'width': - // returns '0px' for hidden elements; we want it to return null - if (!Element.visible(element)) return null; - - // returns the border-box dimensions rather than the content-box - // dimensions, so we subtract padding and borders from the value - var dim = parseInt(proceed(element, style), 10); - - if (dim !== element['offset' + style.capitalize()]) - return dim + 'px'; - - var properties; - if (style === 'height') { - properties = ['border-top-width', 'padding-top', - 'padding-bottom', 'border-bottom-width']; - } - else { - properties = ['border-left-width', 'padding-left', - 'padding-right', 'border-right-width']; - } - return properties.inject(dim, function(memo, property) { - var val = proceed(element, property); - return val === null ? memo : memo - parseInt(val, 10); - }) + 'px'; - default: return proceed(element, style); - } - } - ); - - Element.Methods.readAttribute = Element.Methods.readAttribute.wrap( - function(proceed, element, attribute) { - if (attribute === 'title') return element.title; - return proceed(element, attribute); - } - ); -} - -else if (Prototype.Browser.IE) { - // IE doesn't report offsets correctly for static elements, so we change them - // to "relative" to get the values, then change them back. - Element.Methods.getOffsetParent = Element.Methods.getOffsetParent.wrap( - function(proceed, element) { - element = $(element); - var position = element.getStyle('position'); - if (position !== 'static') return proceed(element); - element.setStyle({ position: 'relative' }); - var value = proceed(element); - element.setStyle({ position: position }); - return value; - } - ); - - $w('positionedOffset viewportOffset').each(function(method) { - Element.Methods[method] = Element.Methods[method].wrap( - function(proceed, element) { - element = $(element); - var position = element.getStyle('position'); - if (position !== 'static') return proceed(element); - // Trigger hasLayout on the offset parent so that IE6 reports - // accurate offsetTop and offsetLeft values for position: fixed. - var offsetParent = element.getOffsetParent(); - if (offsetParent && offsetParent.getStyle('position') === 'fixed') - offsetParent.setStyle({ zoom: 1 }); - element.setStyle({ position: 'relative' }); - var value = proceed(element); - element.setStyle({ position: position }); - return value; - } - ); - }); - - Element.Methods.getStyle = function(element, style) { - element = $(element); - style = (style == 'float' || style == 'cssFloat') ? 'styleFloat' : style.camelize(); - var value = element.style[style]; - if (!value && element.currentStyle) value = element.currentStyle[style]; - - if (style == 'opacity') { - if (value = (element.getStyle('filter') || '').match(/alpha\(opacity=(.*)\)/)) - if (value[1]) return parseFloat(value[1]) / 100; - return 1.0; - } - - if (value == 'auto') { - if ((style == 'width' || style == 'height') && (element.getStyle('display') != 'none')) - return element['offset' + style.capitalize()] + 'px'; - return null; - } - return value; - }; - - Element.Methods.setOpacity = function(element, value) { - function stripAlpha(filter){ - return filter.replace(/alpha\([^\)]*\)/gi,''); - } - element = $(element); - var currentStyle = element.currentStyle; - if ((currentStyle && !currentStyle.hasLayout) || - (!currentStyle && element.style.zoom == 'normal')) - element.style.zoom = 1; - - var filter = element.getStyle('filter'), style = element.style; - if (value == 1 || value === '') { - (filter = stripAlpha(filter)) ? - style.filter = filter : style.removeAttribute('filter'); - return element; - } else if (value < 0.00001) value = 0; - style.filter = stripAlpha(filter) + - 'alpha(opacity=' + (value * 100) + ')'; - return element; - }; - - Element._attributeTranslations = { - read: { - names: { - 'class': 'className', - 'for': 'htmlFor' - }, - values: { - _getAttr: function(element, attribute) { - return element.getAttribute(attribute, 2); - }, - _getAttrNode: function(element, attribute) { - var node = element.getAttributeNode(attribute); - return node ? node.value : ""; - }, - _getEv: function(element, attribute) { - attribute = element.getAttribute(attribute); - return attribute ? attribute.toString().slice(23, -2) : null; - }, - _flag: function(element, attribute) { - return $(element).hasAttribute(attribute) ? attribute : null; - }, - style: function(element) { - return element.style.cssText.toLowerCase(); - }, - title: function(element) { - return element.title; - } - } - } - }; - - Element._attributeTranslations.write = { - names: Object.extend({ - cellpadding: 'cellPadding', - cellspacing: 'cellSpacing' - }, Element._attributeTranslations.read.names), - values: { - checked: function(element, value) { - element.checked = !!value; - }, - - style: function(element, value) { - element.style.cssText = value ? value : ''; - } - } - }; - - Element._attributeTranslations.has = {}; - - $w('colSpan rowSpan vAlign dateTime accessKey tabIndex ' + - 'encType maxLength readOnly longDesc').each(function(attr) { - Element._attributeTranslations.write.names[attr.toLowerCase()] = attr; - Element._attributeTranslations.has[attr.toLowerCase()] = attr; - }); - - (function(v) { - Object.extend(v, { - href: v._getAttr, - src: v._getAttr, - type: v._getAttr, - action: v._getAttrNode, - disabled: v._flag, - checked: v._flag, - readonly: v._flag, - multiple: v._flag, - onload: v._getEv, - onunload: v._getEv, - onclick: v._getEv, - ondblclick: v._getEv, - onmousedown: v._getEv, - onmouseup: v._getEv, - onmouseover: v._getEv, - onmousemove: v._getEv, - onmouseout: v._getEv, - onfocus: v._getEv, - onblur: v._getEv, - onkeypress: v._getEv, - onkeydown: v._getEv, - onkeyup: v._getEv, - onsubmit: v._getEv, - onreset: v._getEv, - onselect: v._getEv, - onchange: v._getEv - }); - })(Element._attributeTranslations.read.values); -} - -else if (Prototype.Browser.Gecko && /rv:1\.8\.0/.test(navigator.userAgent)) { - Element.Methods.setOpacity = function(element, value) { - element = $(element); - element.style.opacity = (value == 1) ? 0.999999 : - (value === '') ? '' : (value < 0.00001) ? 0 : value; - return element; - }; -} - -else if (Prototype.Browser.WebKit) { - Element.Methods.setOpacity = function(element, value) { - element = $(element); - element.style.opacity = (value == 1 || value === '') ? '' : - (value < 0.00001) ? 0 : value; - - if (value == 1) - if(element.tagName == 'IMG' && element.width) { - element.width++; element.width--; - } else try { - var n = document.createTextNode(' '); - element.appendChild(n); - element.removeChild(n); - } catch (e) { } - - return element; - }; - - // Safari returns margins on body which is incorrect if the child is absolutely - // positioned. For performance reasons, redefine Element#cumulativeOffset for - // KHTML/WebKit only. - Element.Methods.cumulativeOffset = function(element) { - var valueT = 0, valueL = 0; - do { - valueT += element.offsetTop || 0; - valueL += element.offsetLeft || 0; - if (element.offsetParent == document.body) - if (Element.getStyle(element, 'position') == 'absolute') break; - - element = element.offsetParent; - } while (element); - - return Element._returnOffset(valueL, valueT); - }; -} - -if (Prototype.Browser.IE || Prototype.Browser.Opera) { - // IE and Opera are missing .innerHTML support for TABLE-related and SELECT elements - Element.Methods.update = function(element, content) { - element = $(element); - - if (content && content.toElement) content = content.toElement(); - if (Object.isElement(content)) return element.update().insert(content); - - content = Object.toHTML(content); - var tagName = element.tagName.toUpperCase(); - - if (tagName in Element._insertionTranslations.tags) { - $A(element.childNodes).each(function(node) { element.removeChild(node) }); - Element._getContentFromAnonymousElement(tagName, content.stripScripts()) - .each(function(node) { element.appendChild(node) }); - } - else element.innerHTML = content.stripScripts(); - - content.evalScripts.bind(content).defer(); - return element; - }; -} - -if ('outerHTML' in document.createElement('div')) { - Element.Methods.replace = function(element, content) { - element = $(element); - - if (content && content.toElement) content = content.toElement(); - if (Object.isElement(content)) { - element.parentNode.replaceChild(content, element); - return element; - } - - content = Object.toHTML(content); - var parent = element.parentNode, tagName = parent.tagName.toUpperCase(); - - if (Element._insertionTranslations.tags[tagName]) { - var nextSibling = element.next(); - var fragments = Element._getContentFromAnonymousElement(tagName, content.stripScripts()); - parent.removeChild(element); - if (nextSibling) - fragments.each(function(node) { parent.insertBefore(node, nextSibling) }); - else - fragments.each(function(node) { parent.appendChild(node) }); - } - else element.outerHTML = content.stripScripts(); - - content.evalScripts.bind(content).defer(); - return element; - }; -} - -Element._returnOffset = function(l, t) { - var result = [l, t]; - result.left = l; - result.top = t; - return result; -}; - -Element._getContentFromAnonymousElement = function(tagName, html) { - var div = new Element('div'), t = Element._insertionTranslations.tags[tagName]; - if (t) { - div.innerHTML = t[0] + html + t[1]; - t[2].times(function() { div = div.firstChild }); - } else div.innerHTML = html; - return $A(div.childNodes); -}; - -Element._insertionTranslations = { - before: function(element, node) { - element.parentNode.insertBefore(node, element); - }, - top: function(element, node) { - element.insertBefore(node, element.firstChild); - }, - bottom: function(element, node) { - element.appendChild(node); - }, - after: function(element, node) { - element.parentNode.insertBefore(node, element.nextSibling); - }, - tags: { - TABLE: ['', '
    ', 1], - TBODY: ['', '
    ', 2], - TR: ['', '
    ', 3], - TD: ['
    ', '
    ', 4], - SELECT: ['', 1] - } -}; - -(function() { - Object.extend(this.tags, { - THEAD: this.tags.TBODY, - TFOOT: this.tags.TBODY, - TH: this.tags.TD - }); -}).call(Element._insertionTranslations); - -Element.Methods.Simulated = { - hasAttribute: function(element, attribute) { - attribute = Element._attributeTranslations.has[attribute] || attribute; - var node = $(element).getAttributeNode(attribute); - return node && node.specified; - } -}; - -Element.Methods.ByTag = { }; - -Object.extend(Element, Element.Methods); - -if (!Prototype.BrowserFeatures.ElementExtensions && - document.createElement('div').__proto__) { - window.HTMLElement = { }; - window.HTMLElement.prototype = document.createElement('div').__proto__; - Prototype.BrowserFeatures.ElementExtensions = true; -} - -Element.extend = (function() { - if (Prototype.BrowserFeatures.SpecificElementExtensions) - return Prototype.K; - - var Methods = { }, ByTag = Element.Methods.ByTag; - - var extend = Object.extend(function(element) { - if (!element || element._extendedByPrototype || - element.nodeType != 1 || element == window) return element; - - var methods = Object.clone(Methods), - tagName = element.tagName, property, value; - - // extend methods for specific tags - if (ByTag[tagName]) Object.extend(methods, ByTag[tagName]); - - for (property in methods) { - value = methods[property]; - if (Object.isFunction(value) && !(property in element)) - element[property] = value.methodize(); - } - - element._extendedByPrototype = Prototype.emptyFunction; - return element; - - }, { - refresh: function() { - // extend methods for all tags (Safari doesn't need this) - if (!Prototype.BrowserFeatures.ElementExtensions) { - Object.extend(Methods, Element.Methods); - Object.extend(Methods, Element.Methods.Simulated); - } - } - }); - - extend.refresh(); - return extend; -})(); - -Element.hasAttribute = function(element, attribute) { - if (element.hasAttribute) return element.hasAttribute(attribute); - return Element.Methods.Simulated.hasAttribute(element, attribute); -}; - -Element.addMethods = function(methods) { - var F = Prototype.BrowserFeatures, T = Element.Methods.ByTag; - - if (!methods) { - Object.extend(Form, Form.Methods); - Object.extend(Form.Element, Form.Element.Methods); - Object.extend(Element.Methods.ByTag, { - "FORM": Object.clone(Form.Methods), - "INPUT": Object.clone(Form.Element.Methods), - "SELECT": Object.clone(Form.Element.Methods), - "TEXTAREA": Object.clone(Form.Element.Methods) - }); - } - - if (arguments.length == 2) { - var tagName = methods; - methods = arguments[1]; - } - - if (!tagName) Object.extend(Element.Methods, methods || { }); - else { - if (Object.isArray(tagName)) tagName.each(extend); - else extend(tagName); - } - - function extend(tagName) { - tagName = tagName.toUpperCase(); - if (!Element.Methods.ByTag[tagName]) - Element.Methods.ByTag[tagName] = { }; - Object.extend(Element.Methods.ByTag[tagName], methods); - } - - function copy(methods, destination, onlyIfAbsent) { - onlyIfAbsent = onlyIfAbsent || false; - for (var property in methods) { - var value = methods[property]; - if (!Object.isFunction(value)) continue; - if (!onlyIfAbsent || !(property in destination)) - destination[property] = value.methodize(); - } - } - - function findDOMClass(tagName) { - var klass; - var trans = { - "OPTGROUP": "OptGroup", "TEXTAREA": "TextArea", "P": "Paragraph", - "FIELDSET": "FieldSet", "UL": "UList", "OL": "OList", "DL": "DList", - "DIR": "Directory", "H1": "Heading", "H2": "Heading", "H3": "Heading", - "H4": "Heading", "H5": "Heading", "H6": "Heading", "Q": "Quote", - "INS": "Mod", "DEL": "Mod", "A": "Anchor", "IMG": "Image", "CAPTION": - "TableCaption", "COL": "TableCol", "COLGROUP": "TableCol", "THEAD": - "TableSection", "TFOOT": "TableSection", "TBODY": "TableSection", "TR": - "TableRow", "TH": "TableCell", "TD": "TableCell", "FRAMESET": - "FrameSet", "IFRAME": "IFrame" - }; - if (trans[tagName]) klass = 'HTML' + trans[tagName] + 'Element'; - if (window[klass]) return window[klass]; - klass = 'HTML' + tagName + 'Element'; - if (window[klass]) return window[klass]; - klass = 'HTML' + tagName.capitalize() + 'Element'; - if (window[klass]) return window[klass]; - - window[klass] = { }; - window[klass].prototype = document.createElement(tagName).__proto__; - return window[klass]; - } - - if (F.ElementExtensions) { - copy(Element.Methods, HTMLElement.prototype); - copy(Element.Methods.Simulated, HTMLElement.prototype, true); - } - - if (F.SpecificElementExtensions) { - for (var tag in Element.Methods.ByTag) { - var klass = findDOMClass(tag); - if (Object.isUndefined(klass)) continue; - copy(T[tag], klass.prototype); - } - } - - Object.extend(Element, Element.Methods); - delete Element.ByTag; - - if (Element.extend.refresh) Element.extend.refresh(); - Element.cache = { }; -}; - -document.viewport = { - getDimensions: function() { - var dimensions = { }; - var B = Prototype.Browser; - $w('width height').each(function(d) { - var D = d.capitalize(); - dimensions[d] = (B.WebKit && !document.evaluate) ? self['inner' + D] : - (B.Opera) ? document.body['client' + D] : document.documentElement['client' + D]; - }); - return dimensions; - }, - - getWidth: function() { - return this.getDimensions().width; - }, - - getHeight: function() { - return this.getDimensions().height; - }, - - getScrollOffsets: function() { - return Element._returnOffset( - window.pageXOffset || document.documentElement.scrollLeft || document.body.scrollLeft, - window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop); - } -}; -/* Portions of the Selector class are derived from Jack Slocum’s DomQuery, - * part of YUI-Ext version 0.40, distributed under the terms of an MIT-style - * license. Please see http://www.yui-ext.com/ for more information. */ - -var Selector = Class.create({ - initialize: function(expression) { - this.expression = expression.strip(); - this.compileMatcher(); - }, - - shouldUseXPath: function() { - if (!Prototype.BrowserFeatures.XPath) return false; - - var e = this.expression; - - // Safari 3 chokes on :*-of-type and :empty - if (Prototype.Browser.WebKit && - (e.include("-of-type") || e.include(":empty"))) - return false; - - // XPath can't do namespaced attributes, nor can it read - // the "checked" property from DOM nodes - if ((/(\[[\w-]*?:|:checked)/).test(this.expression)) - return false; - - return true; - }, - - compileMatcher: function() { - if (this.shouldUseXPath()) - return this.compileXPathMatcher(); - - var e = this.expression, ps = Selector.patterns, h = Selector.handlers, - c = Selector.criteria, le, p, m; - - if (Selector._cache[e]) { - this.matcher = Selector._cache[e]; - return; - } - - this.matcher = ["this.matcher = function(root) {", - "var r = root, h = Selector.handlers, c = false, n;"]; - - while (e && le != e && (/\S/).test(e)) { - le = e; - for (var i in ps) { - p = ps[i]; - if (m = e.match(p)) { - this.matcher.push(Object.isFunction(c[i]) ? c[i](m) : - new Template(c[i]).evaluate(m)); - e = e.replace(m[0], ''); - break; - } - } - } - - this.matcher.push("return h.unique(n);\n}"); - eval(this.matcher.join('\n')); - Selector._cache[this.expression] = this.matcher; - }, - - compileXPathMatcher: function() { - var e = this.expression, ps = Selector.patterns, - x = Selector.xpath, le, m; - - if (Selector._cache[e]) { - this.xpath = Selector._cache[e]; return; - } - - this.matcher = ['.//*']; - while (e && le != e && (/\S/).test(e)) { - le = e; - for (var i in ps) { - if (m = e.match(ps[i])) { - this.matcher.push(Object.isFunction(x[i]) ? x[i](m) : - new Template(x[i]).evaluate(m)); - e = e.replace(m[0], ''); - break; - } - } - } - - this.xpath = this.matcher.join(''); - Selector._cache[this.expression] = this.xpath; - }, - - findElements: function(root) { - root = root || document; - if (this.xpath) return document._getElementsByXPath(this.xpath, root); - return this.matcher(root); - }, - - match: function(element) { - this.tokens = []; - - var e = this.expression, ps = Selector.patterns, as = Selector.assertions; - var le, p, m; - - while (e && le !== e && (/\S/).test(e)) { - le = e; - for (var i in ps) { - p = ps[i]; - if (m = e.match(p)) { - // use the Selector.assertions methods unless the selector - // is too complex. - if (as[i]) { - this.tokens.push([i, Object.clone(m)]); - e = e.replace(m[0], ''); - } else { - // reluctantly do a document-wide search - // and look for a match in the array - return this.findElements(document).include(element); - } - } - } - } - - var match = true, name, matches; - for (var i = 0, token; token = this.tokens[i]; i++) { - name = token[0], matches = token[1]; - if (!Selector.assertions[name](element, matches)) { - match = false; break; - } - } - - return match; - }, - - toString: function() { - return this.expression; - }, - - inspect: function() { - return "#"; - } -}); - -Object.extend(Selector, { - _cache: { }, - - xpath: { - descendant: "//*", - child: "/*", - adjacent: "/following-sibling::*[1]", - laterSibling: '/following-sibling::*', - tagName: function(m) { - if (m[1] == '*') return ''; - return "[local-name()='" + m[1].toLowerCase() + - "' or local-name()='" + m[1].toUpperCase() + "']"; - }, - className: "[contains(concat(' ', @class, ' '), ' #{1} ')]", - id: "[@id='#{1}']", - attrPresence: function(m) { - m[1] = m[1].toLowerCase(); - return new Template("[@#{1}]").evaluate(m); - }, - attr: function(m) { - m[1] = m[1].toLowerCase(); - m[3] = m[5] || m[6]; - return new Template(Selector.xpath.operators[m[2]]).evaluate(m); - }, - pseudo: function(m) { - var h = Selector.xpath.pseudos[m[1]]; - if (!h) return ''; - if (Object.isFunction(h)) return h(m); - return new Template(Selector.xpath.pseudos[m[1]]).evaluate(m); - }, - operators: { - '=': "[@#{1}='#{3}']", - '!=': "[@#{1}!='#{3}']", - '^=': "[starts-with(@#{1}, '#{3}')]", - '$=': "[substring(@#{1}, (string-length(@#{1}) - string-length('#{3}') + 1))='#{3}']", - '*=': "[contains(@#{1}, '#{3}')]", - '~=': "[contains(concat(' ', @#{1}, ' '), ' #{3} ')]", - '|=': "[contains(concat('-', @#{1}, '-'), '-#{3}-')]" - }, - pseudos: { - 'first-child': '[not(preceding-sibling::*)]', - 'last-child': '[not(following-sibling::*)]', - 'only-child': '[not(preceding-sibling::* or following-sibling::*)]', - 'empty': "[count(*) = 0 and (count(text()) = 0 or translate(text(), ' \t\r\n', '') = '')]", - 'checked': "[@checked]", - 'disabled': "[@disabled]", - 'enabled': "[not(@disabled)]", - 'not': function(m) { - var e = m[6], p = Selector.patterns, - x = Selector.xpath, le, v; - - var exclusion = []; - while (e && le != e && (/\S/).test(e)) { - le = e; - for (var i in p) { - if (m = e.match(p[i])) { - v = Object.isFunction(x[i]) ? x[i](m) : new Template(x[i]).evaluate(m); - exclusion.push("(" + v.substring(1, v.length - 1) + ")"); - e = e.replace(m[0], ''); - break; - } - } - } - return "[not(" + exclusion.join(" and ") + ")]"; - }, - 'nth-child': function(m) { - return Selector.xpath.pseudos.nth("(count(./preceding-sibling::*) + 1) ", m); - }, - 'nth-last-child': function(m) { - return Selector.xpath.pseudos.nth("(count(./following-sibling::*) + 1) ", m); - }, - 'nth-of-type': function(m) { - return Selector.xpath.pseudos.nth("position() ", m); - }, - 'nth-last-of-type': function(m) { - return Selector.xpath.pseudos.nth("(last() + 1 - position()) ", m); - }, - 'first-of-type': function(m) { - m[6] = "1"; return Selector.xpath.pseudos['nth-of-type'](m); - }, - 'last-of-type': function(m) { - m[6] = "1"; return Selector.xpath.pseudos['nth-last-of-type'](m); - }, - 'only-of-type': function(m) { - var p = Selector.xpath.pseudos; return p['first-of-type'](m) + p['last-of-type'](m); - }, - nth: function(fragment, m) { - var mm, formula = m[6], predicate; - if (formula == 'even') formula = '2n+0'; - if (formula == 'odd') formula = '2n+1'; - if (mm = formula.match(/^(\d+)$/)) // digit only - return '[' + fragment + "= " + mm[1] + ']'; - if (mm = formula.match(/^(-?\d*)?n(([+-])(\d+))?/)) { // an+b - if (mm[1] == "-") mm[1] = -1; - var a = mm[1] ? Number(mm[1]) : 1; - var b = mm[2] ? Number(mm[2]) : 0; - predicate = "[((#{fragment} - #{b}) mod #{a} = 0) and " + - "((#{fragment} - #{b}) div #{a} >= 0)]"; - return new Template(predicate).evaluate({ - fragment: fragment, a: a, b: b }); - } - } - } - }, - - criteria: { - tagName: 'n = h.tagName(n, r, "#{1}", c); c = false;', - className: 'n = h.className(n, r, "#{1}", c); c = false;', - id: 'n = h.id(n, r, "#{1}", c); c = false;', - attrPresence: 'n = h.attrPresence(n, r, "#{1}", c); c = false;', - attr: function(m) { - m[3] = (m[5] || m[6]); - return new Template('n = h.attr(n, r, "#{1}", "#{3}", "#{2}", c); c = false;').evaluate(m); - }, - pseudo: function(m) { - if (m[6]) m[6] = m[6].replace(/"/g, '\\"'); - return new Template('n = h.pseudo(n, "#{1}", "#{6}", r, c); c = false;').evaluate(m); - }, - descendant: 'c = "descendant";', - child: 'c = "child";', - adjacent: 'c = "adjacent";', - laterSibling: 'c = "laterSibling";' - }, - - patterns: { - // combinators must be listed first - // (and descendant needs to be last combinator) - laterSibling: /^\s*~\s*/, - child: /^\s*>\s*/, - adjacent: /^\s*\+\s*/, - descendant: /^\s/, - - // selectors follow - tagName: /^\s*(\*|[\w\-]+)(\b|$)?/, - id: /^#([\w\-\*]+)(\b|$)/, - className: /^\.([\w\-\*]+)(\b|$)/, - pseudo: -/^:((first|last|nth|nth-last|only)(-child|-of-type)|empty|checked|(en|dis)abled|not)(\((.*?)\))?(\b|$|(?=\s|[:+~>]))/, - attrPresence: /^\[([\w]+)\]/, - attr: /\[((?:[\w-]*:)?[\w-]+)\s*(?:([!^$*~|]?=)\s*((['"])([^\4]*?)\4|([^'"][^\]]*?)))?\]/ - }, - - // for Selector.match and Element#match - assertions: { - tagName: function(element, matches) { - return matches[1].toUpperCase() == element.tagName.toUpperCase(); - }, - - className: function(element, matches) { - return Element.hasClassName(element, matches[1]); - }, - - id: function(element, matches) { - return element.id === matches[1]; - }, - - attrPresence: function(element, matches) { - return Element.hasAttribute(element, matches[1]); - }, - - attr: function(element, matches) { - var nodeValue = Element.readAttribute(element, matches[1]); - return nodeValue && Selector.operators[matches[2]](nodeValue, matches[5] || matches[6]); - } - }, - - handlers: { - // UTILITY FUNCTIONS - // joins two collections - concat: function(a, b) { - for (var i = 0, node; node = b[i]; i++) - a.push(node); - return a; - }, - - // marks an array of nodes for counting - mark: function(nodes) { - var _true = Prototype.emptyFunction; - for (var i = 0, node; node = nodes[i]; i++) - node._countedByPrototype = _true; - return nodes; - }, - - unmark: function(nodes) { - for (var i = 0, node; node = nodes[i]; i++) - node._countedByPrototype = undefined; - return nodes; - }, - - // mark each child node with its position (for nth calls) - // "ofType" flag indicates whether we're indexing for nth-of-type - // rather than nth-child - index: function(parentNode, reverse, ofType) { - parentNode._countedByPrototype = Prototype.emptyFunction; - if (reverse) { - for (var nodes = parentNode.childNodes, i = nodes.length - 1, j = 1; i >= 0; i--) { - var node = nodes[i]; - if (node.nodeType == 1 && (!ofType || node._countedByPrototype)) node.nodeIndex = j++; - } - } else { - for (var i = 0, j = 1, nodes = parentNode.childNodes; node = nodes[i]; i++) - if (node.nodeType == 1 && (!ofType || node._countedByPrototype)) node.nodeIndex = j++; - } - }, - - // filters out duplicates and extends all nodes - unique: function(nodes) { - if (nodes.length == 0) return nodes; - var results = [], n; - for (var i = 0, l = nodes.length; i < l; i++) - if (!(n = nodes[i])._countedByPrototype) { - n._countedByPrototype = Prototype.emptyFunction; - results.push(Element.extend(n)); - } - return Selector.handlers.unmark(results); - }, - - // COMBINATOR FUNCTIONS - descendant: function(nodes) { - var h = Selector.handlers; - for (var i = 0, results = [], node; node = nodes[i]; i++) - h.concat(results, node.getElementsByTagName('*')); - return results; - }, - - child: function(nodes) { - var h = Selector.handlers; - for (var i = 0, results = [], node; node = nodes[i]; i++) { - for (var j = 0, child; child = node.childNodes[j]; j++) - if (child.nodeType == 1 && child.tagName != '!') results.push(child); - } - return results; - }, - - adjacent: function(nodes) { - for (var i = 0, results = [], node; node = nodes[i]; i++) { - var next = this.nextElementSibling(node); - if (next) results.push(next); - } - return results; - }, - - laterSibling: function(nodes) { - var h = Selector.handlers; - for (var i = 0, results = [], node; node = nodes[i]; i++) - h.concat(results, Element.nextSiblings(node)); - return results; - }, - - nextElementSibling: function(node) { - while (node = node.nextSibling) - if (node.nodeType == 1) return node; - return null; - }, - - previousElementSibling: function(node) { - while (node = node.previousSibling) - if (node.nodeType == 1) return node; - return null; - }, - - // TOKEN FUNCTIONS - tagName: function(nodes, root, tagName, combinator) { - var uTagName = tagName.toUpperCase(); - var results = [], h = Selector.handlers; - if (nodes) { - if (combinator) { - // fastlane for ordinary descendant combinators - if (combinator == "descendant") { - for (var i = 0, node; node = nodes[i]; i++) - h.concat(results, node.getElementsByTagName(tagName)); - return results; - } else nodes = this[combinator](nodes); - if (tagName == "*") return nodes; - } - for (var i = 0, node; node = nodes[i]; i++) - if (node.tagName.toUpperCase() === uTagName) results.push(node); - return results; - } else return root.getElementsByTagName(tagName); - }, - - id: function(nodes, root, id, combinator) { - var targetNode = $(id), h = Selector.handlers; - if (!targetNode) return []; - if (!nodes && root == document) return [targetNode]; - if (nodes) { - if (combinator) { - if (combinator == 'child') { - for (var i = 0, node; node = nodes[i]; i++) - if (targetNode.parentNode == node) return [targetNode]; - } else if (combinator == 'descendant') { - for (var i = 0, node; node = nodes[i]; i++) - if (Element.descendantOf(targetNode, node)) return [targetNode]; - } else if (combinator == 'adjacent') { - for (var i = 0, node; node = nodes[i]; i++) - if (Selector.handlers.previousElementSibling(targetNode) == node) - return [targetNode]; - } else nodes = h[combinator](nodes); - } - for (var i = 0, node; node = nodes[i]; i++) - if (node == targetNode) return [targetNode]; - return []; - } - return (targetNode && Element.descendantOf(targetNode, root)) ? [targetNode] : []; - }, - - className: function(nodes, root, className, combinator) { - if (nodes && combinator) nodes = this[combinator](nodes); - return Selector.handlers.byClassName(nodes, root, className); - }, - - byClassName: function(nodes, root, className) { - if (!nodes) nodes = Selector.handlers.descendant([root]); - var needle = ' ' + className + ' '; - for (var i = 0, results = [], node, nodeClassName; node = nodes[i]; i++) { - nodeClassName = node.className; - if (nodeClassName.length == 0) continue; - if (nodeClassName == className || (' ' + nodeClassName + ' ').include(needle)) - results.push(node); - } - return results; - }, - - attrPresence: function(nodes, root, attr, combinator) { - if (!nodes) nodes = root.getElementsByTagName("*"); - if (nodes && combinator) nodes = this[combinator](nodes); - var results = []; - for (var i = 0, node; node = nodes[i]; i++) - if (Element.hasAttribute(node, attr)) results.push(node); - return results; - }, - - attr: function(nodes, root, attr, value, operator, combinator) { - if (!nodes) nodes = root.getElementsByTagName("*"); - if (nodes && combinator) nodes = this[combinator](nodes); - var handler = Selector.operators[operator], results = []; - for (var i = 0, node; node = nodes[i]; i++) { - var nodeValue = Element.readAttribute(node, attr); - if (nodeValue === null) continue; - if (handler(nodeValue, value)) results.push(node); - } - return results; - }, - - pseudo: function(nodes, name, value, root, combinator) { - if (nodes && combinator) nodes = this[combinator](nodes); - if (!nodes) nodes = root.getElementsByTagName("*"); - return Selector.pseudos[name](nodes, value, root); - } - }, - - pseudos: { - 'first-child': function(nodes, value, root) { - for (var i = 0, results = [], node; node = nodes[i]; i++) { - if (Selector.handlers.previousElementSibling(node)) continue; - results.push(node); - } - return results; - }, - 'last-child': function(nodes, value, root) { - for (var i = 0, results = [], node; node = nodes[i]; i++) { - if (Selector.handlers.nextElementSibling(node)) continue; - results.push(node); - } - return results; - }, - 'only-child': function(nodes, value, root) { - var h = Selector.handlers; - for (var i = 0, results = [], node; node = nodes[i]; i++) - if (!h.previousElementSibling(node) && !h.nextElementSibling(node)) - results.push(node); - return results; - }, - 'nth-child': function(nodes, formula, root) { - return Selector.pseudos.nth(nodes, formula, root); - }, - 'nth-last-child': function(nodes, formula, root) { - return Selector.pseudos.nth(nodes, formula, root, true); - }, - 'nth-of-type': function(nodes, formula, root) { - return Selector.pseudos.nth(nodes, formula, root, false, true); - }, - 'nth-last-of-type': function(nodes, formula, root) { - return Selector.pseudos.nth(nodes, formula, root, true, true); - }, - 'first-of-type': function(nodes, formula, root) { - return Selector.pseudos.nth(nodes, "1", root, false, true); - }, - 'last-of-type': function(nodes, formula, root) { - return Selector.pseudos.nth(nodes, "1", root, true, true); - }, - 'only-of-type': function(nodes, formula, root) { - var p = Selector.pseudos; - return p['last-of-type'](p['first-of-type'](nodes, formula, root), formula, root); - }, - - // handles the an+b logic - getIndices: function(a, b, total) { - if (a == 0) return b > 0 ? [b] : []; - return $R(1, total).inject([], function(memo, i) { - if (0 == (i - b) % a && (i - b) / a >= 0) memo.push(i); - return memo; - }); - }, - - // handles nth(-last)-child, nth(-last)-of-type, and (first|last)-of-type - nth: function(nodes, formula, root, reverse, ofType) { - if (nodes.length == 0) return []; - if (formula == 'even') formula = '2n+0'; - if (formula == 'odd') formula = '2n+1'; - var h = Selector.handlers, results = [], indexed = [], m; - h.mark(nodes); - for (var i = 0, node; node = nodes[i]; i++) { - if (!node.parentNode._countedByPrototype) { - h.index(node.parentNode, reverse, ofType); - indexed.push(node.parentNode); - } - } - if (formula.match(/^\d+$/)) { // just a number - formula = Number(formula); - for (var i = 0, node; node = nodes[i]; i++) - if (node.nodeIndex == formula) results.push(node); - } else if (m = formula.match(/^(-?\d*)?n(([+-])(\d+))?/)) { // an+b - if (m[1] == "-") m[1] = -1; - var a = m[1] ? Number(m[1]) : 1; - var b = m[2] ? Number(m[2]) : 0; - var indices = Selector.pseudos.getIndices(a, b, nodes.length); - for (var i = 0, node, l = indices.length; node = nodes[i]; i++) { - for (var j = 0; j < l; j++) - if (node.nodeIndex == indices[j]) results.push(node); - } - } - h.unmark(nodes); - h.unmark(indexed); - return results; - }, - - 'empty': function(nodes, value, root) { - for (var i = 0, results = [], node; node = nodes[i]; i++) { - // IE treats comments as element nodes - if (node.tagName == '!' || (node.firstChild && !node.innerHTML.match(/^\s*$/))) continue; - results.push(node); - } - return results; - }, - - 'not': function(nodes, selector, root) { - var h = Selector.handlers, selectorType, m; - var exclusions = new Selector(selector).findElements(root); - h.mark(exclusions); - for (var i = 0, results = [], node; node = nodes[i]; i++) - if (!node._countedByPrototype) results.push(node); - h.unmark(exclusions); - return results; - }, - - 'enabled': function(nodes, value, root) { - for (var i = 0, results = [], node; node = nodes[i]; i++) - if (!node.disabled) results.push(node); - return results; - }, - - 'disabled': function(nodes, value, root) { - for (var i = 0, results = [], node; node = nodes[i]; i++) - if (node.disabled) results.push(node); - return results; - }, - - 'checked': function(nodes, value, root) { - for (var i = 0, results = [], node; node = nodes[i]; i++) - if (node.checked) results.push(node); - return results; - } - }, - - operators: { - '=': function(nv, v) { return nv == v; }, - '!=': function(nv, v) { return nv != v; }, - '^=': function(nv, v) { return nv.startsWith(v); }, - '$=': function(nv, v) { return nv.endsWith(v); }, - '*=': function(nv, v) { return nv.include(v); }, - '~=': function(nv, v) { return (' ' + nv + ' ').include(' ' + v + ' '); }, - '|=': function(nv, v) { return ('-' + nv.toUpperCase() + '-').include('-' + v.toUpperCase() + '-'); } - }, - - split: function(expression) { - var expressions = []; - expression.scan(/(([\w#:.~>+()\s-]+|\*|\[.*?\])+)\s*(,|$)/, function(m) { - expressions.push(m[1].strip()); - }); - return expressions; - }, - - matchElements: function(elements, expression) { - var matches = $$(expression), h = Selector.handlers; - h.mark(matches); - for (var i = 0, results = [], element; element = elements[i]; i++) - if (element._countedByPrototype) results.push(element); - h.unmark(matches); - return results; - }, - - findElement: function(elements, expression, index) { - if (Object.isNumber(expression)) { - index = expression; expression = false; - } - return Selector.matchElements(elements, expression || '*')[index || 0]; - }, - - findChildElements: function(element, expressions) { - expressions = Selector.split(expressions.join(',')); - var results = [], h = Selector.handlers; - for (var i = 0, l = expressions.length, selector; i < l; i++) { - selector = new Selector(expressions[i].strip()); - h.concat(results, selector.findElements(element)); - } - return (l > 1) ? h.unique(results) : results; - } -}); - -if (Prototype.Browser.IE) { - Object.extend(Selector.handlers, { - // IE returns comment nodes on getElementsByTagName("*"). - // Filter them out. - concat: function(a, b) { - for (var i = 0, node; node = b[i]; i++) - if (node.tagName !== "!") a.push(node); - return a; - }, - - // IE improperly serializes _countedByPrototype in (inner|outer)HTML. - unmark: function(nodes) { - for (var i = 0, node; node = nodes[i]; i++) - node.removeAttribute('_countedByPrototype'); - return nodes; - } - }); -} - -function $$() { - return Selector.findChildElements(document, $A(arguments)); -} -var Form = { - reset: function(form) { - $(form).reset(); - return form; - }, - - serializeElements: function(elements, options) { - if (typeof options != 'object') options = { hash: !!options }; - else if (Object.isUndefined(options.hash)) options.hash = true; - var key, value, submitted = false, submit = options.submit; - - var data = elements.inject({ }, function(result, element) { - if (!element.disabled && element.name) { - key = element.name; value = $(element).getValue(); - if (value != null && (element.type != 'submit' || (!submitted && - submit !== false && (!submit || key == submit) && (submitted = true)))) { - if (key in result) { - // a key is already present; construct an array of values - if (!Object.isArray(result[key])) result[key] = [result[key]]; - result[key].push(value); - } - else result[key] = value; - } - } - return result; - }); - - return options.hash ? data : Object.toQueryString(data); - } -}; - -Form.Methods = { - serialize: function(form, options) { - return Form.serializeElements(Form.getElements(form), options); - }, - - getElements: function(form) { - return $A($(form).getElementsByTagName('*')).inject([], - function(elements, child) { - if (Form.Element.Serializers[child.tagName.toLowerCase()]) - elements.push(Element.extend(child)); - return elements; - } - ); - }, - - getInputs: function(form, typeName, name) { - form = $(form); - var inputs = form.getElementsByTagName('input'); - - if (!typeName && !name) return $A(inputs).map(Element.extend); - - for (var i = 0, matchingInputs = [], length = inputs.length; i < length; i++) { - var input = inputs[i]; - if ((typeName && input.type != typeName) || (name && input.name != name)) - continue; - matchingInputs.push(Element.extend(input)); - } - - return matchingInputs; - }, - - disable: function(form) { - form = $(form); - Form.getElements(form).invoke('disable'); - return form; - }, - - enable: function(form) { - form = $(form); - Form.getElements(form).invoke('enable'); - return form; - }, - - findFirstElement: function(form) { - var elements = $(form).getElements().findAll(function(element) { - return 'hidden' != element.type && !element.disabled; - }); - var firstByIndex = elements.findAll(function(element) { - return element.hasAttribute('tabIndex') && element.tabIndex >= 0; - }).sortBy(function(element) { return element.tabIndex }).first(); - - return firstByIndex ? firstByIndex : elements.find(function(element) { - return ['input', 'select', 'textarea'].include(element.tagName.toLowerCase()); - }); - }, - - focusFirstElement: function(form) { - form = $(form); - form.findFirstElement().activate(); - return form; - }, - - request: function(form, options) { - form = $(form), options = Object.clone(options || { }); - - var params = options.parameters, action = form.readAttribute('action') || ''; - if (action.blank()) action = window.location.href; - options.parameters = form.serialize(true); - - if (params) { - if (Object.isString(params)) params = params.toQueryParams(); - Object.extend(options.parameters, params); - } - - if (form.hasAttribute('method') && !options.method) - options.method = form.method; - - return new Ajax.Request(action, options); - } -}; - -/*--------------------------------------------------------------------------*/ - -Form.Element = { - focus: function(element) { - $(element).focus(); - return element; - }, - - select: function(element) { - $(element).select(); - return element; - } -}; - -Form.Element.Methods = { - serialize: function(element) { - element = $(element); - if (!element.disabled && element.name) { - var value = element.getValue(); - if (value != undefined) { - var pair = { }; - pair[element.name] = value; - return Object.toQueryString(pair); - } - } - return ''; - }, - - getValue: function(element) { - element = $(element); - var method = element.tagName.toLowerCase(); - return Form.Element.Serializers[method](element); - }, - - setValue: function(element, value) { - element = $(element); - var method = element.tagName.toLowerCase(); - Form.Element.Serializers[method](element, value); - return element; - }, - - clear: function(element) { - $(element).value = ''; - return element; - }, - - present: function(element) { - return $(element).value != ''; - }, - - activate: function(element) { - element = $(element); - try { - element.focus(); - if (element.select && (element.tagName.toLowerCase() != 'input' || - !['button', 'reset', 'submit'].include(element.type))) - element.select(); - } catch (e) { } - return element; - }, - - disable: function(element) { - element = $(element); - element.blur(); - element.disabled = true; - return element; - }, - - enable: function(element) { - element = $(element); - element.disabled = false; - return element; - } -}; - -/*--------------------------------------------------------------------------*/ - -var Field = Form.Element; -var $F = Form.Element.Methods.getValue; - -/*--------------------------------------------------------------------------*/ - -Form.Element.Serializers = { - input: function(element, value) { - switch (element.type.toLowerCase()) { - case 'checkbox': - case 'radio': - return Form.Element.Serializers.inputSelector(element, value); - default: - return Form.Element.Serializers.textarea(element, value); - } - }, - - inputSelector: function(element, value) { - if (Object.isUndefined(value)) return element.checked ? element.value : null; - else element.checked = !!value; - }, - - textarea: function(element, value) { - if (Object.isUndefined(value)) return element.value; - else element.value = value; - }, - - select: function(element, index) { - if (Object.isUndefined(index)) - return this[element.type == 'select-one' ? - 'selectOne' : 'selectMany'](element); - else { - var opt, value, single = !Object.isArray(index); - for (var i = 0, length = element.length; i < length; i++) { - opt = element.options[i]; - value = this.optionValue(opt); - if (single) { - if (value == index) { - opt.selected = true; - return; - } - } - else opt.selected = index.include(value); - } - } - }, - - selectOne: function(element) { - var index = element.selectedIndex; - return index >= 0 ? this.optionValue(element.options[index]) : null; - }, - - selectMany: function(element) { - var values, length = element.length; - if (!length) return null; - - for (var i = 0, values = []; i < length; i++) { - var opt = element.options[i]; - if (opt.selected) values.push(this.optionValue(opt)); - } - return values; - }, - - optionValue: function(opt) { - // extend element because hasAttribute may not be native - return Element.extend(opt).hasAttribute('value') ? opt.value : opt.text; - } -}; - -/*--------------------------------------------------------------------------*/ - -Abstract.TimedObserver = Class.create(PeriodicalExecuter, { - initialize: function($super, element, frequency, callback) { - $super(callback, frequency); - this.element = $(element); - this.lastValue = this.getValue(); - }, - - execute: function() { - var value = this.getValue(); - if (Object.isString(this.lastValue) && Object.isString(value) ? - this.lastValue != value : String(this.lastValue) != String(value)) { - this.callback(this.element, value); - this.lastValue = value; - } - } -}); - -Form.Element.Observer = Class.create(Abstract.TimedObserver, { - getValue: function() { - return Form.Element.getValue(this.element); - } -}); - -Form.Observer = Class.create(Abstract.TimedObserver, { - getValue: function() { - return Form.serialize(this.element); - } -}); - -/*--------------------------------------------------------------------------*/ - -Abstract.EventObserver = Class.create({ - initialize: function(element, callback) { - this.element = $(element); - this.callback = callback; - - this.lastValue = this.getValue(); - if (this.element.tagName.toLowerCase() == 'form') - this.registerFormCallbacks(); - else - this.registerCallback(this.element); - }, - - onElementEvent: function() { - var value = this.getValue(); - if (this.lastValue != value) { - this.callback(this.element, value); - this.lastValue = value; - } - }, - - registerFormCallbacks: function() { - Form.getElements(this.element).each(this.registerCallback, this); - }, - - registerCallback: function(element) { - if (element.type) { - switch (element.type.toLowerCase()) { - case 'checkbox': - case 'radio': - Event.observe(element, 'click', this.onElementEvent.bind(this)); - break; - default: - Event.observe(element, 'change', this.onElementEvent.bind(this)); - break; - } - } - } -}); - -Form.Element.EventObserver = Class.create(Abstract.EventObserver, { - getValue: function() { - return Form.Element.getValue(this.element); - } -}); - -Form.EventObserver = Class.create(Abstract.EventObserver, { - getValue: function() { - return Form.serialize(this.element); - } -}); -if (!window.Event) var Event = { }; - -Object.extend(Event, { - KEY_BACKSPACE: 8, - KEY_TAB: 9, - KEY_RETURN: 13, - KEY_ESC: 27, - KEY_LEFT: 37, - KEY_UP: 38, - KEY_RIGHT: 39, - KEY_DOWN: 40, - KEY_DELETE: 46, - KEY_HOME: 36, - KEY_END: 35, - KEY_PAGEUP: 33, - KEY_PAGEDOWN: 34, - KEY_INSERT: 45, - - cache: { }, - - relatedTarget: function(event) { - var element; - switch(event.type) { - case 'mouseover': element = event.fromElement; break; - case 'mouseout': element = event.toElement; break; - default: return null; - } - return Element.extend(element); - } -}); - -Event.Methods = (function() { - var isButton; - - if (Prototype.Browser.IE) { - var buttonMap = { 0: 1, 1: 4, 2: 2 }; - isButton = function(event, code) { - return event.button == buttonMap[code]; - }; - - } else if (Prototype.Browser.WebKit) { - isButton = function(event, code) { - switch (code) { - case 0: return event.which == 1 && !event.metaKey; - case 1: return event.which == 1 && event.metaKey; - default: return false; - } - }; - - } else { - isButton = function(event, code) { - return event.which ? (event.which === code + 1) : (event.button === code); - }; - } - - return { - isLeftClick: function(event) { return isButton(event, 0) }, - isMiddleClick: function(event) { return isButton(event, 1) }, - isRightClick: function(event) { return isButton(event, 2) }, - - element: function(event) { - var node = Event.extend(event).target; - return Element.extend(node.nodeType == Node.TEXT_NODE ? node.parentNode : node); - }, - - findElement: function(event, expression) { - var element = Event.element(event); - if (!expression) return element; - var elements = [element].concat(element.ancestors()); - return Selector.findElement(elements, expression, 0); - }, - - pointer: function(event) { - return { - x: event.pageX || (event.clientX + - (document.documentElement.scrollLeft || document.body.scrollLeft)), - y: event.pageY || (event.clientY + - (document.documentElement.scrollTop || document.body.scrollTop)) - }; - }, - - pointerX: function(event) { return Event.pointer(event).x }, - pointerY: function(event) { return Event.pointer(event).y }, - - stop: function(event) { - Event.extend(event); - event.preventDefault(); - event.stopPropagation(); - event.stopped = true; - } - }; -})(); - -Event.extend = (function() { - var methods = Object.keys(Event.Methods).inject({ }, function(m, name) { - m[name] = Event.Methods[name].methodize(); - return m; - }); - - if (Prototype.Browser.IE) { - Object.extend(methods, { - stopPropagation: function() { this.cancelBubble = true }, - preventDefault: function() { this.returnValue = false }, - inspect: function() { return "[object Event]" } - }); - - return function(event) { - if (!event) return false; - if (event._extendedByPrototype) return event; - - event._extendedByPrototype = Prototype.emptyFunction; - var pointer = Event.pointer(event); - Object.extend(event, { - target: event.srcElement, - relatedTarget: Event.relatedTarget(event), - pageX: pointer.x, - pageY: pointer.y - }); - return Object.extend(event, methods); - }; - - } else { - Event.prototype = Event.prototype || document.createEvent("HTMLEvents").__proto__; - Object.extend(Event.prototype, methods); - return Prototype.K; - } -})(); - -Object.extend(Event, (function() { - var cache = Event.cache; - - function getEventID(element) { - if (element._prototypeEventID) return element._prototypeEventID[0]; - arguments.callee.id = arguments.callee.id || 1; - return element._prototypeEventID = [++arguments.callee.id]; - } - - function getDOMEventName(eventName) { - if (eventName && eventName.include(':')) return "dataavailable"; - return eventName; - } - - function getCacheForID(id) { - return cache[id] = cache[id] || { }; - } - - function getWrappersForEventName(id, eventName) { - var c = getCacheForID(id); - return c[eventName] = c[eventName] || []; - } - - function createWrapper(element, eventName, handler) { - var id = getEventID(element); - var c = getWrappersForEventName(id, eventName); - if (c.pluck("handler").include(handler)) return false; - - var wrapper = function(event) { - if (!Event || !Event.extend || - (event.eventName && event.eventName != eventName)) - return false; - - Event.extend(event); - handler.call(element, event); - }; - - wrapper.handler = handler; - c.push(wrapper); - return wrapper; - } - - function findWrapper(id, eventName, handler) { - var c = getWrappersForEventName(id, eventName); - return c.find(function(wrapper) { return wrapper.handler == handler }); - } - - function destroyWrapper(id, eventName, handler) { - var c = getCacheForID(id); - if (!c[eventName]) return false; - c[eventName] = c[eventName].without(findWrapper(id, eventName, handler)); - } - - function destroyCache() { - for (var id in cache) - for (var eventName in cache[id]) - cache[id][eventName] = null; - } - - if (window.attachEvent) { - window.attachEvent("onunload", destroyCache); - } - - return { - observe: function(element, eventName, handler) { - element = $(element); - var name = getDOMEventName(eventName); - - var wrapper = createWrapper(element, eventName, handler); - if (!wrapper) return element; - - if (element.addEventListener) { - element.addEventListener(name, wrapper, false); - } else { - element.attachEvent("on" + name, wrapper); - } - - return element; - }, - - stopObserving: function(element, eventName, handler) { - element = $(element); - var id = getEventID(element), name = getDOMEventName(eventName); - - if (!handler && eventName) { - getWrappersForEventName(id, eventName).each(function(wrapper) { - element.stopObserving(eventName, wrapper.handler); - }); - return element; - - } else if (!eventName) { - Object.keys(getCacheForID(id)).each(function(eventName) { - element.stopObserving(eventName); - }); - return element; - } - - var wrapper = findWrapper(id, eventName, handler); - if (!wrapper) return element; - - if (element.removeEventListener) { - element.removeEventListener(name, wrapper, false); - } else { - element.detachEvent("on" + name, wrapper); - } - - destroyWrapper(id, eventName, handler); - - return element; - }, - - fire: function(element, eventName, memo) { - element = $(element); - if (element == document && document.createEvent && !element.dispatchEvent) - element = document.documentElement; - - var event; - if (document.createEvent) { - event = document.createEvent("HTMLEvents"); - event.initEvent("dataavailable", true, true); - } else { - event = document.createEventObject(); - event.eventType = "ondataavailable"; - } - - event.eventName = eventName; - event.memo = memo || { }; - - if (document.createEvent) { - element.dispatchEvent(event); - } else { - element.fireEvent(event.eventType, event); - } - - return Event.extend(event); - } - }; -})()); - -Object.extend(Event, Event.Methods); - -Element.addMethods({ - fire: Event.fire, - observe: Event.observe, - stopObserving: Event.stopObserving -}); - -Object.extend(document, { - fire: Element.Methods.fire.methodize(), - observe: Element.Methods.observe.methodize(), - stopObserving: Element.Methods.stopObserving.methodize(), - loaded: false -}); - -(function() { - /* Support for the DOMContentLoaded event is based on work by Dan Webb, - Matthias Miller, Dean Edwards and John Resig. */ - - var timer; - - function fireContentLoadedEvent() { - if (document.loaded) return; - if (timer) window.clearInterval(timer); - document.fire("dom:loaded"); - document.loaded = true; - } - - if (document.addEventListener) { - if (Prototype.Browser.WebKit) { - timer = window.setInterval(function() { - if (/loaded|complete/.test(document.readyState)) - fireContentLoadedEvent(); - }, 0); - - Event.observe(window, "load", fireContentLoadedEvent); - - } else { - document.addEventListener("DOMContentLoaded", - fireContentLoadedEvent, false); - } - - } else { - document.write(" - - - - -
    - - -
    -
    -

    WysiHat JavaScript framework

    -
    -
    - - - -
    - - <%= @content_for_layout %> - -
    - -
    - - - diff --git a/website/namespace.erb b/website/namespace.erb deleted file mode 100644 index 7ab6306..0000000 --- a/website/namespace.erb +++ /dev/null @@ -1,102 +0,0 @@ -<% d = @doc_instance %> - -<% @title = "#{d.full_name} (#{d.type})" %> - -

    - <%= d.type %> - <%= d.full_name %> -

    - - -<% # Is it a CLASS? %> -<% if @doc_instance.is_a?(Documentation::Klass) %> - - <% if @doc_instance.superklass %> -
    -

    Superclass

    -

    <%= auto_link(d.superklass, false) %>

    -
    - <% end %> - - <% unless @doc_instance.subklasses.empty? %> -
    -

    Subclasses

    -

    <%= d.subklasses.map { |s| auto_link(s, false) }.join(', ') %>

    -
    - <% end %> - -<% end %> - -<% # Does it have MIXINS? %> -<% unless @doc_instance.mixins.empty? %> -
    -

    Includes

    -

    <%= d.mixins.map { |m| auto_link(m, false) }.join(', ') %>

    -
    -<% end %> - -<% # Are there related utilities? %> -<% unless @doc_instance.related_utilities.empty? %> -
    -

    Related utilities

    -

    <%= d.related_utilities.map { |u| auto_link(u, false) }.join(', ') %>

    -
    -<% end %> - -
    - <% unless d.all_methods.empty? && d.mixins.empty? %> -

    Methods

    - -
      - <% d.all_methods.each do |method| %> -
    • <%= auto_link(method, true, :class => class_names_for(method)) %>
    • - <% end %> -
    - - <% unless @doc_instance.mixins.empty? %> - <% d.mixins.each do |mixin| %> -

    Inherited from <%= auto_link(mixin) %>

    -
      - <% mixin.all_methods.each do |method| %> -
    • <%= auto_link(method, true, :class => class_names_for(method)) %>
    • - <% end %> -
    - <% end %> - <% end %> - <% end %> -
    - -<% if d.is_a?(Documentation::Klass) && d.constructor %> -
    -

    Constructor

    -

    -

    <%= d.constructor.ebnf_expressions %>
    -

    - -

    <%= htmlize(d.constructor.description) %>

    -
    -<% end %> - - -<% -types = { - :constants => "Constants", - :klass_methods => "Class methods", - :klass_properties => "Class properties", - :instance_methods => "Instance methods", - :instance_properties => "Instance properties" -} -%> - -<% types.each do |method, title| %> - <% methods = d.send(method) %> - <% unless methods.empty? %> -
    -

    <%= title %>

    -
      - <%= include "partials/short_description", :collection => methods %> -
    -
    - <% end %> -<% end %> - diff --git a/website/partials/short_description.erb b/website/partials/short_description.erb deleted file mode 100644 index a65b372..0000000 --- a/website/partials/short_description.erb +++ /dev/null @@ -1,28 +0,0 @@ -
  • <%= object.name %>

    - - <% if object.is_a?(Documentation::Method) %> - <%= method_synopsis(object) %> - <% unless object.arguments.empty? %> -
      - <% object.arguments.each do |arg| %> -
    • - <%= arg.name %> - <% unless arg.types.empty? %> - (<%= arg.types.map { |t| auto_link(t, false) }.join(' | ') %>) - <% end %> - <%= ' – ' + arg.description unless arg.description.empty? %> -
    • - <% end %> -
    - <% end %> - <% end %> - - <% unless object.aliases.empty? %> -

    Aliased as: <%= object.aliases.map { |a| auto_link(a, false) }.join(', ') %>

    - <% end %> - <% if object.alias? %> -

    Alias for <%= auto_link(object.alias_of, false) %>

    - <% end %> - - <%= htmlize(object.description) %> -
  • diff --git a/website/section.erb b/website/section.erb deleted file mode 100644 index ab2f7cc..0000000 --- a/website/section.erb +++ /dev/null @@ -1,4 +0,0 @@ -<% @title = "#{@doc_instance.full_name} section" %> - -

    <%= @doc_instance.name %>

    - diff --git a/website/utility.erb b/website/utility.erb deleted file mode 100644 index 25b9804..0000000 --- a/website/utility.erb +++ /dev/null @@ -1,13 +0,0 @@ -<% d = @doc_instance %> - -<% @title = "#{@doc_instance.full_name} utility" %> - -

    utility <%= @doc_instance.name %>

    - -<% # What's it related to? %> -<% unless @doc_instance.related_to.nil? %> - -<% end %> \ No newline at end of file From cde33db84ee4e791222769fc917a240e81e5dfcf Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Wed, 3 Feb 2010 14:27:49 -0600 Subject: [PATCH 027/164] draft 0.2.1 release notes --- CHANGELOG | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/CHANGELOG b/CHANGELOG index 926e829..f5baf42 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,11 @@ +*0.2.1* + +* Added Editor#queryValueCommandState for testing custom query commands. + +* Added execCommand delegates for fontSelection, fontSizeSelection, + backgroundColorSelection, backgroundColorSelected, alignSelection, + and alignSelected. + *0.2* (March 29, 2009) * For performance reasons, automatic textarea saving is now disabled by From 7267e5885f01d8ff027dc40ceb20b41af3684d85 Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Wed, 3 Feb 2010 14:30:05 -0600 Subject: [PATCH 028/164] untabify --- src/wysihat/editor/commands.js | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/wysihat/editor/commands.js b/src/wysihat/editor/commands.js index 583390e..41f0bce 100644 --- a/src/wysihat/editor/commands.js +++ b/src/wysihat/editor/commands.js @@ -289,15 +289,15 @@ WysiHat.Commands = (function() { * Fetches the styles (from the styleSelectors hash) from the current * selection and returns it as a hash **/ - function getSelectedStyles() { - var styles = $H({}); - var editor = this; - editor.styleSelectors.each(function(style){ - var node = editor.selection.getNode(); + function getSelectedStyles() { + var styles = $H({}); + var editor = this; + editor.styleSelectors.each(function(style){ + var node = editor.selection.getNode(); styles.set(style.first(), Element.getStyle(node, style.last())); - }); - return styles; - } + }); + return styles; + } return { boldSelection: boldSelection, From 2f7761f740a3992ca7a94eb0a62924ef2cd0a821 Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Wed, 3 Feb 2010 14:32:52 -0600 Subject: [PATCH 029/164] 0.2.1 release --- CHANGELOG | 2 +- src/wysihat.js | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index f5baf42..e628f56 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,4 +1,4 @@ -*0.2.1* +*0.2.1* (February 3, 2010) * Added Editor#queryValueCommandState for testing custom query commands. diff --git a/src/wysihat.js b/src/wysihat.js index fe7eb11..7bc741d 100644 --- a/src/wysihat.js +++ b/src/wysihat.js @@ -1,5 +1,5 @@ -/* WysiHat - WYSIWYG JavaScript framework, version 0.2 - * (c) 2008-2009 Joshua Peek +/* WysiHat - WYSIWYG JavaScript framework, version 0.2.1 + * (c) 2008-2010 Joshua Peek * * WysiHat is freely distributable under the terms of an MIT-style license. *--------------------------------------------------------------------------*/ From a139d9df7c00bd31d77f242d1283dbb8756d4e68 Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Wed, 3 Feb 2010 15:22:06 -0600 Subject: [PATCH 030/164] dump iframe support --- src/wysihat.js | 1 - src/wysihat/dom/selection.js | 23 +++-- src/wysihat/editor.js | 38 ++++---- src/wysihat/editor/commands.js | 4 - src/wysihat/editor/events.js | 41 ++++----- src/wysihat/editor/persistence.js | 24 ++++-- src/wysihat/editor/window.js | 64 -------------- src/wysihat/models.js | 2 - src/wysihat/models/editable.js | 44 ---------- src/wysihat/models/iframe.js | 138 ------------------------------ test/editor.html | 8 ++ 11 files changed, 73 insertions(+), 314 deletions(-) delete mode 100644 src/wysihat/editor/window.js delete mode 100644 src/wysihat/models.js delete mode 100644 src/wysihat/models/editable.js delete mode 100644 src/wysihat/models/iframe.js diff --git a/src/wysihat.js b/src/wysihat.js index 7bc741d..2f54389 100644 --- a/src/wysihat.js +++ b/src/wysihat.js @@ -14,7 +14,6 @@ var WysiHat = {}; //= require "wysihat/editor" -//= require "wysihat/models" //= require "wysihat/lang" //= require "wysihat/dom" //= require "wysihat/toolbar" diff --git a/src/wysihat/dom/selection.js b/src/wysihat/dom/selection.js index ca0c2ab..97fa934 100644 --- a/src/wysihat/dom/selection.js +++ b/src/wysihat/dom/selection.js @@ -7,9 +7,6 @@ WysiHat.Selection = Class.create((function() { * - editor (WysiHat.Editor): the editor object that you want to bind to **/ function initialize(editor) { - this.window = editor.getWindow(); - this.document = editor.getDocument(); - if (Prototype.Browser.IE) { editor.observe('wysihat:cursormove', saveRange.bind(this)); editor.observe('wysihat:focus', restoreRange); @@ -21,7 +18,7 @@ WysiHat.Selection = Class.create((function() { * Get selected text. **/ function getSelection() { - return this.window.getSelection ? this.window.getSelection() : this.document.selection; + return window.getSelection ? window.getSelection() : document.selection; } /** @@ -54,17 +51,17 @@ WysiHat.Selection = Class.create((function() { var selection = this.getSelection(); if (Prototype.Browser.IE) { - var range = createRangeFromElement(this.document, node); + var range = createRangeFromElement(document, node); range.select(); } else if (Prototype.Browser.WebKit) { selection.setBaseAndExtent(node, 0, node, node.innerText.length); } else if (Prototype.Browser.Opera) { - range = this.document.createRange(); + range = document.createRange(); range.selectNode(node); selection.removeAllRanges(); selection.addRange(range); } else { - var range = createRangeFromElement(this.document, node); + var range = createRangeFromElement(document, node); selection.removeAllRanges(); selection.addRange(range); } @@ -101,7 +98,7 @@ WysiHat.Selection = Class.create((function() { candidates[candidates.length] = children[j]; nodes = [parent]; for (var ii = 0, r2; ii < candidates.length; ii++) { - r2 = createRangeFromElement(this.document, candidates[ii]); + r2 = createRangeFromElement(document, candidates[ii]); if (r2 && compareRanges(range, r2)) nodes[nodes.length] = candidates[ii]; } @@ -152,17 +149,17 @@ WysiHat.Selection = Class.create((function() { }; function setBookmark() { - var bookmark = this.document.getElementById('bookmark'); + var bookmark = document.getElementById('bookmark'); if (bookmark) bookmark.parentNode.removeChild(bookmark); - bookmark = this.document.createElement('span'); + bookmark = document.createElement('span'); bookmark.id = 'bookmark'; bookmark.innerHTML = ' '; if (Prototype.Browser.IE) { - var range = this.document.selection.createRange(); - var parent = this.document.createElement('div'); + var range = document.selection.createRange(); + var parent = document.createElement('div'); parent.appendChild(bookmark); range.collapse(); range.pasteHTML(parent.innerHTML); @@ -174,7 +171,7 @@ WysiHat.Selection = Class.create((function() { } function moveToBookmark() { - var bookmark = this.document.getElementById('bookmark'); + var bookmark = document.getElementById('bookmark'); if (!bookmark) return; diff --git a/src/wysihat/editor.js b/src/wysihat/editor.js index d8a75f4..b8b6df7 100644 --- a/src/wysihat/editor.js +++ b/src/wysihat/editor.js @@ -14,31 +14,32 @@ WysiHat.Editor = { textarea = $(textarea); textarea.hide(); - var model = options.get('model') || WysiHat.iFrame; - var initializer = block; + var editArea = new Element('div', { + 'id': textarea.id + '_editor', + 'class': 'editor', + 'contenteditable': 'true' + }); + editArea.textarea = textarea; - return model.create(textarea, function(editArea) { - var document = editArea.getDocument(); - var window = editArea.getWindow(); + WysiHat.Editor.extend(editArea); - editArea.load(); + editArea.selection = new WysiHat.Selection(editArea); - Event.observe(window, 'focus', function(event) { editArea.focus(); }); - Event.observe(window, 'blur', function(event) { editArea.blur(); }); + editArea.load(); - editArea._observeEvents(); + editArea._observeEvents(); - // Firefox starts "locked" - // Insert a character bogus character and undo - if (Prototype.Browser.Gecko) { - editArea.execCommand('undo', false, null); - } + // Firefox starts "locked" + // Insert a character bogus character and undo + if (Prototype.Browser.Gecko) { + editArea.execCommand('undo', false, null); + } - if (initializer) - initializer(editArea); + if (block) block(editArea); - editArea.focus(); - }); + textarea.insert({before: editArea}); + + return editArea; }, /** section: wysihat @@ -79,4 +80,3 @@ WysiHat.Editor = { //= require "editor/commands" //= require "editor/events" //= require "editor/persistence" -//= require "editor/window" diff --git a/src/wysihat/editor/commands.js b/src/wysihat/editor/commands.js index 41f0bce..6a028bb 100644 --- a/src/wysihat/editor/commands.js +++ b/src/wysihat/editor/commands.js @@ -250,8 +250,6 @@ WysiHat.Commands = (function() { * A simple delegation method to the documents execCommand method. **/ function execCommand(command, ui, value) { - var document = this.getDocument(); - if (Prototype.Browser.IE) this.selection.restore(); var handler = this.commands.get(command); @@ -274,8 +272,6 @@ WysiHat.Commands = (function() { * editor.queryCommands.set("link", editor.linkSelected); **/ function queryCommandState(state) { - var document = this.getDocument(); - var handler = this.queryCommands.get(state); if (handler) return handler.bind(this)(); diff --git a/src/wysihat/editor/events.js b/src/wysihat/editor/events.js index b47fe04..56449af 100644 --- a/src/wysihat/editor/events.js +++ b/src/wysihat/editor/events.js @@ -32,36 +32,36 @@ WysiHat.Events = (function() { 'keyup' ]; - function forwardEvents(document, editor) { + function forwardEvents(editor) { eventsToFoward.each(function(event) { - Event.observe(document, event, function(e) { + Event.observe(editor, event, function(e) { editor.fire('wysihat:' + event); }); }); } - function observePasteEvent(window, document, editor) { - Event.observe(document, 'keydown', function(event) { + function observePasteEvent(editor) { + Event.observe(editor, 'keydown', function(event) { if (event.keyCode == 86) editor.fire("wysihat:paste"); }); - Event.observe(window, 'paste', function(event) { + Event.observe(editor, 'paste', function(event) { editor.fire("wysihat:paste"); }); } - function observeFocus(window, editor) { - Event.observe(window, 'focus', function(event) { + function observeFocus(editor) { + Event.observe(editor, 'focus', function(event) { editor.fire("wysihat:focus"); }); - Event.observe(window, 'blur', function(event) { + Event.observe(editor, 'blur', function(event) { editor.fire("wysihat:blur"); }); } - function observeSelections(document, editor) { + function observeSelections(editor) { Event.observe(document, 'mouseup', function(event) { var range = editor.selection.getRange(); if (!range.collapsed) @@ -69,7 +69,7 @@ WysiHat.Events = (function() { }); } - function observeChanges(document, editor) { + function observeChanges(editor) { var previousContents = editor.rawContent(); Event.observe(document, 'keyup', function(event) { var contents = editor.rawContent(); @@ -80,7 +80,7 @@ WysiHat.Events = (function() { }); } - function observeCursorMovements(document, editor) { + function observeCursorMovements(editor) { var previousRange = editor.selection.getRange(); var handler = function(event) { var range = editor.selection.getRange(); @@ -90,23 +90,20 @@ WysiHat.Events = (function() { } }; - Event.observe(document, 'keyup', handler); - Event.observe(document, 'mouseup', handler); + Event.observe(editor, 'keyup', handler); + Event.observe(editor, 'mouseup', handler); } function observeEvents() { if (this._observers_setup) return; - var document = this.getDocument(); - var window = this.getWindow(); - - forwardEvents(document, this); - observePasteEvent(window, document, this); - observeFocus(window, this); - observeSelections(document, this); - observeChanges(document, this); - observeCursorMovements(document, this); + forwardEvents(this); + observePasteEvent(this); + observeFocus(this); + observeSelections(this); + observeChanges(this); + observeCursorMovements(this); this._observers_setup = true; } diff --git a/src/wysihat/editor/persistence.js b/src/wysihat/editor/persistence.js index b1cfa1e..4d04a87 100644 --- a/src/wysihat/editor/persistence.js +++ b/src/wysihat/editor/persistence.js @@ -5,6 +5,14 @@ * extracting and filtering content going in and out of the editor. **/ WysiHat.Persistence = (function() { + function rawContent() { + return this.innerHTML; + } + + function setRawContent(text) { + this.innerHTML = text; + } + /** * WysiHat.Persistence#outputFilter(text) -> String * - text (String): HTML string @@ -95,13 +103,15 @@ WysiHat.Persistence = (function() { } return { - outputFilter: outputFilter, - inputFilter: inputFilter, - content: content, - setContent: setContent, - save: save, - load: load, - reload: reload + rawContent: rawContent, + setRawContent: setRawContent, + outputFilter: outputFilter, + inputFilter: inputFilter, + content: content, + setContent: setContent, + save: save, + load: load, + reload: reload }; })(); diff --git a/src/wysihat/editor/window.js b/src/wysihat/editor/window.js deleted file mode 100644 index 8a7ae97..0000000 --- a/src/wysihat/editor/window.js +++ /dev/null @@ -1,64 +0,0 @@ -/** section: wysihat - * mixin WysiHat.Window - * - * Methods will be mixed into the editor element. These methods handle window - * events such as focus and blur events on the editor. -**/ -WysiHat.Window = (function() { - /** - * WysiHat.Window#getDocument() -> Document - * - * Cross browser method to return the iFrame's document. - * You should not need to access this directly, and this API is not final. - **/ - function getDocument() { - return this.contentDocument || this.contentWindow.document; - } - - /** - * WysiHat.Window#getWindow() -> Window - * - * Cross browser method to return the iFrame's window. - * You should not need to access this directly, and this API is not final. - **/ - function getWindow() { - if (this.contentDocument && this.contentDocument.defaultView) - return this.contentDocument.defaultView; - else if (this.contentWindow.document) - return this.contentWindow; - else - return null; - } - - /** - * WysiHat.Window#focus() -> undefined - * - * binds observers to mouseup, mousemove, keypress, and keyup on focus - **/ - function focus() { - this.getWindow().focus(); - - if (this.hasFocus) - return; - - this.hasFocus = true; - } - - /** - * WysiHat.Window#blur() -> undefined - * - * removes observers to mouseup, mousemove, keypress, and keyup on blur - **/ - function blur() { - this.hasFocus = false; - } - - return { - getDocument: getDocument, - getWindow: getWindow, - focus: focus, - blur: blur - }; -})(); - -WysiHat.Editor.include(WysiHat.Window); diff --git a/src/wysihat/models.js b/src/wysihat/models.js deleted file mode 100644 index b9765c0..0000000 --- a/src/wysihat/models.js +++ /dev/null @@ -1,2 +0,0 @@ -//= require "models/iframe" -//= require "models/editable" diff --git a/src/wysihat/models/editable.js b/src/wysihat/models/editable.js deleted file mode 100644 index 551a25f..0000000 --- a/src/wysihat/models/editable.js +++ /dev/null @@ -1,44 +0,0 @@ -/** section: wysihat - * WysiHat.Editable - * includes WysiHat.Commands, WysiHat.Persistence, WysiHat.Window, WysiHat.Editable.Methods -**/ -WysiHat.Editable = { - create: function(textarea, callback) { - var editArea = new Element('div', { - 'id': textarea.id + '_editor', - 'class': 'editor', - 'contenteditable': 'true' - }); - editArea.textarea = textarea; - - WysiHat.Editor.extend(editArea); - Object.extend(editArea, WysiHat.Editable.Methods); - - callback(editArea); - - textarea.insert({before: editArea}); - - return editArea; - } -}; - -/** section: wysihat - * mixin WysiHat.Editable.Methods -**/ -WysiHat.Editable.Methods = { - getDocument: function() { - return document; - }, - - getWindow: function() { - return window; - }, - - rawContent: function() { - return this.innerHTML; - }, - - setRawContent: function(text) { - this.innerHTML = text; - } -}; diff --git a/src/wysihat/models/iframe.js b/src/wysihat/models/iframe.js deleted file mode 100644 index ef4f517..0000000 --- a/src/wysihat/models/iframe.js +++ /dev/null @@ -1,138 +0,0 @@ -/** section: wysihat - * WysiHat.iFrame - * includes WysiHat.Commands, WysiHat.Persistence, WysiHat.Window, WysiHat.iFrame.Methods -**/ -WysiHat.iFrame = { - create: function(textarea, callback) { - var editArea = new Element('iframe', { 'id': textarea.id + '_editor', 'class': 'editor' }); - - Object.extend(editArea, WysiHat.iFrame.Methods); - WysiHat.Editor.extend(editArea); - - editArea.attach(textarea, callback); - textarea.insert({before: editArea}); - - return editArea; - } -}; - -/** section: wysihat - * mixin WysiHat.iFrame.Methods -**/ -WysiHat.iFrame.Methods = { - attach: function(element, callback) { - this.textarea = element; - - // Use onload because iframes are not always immediately accessible - this.observe('load', function() { - try { - var document = this.getDocument(); - } catch(e) { return; } // No iframe, just stop - - this.selection = new WysiHat.Selection(this); - - // If designMode is still off let this function continue because the - // iframe may still may be immutable on the first run - if (this.ready && document.designMode == 'on') - return; - - this.setStyle({}); - document.designMode = 'on'; - callback(this); - this.ready = true; - this.fire('wysihat:ready'); - }); - }, - - /** - * WysiHat.iFrame#unattach() -> undefined - * - * Remove the iframe - **/ - unattach: function() { - this.remove(); - }, - - /** - * WysiHat.iFrame#whenReady(callback) -> Element - * - callback (Function): a function that is called if or when the editor is - * 'ready'. - * - * Some things, like the selection object, are invalid until the - * editor is ready. For certain browsers, the editor is ready - * instantaneously, for other browsers this happens after a short delay. - * Use this method to avoid having to think about it. - **/ - whenReady: function(callback) { - if (this.ready) { - callback(this); - } else { - var editor = this; - editor.observe('wysihat:ready', function() { callback(editor); }); - } - return this; - }, - - /** - * WysiHat.iFrame.Methods#setStyle(styles) -> Element - * - styles (Hash): Styles are passed as a hash of property-value pairs in - * which the properties are specified in their camelized form. - * - * Sets the style of the body element inside the iFrame. You can use this to - * change the font size and family of the editable text. This method also - * removes paragraph margins for IE and Opera so it feels you start a new - * line when you hit enter, not a new paragraph. - **/ - setStyle: function(styles) { - var document = this.getDocument(); - - var element = this; - if (!this.ready) - return setTimeout(function() { element.setStyle(styles); }, 1); - - if (Prototype.Browser.IE) { - var style = document.createStyleSheet(); - style.addRule("body", "border: 0"); - style.addRule("p", "margin: 0"); - - $H(styles).each(function(pair) { - var value = pair.first().underscore().dasherize() + ": " + pair.last(); - style.addRule("body", value); - }); - } else if (Prototype.Browser.Opera) { - var style = Element('style').update("p { margin: 0; }"); - var head = document.getElementsByTagName('head')[0]; - head.appendChild(style); - } else { - Element.setStyle(document.body, styles); - } - - return this; - }, - - /** - * WysiHat.iFrame.Methods#getStyle(style) -> string - * - style specificication (i.e. backgroundColor) - * - * Returns the style from the element based on the given style - */ - getStyle: function(style) { - var document = this.getDocument(); - return Element.getStyle(document.body, style); - }, - - rawContent: function() { - var document = this.getDocument(); - - if (document.body) - return document.body.innerHTML; - else - return ""; - }, - - setRawContent: function(text) { - var document = this.getDocument(); - if (document.body) - document.body.innerHTML = text; - } -}; diff --git a/test/editor.html b/test/editor.html index abe28be..382ba50 100644 --- a/test/editor.html +++ b/test/editor.html @@ -16,6 +16,14 @@ height: 100px; } + .editor { + clear: both; + margin: 5px 0; + padding: 5px; + border: 1px solid #acacac; + outline: none; + } + .editor_toolbar .button { float: left; margin: 2px 5px; From f1b826824db303b62c849d3ed1f0cf89f53c62f0 Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Wed, 3 Feb 2010 15:23:51 -0600 Subject: [PATCH 031/164] remove ff start up hack --- src/wysihat/editor.js | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/wysihat/editor.js b/src/wysihat/editor.js index b8b6df7..c3d4f68 100644 --- a/src/wysihat/editor.js +++ b/src/wysihat/editor.js @@ -29,12 +29,6 @@ WysiHat.Editor = { editArea._observeEvents(); - // Firefox starts "locked" - // Insert a character bogus character and undo - if (Prototype.Browser.Gecko) { - editArea.execCommand('undo', false, null); - } - if (block) block(editArea); textarea.insert({before: editArea}); From 9d586e3570e7c4a451a996cc5f69c2557a622a3c Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Wed, 3 Feb 2010 15:26:41 -0600 Subject: [PATCH 032/164] drop legacy browser support --- README.markdown | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/README.markdown b/README.markdown index 9cf3243..e4ca53c 100644 --- a/README.markdown +++ b/README.markdown @@ -11,11 +11,10 @@ way and leaves the UI design to you. WysiHat currently supports: -* Microsoft Internet Explorer for Windows, version 6.0 and higher -* Mozilla Firefox 2.0 and higher -* Apple Safari 3.0 and higher -* Opera 9.52 and higher -* Google Chrome +* Microsoft Internet Explorer for Windows, version 7.0 +* Mozilla Firefox 3.0 +* Apple Safari 4.0 +* Google Chrome 4.0 ### Dependencies From 9d9c833486a6287338653f866e3415b3e09f053f Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Wed, 3 Feb 2010 15:33:23 -0600 Subject: [PATCH 033/164] Set a min height on test editor --- test/editor.html | 1 + 1 file changed, 1 insertion(+) diff --git a/test/editor.html b/test/editor.html index 382ba50..e6a138a 100644 --- a/test/editor.html +++ b/test/editor.html @@ -18,6 +18,7 @@ .editor { clear: both; + min-height: 100px; margin: 5px 0; padding: 5px; border: 1px solid #acacac; From 7e197dff8da53009dfad14732059b968d017f527 Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Wed, 3 Feb 2010 15:39:31 -0600 Subject: [PATCH 034/164] stop fowarding native events --- src/wysihat/editor/events.js | 30 ++++-------------------------- 1 file changed, 4 insertions(+), 26 deletions(-) diff --git a/src/wysihat/editor/events.js b/src/wysihat/editor/events.js index 56449af..7078209 100644 --- a/src/wysihat/editor/events.js +++ b/src/wysihat/editor/events.js @@ -19,27 +19,6 @@ * still advancing. **/ WysiHat.Events = (function() { - var eventsToFoward = [ - 'click', - 'dblclick', - 'mousedown', - 'mouseup', - 'mouseover', - 'mousemove', - 'mouseout', - 'keypress', - 'keydown', - 'keyup' - ]; - - function forwardEvents(editor) { - eventsToFoward.each(function(event) { - Event.observe(editor, event, function(e) { - editor.fire('wysihat:' + event); - }); - }); - } - function observePasteEvent(editor) { Event.observe(editor, 'keydown', function(event) { if (event.keyCode == 86) @@ -98,12 +77,11 @@ WysiHat.Events = (function() { if (this._observers_setup) return; - forwardEvents(this); observePasteEvent(this); - observeFocus(this); - observeSelections(this); - observeChanges(this); - observeCursorMovements(this); + // observeFocus(this); + // observeSelections(this); + // observeChanges(this); + // observeCursorMovements(this); this._observers_setup = true; } From 3483eea6910118948af3c776e7d7841f997d24e6 Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Wed, 3 Feb 2010 15:44:12 -0600 Subject: [PATCH 035/164] disable wysihat:change event cause its breaking undo/redo --- src/wysihat/editor/events.js | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/src/wysihat/editor/events.js b/src/wysihat/editor/events.js index 7078209..5852203 100644 --- a/src/wysihat/editor/events.js +++ b/src/wysihat/editor/events.js @@ -41,7 +41,7 @@ WysiHat.Events = (function() { } function observeSelections(editor) { - Event.observe(document, 'mouseup', function(event) { + Event.observe(editor, 'mouseup', function(event) { var range = editor.selection.getRange(); if (!range.collapsed) editor.fire("wysihat:select"); @@ -50,10 +50,11 @@ WysiHat.Events = (function() { function observeChanges(editor) { var previousContents = editor.rawContent(); - Event.observe(document, 'keyup', function(event) { + Event.observe(editor, 'keyup', function(event) { var contents = editor.rawContent(); if (previousContents != contents) { - editor.fire("wysihat:change"); + // FIXME: firing an event here breaks Undo/Redo + // editor.fire("wysihat:change"); previousContents = contents; } }); @@ -78,10 +79,10 @@ WysiHat.Events = (function() { return; observePasteEvent(this); - // observeFocus(this); - // observeSelections(this); - // observeChanges(this); - // observeCursorMovements(this); + observeFocus(this); + observeSelections(this); + observeChanges(this); + observeCursorMovements(this); this._observers_setup = true; } From a6238707d28696644bc25e41ef457244b30c4a6b Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Wed, 3 Feb 2010 16:21:52 -0600 Subject: [PATCH 036/164] IE expects "contentEditable" --- src/wysihat/editor.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/wysihat/editor.js b/src/wysihat/editor.js index c3d4f68..920657b 100644 --- a/src/wysihat/editor.js +++ b/src/wysihat/editor.js @@ -17,7 +17,7 @@ WysiHat.Editor = { var editArea = new Element('div', { 'id': textarea.id + '_editor', 'class': 'editor', - 'contenteditable': 'true' + 'contentEditable': 'true' }); editArea.textarea = textarea; From 91e729404e71ef3f93cca887df17f01da4fcea20 Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Wed, 3 Feb 2010 16:26:53 -0600 Subject: [PATCH 037/164] No margin between Ps in IE --- test/editor.html | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/test/editor.html b/test/editor.html index e6a138a..309d71c 100644 --- a/test/editor.html +++ b/test/editor.html @@ -25,6 +25,10 @@ outline: none; } + .editor p { + margin: 0; + } + .editor_toolbar .button { float: left; margin: 2px 5px; From 762a5a511578b75ed90bed675e6eb092937e60de Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Wed, 3 Feb 2010 16:32:00 -0600 Subject: [PATCH 038/164] disable wysihat:select for now since it breaks double clicking to select words in IE --- src/wysihat/editor/events.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/wysihat/editor/events.js b/src/wysihat/editor/events.js index 5852203..df0bafb 100644 --- a/src/wysihat/editor/events.js +++ b/src/wysihat/editor/events.js @@ -43,8 +43,9 @@ WysiHat.Events = (function() { function observeSelections(editor) { Event.observe(editor, 'mouseup', function(event) { var range = editor.selection.getRange(); - if (!range.collapsed) - editor.fire("wysihat:select"); + // FIXME: firing an event here breaks double clicking to select word in IE + // if (!range.collapsed) + // editor.fire("wysihat:select"); }); } From 125d509f0ba007c7a2b9bd383c1ce259711b57fa Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Wed, 3 Feb 2010 16:42:48 -0600 Subject: [PATCH 039/164] update pdoc --- vendor/pdoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vendor/pdoc b/vendor/pdoc index a4609a7..8e91f82 160000 --- a/vendor/pdoc +++ b/vendor/pdoc @@ -1 +1 @@ -Subproject commit a4609a72bd7d8c0c176ac4840f692dbe307eaeeb +Subproject commit 8e91f8247a5e891960fce1ecb140f00c64cedd2c From 478d53066f3ec5ab5a97a707c4e4a66286c47439 Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Wed, 3 Feb 2010 16:43:47 -0600 Subject: [PATCH 040/164] update prototype --- vendor/prototype | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vendor/prototype b/vendor/prototype index 8bfd9cf..f405b2c 160000 --- a/vendor/prototype +++ b/vendor/prototype @@ -1 +1 @@ -Subproject commit 8bfd9cf55563110a055204192458b047bbc920cd +Subproject commit f405b2c510e09b55d08c926a9e1a5c2e2d0a1834 From ac742088f10f1444f62319205d5e1811990fa960 Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Wed, 3 Feb 2010 16:44:45 -0600 Subject: [PATCH 041/164] update unittest_js --- vendor/unittest_js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vendor/unittest_js b/vendor/unittest_js index fefb554..b811722 160000 --- a/vendor/unittest_js +++ b/vendor/unittest_js @@ -1 +1 @@ -Subproject commit fefb554e20a70b15517490dfe620486c420b6df2 +Subproject commit b81172275ea4a6530a5bac4912f2f18ecbfe9feb From 76e070bfb319eb75370d77e9c29d3fe59d6c7011 Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Wed, 3 Feb 2010 16:48:40 -0600 Subject: [PATCH 042/164] IE conditional for sanitizer tests --- test/unit/sanitizer_test.js | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/test/unit/sanitizer_test.js b/test/unit/sanitizer_test.js index 8d3b19b..fc511eb 100644 --- a/test/unit/sanitizer_test.js +++ b/test/unit/sanitizer_test.js @@ -52,10 +52,17 @@ new Test.Unit.Runner({ "Google".sanitize({tags: ["a"], attributes: ["href"]}) ); - runner.assertEqual( - "Hello ", - "Hello ".sanitize({tags: ["span"], attributes: ["id"]}) - ); + if (Prototype.Browser.IE) { + runner.assertEqual( + "Hello ", + "Hello ".sanitize({tags: ["span"], attributes: ["id"]}) + ); + } else { + runner.assertEqual( + "Hello ", + "Hello ".sanitize({tags: ["span"], attributes: ["id"]}) + ); + } runner.assertEqual( "", From 484ef262cb75ecd285c452f4064503a741c3667f Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Wed, 3 Feb 2010 16:52:30 -0600 Subject: [PATCH 043/164] dump editor tests --- test/unit/editor_test.js | 176 --------------------------------------- 1 file changed, 176 deletions(-) delete mode 100644 test/unit/editor_test.js diff --git a/test/unit/editor_test.js b/test/unit/editor_test.js deleted file mode 100644 index 480f5fb..0000000 --- a/test/unit/editor_test.js +++ /dev/null @@ -1,176 +0,0 @@ - -new Test.Unit.Runner({ - testAttachAndCreateIframe: function() { - var runner = this; - - var editor = WysiHat.Editor.attach('content'); - editor.whenReady(function() { - this.editor = editor; - runner.assertNotVisible($('content')); - runner.assert($('content_editor')); - runner.assert($('content_editor').ready); - runner.assert($('content_editor').getDocument()); - runner.assert($('content_editor').getWindow()); - runner.assertEqual('on', $('content_editor').getDocument().designMode); - }); - - runner.wait(1000, function() {}); - }, - - testIncludedModules: function() { with(this) { - var module = { xyz123: function() {} }; - var editor1 = WysiHat.Editor.attach('content'); - - WysiHat.Editor.include(module); - var editor2 = WysiHat.Editor.attach('content'); - - // The extension should not be retrospective. - assert(!Object.isFunction(editor1.xyz123)); - - // The extension should work on subsequent editors though. - assert(Object.isFunction(editor2.xyz123)); - editor1.unattach(); - editor2.unattach(); - }}, - - testBold: function() { with(this) { - testFontFormat('bold'); - testNestedFontFormat('bold', 'b'); - }}, - - testItalic: function() { with(this) { - testFontFormat('italic'); - testNestedFontFormat('italic', 'i'); - }}, - - testUnderline: function() { with(this) { - testFontFormat('underline'); - testNestedFontFormat('underline', 'u'); - }}, - - testFonts: function() { with(this) { - editor.setContent("Times"); - selectFirstNode(); - editor.fontSelection('times'); - assertEqual('times', editor.fontSelected()); - }}, - - testNestedFonts: function() { with(this) { - editor.setContent("Times"); - selectFirstNode(); - assertEqual('times', editor.fontSelected()); - }}, - - testFontSizes: function() { with(this) { - editor.setContent("Font Size"); - selectFirstNode(); - editor.fontSizeSelection(5); - assertEqual(5, editor.fontSizeSelected()); - }}, - - testNestedFontSizes: function() { with(this) { - editor.setContent("Font Size"); - selectFirstNode(); - assertEqual(7, editor.fontSizeSelected()); - }}, - - testColor: function() { with (this) { - editor.setContent('color'); - selectFirstNode(); - editor.colorSelection("#555555"); - assertEqual('#555555', editor.colorSelected()); - }}, - - testNestedColor: function() { with(this) { - editor.setContent("color"); - selectFirstNode(); - assertEqual('#555555', editor.colorSelected()); - }}, - - testBackgroundColor: function() { with(this) { - editor.setContent("background color"); - selectFirstNode(); - editor.backgroundColorSelection("#555555"); - assertEqual('#555555', editor.backgroundColorSelected()); - }}, - - testNestedBackgroundColor: function() { with(this) { - editor.setContent("background-color"); - selectFirstNode(); - assertEqual('#555555', editor.backgroundColorSelected()); - }}, - - testAlignment: function() { with(this) { - editor.setContent("align me"); - selectFirstNode(); - editor.alignSelection('right'); - assertEqual('right', editor.alignSelected()); - }}, - - testNestedAlignment: function() { with(this) { - editor.setContent("
    align me
    "); - selectFirstNode(); - assertEqual('right', editor.alignSelected()); - }}, - - testStyle: function(){ with(this) { - editor.setContent("set my style"); - selectFirstNode(); - editor.setStyle( {fontFamily: "arial", fontSize: "13px"} ); - assert(editor.getStyle('fontFamily')); - assertEqual('arial', editor.getStyle('fontFamily')); - assertEqual('13px', editor.getStyle('fontSize')); - }}, - - testLink: function() { with (this) { - link = 'http://github.com/'; - editor.setContent('linky link'); - selectFirstNode(); - - wait(500, function() { - var textarea = $('link').down('textarea'); - textarea.focus(); // simulate real world case of IE losing focus - textarea.value = link; - editor.linkSelection(link); - assert(editor.linkSelected()); - assertEqual(link, editor.selection.getNode().href); - }); - }}, - - testNestedLink: function(){ with (this) { - link = 'http://github.com/'; - editor.setContent('linky link'); - selectFirstNode(); - assert(editor.linkSelected()); - assertEqual(link, editor.selection.getNode().href); - }}, - - testUnattach: function() { with(this) { - editor.setContent("Unattached"); - editor.unattach(); - var iframe = $('content_editor'); - assert(!iframe); - }}, - - setup: function() { - this.selectFirstNode = function() { - var iframe = $('content_editor'); - var doc = iframe.contentDocument || iframe.contentWindow.document; - var node = doc.body.childNodes[0]; - editor.selection.selectNode(node); - }; - - this.testFontFormat = function(styleType) { - editor.setContent('Boldly going where no man has gone before'); - this.selectFirstNode(); - editor[styleType + 'Selection'](); - this.assert(editor[styleType + 'Selected']()); - }; - - this.testNestedFontFormat = function(styleType, styleTag) { - editor.setContent('<' + styleTag + '>Boldly going where no man has gone before'); - this.selectFirstNode(); - this.assert(editor[styleType + 'Selected']()); - }; - } -}); \ No newline at end of file From fece4b2d0ce3043ef5073cab6ed2a4cc69351980 Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Wed, 3 Feb 2010 16:52:46 -0600 Subject: [PATCH 044/164] dump toolbar tests --- test/unit/toolbar_test.js | 24 ------------------------ 1 file changed, 24 deletions(-) delete mode 100644 test/unit/toolbar_test.js diff --git a/test/unit/toolbar_test.js b/test/unit/toolbar_test.js deleted file mode 100644 index a969314..0000000 --- a/test/unit/toolbar_test.js +++ /dev/null @@ -1,24 +0,0 @@ -new Test.Unit.Runner({ - testToolbarWithBasicButtonSet: function() { - var runner = this; - - var editor = WysiHat.Editor.attach('content'); - var toolbar = new WysiHat.Toolbar(editor); - toolbar.addButtonSet(WysiHat.Toolbar.ButtonSets.Basic); - - editor.whenReady(function() { - runner.assertEqual(editor, toolbar.editor); - runner.assertEqual(toolbar.element, $$('div.editor_toolbar').first()); - - runner.assert($$('div.editor_toolbar').first()); - runner.assert($$('div.editor_toolbar a.bold').first()); - runner.assertEqual("bold", $$('div.editor_toolbar a.bold').first().innerHTML.toLowerCase()); - runner.assert($$('div.editor_toolbar a.underline').first()); - runner.assertEqual("underline", $$('div.editor_toolbar a.underline').first().innerHTML.toLowerCase()); - runner.assert($$('div.editor_toolbar a.italic').first()); - runner.assertEqual("italic", $$('div.editor_toolbar a.italic').first().innerHTML.toLowerCase()); - }); - - runner.wait(1000, function() {}); - } -}); From 3f915e1f8a39505951f258de3645cdf37db664e5 Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Wed, 3 Feb 2010 16:54:52 -0600 Subject: [PATCH 045/164] unused fixtures --- test/unit/fixtures/editor.html | 6 ------ test/unit/fixtures/toolbar.html | 3 --- 2 files changed, 9 deletions(-) delete mode 100644 test/unit/fixtures/editor.html delete mode 100644 test/unit/fixtures/toolbar.html diff --git a/test/unit/fixtures/editor.html b/test/unit/fixtures/editor.html deleted file mode 100644 index f551114..0000000 --- a/test/unit/fixtures/editor.html +++ /dev/null @@ -1,6 +0,0 @@ -
    - -
    - \ No newline at end of file diff --git a/test/unit/fixtures/toolbar.html b/test/unit/fixtures/toolbar.html deleted file mode 100644 index f757b81..0000000 --- a/test/unit/fixtures/toolbar.html +++ /dev/null @@ -1,3 +0,0 @@ -
    - -
    From 32487db2e284fe519f3a845f6ae3daf27d1ca7df Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Wed, 3 Feb 2010 16:56:53 -0600 Subject: [PATCH 046/164] add iframe removal to changelog --- CHANGELOG | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CHANGELOG b/CHANGELOG index e628f56..d29ea32 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,8 @@ +*0.3* + +* Removed iFrame support and transitioned to contentEditable. This drops + support for Firefox 2. + *0.2.1* (February 3, 2010) * Added Editor#queryValueCommandState for testing custom query commands. From 3e60bbd5053d876a0058bb7cd78aa66cd5853530 Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Wed, 3 Feb 2010 17:10:47 -0600 Subject: [PATCH 047/164] tset insertHTML --- src/wysihat/editor/commands.js | 2 +- test/unit/editor_test.js | 14 ++++++++++++++ test/unit/fixtures/editor.html | 3 +++ 3 files changed, 18 insertions(+), 1 deletion(-) create mode 100644 test/unit/editor_test.js create mode 100644 test/unit/fixtures/editor.html diff --git a/src/wysihat/editor/commands.js b/src/wysihat/editor/commands.js index 6a028bb..581aaa5 100644 --- a/src/wysihat/editor/commands.js +++ b/src/wysihat/editor/commands.js @@ -231,7 +231,7 @@ WysiHat.Commands = (function() { **/ function insertHTML(html) { if (Prototype.Browser.IE) { - var range = this._selection.getRange(); + var range = document.selection.createRange(); range.pasteHTML(html); range.collapse(false); range.select(); diff --git a/test/unit/editor_test.js b/test/unit/editor_test.js new file mode 100644 index 0000000..b22f966 --- /dev/null +++ b/test/unit/editor_test.js @@ -0,0 +1,14 @@ +new Test.Unit.Runner({ + setup: function() { + this.textarea = $('content'); + this.editor = WysiHat.Editor.attach(this.textarea); + }, + + testInsertHTML: function() { + var runner = this; + + this.editor.focus(); + this.editor.insertHTML("Hello."); + runner.assertEqual("

    Hello.

    ", this.editor.content()); + } +}); diff --git a/test/unit/fixtures/editor.html b/test/unit/fixtures/editor.html new file mode 100644 index 0000000..f757b81 --- /dev/null +++ b/test/unit/fixtures/editor.html @@ -0,0 +1,3 @@ +
    + +
    From 83d0091deb675d59fc599d25d2d9745ab7f50991 Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Wed, 3 Feb 2010 17:47:14 -0600 Subject: [PATCH 048/164] simple editor test for bold selection --- src/wysihat/editor.js | 10 ++++++++-- test/unit/editor_test.js | 22 ++++++++++++++++++++-- 2 files changed, 28 insertions(+), 4 deletions(-) diff --git a/src/wysihat/editor.js b/src/wysihat/editor.js index 920657b..814cf74 100644 --- a/src/wysihat/editor.js +++ b/src/wysihat/editor.js @@ -10,12 +10,18 @@ WysiHat.Editor = { * Creates a new editor for the textarea. **/ attach: function(textarea, options, block) { + var editArea; + options = $H(options); textarea = $(textarea); textarea.hide(); - var editArea = new Element('div', { - 'id': textarea.id + '_editor', + var id = textarea.id + '_editor'; + if (editArea = $(id)) + return editArea; + + editArea = new Element('div', { + 'id': id, 'class': 'editor', 'contentEditable': 'true' }); diff --git a/test/unit/editor_test.js b/test/unit/editor_test.js index b22f966..17ff904 100644 --- a/test/unit/editor_test.js +++ b/test/unit/editor_test.js @@ -2,13 +2,31 @@ new Test.Unit.Runner({ setup: function() { this.textarea = $('content'); this.editor = WysiHat.Editor.attach(this.textarea); + this.editor.focus(); + }, + + teardown: function() { + this.editor.setContent(""); + this.textarea.value = ""; }, testInsertHTML: function() { var runner = this; - this.editor.focus(); - this.editor.insertHTML("Hello."); + this.editor.insertHTML("

    Hello.

    "); runner.assertEqual("

    Hello.

    ", this.editor.content()); + }, + + testBoldSelection: function() { + var runner = this; + + // this.editor.insertHTML("

    Hello.

    "); + this.editor.setContent("

    Hello.

    "); + + this.editor.selection.selectNode(this.editor.down(0)); + this.editor.boldSelection(); + + runner.assert(this.editor.boldSelected()); + runner.assertEqual("

    Hello.

    ", this.editor.content()); } }); From cdada4c4763efd11ca6c3a5385c047c767d25d73 Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Wed, 3 Feb 2010 19:30:45 -0600 Subject: [PATCH 049/164] ujs event observers --- src/wysihat/dom/selection.js | 3 +- src/wysihat/editor.js | 2 - src/wysihat/editor/events.js | 114 +++++++++++++----------------- src/wysihat/editor/persistence.js | 3 +- test/editor.html | 2 +- 5 files changed, 54 insertions(+), 70 deletions(-) diff --git a/src/wysihat/dom/selection.js b/src/wysihat/dom/selection.js index 97fa934..5efdcc7 100644 --- a/src/wysihat/dom/selection.js +++ b/src/wysihat/dom/selection.js @@ -208,6 +208,7 @@ WysiHat.Selection = Class.create((function() { selectNode: selectNode, setBookmark: setBookmark, moveToBookmark: moveToBookmark, - restore: restoreRange + restore: restoreRange, + previousRange: null }; })()); diff --git a/src/wysihat/editor.js b/src/wysihat/editor.js index 814cf74..e38a000 100644 --- a/src/wysihat/editor.js +++ b/src/wysihat/editor.js @@ -33,8 +33,6 @@ WysiHat.Editor = { editArea.load(); - editArea._observeEvents(); - if (block) block(editArea); textarea.insert({before: editArea}); diff --git a/src/wysihat/editor/events.js b/src/wysihat/editor/events.js index df0bafb..0ecf97e 100644 --- a/src/wysihat/editor/events.js +++ b/src/wysihat/editor/events.js @@ -1,8 +1,4 @@ /** section: wysihat - * mixin WysiHat.Events - * - * Forwards common DOM events to the editor element. All events are - * prefixed with 'wysihat:'. * * To observe any double clicks in the editor: * @@ -18,79 +14,67 @@ * press. Is also fired when something is typed because the cursor is * still advancing. **/ -WysiHat.Events = (function() { - function observePasteEvent(editor) { - Event.observe(editor, 'keydown', function(event) { - if (event.keyCode == 86) - editor.fire("wysihat:paste"); - }); +document.observe("dom:loaded", function() { + $(document.body).observe("keydown", function(event) { + var editor = event.findElement("div.editor"); + if (editor && event.keyCode == 86) editor.fire("wysihat:paste"); + }); - Event.observe(editor, 'paste', function(event) { - editor.fire("wysihat:paste"); - }); - } + $(document.body).observe("paste", function(event) { + var editor = event.findElement("div.editor"); + if (editor) editor.fire("wysihat:paste"); + }); - function observeFocus(editor) { - Event.observe(editor, 'focus', function(event) { - editor.fire("wysihat:focus"); - }); - Event.observe(editor, 'blur', function(event) { - editor.fire("wysihat:blur"); - }); + var observeCursorMovementsHandler = function(editor) { + var range = editor.selection.getRange(); + if (editor.selection.previousRange != range) { + editor.fire("wysihat:cursormove"); + editor.selection.previousRange = range; + } } - function observeSelections(editor) { - Event.observe(editor, 'mouseup', function(event) { - var range = editor.selection.getRange(); - // FIXME: firing an event here breaks double clicking to select word in IE - // if (!range.collapsed) - // editor.fire("wysihat:select"); - }); - } + $(document.body).observe("keyup", function(event) { + var editor = event.findElement("div.editor"); + if (editor) { + observeCursorMovementsHandler(editor); - function observeChanges(editor) { - var previousContents = editor.rawContent(); - Event.observe(editor, 'keyup', function(event) { var contents = editor.rawContent(); - if (previousContents != contents) { - // FIXME: firing an event here breaks Undo/Redo - // editor.fire("wysihat:change"); - previousContents = contents; + if (editor.previousContents != contents) { + editor.fire("wysihat:change"); + editor.previousContents = contents; } - }); - } + } + }); - function observeCursorMovements(editor) { - var previousRange = editor.selection.getRange(); - var handler = function(event) { - var range = editor.selection.getRange(); - if (previousRange != range) { - editor.fire("wysihat:cursormove"); - previousRange = range; - } - }; + $(document.body).observe("mouseup", function(event) { + var editor = event.findElement("div.editor"); + if (editor) { + observeCursorMovementsHandler(editor); - Event.observe(editor, 'keyup', handler); - Event.observe(editor, 'mouseup', handler); - } - - function observeEvents() { - if (this._observers_setup) - return; + var range = editor.selection.getRange(); + // FIXME: firing an event here breaks double clicking to select word in IE + // if (!range.collapsed) + // editor.fire("wysihat:select"); + } + }); - observePasteEvent(this); - observeFocus(this); - observeSelections(this); - observeChanges(this); - observeCursorMovements(this); - this._observers_setup = true; - } + var focusInHandler = function(event) { + var editor = event.findElement("div.editor"); + if (editor) editor.fire("wysihat:focus") + }; - return { - _observeEvents: observeEvents + var focusOutHandler = function(event) { + var editor = event.findElement("div.editor"); + if (editor) editor.fire("wysihat:blur") }; -})(); -WysiHat.Editor.include(WysiHat.Events); + if (document.addEventListener) { + document.addEventListener("focus", focusInHandler, true); + document.addEventListener("blur", focusOutHandler, true); + } else { + document.observe("focusin", focusInHandler); + document.observe("focusout", focusOutHandler); + } +}); diff --git a/src/wysihat/editor/persistence.js b/src/wysihat/editor/persistence.js index 4d04a87..9c6b349 100644 --- a/src/wysihat/editor/persistence.js +++ b/src/wysihat/editor/persistence.js @@ -111,7 +111,8 @@ WysiHat.Persistence = (function() { setContent: setContent, save: save, load: load, - reload: reload + reload: reload, + previousContents: null }; })(); diff --git a/test/editor.html b/test/editor.html index 309d71c..0298beb 100644 --- a/test/editor.html +++ b/test/editor.html @@ -92,7 +92,7 @@ textarea.disable(); editor.observe("wysihat:change", function(event) { - $('note_body_raw').value = event.target.rawContent(); + $('note_body_raw').value = event.element().rawContent() }); }); From 6c7ce4bbaae622ebbab36560eeb0c83f045be31d Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Thu, 4 Feb 2010 08:26:08 -0600 Subject: [PATCH 050/164] move button sets into toolbar file --- src/wysihat/toolbar.js | 22 +++++++++++++++++++++- src/wysihat/toolbar/button_sets.js | 21 --------------------- 2 files changed, 21 insertions(+), 22 deletions(-) delete mode 100644 src/wysihat/toolbar/button_sets.js diff --git a/src/wysihat/toolbar.js b/src/wysihat/toolbar.js index 4b4bb63..5f867df 100644 --- a/src/wysihat/toolbar.js +++ b/src/wysihat/toolbar.js @@ -222,4 +222,24 @@ WysiHat.Toolbar = Class.create((function() { }; })()); -//= require "toolbar/button_sets" +/** + * WysiHat.Toolbar.ButtonSets + * + * A namespace for various sets of Toolbar buttons. These sets should be + * compatible with WysiHat.Toolbar, and can be added to the toolbar with: + * toolbar.addButtonSet(WysiHat.Toolbar.ButtonSets.Basic); +**/ +WysiHat.Toolbar.ButtonSets = {}; + +/** + * WysiHat.Toolbar.ButtonSets.Basic + * + * A basic set of buttons: bold, underline, and italic. This set is + * compatible with WysiHat.Toolbar, and can be added to the toolbar with: + * toolbar.addButtonSet(WysiHat.Toolbar.ButtonSets.Basic); +**/ +WysiHat.Toolbar.ButtonSets.Basic = $A([ + { label: "Bold" }, + { label: "Underline" }, + { label: "Italic" } +]); diff --git a/src/wysihat/toolbar/button_sets.js b/src/wysihat/toolbar/button_sets.js deleted file mode 100644 index 4ac4e3a..0000000 --- a/src/wysihat/toolbar/button_sets.js +++ /dev/null @@ -1,21 +0,0 @@ -/** - * WysiHat.Toolbar.ButtonSets - * - * A namespace for various sets of Toolbar buttons. These sets should be - * compatible with WysiHat.Toolbar, and can be added to the toolbar with: - * toolbar.addButtonSet(WysiHat.Toolbar.ButtonSets.Basic); -**/ -WysiHat.Toolbar.ButtonSets = {}; - -/** - * WysiHat.Toolbar.ButtonSets.Basic - * - * A basic set of buttons: bold, underline, and italic. This set is - * compatible with WysiHat.Toolbar, and can be added to the toolbar with: - * toolbar.addButtonSet(WysiHat.Toolbar.ButtonSets.Basic); -**/ -WysiHat.Toolbar.ButtonSets.Basic = $A([ - { label: "Bold" }, - { label: "Underline" }, - { label: "Italic" } -]); From 355e1eb263775d3c583dbfa76aa25f5218fbb48d Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Thu, 4 Feb 2010 08:29:13 -0600 Subject: [PATCH 051/164] removed callback arg from attach --- src/wysihat/editor.js | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/wysihat/editor.js b/src/wysihat/editor.js index e38a000..f586100 100644 --- a/src/wysihat/editor.js +++ b/src/wysihat/editor.js @@ -9,7 +9,7 @@ WysiHat.Editor = { * * Creates a new editor for the textarea. **/ - attach: function(textarea, options, block) { + attach: function(textarea, options) { var editArea; options = $H(options); @@ -33,8 +33,6 @@ WysiHat.Editor = { editArea.load(); - if (block) block(editArea); - textarea.insert({before: editArea}); return editArea; From 12427ff0a276770dc91c1c0f4f652a60807c4d44 Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Thu, 4 Feb 2010 08:46:31 -0600 Subject: [PATCH 052/164] wrap up events in observer class --- src/wysihat/editor.js | 2 +- src/wysihat/editor/events.js | 80 ---------------------------- src/wysihat/editor/observer.js | 97 ++++++++++++++++++++++++++++++++++ test/editor.html | 2 + 4 files changed, 100 insertions(+), 81 deletions(-) delete mode 100644 src/wysihat/editor/events.js create mode 100644 src/wysihat/editor/observer.js diff --git a/src/wysihat/editor.js b/src/wysihat/editor.js index f586100..524411c 100644 --- a/src/wysihat/editor.js +++ b/src/wysihat/editor.js @@ -74,5 +74,5 @@ WysiHat.Editor = { }; //= require "editor/commands" -//= require "editor/events" +//= require "editor/observer" //= require "editor/persistence" diff --git a/src/wysihat/editor/events.js b/src/wysihat/editor/events.js deleted file mode 100644 index 0ecf97e..0000000 --- a/src/wysihat/editor/events.js +++ /dev/null @@ -1,80 +0,0 @@ -/** section: wysihat - * - * To observe any double clicks in the editor: - * - * var editor = WysiHat.Editor.attach(textarea); - * editor.observe('wysihat:dblclick', handler); - * - * The most useful event is the 'wysihat:change' event. It is fired anytime - * the text contents of the editor changes. This does not include any mouse - * or cursor movements. - * - * In addition to the standard DOM events, 'wysihat:cursormove' is fired - * anytime the cursor is moved from any mouse clicks or moved by a key - * press. Is also fired when something is typed because the cursor is - * still advancing. -**/ -document.observe("dom:loaded", function() { - $(document.body).observe("keydown", function(event) { - var editor = event.findElement("div.editor"); - if (editor && event.keyCode == 86) editor.fire("wysihat:paste"); - }); - - $(document.body).observe("paste", function(event) { - var editor = event.findElement("div.editor"); - if (editor) editor.fire("wysihat:paste"); - }); - - - var observeCursorMovementsHandler = function(editor) { - var range = editor.selection.getRange(); - if (editor.selection.previousRange != range) { - editor.fire("wysihat:cursormove"); - editor.selection.previousRange = range; - } - } - - $(document.body).observe("keyup", function(event) { - var editor = event.findElement("div.editor"); - if (editor) { - observeCursorMovementsHandler(editor); - - var contents = editor.rawContent(); - if (editor.previousContents != contents) { - editor.fire("wysihat:change"); - editor.previousContents = contents; - } - } - }); - - $(document.body).observe("mouseup", function(event) { - var editor = event.findElement("div.editor"); - if (editor) { - observeCursorMovementsHandler(editor); - - var range = editor.selection.getRange(); - // FIXME: firing an event here breaks double clicking to select word in IE - // if (!range.collapsed) - // editor.fire("wysihat:select"); - } - }); - - - var focusInHandler = function(event) { - var editor = event.findElement("div.editor"); - if (editor) editor.fire("wysihat:focus") - }; - - var focusOutHandler = function(event) { - var editor = event.findElement("div.editor"); - if (editor) editor.fire("wysihat:blur") - }; - - if (document.addEventListener) { - document.addEventListener("focus", focusInHandler, true); - document.addEventListener("blur", focusOutHandler, true); - } else { - document.observe("focusin", focusInHandler); - document.observe("focusout", focusOutHandler); - } -}); diff --git a/src/wysihat/editor/observer.js b/src/wysihat/editor/observer.js new file mode 100644 index 0000000..c215830 --- /dev/null +++ b/src/wysihat/editor/observer.js @@ -0,0 +1,97 @@ +WysiHat.Observer = Class.create({ + initialize: function(region) { + this.region = $(region); + this.registerObservers(); + this.start(); + }, + + registerObservers: function() { + this.region.observe("keydown", this.onRegionKeyDown.bind(this)); + this.region.observe("keyup", this.onRegionKeyUp.bind(this)); + this.region.observe("paste", this.onRegionPaste.bind(this)); + this.region.observe("mouseup", this.onRegionMouseUp.bind(this)); + + if (this.region.addEventListener) { + this.region.addEventListener("focus", this.onRegionFocusIn.bind(this), true); + this.region.addEventListener("blur", this.onRegionFocusOut.bind(this), true); + } else { + this.region.observe("focusin", this.onRegionFocusIn.bind(this)); + this.region.observe("focusout", this.onRegionFocusOut.bind(this)); + } + }, + + start: function() { + this.started = true; + }, + + stop: function() { + this.started = false; + }, + + onRegionKeyDown: function(event) { + if (!this.started) return; + var element = event.findElement("div.editor"); + if (element && event.keyCode == 86) element.fire("wysihat:paste"); + }, + + onRegionKeyUp: function(event) { + if (!this.started) return; + + var element = event.findElement("div.editor"); + if (element) { + this.observeCursorMovements(element); + this.observeChanges(element); + } + }, + + onRegionPaste: function(event) { + if (!this.started) return; + var element = event.findElement("div.editor"); + if (element) element.fire("wysihat:paste"); + }, + + onRegionMouseUp: function(event) { + if (!this.started) return; + + var element = event.findElement("div.editor"); + if (element) { + this.observeCursorMovements(element); + this.observeSelections(element); + } + }, + + onRegionFocusIn: function(event) { + if (!this.started) return; + var element = event.findElement("div.editor"); + if (element) element.fire("wysihat:focus"); + }, + + onRegionFocusOut: function(event) { + if (!this.started) return; + var element = event.findElement("div.editor"); + if (element) element.fire("wysihat:blur"); + }, + + observeChanges: function(editor) { + var contents = editor.rawContent(); + if (editor.previousContents != contents) { + editor.fire("wysihat:change"); + editor.previousContents = contents; + } + }, + + observeCursorMovements: function(editor) { + var range = editor.selection.getRange(); + if (editor.selection.previousRange != range) { + editor.fire("wysihat:cursormove"); + editor.selection.previousRange = range; + } + }, + + observeSelections: function(editor) { + var range = editor.selection.getRange(); + // FIXME: firing an event here breaks double clicking to select word in IE + // if (!range.collapsed) + // editor.fire("wysihat:select"); + } +}); diff --git a/test/editor.html b/test/editor.html index 0298beb..4f24200 100644 --- a/test/editor.html +++ b/test/editor.html @@ -61,6 +61,8 @@ var textarea, editor; Event.observe(window, 'load', function() { + new WysiHat.Observer(document.body); + textarea = $('note_body'); editor = WysiHat.Editor.attach(textarea); var toolbar = new WysiHat.Toolbar(editor); From bdae26195fd094b113b1f9611440bbb5c27d382a Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Thu, 4 Feb 2010 08:53:50 -0600 Subject: [PATCH 053/164] cleanup test editor js --- src/wysihat/editor.js | 5 +---- test/editor.html | 19 ++++++------------- 2 files changed, 7 insertions(+), 17 deletions(-) diff --git a/src/wysihat/editor.js b/src/wysihat/editor.js index 524411c..1e9e7e8 100644 --- a/src/wysihat/editor.js +++ b/src/wysihat/editor.js @@ -9,10 +9,9 @@ WysiHat.Editor = { * * Creates a new editor for the textarea. **/ - attach: function(textarea, options) { + attach: function(textarea) { var editArea; - options = $H(options); textarea = $(textarea); textarea.hide(); @@ -28,11 +27,9 @@ WysiHat.Editor = { editArea.textarea = textarea; WysiHat.Editor.extend(editArea); - editArea.selection = new WysiHat.Selection(editArea); editArea.load(); - textarea.insert({before: editArea}); return editArea; diff --git a/test/editor.html b/test/editor.html index 4f24200..c41d15d 100644 --- a/test/editor.html +++ b/test/editor.html @@ -63,8 +63,7 @@ Event.observe(window, 'load', function() { new WysiHat.Observer(document.body); - textarea = $('note_body'); - editor = WysiHat.Editor.attach(textarea); + editor = WysiHat.Editor.attach('note_body'); var toolbar = new WysiHat.Toolbar(editor); toolbar.addButtonSet(WysiHat.Toolbar.ButtonSets.Basic); @@ -82,33 +81,27 @@ editor.observe("wysihat:change", function(event) { editor.save(); + $('note_body_raw').setValue(event.element().rawContent()); }); editor.observe("wysihat:paste", function(event) { - setTimeout(function() { - editor.reload(); - }, 1); + (function() { editor.reload(); }).defer(); }); - textarea.show(); - textarea.disable(); - - editor.observe("wysihat:change", function(event) { - $('note_body_raw').value = event.element().rawContent() - }); + $('note_body').show(); });
    - - +
    From 6ede2ac1e57f08fbf110a6de6885804e154325d5 Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Thu, 4 Feb 2010 08:57:02 -0600 Subject: [PATCH 054/164] no iframe to style --- test/editor.html | 6 ------ 1 file changed, 6 deletions(-) diff --git a/test/editor.html b/test/editor.html index c41d15d..bafd94b 100644 --- a/test/editor.html +++ b/test/editor.html @@ -5,12 +5,6 @@ WYSIWYG - - - - -

    WysiHat functional tests for the Selection class

    - -
    - -
    - -
    log empty
    - - -

    testGetSelection: Select "ipsum" and click here

    - - - - -

    testGetRange: Select "ipsum" and click here

    - - - - -

    testGetNode: Select "ipsum" and click here

    - - - -

    testSelectNode: click here

    - - - -

    testCursorMove: click here, click in the text, and click here again

    - - - -

    - testSelectionState: select the entire first line, - then click here -

    - - -

    - testSelectImage: click on the image, - then click here -

    - - - - From fd006a4edcab1ceb7a8f42111f24e32a01d3e837 Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Mon, 8 Feb 2010 15:15:02 -0600 Subject: [PATCH 076/164] ignore invalid command --- src/wysihat/editor/commands.js | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/src/wysihat/editor/commands.js b/src/wysihat/editor/commands.js index 9dfb364..06507f4 100644 --- a/src/wysihat/editor/commands.js +++ b/src/wysihat/editor/commands.js @@ -291,10 +291,13 @@ WysiHat.Commands = (function() { **/ function execCommand(command, ui, value) { var handler = this.commands.get(command); - if (handler) + if (handler) { handler.bind(this)(value); - else - document.execCommand(command, ui, value); + } else { + try { + document.execCommand(command, ui, value); + } catch(e) { return null; } + } } /** @@ -311,10 +314,13 @@ WysiHat.Commands = (function() { **/ function queryCommandState(state) { var handler = this.queryCommands.get(state); - if (handler) + if (handler) { return handler.bind(this)(); - else - return document.queryCommandState(state); + } else { + try { + return document.queryCommandState(state); + } catch(e) { return null; } + } } /** From 9312fde55e6b63ad49bc3dcd1102a25e15efa574 Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Mon, 8 Feb 2010 17:09:03 -0600 Subject: [PATCH 077/164] dump sprockwatch --- Rakefile | 12 +----------- vendor/sprockwatch/lib/sprockwatch.rb | 19 ------------------- 2 files changed, 1 insertion(+), 30 deletions(-) delete mode 100644 vendor/sprockwatch/lib/sprockwatch.rb diff --git a/Rakefile b/Rakefile index 2118aaf..e1bca78 100644 --- a/Rakefile +++ b/Rakefile @@ -21,11 +21,6 @@ task :default => :dist desc "Builds the distribution." task :dist => ["sprocketize:prototype", "sprocketize:wysihat"] -task :watch do - ENV['WATCH'] = "1" - Rake::Task["sprocketize:wysihat"].invoke -end - namespace :sprocketize do task :dist_dir do FileUtils.mkdir_p(WYSIHAT_DIST_DIR) @@ -33,7 +28,6 @@ namespace :sprocketize do task :wysihat => [:update_submodules, :dist_dir] do require File.join(WYSIHAT_ROOT, "vendor", "sprockets", "lib", "sprockets") - require File.join(WYSIHAT_ROOT, "vendor", "sprockwatch", "lib", "sprockwatch") secretary = Sprockets::Secretary.new( :root => File.join(WYSIHAT_ROOT, "src"), @@ -41,11 +35,7 @@ namespace :sprocketize do :source_files => ["wysihat.js"] ) - save = lambda { - secretary.concatenation.save_to(File.join(WYSIHAT_DIST_DIR, "wysihat.js")) - } - - ENV['WATCH'] ? secretary.watch!(&save) : save.call + secretary.concatenation.save_to(File.join(WYSIHAT_DIST_DIR, "wysihat.js")) end task :prototype => [:update_submodules, :dist_dir] do diff --git a/vendor/sprockwatch/lib/sprockwatch.rb b/vendor/sprockwatch/lib/sprockwatch.rb deleted file mode 100644 index 576d532..0000000 --- a/vendor/sprockwatch/lib/sprockwatch.rb +++ /dev/null @@ -1,19 +0,0 @@ -module Sprockets - module Watch - def watch! - previous_mtime = concatenation.mtime - - loop do - sleep 1 - reset! - - if concatenation.mtime > previous_mtime - previous_mtime = concatenation.mtime - yield - end - end - end - end -end - -Sprockets::Secretary.send :include, Sprockets::Watch From 0c233575df0c8e271f6a510a7c36daa153996c5f Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Mon, 8 Feb 2010 17:09:30 -0600 Subject: [PATCH 078/164] test runner doesn't really work --- Rakefile | 26 +------------------------- 1 file changed, 1 insertion(+), 25 deletions(-) diff --git a/Rakefile b/Rakefile index e1bca78..e2bb1c0 100644 --- a/Rakefile +++ b/Rakefile @@ -86,33 +86,9 @@ namespace :doc do end desc "Builds the distribution, runs the JavaScript unit tests and collects their results." -task :test => ['test:build', 'test:run'] +task :test => ['test:build'] namespace :test do - task :run do - testcases = ENV['TESTCASES'] - browsers_to_test = ENV['BROWSERS'] && ENV['BROWSERS'].split(',') - tests_to_run = ENV['TESTS'] && ENV['TESTS'].split(',') - runner = UnittestJS::WEBrickRunner::Runner.new(:test_dir => WYSIHAT_TMP_DIR) - - Dir[File.join(WYSIHAT_TMP_DIR, '*_test.html')].each do |file| - file = File.basename(file) - test = file.sub('_test.html', '') - unless tests_to_run && !tests_to_run.include?(test) - runner.add_test(file, testcases) - end - end - - UnittestJS::Browser::SUPPORTED.each do |browser| - unless browsers_to_test && !browsers_to_test.include?(browser) - runner.add_browser(browser.to_sym) - end - end - - trap('INT') { runner.teardown; exit } - runner.run - end - task :build => [:clean, :dist] do require File.join(WYSIHAT_ROOT, "vendor", "unittest_js", "lib", "unittest_js") builder = UnittestJS::Builder::SuiteBuilder.new({ From 30a55d93c333d210b2f01f1a6389e15915129306 Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Mon, 8 Feb 2010 17:54:59 -0600 Subject: [PATCH 079/164] improve getNode detection --- src/wysihat/dom/range.js | 32 +++++++++++++++++++++++++------- test/unit/range_test.js | 12 ++++++------ 2 files changed, 31 insertions(+), 13 deletions(-) diff --git a/src/wysihat/dom/range.js b/src/wysihat/dom/range.js index ba2d941..d2ba3b4 100644 --- a/src/wysihat/dom/range.js +++ b/src/wysihat/dom/range.js @@ -373,19 +373,37 @@ if (typeof Range == 'undefined') { } Object.extend(Range.prototype, (function() { + function compareRange(other) { + return !( + this.compareBoundaryPoints(0, other) == 1 && + this.compareBoundaryPoints(2, other) == 1 && + this.compareBoundaryPoints(1, other) == 1 && + this.compareBoundaryPoints(3, other) == 1 + || + this.compareBoundaryPoints(0, other) == -1 && + this.compareBoundaryPoints(2, other) == -1 && + this.compareBoundaryPoints(1, other) == -1 && + this.compareBoundaryPoints(3, other) == -1 + ); + } + function getNode() { - var node = this.commonAncestorContainer; + var parent = this.commonAncestorContainer; - if (this.startContainer.length == this.startOffset && this.startContainer.nextSibling) - return this.startContainer.nextSibling; + while (parent.nodeType == Node.TEXT_NODE) + parent = parent.parentNode; - while (node.nodeType == Node.TEXT_NODE) - node = node.parentNode; + var child = parent.childElements().detect(function(child) { + var range = document.createRange(); + range.selectNodeContents(child); + return this.compareRange(range); + }.bind(this)); - return node; + return child || parent; } return { - getNode: getNode + compareRange: compareRange, + getNode: getNode }; })()); diff --git a/test/unit/range_test.js b/test/unit/range_test.js index 5867962..93e9d74 100644 --- a/test/unit/range_test.js +++ b/test/unit/range_test.js @@ -280,12 +280,12 @@ new Test.Unit.Runner({ this.range.selectNode($('lorem')); runner.assertEqual($('lorem'), this.range.getNode(), "getNode"); - this.range.setStart($('lorem'), 0); - this.range.setEnd($('lorem'), 1); - runner.assertEqual($('lorem'), this.range.getNode(), "getNode"); + // this.range.setStart($('lorem'), 0); + // this.range.setEnd($('lorem'), 1); + // runner.assertEqual($('lorem'), this.range.getNode(), "getNode"); - this.range.selectNodeContents($('content')); - this.range.collapse(true); - runner.assertEqual($('lorem'), this.range.getNode(), "getNode"); + // this.range.selectNodeContents($('content')); + // this.range.collapse(true); + // runner.assertEqual($('lorem'), this.range.getNode(), "getNode"); } }); From 15a694229d0bd0c8c20030c569adaf08cceaafe3 Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Mon, 8 Feb 2010 17:55:31 -0600 Subject: [PATCH 080/164] whitespace! --- src/wysihat/dom/range.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/wysihat/dom/range.js b/src/wysihat/dom/range.js index d2ba3b4..9cdea1a 100644 --- a/src/wysihat/dom/range.js +++ b/src/wysihat/dom/range.js @@ -386,7 +386,7 @@ Object.extend(Range.prototype, (function() { this.compareBoundaryPoints(3, other) == -1 ); } - + function getNode() { var parent = this.commonAncestorContainer; From 8ed88c29ce82b8db0a4efa959028b21c3407bfa8 Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Mon, 8 Feb 2010 18:53:17 -0600 Subject: [PATCH 081/164] missed some tests --- src/wysihat/dom/range.js | 33 ++++++++++++++++++--------------- test/unit/range_test.js | 10 +++------- 2 files changed, 21 insertions(+), 22 deletions(-) diff --git a/src/wysihat/dom/range.js b/src/wysihat/dom/range.js index 9cdea1a..b446110 100644 --- a/src/wysihat/dom/range.js +++ b/src/wysihat/dom/range.js @@ -373,18 +373,22 @@ if (typeof Range == 'undefined') { } Object.extend(Range.prototype, (function() { - function compareRange(other) { - return !( - this.compareBoundaryPoints(0, other) == 1 && - this.compareBoundaryPoints(2, other) == 1 && - this.compareBoundaryPoints(1, other) == 1 && - this.compareBoundaryPoints(3, other) == 1 - || - this.compareBoundaryPoints(0, other) == -1 && - this.compareBoundaryPoints(2, other) == -1 && - this.compareBoundaryPoints(1, other) == -1 && - this.compareBoundaryPoints(3, other) == -1 - ); + function beforeRange(range) { + return (this.compareBoundaryPoints(this.START_TO_START, range) == -1 && + this.compareBoundaryPoints(this.START_TO_END, range) == -1 && + this.compareBoundaryPoints(this.END_TO_END, range) == -1 && + this.compareBoundaryPoints(this.END_TO_START, range) == -1); + } + + function afterRange(range) { + return (this.compareBoundaryPoints(this.START_TO_START, range) == 1 && + this.compareBoundaryPoints(this.START_TO_END, range) == 1 && + this.compareBoundaryPoints(this.END_TO_END, range) == 1 && + this.compareBoundaryPoints(this.END_TO_START, range) == 1); + } + + function betweenRange(range) { + return !(beforeRange.bind(this)(range) || afterRange.bind(this)(range)); } function getNode() { @@ -396,14 +400,13 @@ Object.extend(Range.prototype, (function() { var child = parent.childElements().detect(function(child) { var range = document.createRange(); range.selectNodeContents(child); - return this.compareRange(range); + return betweenRange.bind(this)(range); }.bind(this)); return child || parent; } return { - compareRange: compareRange, - getNode: getNode + getNode: getNode }; })()); diff --git a/test/unit/range_test.js b/test/unit/range_test.js index 93e9d74..510918f 100644 --- a/test/unit/range_test.js +++ b/test/unit/range_test.js @@ -280,12 +280,8 @@ new Test.Unit.Runner({ this.range.selectNode($('lorem')); runner.assertEqual($('lorem'), this.range.getNode(), "getNode"); - // this.range.setStart($('lorem'), 0); - // this.range.setEnd($('lorem'), 1); - // runner.assertEqual($('lorem'), this.range.getNode(), "getNode"); - - // this.range.selectNodeContents($('content')); - // this.range.collapse(true); - // runner.assertEqual($('lorem'), this.range.getNode(), "getNode"); + this.range.setStart($('lorem'), 0); + this.range.setEnd($('lorem'), 1); + runner.assertEqual($('lorem'), this.range.getNode(), "getNode"); } }); From cb34a925ae7908171e786d0a1e3eba8cbb6e5c28 Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Mon, 8 Feb 2010 18:55:10 -0600 Subject: [PATCH 082/164] compareRanges is unused --- src/wysihat/dom/selection.js | 30 ------------------------------ 1 file changed, 30 deletions(-) diff --git a/src/wysihat/dom/selection.js b/src/wysihat/dom/selection.js index 4233fab..514fdd2 100644 --- a/src/wysihat/dom/selection.js +++ b/src/wysihat/dom/selection.js @@ -246,36 +246,6 @@ WysiHat.Selection = Class.create((function() { return range; } - function compareRanges(r1, r2) { - if (r1.compareEndPoints) { - return !( - r2.compareEndPoints('StartToStart', r1) == 1 && - r2.compareEndPoints('EndToEnd', r1) == 1 && - r2.compareEndPoints('StartToEnd', r1) == 1 && - r2.compareEndPoints('EndToStart', r1) == 1 - || - r2.compareEndPoints('StartToStart', r1) == -1 && - r2.compareEndPoints('EndToEnd', r1) == -1 && - r2.compareEndPoints('StartToEnd', r1) == -1 && - r2.compareEndPoints('EndToStart', r1) == -1 - ); - } else if (r1.compareBoundaryPoints) { - return !( - r2.compareBoundaryPoints(0, r1) == 1 && - r2.compareBoundaryPoints(2, r1) == 1 && - r2.compareBoundaryPoints(1, r1) == 1 && - r2.compareBoundaryPoints(3, r1) == 1 - || - r2.compareBoundaryPoints(0, r1) == -1 && - r2.compareBoundaryPoints(2, r1) == -1 && - r2.compareBoundaryPoints(1, r1) == -1 && - r2.compareBoundaryPoints(3, r1) == -1 - ); - } - - return null; - }; - function setBookmark() { var bookmark = document.getElementById('bookmark'); if (bookmark) From 833ae6d3a502d6ed13e6b88ea29e3f1a692fe1ad Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Mon, 8 Feb 2010 18:57:11 -0600 Subject: [PATCH 083/164] inline createRangeFromElement --- src/wysihat/dom/selection.js | 22 ++++------------------ 1 file changed, 4 insertions(+), 18 deletions(-) diff --git a/src/wysihat/dom/selection.js b/src/wysihat/dom/selection.js index 514fdd2..0371089 100644 --- a/src/wysihat/dom/selection.js +++ b/src/wysihat/dom/selection.js @@ -205,17 +205,14 @@ WysiHat.Selection = Class.create((function() { var selection = getSelection(); if (Prototype.Browser.IE) { - var range = createRangeFromElement(document, node); + var range = document.body.createTextRange(); + range.moveToElementText(node); range.select(); } else if (Prototype.Browser.WebKit) { selection.setBaseAndExtent(node, 0, node, node.innerText.length); - } else if (Prototype.Browser.Opera) { - range = document.createRange(); - range.selectNode(node); - selection.removeAllRanges(); - selection.addRange(range); } else { - var range = createRangeFromElement(document, node); + var range = document.createRange(); + range.selectNodeContents(node); selection.removeAllRanges(); selection.addRange(range); } @@ -235,17 +232,6 @@ WysiHat.Selection = Class.create((function() { } } - function createRangeFromElement(document, node) { - if (document.body.createTextRange) { - var range = document.body.createTextRange(); - range.moveToElementText(node); - } else if (document.createRange) { - var range = document.createRange(); - range.selectNodeContents(node); - } - return range; - } - function setBookmark() { var bookmark = document.getElementById('bookmark'); if (bookmark) From 195a15c2311ac0da0eb475a88c07af49b6b6685c Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Mon, 8 Feb 2010 20:30:40 -0600 Subject: [PATCH 084/164] use range api for selectNode in Safari --- src/wysihat/dom/selection.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/wysihat/dom/selection.js b/src/wysihat/dom/selection.js index 0371089..951dd92 100644 --- a/src/wysihat/dom/selection.js +++ b/src/wysihat/dom/selection.js @@ -208,8 +208,6 @@ WysiHat.Selection = Class.create((function() { var range = document.body.createTextRange(); range.moveToElementText(node); range.select(); - } else if (Prototype.Browser.WebKit) { - selection.setBaseAndExtent(node, 0, node, node.innerText.length); } else { var range = document.createRange(); range.selectNodeContents(node); From 9ae3a0b571c48630623e4efd49940547ff88d9a4 Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Mon, 8 Feb 2010 21:18:38 -0600 Subject: [PATCH 085/164] drop webkit only selection test --- test/unit/selection_test.js | 23 ----------------------- 1 file changed, 23 deletions(-) diff --git a/test/unit/selection_test.js b/test/unit/selection_test.js index 0f490d5..bd0d302 100644 --- a/test/unit/selection_test.js +++ b/test/unit/selection_test.js @@ -149,28 +149,5 @@ new Test.Unit.Runner({ runner.assertEqual(11, this.selection.focusOffset, "focusOffset"); runner.assertEqual(false, this.selection.isCollapsed, "isCollapsed"); runner.assertEqual(1, this.selection.rangeCount, "rangeCount"); - }, - - // Webkit only - testSetBaseAndExtent: function() { - var runner = this; - - if (this.selection.setBaseAndExtent) { - var element = $('lorem'); - this.selection.setBaseAndExtent(element, 0, element, element.innerText.length); - - runner.assertEqual("Lorem ipsum", this.selection.anchorNode.textContent, "anchorNode.textContent"); - runner.assertEqual(0, this.selection.anchorOffset, "anchorOffset"); - runner.assertEqual("Lorem ipsum", this.selection.focusNode.textContent, "focusNode.textContent"); - runner.assertEqual(11, this.selection.focusOffset, "focusOffset"); - runner.assertEqual(false, this.selection.isCollapsed, "isCollapsed"); - runner.assertEqual(1, this.selection.rangeCount, "rangeCount"); - - // Webkit extensions - runner.assertEqual("Lorem ipsum", this.selection.baseNode.textContent, "baseNode.textContent"); - runner.assertEqual(0, this.selection.baseOffset, "baseOffset"); - runner.assertEqual("Lorem ipsum", this.selection.extentNode.textContent, "extentNode.textContent"); - runner.assertEqual(11, this.selection.extentOffset, "extentOffset"); - } } }); From 77e239dc2e69823bb01d52ba79a552ca20895035 Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Mon, 8 Feb 2010 21:23:49 -0600 Subject: [PATCH 086/164] move getNode selection helper to selection instance --- src/wysihat/dom/selection.js | 51 +++++++++++++++++++++------------- src/wysihat/editor/commands.js | 2 +- test/unit/editor_test.js | 6 ++-- test/unit/selection_test.js | 4 +-- 4 files changed, 36 insertions(+), 27 deletions(-) diff --git a/src/wysihat/dom/selection.js b/src/wysihat/dom/selection.js index 951dd92..76b622e 100644 --- a/src/wysihat/dom/selection.js +++ b/src/wysihat/dom/selection.js @@ -155,6 +155,14 @@ if (!window.getSelection) { }, toString: function() { return this._document.selection.createRange().text; + }, + + // Extension + // TODO: IE selectNode should work with range.selectNode + selectNode: function(element) { + var range = this._document.body.createTextRange(); + range.moveToElementText(element); + range.select(); } }; @@ -163,6 +171,29 @@ if (!window.getSelection) { })(); } +(function() { + var prototype; + if (Prototype.Browser.WebKit) + prototype = window.getSelection(); + else if (Prototype.Browser.Gecko) + prototype = Selection.prototype; + + if (prototype) { + Object.extend(prototype, (function() { + function selectNode(element) { + var range = document.createRange(); + range.selectNode(element); + this.removeAllRanges(); + this.addRange(range); + } + + return { + selectNode: selectNode + } + })()); + } +})(); + /** section: dom * class WysiHat.Selection **/ @@ -197,25 +228,6 @@ WysiHat.Selection = Class.create((function() { return range; } - /** - * WysiHat.Selection#selectNode(node) -> undefined - * - node (Element): Element or node to select - **/ - function selectNode(node) { - var selection = getSelection(); - - if (Prototype.Browser.IE) { - var range = document.body.createTextRange(); - range.moveToElementText(node); - range.select(); - } else { - var range = document.createRange(); - range.selectNodeContents(node); - selection.removeAllRanges(); - selection.addRange(range); - } - } - /** * WysiHat.Selection#getNode() -> Element * Returns selected node. @@ -274,7 +286,6 @@ WysiHat.Selection = Class.create((function() { } return { - selectNode: selectNode, getNode: getNode, setBookmark: setBookmark, moveToBookmark: moveToBookmark diff --git a/src/wysihat/editor/commands.js b/src/wysihat/editor/commands.js index 06507f4..e328997 100644 --- a/src/wysihat/editor/commands.js +++ b/src/wysihat/editor/commands.js @@ -169,7 +169,7 @@ WysiHat.Commands = (function() { function unlinkSelection() { var node = this.selection.getNode(); if (this.linkSelected()) - this.selection.selectNode(node); + window.getSelection().selectNode(node); this.execCommand('unlink', false, null); } diff --git a/test/unit/editor_test.js b/test/unit/editor_test.js index 17ff904..3fa8ac0 100644 --- a/test/unit/editor_test.js +++ b/test/unit/editor_test.js @@ -21,12 +21,12 @@ new Test.Unit.Runner({ var runner = this; // this.editor.insertHTML("

    Hello.

    "); - this.editor.setContent("

    Hello.

    "); + this.editor.setContent('

    Hello.

    '); - this.editor.selection.selectNode(this.editor.down(0)); + window.getSelection().selectNode(this.editor.down('#hello')); this.editor.boldSelection(); runner.assert(this.editor.boldSelected()); - runner.assertEqual("

    Hello.

    ", this.editor.content()); + runner.assertEqual('

    Hello.

    ', this.editor.content()); } }); diff --git a/test/unit/selection_test.js b/test/unit/selection_test.js index bd0d302..e63d92a 100644 --- a/test/unit/selection_test.js +++ b/test/unit/selection_test.js @@ -12,9 +12,7 @@ new Test.Unit.Runner({ testSelectNode: function() { var runner = this; - range = document.createRange(); - range.selectNodeContents($('lorem')); - this.selection.addRange(range); + this.selection.selectNode($('lorem')); runner.assertEqual("Lorem ipsum", this.selection.anchorNode.textContent, "anchorNode.textContent"); runner.assertEqual(0, this.selection.anchorOffset, "anchorOffset"); From e27fd7f181a96d2965c5318b7bbf2d81b4a09a8f Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Mon, 8 Feb 2010 22:04:59 -0600 Subject: [PATCH 087/164] move remaining selection helpers to selection prototype --- src/wysihat/dom/range.js | 2 +- src/wysihat/dom/selection.js | 158 +++++++++++------------------- src/wysihat/editor.js | 1 - src/wysihat/editor/commands.js | 10 +- src/wysihat/editor/persistence.js | 4 +- 5 files changed, 67 insertions(+), 108 deletions(-) diff --git a/src/wysihat/dom/range.js b/src/wysihat/dom/range.js index b446110..74f47ab 100644 --- a/src/wysihat/dom/range.js +++ b/src/wysihat/dom/range.js @@ -403,7 +403,7 @@ Object.extend(Range.prototype, (function() { return betweenRange.bind(this)(range); }.bind(this)); - return child || parent; + return $(child || parent); } return { diff --git a/src/wysihat/dom/selection.js b/src/wysihat/dom/selection.js index 76b622e..128f53e 100644 --- a/src/wysihat/dom/selection.js +++ b/src/wysihat/dom/selection.js @@ -156,13 +156,41 @@ if (!window.getSelection) { toString: function() { return this._document.selection.createRange().text; }, - + // Extension + // TODO: More robust getNode + getNode: function() { + var range = this._document.selection.createRange(); + return $(range.parentElement()); + }, // TODO: IE selectNode should work with range.selectNode selectNode: function(element) { var range = this._document.body.createTextRange(); range.moveToElementText(element); range.select(); + }, + setBookmark: function() { + var bookmark = $('bookmark'); + if (bookmark) bookmark.remove(); + + bookmark = new Element('span', { 'id': 'bookmark' }).update(" "); + var parent = new Element('div'); + parent.appendChild(bookmark); + + var range = this._document.selection.createRange(); + range.collapse(); + range.pasteHTML(parent.innerHTML); + }, + moveToBookmark: function() { + var bookmark = $('bookmark'); + if (!bookmark) return; + + var range = this._document.selection.createRange(); + range.moveToElementText(bookmark); + range.collapse(); + range.select(); + + bookmark.remove(); } }; @@ -180,6 +208,13 @@ if (!window.getSelection) { if (prototype) { Object.extend(prototype, (function() { + function getNode() { + if (this.rangeCount > 0) + return this.getRangeAt(0).getNode(); + else + return null; + } + function selectNode(element) { var range = document.createRange(); range.selectNode(element); @@ -187,107 +222,32 @@ if (!window.getSelection) { this.addRange(range); } - return { - selectNode: selectNode - } - })()); - } -})(); + function setBookmark() { + var bookmark = $('bookmark'); + if (bookmark) bookmark.remove(); -/** section: dom - * class WysiHat.Selection -**/ -WysiHat.Selection = Class.create((function() { - /** - * WysiHat.Selection#getSelection() -> Selection - * Get selected text. - **/ - function getSelection() { - return Prototype.Browser.IE ? document.selection : window.getSelection(); - } - - /** - * WysiHat.Selection#getRange() -> Range - * Get range for selected text. - **/ - function getRange() { - var range = null, selection = getSelection(); - - try { - if (selection.getRangeAt) - range = selection.getRangeAt(0); - else - range = selection.createRange(); - } catch(e) { return null; } - - if (Prototype.Browser.WebKit) { - range.setStart(selection.baseNode, selection.baseOffset); - range.setEnd(selection.extentNode, selection.extentOffset); - } - - return range; - } - - /** - * WysiHat.Selection#getNode() -> Element - * Returns selected node. - **/ - function getNode() { - if (Prototype.Browser.IE) { - var range = getRange(); - return range.parentElement(); - } else { - var selection = window.getSelection(); - return selection.getRangeAt(0).getNode(); - } - } - - function setBookmark() { - var bookmark = document.getElementById('bookmark'); - if (bookmark) - bookmark.parentNode.removeChild(bookmark); - - bookmark = document.createElement('span'); - bookmark.id = 'bookmark'; - bookmark.innerHTML = ' '; + bookmark = new Element('span', { 'id': 'bookmark' }).update(" "); + this.getRangeAt(0).insertNode(bookmark); + } - if (Prototype.Browser.IE) { - var range = document.selection.createRange(); - var parent = document.createElement('div'); - parent.appendChild(bookmark); - range.collapse(); - range.pasteHTML(parent.innerHTML); - } - else { - var range = getRange(); - range.insertNode(bookmark); - } - } + function moveToBookmark() { + var bookmark = $('bookmark'); + if (!bookmark) return; - function moveToBookmark() { - var bookmark = document.getElementById('bookmark'); - if (!bookmark) - return; + var range = document.createRange(); + range.setStartBefore(bookmark); + this.removeAllRanges(); + this.addRange(range); - if (Prototype.Browser.IE) { - var range = getRange(); - range.moveToElementText(bookmark); - range.collapse(); - range.select(); - } else if (Prototype.Browser.WebKit) { - var selection = getSelection(); - selection.setBaseAndExtent(bookmark, 0, bookmark, 0); - } else { - var range = getRange(); - range.setStartBefore(bookmark); - } + bookmark.remove(); + } - bookmark.parentNode.removeChild(bookmark); + return { + getNode: getNode, + selectNode: selectNode, + setBookmark: setBookmark, + moveToBookmark: moveToBookmark + } + })()); } - - return { - getNode: getNode, - setBookmark: setBookmark, - moveToBookmark: moveToBookmark - }; -})()); +})(); diff --git a/src/wysihat/editor.js b/src/wysihat/editor.js index 7a1ca81..0e5e984 100644 --- a/src/wysihat/editor.js +++ b/src/wysihat/editor.js @@ -27,7 +27,6 @@ WysiHat.Editor = { editArea.textarea = textarea; WysiHat.Editor.extend(editArea); - editArea.selection = new WysiHat.Selection(); editArea.load(); textarea.insert({before: editArea}); diff --git a/src/wysihat/editor/commands.js b/src/wysihat/editor/commands.js index e328997..6d2b738 100644 --- a/src/wysihat/editor/commands.js +++ b/src/wysihat/editor/commands.js @@ -147,7 +147,7 @@ WysiHat.Commands = (function() { * Returns the alignment of the selected text area **/ function alignSelected() { - var node = this.selection.getNode(); + var node = window.getSelection().getNode(); return Element.getStyle(node, 'textAlign'); } @@ -167,7 +167,7 @@ WysiHat.Commands = (function() { * Selects the entire link at the cursor and removes it **/ function unlinkSelection() { - var node = this.selection.getNode(); + var node = window.getSelection().getNode(); if (this.linkSelected()) window.getSelection().selectNode(node); @@ -180,7 +180,7 @@ WysiHat.Commands = (function() { * Check if current selection is link. **/ function linkSelected() { - var node = this.selection.getNode(); + var node = window.getSelection().getNode(); return node ? node.tagName.toUpperCase() == 'A' : false; } @@ -220,7 +220,7 @@ WysiHat.Commands = (function() { * Check if current selection is within an ordered list. **/ function orderedListSelected() { - var element = $(this.selection.getNode()); + var element = window.getSelection().getNode(); return element.match("[contenteditable=true] ol, [contenteditable=true] ol *"); } @@ -249,7 +249,7 @@ WysiHat.Commands = (function() { * Check if current selection is within an unordered list. **/ function unorderedListSelected() { - var element = $(this.selection.getNode()); + var element = window.getSelection().getNode(); return element.match("[contenteditable=true] ul, [contenteditable=true] ul *"); } diff --git a/src/wysihat/editor/persistence.js b/src/wysihat/editor/persistence.js index 0f9c08b..24bcef8 100644 --- a/src/wysihat/editor/persistence.js +++ b/src/wysihat/editor/persistence.js @@ -88,10 +88,10 @@ WysiHat.Persistence = (function() { * Saves current contents and loads contents into editor. **/ function reload() { - this.selection.setBookmark(); + window.getSelection().setBookmark(); this.save(); this.load(); - this.selection.moveToBookmark(); + window.getSelection().moveToBookmark(); } return { From 93ddafa681835553bb148d0bd7f977fb2edc50ea Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Wed, 10 Feb 2010 09:08:12 -0600 Subject: [PATCH 088/164] remove less important examples --- examples/align_selection.html | 93 ------------------------- examples/flash.html | 93 ------------------------- examples/font_selection.html | 118 -------------------------------- examples/heading_selection.html | 115 ------------------------------- examples/undo_redo.html | 81 ---------------------- 5 files changed, 500 deletions(-) delete mode 100644 examples/align_selection.html delete mode 100644 examples/flash.html delete mode 100644 examples/font_selection.html delete mode 100644 examples/heading_selection.html delete mode 100644 examples/undo_redo.html diff --git a/examples/align_selection.html b/examples/align_selection.html deleted file mode 100644 index 1a5560d..0000000 --- a/examples/align_selection.html +++ /dev/null @@ -1,93 +0,0 @@ - - - - WYSIWYG - - - - - - - - - - -

    Oops, you need to build the package before running this example. It's easy: just run rake in the project's directory.

    - -

    This example shows you how to create a simple UI for aligning text.

    - -
    -  Event.observe(window, 'load', function() {
    -    var editor = WysiHat.Editor.attach('content');
    -    var toolbar = new WysiHat.Toolbar(editor);
    -
    -    toolbar.addButton({
    -      label: "Align left",
    -      handler: function(editor) { return editor.alignSelection('left'); }
    -    });
    -
    -    toolbar.addButton({
    -      label: "Align center",
    -      handler: function(editor) { return editor.alignSelection('center'); }
    -    });
    -
    -    toolbar.addButton({
    -      label: "Align right",
    -      handler: function(editor) { return editor.alignSelection('right'); }
    -    });
    -  });
    -
    - -
    - - -
    - - diff --git a/examples/flash.html b/examples/flash.html deleted file mode 100644 index 99188fc..0000000 --- a/examples/flash.html +++ /dev/null @@ -1,93 +0,0 @@ - - - - WYSIWYG - - - - - - - - - - -

    Oops, you need to build the package before running this example. It's easy: just run rake in the project's directory.

    - -

    This example shows you how to create a simple UI for adding Flash movies

    - -
    -  var FlashHelper = {
    -    promptFlash: function() {
    -      var value = prompt("Enter a Url", "http://wwwimages.adobe.com/www.adobe.com/homepage/en_us/fma_rotation/fma2/maxintro.swf")
    -      if(value)
    -        this.insertHTML(
    -          '<object><param name="movie" value="' + value + '"><embed src="' + value + '"></embed></object>'
    -        );
    -    }
    -  }
    -
    -  WysiHat.Editor.include(FlashHelper);
    -
    -  Event.observe(window, 'load', function() {
    -    var editor = WysiHat.Editor.attach('content');
    -    var toolbar = new WysiHat.Toolbar(editor);
    -
    -    toolbar.addButton({
    -      label: "Flash",
    -      handler: function(editor) { return editor.promptFlash(); }
    -    });
    -  });
    -
    - -
    - -
    - - diff --git a/examples/font_selection.html b/examples/font_selection.html deleted file mode 100644 index 8e973d6..0000000 --- a/examples/font_selection.html +++ /dev/null @@ -1,118 +0,0 @@ - - - - WYSIWYG - - - - - - - - - - -

    Oops, you need to build the package before running this example. It's easy: just run rake in the project's directory.

    - -

    This example shows you a very simple way to create buttons for selecting fonts.

    - -
    -  Event.observe(window, 'load', function() {
    -    var editor = WysiHat.Editor.attach('content');
    -    var toolbar = new WysiHat.Toolbar(editor);
    -    
    -    toolbar.addButton({
    -      label: "Georgia",
    -      handler: function(editor) { return editor.fontSelection('Georgia'); }
    -    });
    -    
    -    toolbar.addButton({
    -      label: "Arial",
    -      handler: function(editor) { return editor.fontSelection('Arial'); }
    -    });
    -    
    -    var georgiaButton = $$('.editor_toolbar .georgia').first();
    -    editor.observe('wysihat:cursormove', function(event) {
    -      if (editor.getSelectedStyles().get('fontname') == 'Georgia')
    -        georgiaButton.addClassName('selected')
    -      else
    -        georgiaButton.removeClassName('selected');
    -    });
    -    
    -    var arialButton = $$('.editor_toolbar .arial').first();
    -    editor.observe('wysihat:cursormove', function(event) {
    -      if (editor.getSelectedStyles().get('fontname') == 'Arial')
    -        arialButton.addClassName('selected')
    -      else
    -        arialButton.removeClassName('selected');
    -    });
    -
    - -
    - - - - -
    - - diff --git a/examples/heading_selection.html b/examples/heading_selection.html deleted file mode 100644 index d9b3860..0000000 --- a/examples/heading_selection.html +++ /dev/null @@ -1,115 +0,0 @@ - - - - WYSIWYG - - - - - - - - - - -

    Oops, you need to build the package before running this example. It's easy: just run rake in the project's directory.

    - -

    This example shows you how to create a simple UI for using html headings.

    - -
    -  Event.observe(window, 'load', function() {
    -    var editor = WysiHat.Editor.attach('content');
    -    var toolbar = new WysiHat.Toolbar(editor);
    -
    -    toolbar.addButton({
    -      label: "H1",
    -      handler: function(editor) { return editor.formatblockSelection('h1'); }
    -    });
    -
    -    toolbar.addButton({
    -      label: "H2",
    -      handler: function(editor) { return editor.formatblockSelection('h2'); }
    -    });
    -
    -    toolbar.addButton({
    -      label: "H3",
    -      handler: function(editor) { return editor.formatblockSelection('h3'); }
    -    });
    -
    -    toolbar.addButton({
    -      label: "default",
    -      handler: function(editor) { return editor.formatblockSelection('p'); }
    -    });
    -  });
    -
    - -
    - - -
    - - diff --git a/examples/undo_redo.html b/examples/undo_redo.html deleted file mode 100644 index abb8886..0000000 --- a/examples/undo_redo.html +++ /dev/null @@ -1,81 +0,0 @@ - - - - WYSIWYG - - - - - - - - - - -

    Oops, you need to build the package before running this example. It's easy: just run rake in the project's directory.

    - -

    This example shows you how to create a simple UI for using Undo & Redo functionality

    - -
    -  Event.observe(window, 'load', function() {
    -    var editor = WysiHat.Editor.attach('content');
    -    var toolbar = new WysiHat.Toolbar(editor);
    -    
    -    toolbar.addButton({
    -      label: "Undo"
    -    });
    -    
    -    toolbar.addButton({
    -      label: "Redo"
    -    });
    -  });
    -
    - -
    - - -
    - - From 36ec2720e4c2d581e55ef63c2df3f2efa876e657 Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Wed, 10 Feb 2010 09:58:49 -0600 Subject: [PATCH 089/164] use function bind --- src/wysihat/toolbar.js | 23 ++++++++++------------- 1 file changed, 10 insertions(+), 13 deletions(-) diff --git a/src/wysihat/toolbar.js b/src/wysihat/toolbar.js index 1ed6bf0..fbc867d 100644 --- a/src/wysihat/toolbar.js +++ b/src/wysihat/toolbar.js @@ -47,10 +47,9 @@ WysiHat.Toolbar = Class.create((function() { * Adds a button set to the toolbar. **/ function addButtonSet(set) { - var toolbar = this; $A(set).each(function(button){ - toolbar.addButton(button); - }); + this.addButton(button); + }.bind(this)); } /** @@ -142,11 +141,10 @@ WysiHat.Toolbar = Class.create((function() { * Bind handler to elements onclick event. **/ function observeButtonClick(element, handler) { - var toolbar = this; element.observe('click', function(event) { - handler(toolbar.editor); - Event.stop(event); - }); + handler(this.editor); + event.stop(); + }.bind(this)); } /** @@ -178,15 +176,14 @@ WysiHat.Toolbar = Class.create((function() { * calls updateButtonState. **/ function observeStateChanges(element, name, handler) { - var toolbar = this; - var previousState = false; - toolbar.editor.observe("selection:change", function(event) { - var state = handler(toolbar.editor); + var previousState; + this.editor.observe("selection:change", function(event) { + var state = handler(this.editor); if (state != previousState) { previousState = state; - toolbar.updateButtonState(element, name, state); + this.updateButtonState(element, name, state); } - }); + }.bind(this)); } /** From 94bc0e09f57ff429c93bab55664b24fbe5c9678e Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Wed, 10 Feb 2010 10:23:14 -0600 Subject: [PATCH 090/164] drop persistence helpers --- src/wysihat/editor.js | 3 +- src/wysihat/editor/persistence.js | 108 ------------------------------ test/editor.html | 14 ++-- test/unit/editor_test.js | 8 +-- 4 files changed, 14 insertions(+), 119 deletions(-) delete mode 100644 src/wysihat/editor/persistence.js diff --git a/src/wysihat/editor.js b/src/wysihat/editor.js index 0e5e984..f9b140f 100644 --- a/src/wysihat/editor.js +++ b/src/wysihat/editor.js @@ -24,11 +24,11 @@ WysiHat.Editor = { 'class': 'editor', 'contentEditable': 'true' }); + editArea.update(textarea.value.formatHTMLInput()); editArea.textarea = textarea; WysiHat.Editor.extend(editArea); - editArea.load(); textarea.insert({before: editArea}); return editArea; @@ -70,4 +70,3 @@ WysiHat.Editor = { }; //= require "editor/commands" -//= require "editor/persistence" diff --git a/src/wysihat/editor/persistence.js b/src/wysihat/editor/persistence.js deleted file mode 100644 index 24bcef8..0000000 --- a/src/wysihat/editor/persistence.js +++ /dev/null @@ -1,108 +0,0 @@ -/** section: wysihat - * mixin WysiHat.Persistence - * - * Methods will be mixed into the editor element. These methods deal with - * extracting and filtering content going in and out of the editor. -**/ -WysiHat.Persistence = (function() { - /** - * WysiHat.Persistence#outputFilter(text) -> String - * - text (String): HTML string - * - * Use to filter content coming out of the editor. By default it calls - * text.formatHTMLOutput. This method has been extract so you can override - * it and provide your own custom output filter. - **/ - function outputFilter(text) { - return text.formatHTMLOutput(); - } - - /** - * WysiHat.Persistence#inputFilter(text) -> String - * - text (String): HTML string - * - * Use to filter content going into the editor. By default it calls - * text.formatHTMLInput. This method has been extract so you can override - * it and provide your own custom input filter. - **/ - function inputFilter(text) { - return text.formatHTMLInput(); - } - - /** - * WysiHat.Persistence#content() -> String - * - * Returns the editors HTML contents. The contents are first passed - * through outputFilter. - * - * You can replace the generic outputFilter with your own function. The - * default behavior is to use String#formatHTMLOutput. - * - * editor.outputFilter = function(text) { - * return MyUtils.format_and_santize(text); - * }; - **/ - function content() { - return this.outputFilter(this.innerHTML); - } - - /** - * WysiHat.Persistence#setContent(text) -> undefined - * - text (String): HTML string - * - * Replaces editor's entire contents with the given HTML. The contents are - * first passed through inputFilter. - * - * You can replace the generic inputFilter with your own function. The - * default behavior is to use String#formatHTMLInput. - * - * editor.inputFilter = function(text) { - * return MyUtils.format_and_santize(text); - * }; - **/ - function setContent(text) { - this.innerHTML = this.inputFilter(text); - } - - /** - * WysiHat.Persistence#save() -> undefined - * - * Saves editors contents back out to the textarea. - **/ - function save() { - this.textarea.value = this.content(); - } - - /** - * WysiHat.Persistence#load() -> undefined - * - * Loads textarea contents into editor. - **/ - function load() { - this.setContent(this.textarea.value); - } - - /** - * WysiHat.Persistence#reload() -> undefined - * - * Saves current contents and loads contents into editor. - **/ - function reload() { - window.getSelection().setBookmark(); - this.save(); - this.load(); - window.getSelection().moveToBookmark(); - } - - return { - outputFilter: outputFilter, - inputFilter: inputFilter, - content: content, - setContent: setContent, - save: save, - load: load, - reload: reload - }; -})(); - -WysiHat.Editor.include(WysiHat.Persistence); diff --git a/test/editor.html b/test/editor.html index e534288..3cbe1f2 100644 --- a/test/editor.html +++ b/test/editor.html @@ -68,20 +68,24 @@ handler: function(editor) { return editor.promptLinkSelection(); } }); - editor.outputFilter = function(text) { - return text.formatHTMLOutput().sanitize({ + function sanitize(text) { + return text.sanitize({ tags: ['span', 'p', 'br', 'strong', 'em', 'u', 'a'], attributes: ['id', 'href'] }); - }; + } editor.observe("field:change", function(event) { - editor.save(); + $('note_body').setValue(sanitize(editor.innerHTML.formatHTMLOutput())); $('note_body_raw').setValue(event.element().innerHTML); }); editor.observe("paste", function(event) { - (function() { editor.reload(); }).defer(); + (function() { + window.getSelection().setBookmark(); + editor.innerHTML = sanitize(editor.innerHTML.formatHTMLOutput()).formatHTMLInput(); + window.getSelection().moveToBookmark(); + }).defer(); }); $('note_body').show(); diff --git a/test/unit/editor_test.js b/test/unit/editor_test.js index 3fa8ac0..9ec7f99 100644 --- a/test/unit/editor_test.js +++ b/test/unit/editor_test.js @@ -6,7 +6,7 @@ new Test.Unit.Runner({ }, teardown: function() { - this.editor.setContent(""); + this.editor.innerHTML = ""; this.textarea.value = ""; }, @@ -14,19 +14,19 @@ new Test.Unit.Runner({ var runner = this; this.editor.insertHTML("

    Hello.

    "); - runner.assertEqual("

    Hello.

    ", this.editor.content()); + runner.assertEqual("

    Hello.

    ", this.editor.innerHTML.formatHTMLOutput()); }, testBoldSelection: function() { var runner = this; // this.editor.insertHTML("

    Hello.

    "); - this.editor.setContent('

    Hello.

    '); + this.editor.innerHTML = '

    Hello.

    '.formatHTMLInput(); window.getSelection().selectNode(this.editor.down('#hello')); this.editor.boldSelection(); runner.assert(this.editor.boldSelected()); - runner.assertEqual('

    Hello.

    ', this.editor.content()); + runner.assertEqual('

    Hello.

    ', this.editor.innerHTML.formatHTMLOutput()); } }); From 3b2447689fd96c9e69154ecfeaf2167cd08b8874 Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Wed, 10 Feb 2010 11:53:27 -0600 Subject: [PATCH 091/164] alias commands to $E utility --- src/wysihat.js | 1 + src/wysihat/{editor => }/commands.js | 86 ++++++++++++++-------------- src/wysihat/editor.js | 44 +------------- test/editor.html | 24 ++++---- 4 files changed, 56 insertions(+), 99 deletions(-) rename src/wysihat/{editor => }/commands.js (80%) diff --git a/src/wysihat.js b/src/wysihat.js index 6eaf690..409ece2 100644 --- a/src/wysihat.js +++ b/src/wysihat.js @@ -14,6 +14,7 @@ var WysiHat = {}; //= require "wysihat/editor" +//= require "wysihat/commands" //= require "wysihat/dom/range" //= require "wysihat/dom/selection" diff --git a/src/wysihat/editor/commands.js b/src/wysihat/commands.js similarity index 80% rename from src/wysihat/editor/commands.js rename to src/wysihat/commands.js index 6d2b738..9aea16a 100644 --- a/src/wysihat/editor/commands.js +++ b/src/wysihat/commands.js @@ -1,4 +1,4 @@ -//= require "../dom/selection" +//= require "dom/selection" /** section: wysihat * mixin WysiHat.Commands @@ -15,7 +15,7 @@ * In this example, it is important to stop the click event so you don't * lose your current selection. **/ -WysiHat.Commands = (function() { +var $E = WysiHat.Commands = (function(window) { /** * WysiHat.Commands#boldSelection() -> undefined * @@ -271,7 +271,7 @@ WysiHat.Commands = (function() { **/ function insertHTML(html) { if (Prototype.Browser.IE) { - var range = document.selection.createRange(); + var range = window.document.selection.createRange(); range.pasteHTML(html); range.collapse(false); range.select(); @@ -295,7 +295,7 @@ WysiHat.Commands = (function() { handler.bind(this)(value); } else { try { - document.execCommand(command, ui, value); + window.document.execCommand(command, ui, value); } catch(e) { return null; } } } @@ -318,7 +318,7 @@ WysiHat.Commands = (function() { return handler.bind(this)(); } else { try { - return document.queryCommandState(state); + return window.document.queryCommandState(state); } catch(e) { return null; } } } @@ -340,52 +340,50 @@ WysiHat.Commands = (function() { } return { - boldSelection: boldSelection, - boldSelected: boldSelected, - underlineSelection: underlineSelection, - underlineSelected: underlineSelected, - italicSelection: italicSelection, - italicSelected: italicSelected, - strikethroughSelection: strikethroughSelection, - blockquoteSelection: blockquoteSelection, - fontSelection: fontSelection, - fontSizeSelection: fontSizeSelection, - colorSelection: colorSelection, - backgroundColorSelection: backgroundColorSelection, - alignSelection: alignSelection, - alignSelected: alignSelected, - linkSelection: linkSelection, - unlinkSelection: unlinkSelection, - linkSelected: linkSelected, - formatblockSelection: formatblockSelection, - toggleOrderedList: toggleOrderedList, - insertOrderedList: insertOrderedList, - orderedListSelected: orderedListSelected, - toggleUnorderedList: toggleUnorderedList, - insertUnorderedList: insertUnorderedList, - unorderedListSelected: unorderedListSelected, - insertImage: insertImage, - insertHTML: insertHTML, - execCommand: execCommand, - queryCommandState: queryCommandState, - getSelectedStyles: getSelectedStyles, + boldSelection: boldSelection, + boldSelected: boldSelected, + underlineSelection: underlineSelection, + underlineSelected: underlineSelected, + italicSelection: italicSelection, + italicSelected: italicSelected, + strikethroughSelection: strikethroughSelection, + blockquoteSelection: blockquoteSelection, + fontSelection: fontSelection, + fontSizeSelection: fontSizeSelection, + colorSelection: colorSelection, + backgroundColorSelection: backgroundColorSelection, + alignSelection: alignSelection, + alignSelected: alignSelected, + linkSelection: linkSelection, + unlinkSelection: unlinkSelection, + linkSelected: linkSelected, + formatblockSelection: formatblockSelection, + toggleOrderedList: toggleOrderedList, + insertOrderedList: insertOrderedList, + orderedListSelected: orderedListSelected, + toggleUnorderedList: toggleUnorderedList, + insertUnorderedList: insertUnorderedList, + unorderedListSelected: unorderedListSelected, + insertImage: insertImage, + insertHTML: insertHTML, + execCommand: execCommand, + queryCommandState: queryCommandState, + getSelectedStyles: getSelectedStyles, commands: $H({}), queryCommands: $H({ - link: linkSelected, - orderedlist: orderedListSelected, + link: linkSelected, + orderedlist: orderedListSelected, unorderedlist: unorderedListSelected }), styleSelectors: $H({ - fontname: 'fontFamily', - fontsize: 'fontSize', - forecolor: 'color', - hilitecolor: 'backgroundColor', - backcolor: 'backgroundColor' + fontname: 'fontFamily', + fontsize: 'fontSize', + forecolor: 'color', + hilitecolor: 'backgroundColor', + backcolor: 'backgroundColor' }) }; -})(); - -WysiHat.Editor.include(WysiHat.Commands); +})(window); diff --git a/src/wysihat/editor.js b/src/wysihat/editor.js index f9b140f..b3be4e5 100644 --- a/src/wysihat/editor.js +++ b/src/wysihat/editor.js @@ -13,11 +13,9 @@ WysiHat.Editor = { var editArea; textarea = $(textarea); - textarea.hide(); var id = textarea.id + '_editor'; - if (editArea = $(id)) - return editArea; + if (editArea = $(id)) return editArea; editArea = new Element('div', { 'id': id, @@ -25,48 +23,12 @@ WysiHat.Editor = { 'contentEditable': 'true' }); editArea.update(textarea.value.formatHTMLInput()); - editArea.textarea = textarea; - WysiHat.Editor.extend(editArea); + Object.extend(editArea, WysiHat.Commands); textarea.insert({before: editArea}); + textarea.hide(); return editArea; - }, - - /** section: wysihat - * WysiHat.Editor.include(module) -> Array - * - module (Object): an object that will extend each editor element. - * - * Provides extensibility for the editor. Register a module via this method, - * and its function properties will be available on any editor instance. - * - * eg: - * WysiHat.Editor.include({echo: function(val) { alert(val) }}) - * - * This makes the 'echo' function defined in that module available directly - * on the editor instance. Consequently (if 'editor' is the result of - * a prior call to WysiHat.Editor.attach)... - * - * editor.echo('Hello world!') - * - * ... will show an alert box. - * - * You must register the module via this method *before* the editor - * instance is created -- this is not retrospective, and extant editor - * instances will be unaffected. - **/ - include: function(module) { - this.includedModules = this.includedModules || $A([]); - this.includedModules.push(module); - }, - - extend: function(object) { - var modules = this.includedModules || $A([]); - modules.each(function(module) { - Object.extend(object, module); - }); } }; - -//= require "editor/commands" diff --git a/test/editor.html b/test/editor.html index 3cbe1f2..2f47e7e 100644 --- a/test/editor.html +++ b/test/editor.html @@ -41,23 +41,19 @@ @@ -55,7 +68,9 @@ } document.observe("dom:loaded", function() { - editor = WysiHat.Editor.attach('note_body'); + editor = $('note_body_editor'); + Object.extend(editor, WysiHat.Commands); + var toolbar = new WysiHat.Toolbar(editor); toolbar.addButtonSet(WysiHat.Toolbar.ButtonSets.Basic); @@ -91,12 +106,12 @@
    - -<p>...right there.</p> +

    Raw Contents

    From c740de77b1a36e25ab3b9c371ad56c5725d3ff76 Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Fri, 12 Feb 2010 11:12:53 -0600 Subject: [PATCH 096/164] fire field changed event on execCommand --- src/wysihat/commands.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/wysihat/commands.js b/src/wysihat/commands.js index 9aea16a..d933a1e 100644 --- a/src/wysihat/commands.js +++ b/src/wysihat/commands.js @@ -1,4 +1,5 @@ //= require "dom/selection" +//= require "events/field_change" /** section: wysihat * mixin WysiHat.Commands @@ -298,6 +299,8 @@ var $E = WysiHat.Commands = (function(window) { window.document.execCommand(command, ui, value); } catch(e) { return null; } } + + document.activeElement.fire("field:change"); } /** From 62c73528c7474a5893d25931e0014d16529fbefe Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Fri, 12 Feb 2010 11:21:24 -0600 Subject: [PATCH 097/164] only fire selection:change when the previous range is different --- src/wysihat/dom/range.js | 21 ++++++++++++++++++--- src/wysihat/events/selection_change.js | 2 +- 2 files changed, 19 insertions(+), 4 deletions(-) diff --git a/src/wysihat/dom/range.js b/src/wysihat/dom/range.js index 74f47ab..ddf0406 100644 --- a/src/wysihat/dom/range.js +++ b/src/wysihat/dom/range.js @@ -374,6 +374,7 @@ if (typeof Range == 'undefined') { Object.extend(Range.prototype, (function() { function beforeRange(range) { + if (!range || !range.compareBoundaryPoints) return false; return (this.compareBoundaryPoints(this.START_TO_START, range) == -1 && this.compareBoundaryPoints(this.START_TO_END, range) == -1 && this.compareBoundaryPoints(this.END_TO_END, range) == -1 && @@ -381,6 +382,7 @@ Object.extend(Range.prototype, (function() { } function afterRange(range) { + if (!range || !range.compareBoundaryPoints) return false; return (this.compareBoundaryPoints(this.START_TO_START, range) == 1 && this.compareBoundaryPoints(this.START_TO_END, range) == 1 && this.compareBoundaryPoints(this.END_TO_END, range) == 1 && @@ -388,7 +390,16 @@ Object.extend(Range.prototype, (function() { } function betweenRange(range) { - return !(beforeRange.bind(this)(range) || afterRange.bind(this)(range)); + if (!range || !range.compareBoundaryPoints) return false; + return !(this.beforeRange(range) || this.afterRange(range)); + } + + function equalRange(range) { + if (!range || !range.compareBoundaryPoints) return false; + return (this.compareBoundaryPoints(this.START_TO_START, range) == 0 && + this.compareBoundaryPoints(this.START_TO_END, range) == 0 && + this.compareBoundaryPoints(this.END_TO_END, range) == 0 && + this.compareBoundaryPoints(this.END_TO_START, range) == 0); } function getNode() { @@ -400,13 +411,17 @@ Object.extend(Range.prototype, (function() { var child = parent.childElements().detect(function(child) { var range = document.createRange(); range.selectNodeContents(child); - return betweenRange.bind(this)(range); + return this.betweenRange(range); }.bind(this)); return $(child || parent); } return { - getNode: getNode + beforeRange: beforeRange, + afterRange: afterRange, + betweenRange: betweenRange, + equalRange: equalRange, + getNode: getNode }; })()); diff --git a/src/wysihat/events/selection_change.js b/src/wysihat/events/selection_change.js index 7c4e128..c5af8a3 100644 --- a/src/wysihat/events/selection_change.js +++ b/src/wysihat/events/selection_change.js @@ -22,7 +22,7 @@ if (selection.rangeCount < 1) return; var range = selection.getRangeAt(0); - if (previousRange == range) return; + if (range && range.equalRange(previousRange)) return; previousRange = range; element = range.commonAncestorContainer; From e4951237b1fa369500bcc4134b5fd0367b35ba0e Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Fri, 12 Feb 2010 16:41:33 -0600 Subject: [PATCH 098/164] onselectionchange event isn't ready till dom load --- src/wysihat/events/selection_change.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/wysihat/events/selection_change.js b/src/wysihat/events/selection_change.js index c5af8a3..18aff7c 100644 --- a/src/wysihat/events/selection_change.js +++ b/src/wysihat/events/selection_change.js @@ -1,4 +1,4 @@ -(function() { +document.observe("dom:loaded", function() { if ('onselectionchange' in document) { function selectionChangeHandler() { var range = document.selection.createRange(); @@ -36,4 +36,4 @@ document.observe("mouseup", selectionChangeHandler); document.observe("keyup", selectionChangeHandler); } -})(); +}); From a6c461242ef3ac3f8d83e8cb8dac1e777e0b7545 Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Fri, 12 Feb 2010 16:55:34 -0600 Subject: [PATCH 099/164] can't declare a function in a conditional --- src/wysihat/events/selection_change.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/wysihat/events/selection_change.js b/src/wysihat/events/selection_change.js index 18aff7c..fa99ba4 100644 --- a/src/wysihat/events/selection_change.js +++ b/src/wysihat/events/selection_change.js @@ -1,6 +1,6 @@ document.observe("dom:loaded", function() { if ('onselectionchange' in document) { - function selectionChangeHandler() { + var selectionChangeHandler = function() { var range = document.selection.createRange(); var element = range.parentElement(); $(element).fire("selection:change"); @@ -10,7 +10,7 @@ document.observe("dom:loaded", function() { } else { var previousRange; - function selectionChangeHandler() { + var selectionChangeHandler = function() { var element = document.activeElement; var elementTagName = element.tagName.toLowerCase(); From 2bd0dbac1b9936edf6b3978a6fda005c2c77bcbd Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Tue, 16 Feb 2010 10:36:20 -0600 Subject: [PATCH 100/164] reorg dom range and selection files --- src/wysihat.js | 2 + src/wysihat/dom/bookmark.js | 62 +++++ src/wysihat/dom/ierange.js | 435 +++++++++++++++++++++++++++++++++++ src/wysihat/dom/range.js | 374 +----------------------------- src/wysihat/dom/selection.js | 284 ++++------------------- 5 files changed, 541 insertions(+), 616 deletions(-) create mode 100644 src/wysihat/dom/bookmark.js create mode 100644 src/wysihat/dom/ierange.js diff --git a/src/wysihat.js b/src/wysihat.js index 409ece2..81ff8b9 100644 --- a/src/wysihat.js +++ b/src/wysihat.js @@ -16,8 +16,10 @@ var WysiHat = {}; //= require "wysihat/editor" //= require "wysihat/commands" +//= require "wysihat/dom/ierange" //= require "wysihat/dom/range" //= require "wysihat/dom/selection" +//= require "wysihat/dom/bookmark" //= require "wysihat/events/field_change" //= require "wysihat/events/selection_change" diff --git a/src/wysihat/dom/bookmark.js b/src/wysihat/dom/bookmark.js new file mode 100644 index 0000000..202106e --- /dev/null +++ b/src/wysihat/dom/bookmark.js @@ -0,0 +1,62 @@ +//= require "selection" + +if (Prototype.Browser.IE) { + Object.extend(Selection.prototype, (function() { + function setBookmark() { + var bookmark = $('bookmark'); + if (bookmark) bookmark.remove(); + + bookmark = new Element('span', { 'id': 'bookmark' }).update(" "); + var parent = new Element('div'); + parent.appendChild(bookmark); + + var range = this._document.selection.createRange(); + range.collapse(); + range.pasteHTML(parent.innerHTML); + } + + function moveToBookmark() { + var bookmark = $('bookmark'); + if (!bookmark) return; + + var range = this._document.selection.createRange(); + range.moveToElementText(bookmark); + range.collapse(); + range.select(); + + bookmark.remove(); + } + + return { + setBookmark: setBookmark, + moveToBookmark: moveToBookmark + } + })()); +} else { + Object.extend(Selection.prototype, (function() { + function setBookmark() { + var bookmark = $('bookmark'); + if (bookmark) bookmark.remove(); + + bookmark = new Element('span', { 'id': 'bookmark' }).update(" "); + this.getRangeAt(0).insertNode(bookmark); + } + + function moveToBookmark() { + var bookmark = $('bookmark'); + if (!bookmark) return; + + var range = document.createRange(); + range.setStartBefore(bookmark); + this.removeAllRanges(); + this.addRange(range); + + bookmark.remove(); + } + + return { + setBookmark: setBookmark, + moveToBookmark: moveToBookmark + } + })()); +} diff --git a/src/wysihat/dom/ierange.js b/src/wysihat/dom/ierange.js new file mode 100644 index 0000000..de6797e --- /dev/null +++ b/src/wysihat/dom/ierange.js @@ -0,0 +1,435 @@ +/** IE Selection and Range classes + * + * Original created by Tim Cameron Ryan + * http://github.com/timcameronryan/IERange + * Copyright (c) 2009 Tim Cameron Ryan + * Released under the MIT/X License + * + * Modified by Joshua Peek +**/ +if (!window.getSelection) { + // TODO: Move this object into a closure + var DOMUtils = { + findChildPosition: function(node) { + for (var i = 0; node = node.previousSibling; i++) + continue; + return i; + }, + isDataNode: function(node) { + return node && node.nodeValue !== null && node.data !== null; + }, + isAncestorOf: function(parent, node) { + return !DOMUtils.isDataNode(parent) && + (parent.contains(DOMUtils.isDataNode(node) ? node.parentNode : node) || + node.parentNode == parent); + }, + isAncestorOrSelf: function(root, node) { + return DOMUtils.isAncestorOf(root, node) || root == node; + }, + findClosestAncestor: function(root, node) { + if (DOMUtils.isAncestorOf(root, node)) + while (node && node.parentNode != root) + node = node.parentNode; + return node; + }, + getNodeLength: function(node) { + return DOMUtils.isDataNode(node) ? node.length : node.childNodes.length; + }, + splitDataNode: function(node, offset) { + if (!DOMUtils.isDataNode(node)) + return false; + var newNode = node.cloneNode(false); + node.deleteData(offset, node.length); + newNode.deleteData(0, offset); + node.parentNode.insertBefore(newNode, node.nextSibling); + } + }; + + // TODO: Move this object into a closure + var TextRangeUtils = { + convertToDOMRange: function(textRange, document) { + function adoptBoundary(domRange, textRange, bStart) { + // iterate backwards through parent element to find anchor location + var cursorNode = document.createElement('a'), cursor = textRange.duplicate(); + cursor.collapse(bStart); + var parent = cursor.parentElement(); + do { + parent.insertBefore(cursorNode, cursorNode.previousSibling); + cursor.moveToElementText(cursorNode); + } while (cursor.compareEndPoints(bStart ? 'StartToStart' : 'StartToEnd', textRange) > 0 && cursorNode.previousSibling); + + // when we exceed or meet the cursor, we've found the node + if (cursor.compareEndPoints(bStart ? 'StartToStart' : 'StartToEnd', textRange) == -1 && cursorNode.nextSibling) { + // data node + cursor.setEndPoint(bStart ? 'EndToStart' : 'EndToEnd', textRange); + domRange[bStart ? 'setStart' : 'setEnd'](cursorNode.nextSibling, cursor.text.length); + } else { + // element + domRange[bStart ? 'setStartBefore' : 'setEndBefore'](cursorNode); + } + cursorNode.parentNode.removeChild(cursorNode); + } + + // return a DOM range + var domRange = new Range(document); + adoptBoundary(domRange, textRange, true); + adoptBoundary(domRange, textRange, false); + return domRange; + }, + convertFromDOMRange: function(domRange) { + function adoptEndPoint(textRange, domRange, bStart) { + // find anchor node and offset + var container = domRange[bStart ? 'startContainer' : 'endContainer']; + var offset = domRange[bStart ? 'startOffset' : 'endOffset'], textOffset = 0; + var anchorNode = DOMUtils.isDataNode(container) ? container : container.childNodes[offset]; + var anchorParent = DOMUtils.isDataNode(container) ? container.parentNode : container; + // visible data nodes need a text offset + if (container.nodeType == 3 || container.nodeType == 4) + textOffset = offset; + + // create a cursor element node to position range (since we can't select text nodes) + var cursorNode = domRange._document.createElement('a'); + anchorParent.insertBefore(cursorNode, anchorNode); + var cursor = domRange._document.body.createTextRange(); + cursor.moveToElementText(cursorNode); + cursorNode.parentNode.removeChild(cursorNode); + // move range + textRange.setEndPoint(bStart ? 'StartToStart' : 'EndToStart', cursor); + textRange[bStart ? 'moveStart' : 'moveEnd']('character', textOffset); + } + + // return an IE text range + var textRange = domRange._document.body.createTextRange(); + adoptEndPoint(textRange, domRange, true); + adoptEndPoint(textRange, domRange, false); + return textRange; + } + }; + + window.Range = (function() { + function Range(document) { + // save document parameter + this._document = document; + + // initialize range + this.startContainer = this.endContainer = document.body; + this.endOffset = DOMUtils.getNodeLength(document.body); + } + Range.START_TO_START = 0; + Range.START_TO_END = 1; + Range.END_TO_END = 2; + Range.END_TO_START = 3; + + Range.prototype = { + startContainer: null, + startOffset: 0, + endContainer: null, + endOffset: 0, + commonAncestorContainer: null, + collapsed: false, + _document: null, + + _refreshProperties: function() { + // collapsed attribute + this.collapsed = (this.startContainer == this.endContainer && this.startOffset == this.endOffset); + // find common ancestor + var node = this.startContainer; + while (node && node != this.endContainer && !DOMUtils.isAncestorOf(node, this.endContainer)) + node = node.parentNode; + this.commonAncestorContainer = node; + }, + + setStart: function(container, offset) { + this.startContainer = container; + this.startOffset = offset; + this._refreshProperties(); + }, + setEnd: function(container, offset) { + this.endContainer = container; + this.endOffset = offset; + this._refreshProperties(); + }, + setStartBefore: function(refNode) { + // set start to beore this node + this.setStart(refNode.parentNode, DOMUtils.findChildPosition(refNode)); + }, + setStartAfter: function(refNode) { + // select next sibling + this.setStart(refNode.parentNode, DOMUtils.findChildPosition(refNode) + 1); + }, + setEndBefore: function(refNode) { + // set end to beore this node + this.setEnd(refNode.parentNode, DOMUtils.findChildPosition(refNode)); + }, + setEndAfter: function(refNode) { + // select next sibling + this.setEnd(refNode.parentNode, DOMUtils.findChildPosition(refNode) + 1); + }, + selectNode: function(refNode) { + this.setStartBefore(refNode); + this.setEndAfter(refNode); + }, + selectNodeContents: function(refNode) { + this.setStart(refNode, 0); + this.setEnd(refNode, DOMUtils.getNodeLength(refNode)); + }, + collapse: function(toStart) { + if (toStart) + this.setEnd(this.startContainer, this.startOffset); + else + this.setStart(this.endContainer, this.endOffset); + }, + + cloneContents: function() { + // clone subtree + return (function cloneSubtree(iterator) { + for (var node, frag = document.createDocumentFragment(); node = iterator.next(); ) { + node = node.cloneNode(!iterator.hasPartialSubtree()); + if (iterator.hasPartialSubtree()) + node.appendChild(cloneSubtree(iterator.getSubtreeIterator())); + frag.appendChild(node); + } + return frag; + })(new RangeIterator(this)); + }, + extractContents: function() { + // cache range and move anchor points + var range = this.cloneRange(); + if (this.startContainer != this.commonAncestorContainer) + this.setStartAfter(DOMUtils.findClosestAncestor(this.commonAncestorContainer, this.startContainer)); + this.collapse(true); + // extract range + return (function extractSubtree(iterator) { + for (var node, frag = document.createDocumentFragment(); node = iterator.next(); ) { + iterator.hasPartialSubtree() ? node = node.cloneNode(false) : iterator.remove(); + if (iterator.hasPartialSubtree()) + node.appendChild(extractSubtree(iterator.getSubtreeIterator())); + frag.appendChild(node); + } + return frag; + })(new RangeIterator(range)); + }, + deleteContents: function() { + // cache range and move anchor points + var range = this.cloneRange(); + if (this.startContainer != this.commonAncestorContainer) + this.setStartAfter(DOMUtils.findClosestAncestor(this.commonAncestorContainer, this.startContainer)); + this.collapse(true); + // delete range + (function deleteSubtree(iterator) { + while (iterator.next()) + iterator.hasPartialSubtree() ? deleteSubtree(iterator.getSubtreeIterator()) : iterator.remove(); + })(new RangeIterator(range)); + }, + insertNode: function(newNode) { + // set original anchor and insert node + if (DOMUtils.isDataNode(this.startContainer)) { + DOMUtils.splitDataNode(this.startContainer, this.startOffset); + this.startContainer.parentNode.insertBefore(newNode, this.startContainer.nextSibling); + } else { + this.startContainer.insertBefore(newNode, this.startContainer.childNodes[this.startOffset]); + } + // resync start anchor + this.setStart(this.startContainer, this.startOffset); + }, + surroundContents: function(newNode) { + // extract and surround contents + var content = this.extractContents(); + this.insertNode(newNode); + newNode.appendChild(content); + this.selectNode(newNode); + }, + + compareBoundaryPoints: function(how, sourceRange) { + // get anchors + var containerA, offsetA, containerB, offsetB; + switch (how) { + case Range.START_TO_START: + case Range.START_TO_END: + containerA = this.startContainer; + offsetA = this.startOffset; + break; + case Range.END_TO_END: + case Range.END_TO_START: + containerA = this.endContainer; + offsetA = this.endOffset; + break; + } + switch (how) { + case Range.START_TO_START: + case Range.END_TO_START: + containerB = sourceRange.startContainer; + offsetB = sourceRange.startOffset; + break; + case Range.START_TO_END: + case Range.END_TO_END: + containerB = sourceRange.endContainer; + offsetB = sourceRange.endOffset; + break; + } + + // compare + return containerA.sourceIndex < containerB.sourceIndex ? -1 : + containerA.sourceIndex == containerB.sourceIndex ? + offsetA < offsetB ? -1 : offsetA == offsetB ? 0 : 1 + : 1; + }, + cloneRange: function() { + // return cloned range + var range = new Range(this._document); + range.setStart(this.startContainer, this.startOffset); + range.setEnd(this.endContainer, this.endOffset); + return range; + }, + detach: function() { + }, + toString: function() { + return TextRangeUtils.convertFromDOMRange(this).text; + }, + createContextualFragment: function(tagString) { + // parse the tag string in a context node + var content = (DOMUtils.isDataNode(this.startContainer) ? this.startContainer.parentNode : this.startContainer).cloneNode(false); + content.innerHTML = tagString; + // return a document fragment from the created node + for (var fragment = this._document.createDocumentFragment(); content.firstChild; ) + fragment.appendChild(content.firstChild); + return fragment; + } + }; + + function RangeIterator(range) { + this.range = range; + if (range.collapsed) + return; + + // get anchors + var root = range.commonAncestorContainer; + this._next = range.startContainer == root && !DOMUtils.isDataNode(range.startContainer) ? + range.startContainer.childNodes[range.startOffset] : + DOMUtils.findClosestAncestor(root, range.startContainer); + this._end = range.endContainer == root && !DOMUtils.isDataNode(range.endContainer) ? + range.endContainer.childNodes[range.endOffset] : + DOMUtils.findClosestAncestor(root, range.endContainer).nextSibling; + } + + RangeIterator.prototype = { + range: null, + _current: null, + _next: null, + _end: null, + + hasNext: function() { + return !!this._next; + }, + next: function() { + // move to next node + var current = this._current = this._next; + this._next = this._current && this._current.nextSibling != this._end ? + this._current.nextSibling : null; + + // check for partial text nodes + if (DOMUtils.isDataNode(this._current)) { + if (this.range.endContainer == this._current) + (current = current.cloneNode(true)).deleteData(this.range.endOffset, current.length - this.range.endOffset); + if (this.range.startContainer == this._current) + (current = current.cloneNode(true)).deleteData(0, this.range.startOffset); + } + return current; + }, + remove: function() { + // check for partial text nodes + if (DOMUtils.isDataNode(this._current) && + (this.range.startContainer == this._current || this.range.endContainer == this._current)) { + var start = this.range.startContainer == this._current ? this.range.startOffset : 0; + var end = this.range.endContainer == this._current ? this.range.endOffset : this._current.length; + this._current.deleteData(start, end - start); + } else + this._current.parentNode.removeChild(this._current); + }, + hasPartialSubtree: function() { + // check if this node be partially selected + return !DOMUtils.isDataNode(this._current) && + (DOMUtils.isAncestorOrSelf(this._current, this.range.startContainer) || + DOMUtils.isAncestorOrSelf(this._current, this.range.endContainer)); + }, + getSubtreeIterator: function() { + // create a new range + var subRange = new Range(this.range._document); + subRange.selectNodeContents(this._current); + // handle anchor points + if (DOMUtils.isAncestorOrSelf(this._current, this.range.startContainer)) + subRange.setStart(this.range.startContainer, this.range.startOffset); + if (DOMUtils.isAncestorOrSelf(this._current, this.range.endContainer)) + subRange.setEnd(this.range.endContainer, this.range.endOffset); + // return iterator + return new RangeIterator(subRange); + } + }; + + return Range; + })(); + + document.createRange = function() { + return new Range(document); + }; + + window.Selection = (function() { + function Selection(document) { + this._document = document; + + var selection = this; + document.attachEvent('onselectionchange', function() { + selection._selectionChangeHandler(); + }); + } + + Selection.prototype = { + rangeCount: 0, + _document: null, + + _selectionChangeHandler: function() { + this.rangeCount = this._selectionExists(this._document.selection.createRange()) ? 1 : 0; + }, + _selectionExists: function(textRange) { + return textRange.compareEndPoints('StartToEnd', textRange) != 0 || + textRange.parentElement().isContentEditable; + }, + addRange: function(range) { + var selection = this._document.selection.createRange(), textRange = TextRangeUtils.convertFromDOMRange(range); + if (!this._selectionExists(selection)) { + textRange.select(); + } else { + // only modify range if it intersects with current range + if (textRange.compareEndPoints('StartToStart', selection) == -1) + if (textRange.compareEndPoints('StartToEnd', selection) > -1 && + textRange.compareEndPoints('EndToEnd', selection) == -1) + selection.setEndPoint('StartToStart', textRange); + else + if (textRange.compareEndPoints('EndToStart', selection) < 1 && + textRange.compareEndPoints('EndToEnd', selection) > -1) + selection.setEndPoint('EndToEnd', textRange); + selection.select(); + } + }, + removeAllRanges: function() { + this._document.selection.empty(); + }, + getRangeAt: function(index) { + var textRange = this._document.selection.createRange(); + if (this._selectionExists(textRange)) + return TextRangeUtils.convertToDOMRange(textRange, this._document); + return null; + }, + toString: function() { + return this._document.selection.createRange().text; + } + }; + + return Selection; + })(); + + window.getSelection = (function() { + var selection = new Selection(document); + return function() { return selection; }; + })(); +} diff --git a/src/wysihat/dom/range.js b/src/wysihat/dom/range.js index ddf0406..a022358 100644 --- a/src/wysihat/dom/range.js +++ b/src/wysihat/dom/range.js @@ -1,376 +1,4 @@ -if (typeof Range == 'undefined') { - /** IE Range class - * - * Original created by Tim Cameron Ryan - * http://github.com/timcameronryan/IERange - * Copyright (c) 2009 Tim Cameron Ryan - * Released under the MIT/X License - * - * Modified by Joshua Peek - **/ - window.Range = (function() { - var DOMUtils = { - findChildPosition: function(node) { - for (var i = 0; node = node.previousSibling; i++) - continue; - return i; - }, - isDataNode: function(node) { - return node && node.nodeValue !== null && node.data !== null; - }, - isAncestorOf: function(parent, node) { - return !DOMUtils.isDataNode(parent) && - (parent.contains(DOMUtils.isDataNode(node) ? node.parentNode : node) || - node.parentNode == parent); - }, - isAncestorOrSelf: function(root, node) { - return DOMUtils.isAncestorOf(root, node) || root == node; - }, - findClosestAncestor: function(root, node) { - if (DOMUtils.isAncestorOf(root, node)) - while (node && node.parentNode != root) - node = node.parentNode; - return node; - }, - getNodeLength: function(node) { - return DOMUtils.isDataNode(node) ? node.length : node.childNodes.length; - }, - splitDataNode: function(node, offset) { - if (!DOMUtils.isDataNode(node)) - return false; - var newNode = node.cloneNode(false); - node.deleteData(offset, node.length); - newNode.deleteData(0, offset); - node.parentNode.insertBefore(newNode, node.nextSibling); - } - }; - - var TextRangeUtils = { - convertToDOMRange: function(textRange, document) { - function adoptBoundary(domRange, textRange, bStart) { - // iterate backwards through parent element to find anchor location - var cursorNode = document.createElement('a'), cursor = textRange.duplicate(); - cursor.collapse(bStart); - var parent = cursor.parentElement(); - do { - parent.insertBefore(cursorNode, cursorNode.previousSibling); - cursor.moveToElementText(cursorNode); - } while (cursor.compareEndPoints(bStart ? 'StartToStart' : 'StartToEnd', textRange) > 0 && cursorNode.previousSibling); - - // when we exceed or meet the cursor, we've found the node - if (cursor.compareEndPoints(bStart ? 'StartToStart' : 'StartToEnd', textRange) == -1 && cursorNode.nextSibling) { - // data node - cursor.setEndPoint(bStart ? 'EndToStart' : 'EndToEnd', textRange); - domRange[bStart ? 'setStart' : 'setEnd'](cursorNode.nextSibling, cursor.text.length); - } else { - // element - domRange[bStart ? 'setStartBefore' : 'setEndBefore'](cursorNode); - } - cursorNode.parentNode.removeChild(cursorNode); - } - - // return a DOM range - var domRange = new Range(document); - adoptBoundary(domRange, textRange, true); - adoptBoundary(domRange, textRange, false); - return domRange; - }, - convertFromDOMRange: function(domRange) { - function adoptEndPoint(textRange, domRange, bStart) { - // find anchor node and offset - var container = domRange[bStart ? 'startContainer' : 'endContainer']; - var offset = domRange[bStart ? 'startOffset' : 'endOffset'], textOffset = 0; - var anchorNode = DOMUtils.isDataNode(container) ? container : container.childNodes[offset]; - var anchorParent = DOMUtils.isDataNode(container) ? container.parentNode : container; - // visible data nodes need a text offset - if (container.nodeType == 3 || container.nodeType == 4) - textOffset = offset; - - // create a cursor element node to position range (since we can't select text nodes) - var cursorNode = domRange._document.createElement('a'); - anchorParent.insertBefore(cursorNode, anchorNode); - var cursor = domRange._document.body.createTextRange(); - cursor.moveToElementText(cursorNode); - cursorNode.parentNode.removeChild(cursorNode); - // move range - textRange.setEndPoint(bStart ? 'StartToStart' : 'EndToStart', cursor); - textRange[bStart ? 'moveStart' : 'moveEnd']('character', textOffset); - } - - // return an IE text range - var textRange = domRange._document.body.createTextRange(); - adoptEndPoint(textRange, domRange, true); - adoptEndPoint(textRange, domRange, false); - return textRange; - } - }; - - function Range(document) { - // save document parameter - this._document = document; - - // initialize range - this.startContainer = this.endContainer = document.body; - this.endOffset = DOMUtils.getNodeLength(document.body); - } - Range.START_TO_START = 0; - Range.START_TO_END = 1; - Range.END_TO_END = 2; - Range.END_TO_START = 3; - - Range.prototype = { - startContainer: null, - startOffset: 0, - endContainer: null, - endOffset: 0, - commonAncestorContainer: null, - collapsed: false, - _document: null, - - _refreshProperties: function() { - // collapsed attribute - this.collapsed = (this.startContainer == this.endContainer && this.startOffset == this.endOffset); - // find common ancestor - var node = this.startContainer; - while (node && node != this.endContainer && !DOMUtils.isAncestorOf(node, this.endContainer)) - node = node.parentNode; - this.commonAncestorContainer = node; - }, - - setStart: function(container, offset) { - this.startContainer = container; - this.startOffset = offset; - this._refreshProperties(); - }, - setEnd: function(container, offset) { - this.endContainer = container; - this.endOffset = offset; - this._refreshProperties(); - }, - setStartBefore: function(refNode) { - // set start to beore this node - this.setStart(refNode.parentNode, DOMUtils.findChildPosition(refNode)); - }, - setStartAfter: function(refNode) { - // select next sibling - this.setStart(refNode.parentNode, DOMUtils.findChildPosition(refNode) + 1); - }, - setEndBefore: function(refNode) { - // set end to beore this node - this.setEnd(refNode.parentNode, DOMUtils.findChildPosition(refNode)); - }, - setEndAfter: function(refNode) { - // select next sibling - this.setEnd(refNode.parentNode, DOMUtils.findChildPosition(refNode) + 1); - }, - selectNode: function(refNode) { - this.setStartBefore(refNode); - this.setEndAfter(refNode); - }, - selectNodeContents: function(refNode) { - this.setStart(refNode, 0); - this.setEnd(refNode, DOMUtils.getNodeLength(refNode)); - }, - collapse: function(toStart) { - if (toStart) - this.setEnd(this.startContainer, this.startOffset); - else - this.setStart(this.endContainer, this.endOffset); - }, - - cloneContents: function() { - // clone subtree - return (function cloneSubtree(iterator) { - for (var node, frag = document.createDocumentFragment(); node = iterator.next(); ) { - node = node.cloneNode(!iterator.hasPartialSubtree()); - if (iterator.hasPartialSubtree()) - node.appendChild(cloneSubtree(iterator.getSubtreeIterator())); - frag.appendChild(node); - } - return frag; - })(new RangeIterator(this)); - }, - extractContents: function() { - // cache range and move anchor points - var range = this.cloneRange(); - if (this.startContainer != this.commonAncestorContainer) - this.setStartAfter(DOMUtils.findClosestAncestor(this.commonAncestorContainer, this.startContainer)); - this.collapse(true); - // extract range - return (function extractSubtree(iterator) { - for (var node, frag = document.createDocumentFragment(); node = iterator.next(); ) { - iterator.hasPartialSubtree() ? node = node.cloneNode(false) : iterator.remove(); - if (iterator.hasPartialSubtree()) - node.appendChild(extractSubtree(iterator.getSubtreeIterator())); - frag.appendChild(node); - } - return frag; - })(new RangeIterator(range)); - }, - deleteContents: function() { - // cache range and move anchor points - var range = this.cloneRange(); - if (this.startContainer != this.commonAncestorContainer) - this.setStartAfter(DOMUtils.findClosestAncestor(this.commonAncestorContainer, this.startContainer)); - this.collapse(true); - // delete range - (function deleteSubtree(iterator) { - while (iterator.next()) - iterator.hasPartialSubtree() ? deleteSubtree(iterator.getSubtreeIterator()) : iterator.remove(); - })(new RangeIterator(range)); - }, - insertNode: function(newNode) { - // set original anchor and insert node - if (DOMUtils.isDataNode(this.startContainer)) { - DOMUtils.splitDataNode(this.startContainer, this.startOffset); - this.startContainer.parentNode.insertBefore(newNode, this.startContainer.nextSibling); - } else { - this.startContainer.insertBefore(newNode, this.startContainer.childNodes[this.startOffset]); - } - // resync start anchor - this.setStart(this.startContainer, this.startOffset); - }, - surroundContents: function(newNode) { - // extract and surround contents - var content = this.extractContents(); - this.insertNode(newNode); - newNode.appendChild(content); - this.selectNode(newNode); - }, - - compareBoundaryPoints: function(how, sourceRange) { - // get anchors - var containerA, offsetA, containerB, offsetB; - switch (how) { - case Range.START_TO_START: - case Range.START_TO_END: - containerA = this.startContainer; - offsetA = this.startOffset; - break; - case Range.END_TO_END: - case Range.END_TO_START: - containerA = this.endContainer; - offsetA = this.endOffset; - break; - } - switch (how) { - case Range.START_TO_START: - case Range.END_TO_START: - containerB = sourceRange.startContainer; - offsetB = sourceRange.startOffset; - break; - case Range.START_TO_END: - case Range.END_TO_END: - containerB = sourceRange.endContainer; - offsetB = sourceRange.endOffset; - break; - } - - // compare - return containerA.sourceIndex < containerB.sourceIndex ? -1 : - containerA.sourceIndex == containerB.sourceIndex ? - offsetA < offsetB ? -1 : offsetA == offsetB ? 0 : 1 - : 1; - }, - cloneRange: function() { - // return cloned range - var range = new Range(this._document); - range.setStart(this.startContainer, this.startOffset); - range.setEnd(this.endContainer, this.endOffset); - return range; - }, - detach: function() { - }, - toString: function() { - return TextRangeUtils.convertFromDOMRange(this).text; - }, - createContextualFragment: function(tagString) { - // parse the tag string in a context node - var content = (DOMUtils.isDataNode(this.startContainer) ? this.startContainer.parentNode : this.startContainer).cloneNode(false); - content.innerHTML = tagString; - // return a document fragment from the created node - for (var fragment = this._document.createDocumentFragment(); content.firstChild; ) - fragment.appendChild(content.firstChild); - return fragment; - } - }; - - function RangeIterator(range) { - this.range = range; - if (range.collapsed) - return; - - // get anchors - var root = range.commonAncestorContainer; - this._next = range.startContainer == root && !DOMUtils.isDataNode(range.startContainer) ? - range.startContainer.childNodes[range.startOffset] : - DOMUtils.findClosestAncestor(root, range.startContainer); - this._end = range.endContainer == root && !DOMUtils.isDataNode(range.endContainer) ? - range.endContainer.childNodes[range.endOffset] : - DOMUtils.findClosestAncestor(root, range.endContainer).nextSibling; - } - - RangeIterator.prototype = { - range: null, - _current: null, - _next: null, - _end: null, - - hasNext: function() { - return !!this._next; - }, - next: function() { - // move to next node - var current = this._current = this._next; - this._next = this._current && this._current.nextSibling != this._end ? - this._current.nextSibling : null; - - // check for partial text nodes - if (DOMUtils.isDataNode(this._current)) { - if (this.range.endContainer == this._current) - (current = current.cloneNode(true)).deleteData(this.range.endOffset, current.length - this.range.endOffset); - if (this.range.startContainer == this._current) - (current = current.cloneNode(true)).deleteData(0, this.range.startOffset); - } - return current; - }, - remove: function() { - // check for partial text nodes - if (DOMUtils.isDataNode(this._current) && - (this.range.startContainer == this._current || this.range.endContainer == this._current)) { - var start = this.range.startContainer == this._current ? this.range.startOffset : 0; - var end = this.range.endContainer == this._current ? this.range.endOffset : this._current.length; - this._current.deleteData(start, end - start); - } else - this._current.parentNode.removeChild(this._current); - }, - hasPartialSubtree: function() { - // check if this node be partially selected - return !DOMUtils.isDataNode(this._current) && - (DOMUtils.isAncestorOrSelf(this._current, this.range.startContainer) || - DOMUtils.isAncestorOrSelf(this._current, this.range.endContainer)); - }, - getSubtreeIterator: function() { - // create a new range - var subRange = new Range(this.range._document); - subRange.selectNodeContents(this._current); - // handle anchor points - if (DOMUtils.isAncestorOrSelf(this._current, this.range.startContainer)) - subRange.setStart(this.range.startContainer, this.range.startOffset); - if (DOMUtils.isAncestorOrSelf(this._current, this.range.endContainer)) - subRange.setEnd(this.range.endContainer, this.range.endOffset); - // return iterator - return new RangeIterator(subRange); - } - }; - - return Range; - })(); - - document.createRange = function() { - return new Range(document); - }; -} +//= require "ierange" Object.extend(Range.prototype, (function() { function beforeRange(range) { diff --git a/src/wysihat/dom/selection.js b/src/wysihat/dom/selection.js index 128f53e..99dc6fd 100644 --- a/src/wysihat/dom/selection.js +++ b/src/wysihat/dom/selection.js @@ -1,253 +1,51 @@ +//= require "ierange" //= require "range" -if (!window.getSelection) { - /** IE Selection class - * - * Original created by Tim Cameron Ryan - * http://github.com/timcameronryan/IERange - * Copyright (c) 2009 Tim Cameron Ryan - * Released under the MIT/X License - * - * Modified by Joshua Peek - **/ - window.getSelection = (function() { - var DOMUtils = { - findChildPosition: function(node) { - for (var i = 0; node = node.previousSibling; i++) - continue; - return i; - }, - isDataNode: function(node) { - return node && node.nodeValue !== null && node.data !== null; - }, - isAncestorOf: function(parent, node) { - return !DOMUtils.isDataNode(parent) && - (parent.contains(DOMUtils.isDataNode(node) ? node.parentNode : node) || - node.parentNode == parent); - }, - isAncestorOrSelf: function(root, node) { - return DOMUtils.isAncestorOf(root, node) || root == node; - }, - findClosestAncestor: function(root, node) { - if (DOMUtils.isAncestorOf(root, node)) - while (node && node.parentNode != root) - node = node.parentNode; - return node; - }, - getNodeLength: function(node) { - return DOMUtils.isDataNode(node) ? node.length : node.childNodes.length; - }, - splitDataNode: function(node, offset) { - if (!DOMUtils.isDataNode(node)) - return false; - var newNode = node.cloneNode(false); - node.deleteData(offset, node.length); - newNode.deleteData(0, offset); - node.parentNode.insertBefore(newNode, node.nextSibling); - } - }; - - var TextRangeUtils = { - convertToDOMRange: function(textRange, document) { - function adoptBoundary(domRange, textRange, bStart) { - // iterate backwards through parent element to find anchor location - var cursorNode = document.createElement('a'), cursor = textRange.duplicate(); - cursor.collapse(bStart); - var parent = cursor.parentElement(); - do { - parent.insertBefore(cursorNode, cursorNode.previousSibling); - cursor.moveToElementText(cursorNode); - } while (cursor.compareEndPoints(bStart ? 'StartToStart' : 'StartToEnd', textRange) > 0 && cursorNode.previousSibling); - - // when we exceed or meet the cursor, we've found the node - if (cursor.compareEndPoints(bStart ? 'StartToStart' : 'StartToEnd', textRange) == -1 && cursorNode.nextSibling) { - // data node - cursor.setEndPoint(bStart ? 'EndToStart' : 'EndToEnd', textRange); - domRange[bStart ? 'setStart' : 'setEnd'](cursorNode.nextSibling, cursor.text.length); - } else { - // element - domRange[bStart ? 'setStartBefore' : 'setEndBefore'](cursorNode); - } - cursorNode.parentNode.removeChild(cursorNode); - } - - // return a DOM range - var domRange = new Range(document); - adoptBoundary(domRange, textRange, true); - adoptBoundary(domRange, textRange, false); - return domRange; - }, - convertFromDOMRange: function(domRange) { - function adoptEndPoint(textRange, domRange, bStart) { - // find anchor node and offset - var container = domRange[bStart ? 'startContainer' : 'endContainer']; - var offset = domRange[bStart ? 'startOffset' : 'endOffset'], textOffset = 0; - var anchorNode = DOMUtils.isDataNode(container) ? container : container.childNodes[offset]; - var anchorParent = DOMUtils.isDataNode(container) ? container.parentNode : container; - // visible data nodes need a text offset - if (container.nodeType == 3 || container.nodeType == 4) - textOffset = offset; - - // create a cursor element node to position range (since we can't select text nodes) - var cursorNode = domRange._document.createElement('a'); - anchorParent.insertBefore(cursorNode, anchorNode); - var cursor = domRange._document.body.createTextRange(); - cursor.moveToElementText(cursorNode); - cursorNode.parentNode.removeChild(cursorNode); - // move range - textRange.setEndPoint(bStart ? 'StartToStart' : 'EndToStart', cursor); - textRange[bStart ? 'moveStart' : 'moveEnd']('character', textOffset); - } - - // return an IE text range - var textRange = domRange._document.body.createTextRange(); - adoptEndPoint(textRange, domRange, true); - adoptEndPoint(textRange, domRange, false); - return textRange; - } - }; - - function Selection(document) { - this._document = document; +if (Prototype.Browser.IE) { + Object.extend(Selection.prototype, (function() { + // TODO: More robust getNode + function getNode() { + var range = this._document.selection.createRange(); + return $(range.parentElement()); + } - var selection = this; - document.attachEvent('onselectionchange', function() { - selection._selectionChangeHandler(); - }); + // TODO: IE selectNode should work with range.selectNode + function selectNode(element) { + var range = this._document.body.createTextRange(); + range.moveToElementText(element); + range.select(); } - Selection.prototype = { - rangeCount: 0, - _document: null, + return { + getNode: getNode, + selectNode: selectNode + } + })()); +} else { + // WebKit does not have a public Selection prototype + if (typeof Selection == 'undefined') { + var Selection = {} + Selection.prototype = window.getSelection(); + } - _selectionChangeHandler: function() { - this.rangeCount = this._selectionExists(this._document.selection.createRange()) ? 1 : 0; - }, - _selectionExists: function(textRange) { - return textRange.compareEndPoints('StartToEnd', textRange) != 0 || - textRange.parentElement().isContentEditable; - }, - addRange: function(range) { - var selection = this._document.selection.createRange(), textRange = TextRangeUtils.convertFromDOMRange(range); - if (!this._selectionExists(selection)) { - textRange.select(); - } else { - // only modify range if it intersects with current range - if (textRange.compareEndPoints('StartToStart', selection) == -1) - if (textRange.compareEndPoints('StartToEnd', selection) > -1 && - textRange.compareEndPoints('EndToEnd', selection) == -1) - selection.setEndPoint('StartToStart', textRange); - else - if (textRange.compareEndPoints('EndToStart', selection) < 1 && - textRange.compareEndPoints('EndToEnd', selection) > -1) - selection.setEndPoint('EndToEnd', textRange); - selection.select(); - } - }, - removeAllRanges: function() { - this._document.selection.empty(); - }, - getRangeAt: function(index) { - var textRange = this._document.selection.createRange(); - if (this._selectionExists(textRange)) - return TextRangeUtils.convertToDOMRange(textRange, this._document); + Object.extend(Selection.prototype, (function() { + function getNode() { + if (this.rangeCount > 0) + return this.getRangeAt(0).getNode(); + else return null; - }, - toString: function() { - return this._document.selection.createRange().text; - }, - - // Extension - // TODO: More robust getNode - getNode: function() { - var range = this._document.selection.createRange(); - return $(range.parentElement()); - }, - // TODO: IE selectNode should work with range.selectNode - selectNode: function(element) { - var range = this._document.body.createTextRange(); - range.moveToElementText(element); - range.select(); - }, - setBookmark: function() { - var bookmark = $('bookmark'); - if (bookmark) bookmark.remove(); - - bookmark = new Element('span', { 'id': 'bookmark' }).update(" "); - var parent = new Element('div'); - parent.appendChild(bookmark); - - var range = this._document.selection.createRange(); - range.collapse(); - range.pasteHTML(parent.innerHTML); - }, - moveToBookmark: function() { - var bookmark = $('bookmark'); - if (!bookmark) return; - - var range = this._document.selection.createRange(); - range.moveToElementText(bookmark); - range.collapse(); - range.select(); + } - bookmark.remove(); - } - }; + function selectNode(element) { + var range = document.createRange(); + range.selectNode(element); + this.removeAllRanges(); + this.addRange(range); + } - var selection = new Selection(document); - return function() { return selection; }; - })(); + return { + getNode: getNode, + selectNode: selectNode + } + })()); } - -(function() { - var prototype; - if (Prototype.Browser.WebKit) - prototype = window.getSelection(); - else if (Prototype.Browser.Gecko) - prototype = Selection.prototype; - - if (prototype) { - Object.extend(prototype, (function() { - function getNode() { - if (this.rangeCount > 0) - return this.getRangeAt(0).getNode(); - else - return null; - } - - function selectNode(element) { - var range = document.createRange(); - range.selectNode(element); - this.removeAllRanges(); - this.addRange(range); - } - - function setBookmark() { - var bookmark = $('bookmark'); - if (bookmark) bookmark.remove(); - - bookmark = new Element('span', { 'id': 'bookmark' }).update(" "); - this.getRangeAt(0).insertNode(bookmark); - } - - function moveToBookmark() { - var bookmark = $('bookmark'); - if (!bookmark) return; - - var range = document.createRange(); - range.setStartBefore(bookmark); - this.removeAllRanges(); - this.addRange(range); - - bookmark.remove(); - } - - return { - getNode: getNode, - selectNode: selectNode, - setBookmark: setBookmark, - moveToBookmark: moveToBookmark - } - })()); - } -})(); From 6cc8b5cd15fe78512c7a1cf6b9511306da6fa5f1 Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Tue, 16 Feb 2010 10:51:43 -0600 Subject: [PATCH 101/164] tests for equalRange --- src/wysihat/dom/range.js | 4 ++-- test/unit/range_test.js | 31 +++++++++++++++++++++++++++++++ 2 files changed, 33 insertions(+), 2 deletions(-) diff --git a/src/wysihat/dom/range.js b/src/wysihat/dom/range.js index a022358..18c8970 100644 --- a/src/wysihat/dom/range.js +++ b/src/wysihat/dom/range.js @@ -25,9 +25,9 @@ Object.extend(Range.prototype, (function() { function equalRange(range) { if (!range || !range.compareBoundaryPoints) return false; return (this.compareBoundaryPoints(this.START_TO_START, range) == 0 && - this.compareBoundaryPoints(this.START_TO_END, range) == 0 && + this.compareBoundaryPoints(this.START_TO_END, range) == 1 && this.compareBoundaryPoints(this.END_TO_END, range) == 0 && - this.compareBoundaryPoints(this.END_TO_START, range) == 0); + this.compareBoundaryPoints(this.END_TO_START, range) == -1); } function getNode() { diff --git a/test/unit/range_test.js b/test/unit/range_test.js index 510918f..cb11be4 100644 --- a/test/unit/range_test.js +++ b/test/unit/range_test.js @@ -266,6 +266,37 @@ new Test.Unit.Runner({ runner.assertEqual(expected, $('lorem').innerHTML, "innerHTML"); }, + testEqualRange: function() { + var runner = this; + + if (!this.range.equalRange) { + runner.flunk("equalRange is not implemented"); + return false; + } + + var r1 = document.createRange(); + r1.selectNodeContents($('lorem')); + + var r2 = document.createRange(); + r2.selectNodeContents($('lorem')); + + var r3 = document.createRange(); + r3.selectNodeContents($('consectetuer')); + + runner.assert(r1.equalRange(r1), "r1.equalRange(r1)"); + runner.assert(r2.equalRange(r2), "r2.equalRange(r2)"); + runner.assert(r3.equalRange(r3), "r3.equalRange(r3)"); + + runner.assert(r1.equalRange(r2), "r1.equalRange(r2)"); + runner.assert(r2.equalRange(r1), "r2.equalRange(r1)"); + runner.assert(!r1.equalRange(r3), "r1.equalRange(r3)"); + runner.assert(!r3.equalRange(r1), "r3.equalRange(r1)"); + + runner.assert(!r1.equalRange(null), "r1.equalRange(null)"); + runner.assert(!r2.equalRange(null), "r2.equalRange(null)"); + runner.assert(!r3.equalRange(null), "r3.equalRange(null)"); + }, + testGetNode: function() { var runner = this; From 8322256e6ea620846015d4d8114b5b065bc72f53 Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Tue, 16 Feb 2010 11:28:01 -0600 Subject: [PATCH 102/164] elements after ol and uls should be wrapped in a p --- src/wysihat/lang/formatter.js | 9 ++++++++- test/unit/formatter_test.js | 14 +++++++++++--- 2 files changed, 19 insertions(+), 4 deletions(-) diff --git a/src/wysihat/lang/formatter.js b/src/wysihat/lang/formatter.js index 9750060..5495a32 100644 --- a/src/wysihat/lang/formatter.js +++ b/src/wysihat/lang/formatter.js @@ -44,6 +44,10 @@ Object.extend(String.prototype, (function() { // Treat lines with one space as returns text = text.replace(/

    ( | |\s)<\/p>/g, "

    "); + // Kill line breaks after list elements so they are see as line breaks + text = text.gsub(/<(ol|ul)>\n+/, "<#{1}>"); + text = text.replace(/<\/li>\n+/, ""); + // Line break tags are useless text = text.replace(/
    /g, ""); @@ -75,6 +79,10 @@ Object.extend(String.prototype, (function() { // TODO: Removing the following line does not cause any tests to fail text = text.gsub(/<\/i>/, ""); + // Add double return after block elements + text = text.gsub(/<\/(ol|ul)>/, "\n\n"); + + // Convert double returns into paragraphs text = text.replace(/\n\n+/g, "

    \n\n

    "); @@ -125,7 +133,6 @@ Object.extend(String.prototype, (function() { } }); } while (replaced); - } // TODO: This should be configurable diff --git a/test/unit/formatter_test.js b/test/unit/formatter_test.js index 8abe0ce..f047718 100644 --- a/test/unit/formatter_test.js +++ b/test/unit/formatter_test.js @@ -44,8 +44,10 @@ new Test.Unit.Runner({ ['

    Hello
    \n

    ', '
    Hello
    '], ['

    Hello

    ', '
    Hello
    '], + ['

    ', ''], - ["

    ", ""] + ['
    1. one
    2. two
    \n\n

    not

    ', '
    1. one
    2. two
    not
    '], + ['
    • one
    • two
    \n\n

    not

    ', '
    • one
    • two
    not
    '] ].each(function(assertion) { runner.assertEqual(assertion[0], assertion[1].formatHTMLOutput()); }); @@ -72,7 +74,10 @@ new Test.Unit.Runner({ ['

    Some bold, underline, and italic text

    ', 'Some bold, underline, and italic text'], - ["

    ", ""] + ['

    ', ''], + + ['
    1. one
    2. two
    \n\n

    not

    ', '
    1. one
    2. two
    not
    '], + ['
    • one
    • two
    \n\n

    not

    ', '
    • one
    • two
    not
    '] ].each(function(assertion) { runner.assertEqual(assertion[0], assertion[1].formatHTMLOutput()); }); @@ -98,7 +103,10 @@ new Test.Unit.Runner({ ['

    Some italic text

    ', 'Some italic text'], ['

    Some underlined text

    ', - 'Some underlined text'] + 'Some underlined text'], + + ['
      \n
    1. one
    2. \n
    3. two
    \n

    not

    ', '
      \n
    1. one
    2. \n
    3. two
    \n

    not

    '], + ['
      \n
    • one
    • \n
    • two
    \n

    not

    ', '
      \n
    • one
    • \n
    • two
    \n

    not

    '] ].each(function(assertion) { runner.assertEqual(assertion[0], assertion[1].formatHTMLOutput()); }); From 20be9301b83a75f709dd66713c8b482a65ed726f Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Tue, 16 Feb 2010 11:44:45 -0600 Subject: [PATCH 103/164] test range after insertNode --- test/unit/range_test.js | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/test/unit/range_test.js b/test/unit/range_test.js index cb11be4..83d00bb 100644 --- a/test/unit/range_test.js +++ b/test/unit/range_test.js @@ -247,6 +247,15 @@ new Test.Unit.Runner({ this.range.insertNode(node); runner.assertEqual("inserted!", $('inserted').innerHTML, "innerHTML"); + + runner.assertEqual(Node.ELEMENT_NODE, this.range.startContainer.nodeType, "startContainer.nodeType"); + runner.assertEqual("DIV", this.range.startContainer.tagName, "startContainer.tagName"); + runner.assertEqual(0, this.range.startOffset, "startOffset"); + runner.assertEqual(Node.ELEMENT_NODE, this.range.endContainer.nodeType, "endContainer.nodeType"); + runner.assertEqual("DIV", this.range.endContainer.tagName, "endContainer.tagName"); + runner.assertEqual(2, this.range.endOffset, "endOffset"); + runner.assertEqual(false, this.range.collapsed, "collapsed"); + runner.assertEqual($('content'), this.range.commonAncestorContainer, "commonAncestorContainer") }, testSurrondContents: function() { From 6a92f1bceb82bf63bc8daa2844da48140f40c1eb Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Tue, 16 Feb 2010 12:17:21 -0600 Subject: [PATCH 104/164] more sensible range tests --- test/unit/range_test.js | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/test/unit/range_test.js b/test/unit/range_test.js index 83d00bb..2ef5c97 100644 --- a/test/unit/range_test.js +++ b/test/unit/range_test.js @@ -68,32 +68,32 @@ new Test.Unit.Runner({ testSetStartBefore: function() { var runner = this; - this.range.setStartBefore($('content')); + this.range.setStartBefore($('lorem')); this.range.setEnd($('content'), 2); runner.assertEqual(Node.ELEMENT_NODE, this.range.startContainer.nodeType, "startContainer.nodeType"); runner.assertEqual("DIV", this.range.startContainer.tagName, "startContainer.tagName"); - runner.assertEqual(1, this.range.startOffset, "startOffset"); + runner.assertEqual(0, this.range.startOffset, "startOffset"); runner.assertEqual(Node.ELEMENT_NODE, this.range.endContainer.nodeType, "endContainer.nodeType"); runner.assertEqual("DIV", this.range.endContainer.tagName, "endContainer.tagName"); runner.assertEqual(2, this.range.endOffset, "endOffset"); runner.assertEqual(false, this.range.collapsed, "collapsed"); - runner.assertEqual($('wrapper'), this.range.commonAncestorContainer, "commonAncestorContainer") + runner.assertEqual($('content'), this.range.commonAncestorContainer, "commonAncestorContainer") }, testSetStartAfter: function() { var runner = this; - this.range.setStartAfter($('content')); + this.range.setStartAfter($('lorem')); this.range.setEnd($('content'), 2); runner.assertEqual(Node.ELEMENT_NODE, this.range.startContainer.nodeType, "startContainer.nodeType"); runner.assertEqual("DIV", this.range.startContainer.tagName, "startContainer.tagName"); - runner.assertEqual(2, this.range.startOffset, "startOffset"); + runner.assertEqual(1, this.range.startOffset, "startOffset"); runner.assertEqual(Node.ELEMENT_NODE, this.range.endContainer.nodeType, "endContainer.nodeType"); runner.assertEqual("DIV", this.range.endContainer.tagName, "endContainer.tagName"); runner.assertEqual(2, this.range.endOffset, "endOffset"); - runner.assertEqual(true, this.range.collapsed, "collapsed"); + runner.assertEqual(false, this.range.collapsed, "collapsed"); runner.assertEqual($('content'), this.range.commonAncestorContainer, "commonAncestorContainer") }, @@ -101,32 +101,32 @@ new Test.Unit.Runner({ var runner = this; this.range.setStart($('content'), 0); - this.range.setEndBefore($('content')); + this.range.setEndBefore($('lorem')); runner.assertEqual(Node.ELEMENT_NODE, this.range.startContainer.nodeType, "startContainer.nodeType"); runner.assertEqual("DIV", this.range.startContainer.tagName, "startContainer.tagName"); - runner.assertEqual(1, this.range.startOffset, "startOffset"); + runner.assertEqual(0, this.range.startOffset, "startOffset"); runner.assertEqual(Node.ELEMENT_NODE, this.range.endContainer.nodeType, "endContainer.nodeType"); runner.assertEqual("DIV", this.range.endContainer.tagName, "endContainer.tagName"); - runner.assertEqual(1, this.range.endOffset, "endOffset"); + runner.assertEqual(0, this.range.endOffset, "endOffset"); runner.assertEqual(true, this.range.collapsed, "collapsed"); - runner.assertEqual($('wrapper'), this.range.commonAncestorContainer, "commonAncestorContainer") + runner.assertEqual($('content'), this.range.commonAncestorContainer, "commonAncestorContainer") }, testSetEndAfter: function() { var runner = this; - this.range.setStart($('content'), 1); - this.range.setEndAfter($('content')); + this.range.setStart($('content'), 0); + this.range.setEndAfter($('lorem')); runner.assertEqual(Node.ELEMENT_NODE, this.range.startContainer.nodeType, "startContainer.nodeType"); runner.assertEqual("DIV", this.range.startContainer.tagName, "startContainer.tagName"); - runner.assertEqual(1, this.range.startOffset, "startOffset"); + runner.assertEqual(0, this.range.startOffset, "startOffset"); runner.assertEqual(Node.ELEMENT_NODE, this.range.endContainer.nodeType, "endContainer.nodeType"); runner.assertEqual("DIV", this.range.endContainer.tagName, "endContainer.tagName"); - runner.assertEqual(2, this.range.endOffset, "endOffset"); + runner.assertEqual(1, this.range.endOffset, "endOffset"); runner.assertEqual(false, this.range.collapsed, "collapsed"); - runner.assertEqual($('wrapper'), this.range.commonAncestorContainer, "commonAncestorContainer") + runner.assertEqual($('content'), this.range.commonAncestorContainer, "commonAncestorContainer") }, testCollapse: function() { From b306ceb2f768749763225d8a60d84eedb15510f3 Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Wed, 17 Feb 2010 09:49:35 -0600 Subject: [PATCH 105/164] get safari selection prototype with __proto__ --- src/wysihat/dom/selection.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/wysihat/dom/selection.js b/src/wysihat/dom/selection.js index 99dc6fd..7c1d777 100644 --- a/src/wysihat/dom/selection.js +++ b/src/wysihat/dom/selection.js @@ -25,7 +25,7 @@ if (Prototype.Browser.IE) { // WebKit does not have a public Selection prototype if (typeof Selection == 'undefined') { var Selection = {} - Selection.prototype = window.getSelection(); + Selection.prototype = window.getSelection().__proto__; } Object.extend(Selection.prototype, (function() { From 850fc251125810af8542208be3bdf4a87c4ffe38 Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Wed, 17 Feb 2010 10:22:59 -0600 Subject: [PATCH 106/164] generate expect html with element builder --- test/unit/range_test.js | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/test/unit/range_test.js b/test/unit/range_test.js index 2ef5c97..3fb45d6 100644 --- a/test/unit/range_test.js +++ b/test/unit/range_test.js @@ -261,18 +261,17 @@ new Test.Unit.Runner({ testSurrondContents: function() { var runner = this; - var node = new Element('span', {id: 'wrapper'}); + var node; + + node = new Element('span', {id: 'wrapper'}); this.range.selectNodeContents($('lorem')); this.range.surroundContents(node); - var expected; - if (Prototype.Browser.IE) - expected = "Lorem ipsum"; - else - expected = "Lorem ipsum"; + expected = new Element('div'); + expected.appendChild(new Element('span', {id: 'wrapper'}).update("Lorem ipsum")); - runner.assertEqual(expected, $('lorem').innerHTML, "innerHTML"); + runner.assertEqual(expected.innerHTML, $('lorem').innerHTML, "innerHTML"); }, testEqualRange: function() { From 23ee545ce7dd7358cf3b1e213cb4a8ea3299f3fb Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Wed, 17 Feb 2010 10:23:34 -0600 Subject: [PATCH 107/164] mark insertNode range test as pending --- test/unit/range_test.js | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/test/unit/range_test.js b/test/unit/range_test.js index 3fb45d6..5c551a4 100644 --- a/test/unit/range_test.js +++ b/test/unit/range_test.js @@ -248,14 +248,15 @@ new Test.Unit.Runner({ runner.assertEqual("inserted!", $('inserted').innerHTML, "innerHTML"); - runner.assertEqual(Node.ELEMENT_NODE, this.range.startContainer.nodeType, "startContainer.nodeType"); - runner.assertEqual("DIV", this.range.startContainer.tagName, "startContainer.tagName"); - runner.assertEqual(0, this.range.startOffset, "startOffset"); - runner.assertEqual(Node.ELEMENT_NODE, this.range.endContainer.nodeType, "endContainer.nodeType"); - runner.assertEqual("DIV", this.range.endContainer.tagName, "endContainer.tagName"); - runner.assertEqual(2, this.range.endOffset, "endOffset"); - runner.assertEqual(false, this.range.collapsed, "collapsed"); - runner.assertEqual($('content'), this.range.commonAncestorContainer, "commonAncestorContainer") + // PENDING + // runner.assertEqual(Node.ELEMENT_NODE, this.range.startContainer.nodeType, "startContainer.nodeType"); + // runner.assertEqual("DIV", this.range.startContainer.tagName, "startContainer.tagName"); + // runner.assertEqual(0, this.range.startOffset, "startOffset"); + // runner.assertEqual(Node.ELEMENT_NODE, this.range.endContainer.nodeType, "endContainer.nodeType"); + // runner.assertEqual("DIV", this.range.endContainer.tagName, "endContainer.tagName"); + // runner.assertEqual(2, this.range.endOffset, "endOffset"); + // runner.assertEqual(false, this.range.collapsed, "collapsed"); + // runner.assertEqual($('content'), this.range.commonAncestorContainer, "commonAncestorContainer") }, testSurrondContents: function() { From 115d3f73f74b47d95f19276d5478959a53b2808c Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Wed, 17 Feb 2010 11:07:26 -0600 Subject: [PATCH 108/164] better range container assertions --- test/unit/range_test.js | 149 +++++++++++++++++++++------------------- 1 file changed, 80 insertions(+), 69 deletions(-) diff --git a/test/unit/range_test.js b/test/unit/range_test.js index 5c551a4..0cb6895 100644 --- a/test/unit/range_test.js +++ b/test/unit/range_test.js @@ -15,26 +15,22 @@ new Test.Unit.Runner({ this.range.setStart($('content'), 2); this.range.setEnd($('content'), 2); - runner.assertEqual(Node.ELEMENT_NODE, this.range.startContainer.nodeType, "startContainer.nodeType"); - runner.assertEqual("DIV", this.range.startContainer.tagName, "startContainer.tagName"); + runner.assertEqual($('content'), this.range.startContainer, "startContainer"); runner.assertEqual(2, this.range.startOffset, "startOffset"); - runner.assertEqual(Node.ELEMENT_NODE, this.range.endContainer.nodeType, "endContainer.nodeType"); - runner.assertEqual("DIV", this.range.endContainer.tagName, "endContainer.tagName"); + runner.assertEqual($('content'), this.range.endContainer, "endContainer"); runner.assertEqual(2, this.range.endOffset, "endOffset"); runner.assertEqual(true, this.range.collapsed, "collapsed"); - runner.assertEqual($('content'), this.range.commonAncestorContainer, "commonAncestorContainer") + runner.assertEqual($('content'), this.range.commonAncestorContainer, "commonAncestorContainer"); this.range.setStart($('lorem'), 0); this.range.setEnd($('lorem'), 1); - runner.assertEqual(Node.ELEMENT_NODE, this.range.startContainer.nodeType, "startContainer.nodeType"); - runner.assertEqual("STRONG", this.range.startContainer.tagName, "startContainer.tagName"); + runner.assertEqual($('lorem'), this.range.startContainer, "startContainer"); runner.assertEqual(0, this.range.startOffset, "startOffset"); - runner.assertEqual(Node.ELEMENT_NODE, this.range.endContainer.nodeType, "endContainer.nodeType"); - runner.assertEqual("STRONG", this.range.endContainer.tagName, "endContainer.tagName"); + runner.assertEqual($('lorem'), this.range.endContainer, "endContainer"); runner.assertEqual(1, this.range.endOffset, "endOffset"); runner.assertEqual(false, this.range.collapsed, "collapsed"); - runner.assertEqual($('lorem'), this.range.commonAncestorContainer, "commonAncestorContainer") + runner.assertEqual($('lorem'), this.range.commonAncestorContainer, "commonAncestorContainer"); }, testSetEnd: function() { @@ -43,26 +39,22 @@ new Test.Unit.Runner({ this.range.setStart($('content'), 1); this.range.setEnd($('content'), 2); - runner.assertEqual(Node.ELEMENT_NODE, this.range.startContainer.nodeType, "startContainer.nodeType"); - runner.assertEqual("DIV", this.range.startContainer.tagName, "startContainer.tagName"); + runner.assertEqual($('content'), this.range.startContainer, "startContainer"); runner.assertEqual(1, this.range.startOffset, "startOffset"); - runner.assertEqual(Node.ELEMENT_NODE, this.range.endContainer.nodeType, "endContainer.nodeType"); - runner.assertEqual("DIV", this.range.endContainer.tagName, "endContainer.tagName"); + runner.assertEqual($('content'), this.range.endContainer, "startContainer"); runner.assertEqual(2, this.range.endOffset, "endOffset"); runner.assertEqual(false, this.range.collapsed, "collapsed"); - runner.assertEqual($('content'), this.range.commonAncestorContainer, "commonAncestorContainer") + runner.assertEqual($('content'), this.range.commonAncestorContainer, "commonAncestorContainer"); this.range.setStart($('consectetuer'), 0); this.range.setEnd($('consectetuer'), 1); - runner.assertEqual(Node.ELEMENT_NODE, this.range.startContainer.nodeType, "startContainer.nodeType"); - runner.assertEqual("EM", this.range.startContainer.tagName, "startContainer.tagName"); + runner.assertEqual($('consectetuer'), this.range.startContainer, "startContainer"); runner.assertEqual(0, this.range.startOffset, "startOffset"); - runner.assertEqual(Node.ELEMENT_NODE, this.range.endContainer.nodeType, "endContainer.nodeType"); - runner.assertEqual("EM", this.range.endContainer.tagName, "endContainer.tagName"); + runner.assertEqual($('consectetuer'), this.range.endContainer, "startContainer"); runner.assertEqual(1, this.range.endOffset, "endOffset"); runner.assertEqual(false, this.range.collapsed, "collapsed"); - runner.assertEqual($('consectetuer'), this.range.commonAncestorContainer, "commonAncestorContainer") + runner.assertEqual($('consectetuer'), this.range.commonAncestorContainer, "commonAncestorContainer"); }, testSetStartBefore: function() { @@ -71,14 +63,22 @@ new Test.Unit.Runner({ this.range.setStartBefore($('lorem')); this.range.setEnd($('content'), 2); - runner.assertEqual(Node.ELEMENT_NODE, this.range.startContainer.nodeType, "startContainer.nodeType"); - runner.assertEqual("DIV", this.range.startContainer.tagName, "startContainer.tagName"); + runner.assertEqual($('content'), this.range.startContainer, "startContainer"); runner.assertEqual(0, this.range.startOffset, "startOffset"); - runner.assertEqual(Node.ELEMENT_NODE, this.range.endContainer.nodeType, "endContainer.nodeType"); - runner.assertEqual("DIV", this.range.endContainer.tagName, "endContainer.tagName"); + runner.assertEqual($('content'), this.range.endContainer, "endContainer"); runner.assertEqual(2, this.range.endOffset, "endOffset"); runner.assertEqual(false, this.range.collapsed, "collapsed"); - runner.assertEqual($('content'), this.range.commonAncestorContainer, "commonAncestorContainer") + runner.assertEqual($('content'), this.range.commonAncestorContainer, "commonAncestorContainer"); + + this.range.setStartBefore($('content')); + this.range.setEnd($('content'), 2); + + runner.assertEqual($('wrapper'), this.range.startContainer, "startContainer"); + runner.assertEqual(1, this.range.startOffset, "startOffset"); + runner.assertEqual($('content'), this.range.endContainer, "endContainer"); + runner.assertEqual(2, this.range.endOffset, "endOffset"); + runner.assertEqual(false, this.range.collapsed, "collapsed"); + runner.assertEqual($('wrapper'), this.range.commonAncestorContainer, "commonAncestorContainer"); }, testSetStartAfter: function() { @@ -87,14 +87,12 @@ new Test.Unit.Runner({ this.range.setStartAfter($('lorem')); this.range.setEnd($('content'), 2); - runner.assertEqual(Node.ELEMENT_NODE, this.range.startContainer.nodeType, "startContainer.nodeType"); - runner.assertEqual("DIV", this.range.startContainer.tagName, "startContainer.tagName"); + runner.assertEqual($('content'), this.range.startContainer, "startContainer"); runner.assertEqual(1, this.range.startOffset, "startOffset"); - runner.assertEqual(Node.ELEMENT_NODE, this.range.endContainer.nodeType, "endContainer.nodeType"); - runner.assertEqual("DIV", this.range.endContainer.tagName, "endContainer.tagName"); + runner.assertEqual($('content'), this.range.endContainer, "startContainer"); runner.assertEqual(2, this.range.endOffset, "endOffset"); runner.assertEqual(false, this.range.collapsed, "collapsed"); - runner.assertEqual($('content'), this.range.commonAncestorContainer, "commonAncestorContainer") + runner.assertEqual($('content'), this.range.commonAncestorContainer, "commonAncestorContainer"); }, testSetEndBefore: function() { @@ -103,14 +101,12 @@ new Test.Unit.Runner({ this.range.setStart($('content'), 0); this.range.setEndBefore($('lorem')); - runner.assertEqual(Node.ELEMENT_NODE, this.range.startContainer.nodeType, "startContainer.nodeType"); - runner.assertEqual("DIV", this.range.startContainer.tagName, "startContainer.tagName"); + runner.assertEqual($('content'), this.range.startContainer, "startContainer"); runner.assertEqual(0, this.range.startOffset, "startOffset"); - runner.assertEqual(Node.ELEMENT_NODE, this.range.endContainer.nodeType, "endContainer.nodeType"); - runner.assertEqual("DIV", this.range.endContainer.tagName, "endContainer.tagName"); + runner.assertEqual($('content'), this.range.endContainer, "startContainer"); runner.assertEqual(0, this.range.endOffset, "endOffset"); runner.assertEqual(true, this.range.collapsed, "collapsed"); - runner.assertEqual($('content'), this.range.commonAncestorContainer, "commonAncestorContainer") + runner.assertEqual($('content'), this.range.commonAncestorContainer, "commonAncestorContainer"); }, testSetEndAfter: function() { @@ -119,14 +115,22 @@ new Test.Unit.Runner({ this.range.setStart($('content'), 0); this.range.setEndAfter($('lorem')); - runner.assertEqual(Node.ELEMENT_NODE, this.range.startContainer.nodeType, "startContainer.nodeType"); - runner.assertEqual("DIV", this.range.startContainer.tagName, "startContainer.tagName"); + runner.assertEqual($('content'), this.range.startContainer, "startContainer"); runner.assertEqual(0, this.range.startOffset, "startOffset"); - runner.assertEqual(Node.ELEMENT_NODE, this.range.endContainer.nodeType, "endContainer.nodeType"); - runner.assertEqual("DIV", this.range.endContainer.tagName, "endContainer.tagName"); + runner.assertEqual($('content'), this.range.endContainer, "startContainer"); runner.assertEqual(1, this.range.endOffset, "endOffset"); runner.assertEqual(false, this.range.collapsed, "collapsed"); - runner.assertEqual($('content'), this.range.commonAncestorContainer, "commonAncestorContainer") + runner.assertEqual($('content'), this.range.commonAncestorContainer, "commonAncestorContainer"); + + this.range.setStart($('content'), 0); + this.range.setEndAfter($('content')); + + runner.assertEqual($('content'), this.range.startContainer, "startContainer"); + runner.assertEqual(0, this.range.startOffset, "startOffset"); + runner.assertEqual($('wrapper'), this.range.endContainer, "startContainer"); + runner.assertEqual(2, this.range.endOffset, "endOffset"); + runner.assertEqual(false, this.range.collapsed, "collapsed"); + runner.assertEqual($('wrapper'), this.range.commonAncestorContainer, "commonAncestorContainer"); }, testCollapse: function() { @@ -136,57 +140,67 @@ new Test.Unit.Runner({ this.range.setEnd($('content'), 2); this.range.collapse(true); - runner.assertEqual(Node.ELEMENT_NODE, this.range.startContainer.nodeType, "startContainer.nodeType"); - runner.assertEqual("DIV", this.range.startContainer.tagName, "startContainer.tagName"); + runner.assertEqual($('content'), this.range.startContainer, "startContainer"); runner.assertEqual(1, this.range.startOffset, "startOffset"); - runner.assertEqual(Node.ELEMENT_NODE, this.range.endContainer.nodeType, "endContainer.nodeType"); - runner.assertEqual("DIV", this.range.endContainer.tagName, "endContainer.tagName"); + runner.assertEqual($('content'), this.range.endContainer, "startContainer"); runner.assertEqual(1, this.range.endOffset, "endOffset"); runner.assertEqual(true, this.range.collapsed, "collapsed"); - runner.assertEqual($('content'), this.range.commonAncestorContainer, "commonAncestorContainer") + runner.assertEqual($('content'), this.range.commonAncestorContainer, "commonAncestorContainer"); this.range.setStart($('content'), 1); this.range.setEnd($('content'), 2); this.range.collapse(false); - runner.assertEqual(Node.ELEMENT_NODE, this.range.startContainer.nodeType, "startContainer.nodeType"); - runner.assertEqual("DIV", this.range.startContainer.tagName, "startContainer.tagName"); + runner.assertEqual($('content'), this.range.startContainer, "startContainer"); runner.assertEqual(2, this.range.startOffset, "startOffset"); - runner.assertEqual(Node.ELEMENT_NODE, this.range.endContainer.nodeType, "endContainer.nodeType"); - runner.assertEqual("DIV", this.range.endContainer.tagName, "endContainer.tagName"); + runner.assertEqual($('content'), this.range.endContainer, "startContainer"); runner.assertEqual(2, this.range.endOffset, "endOffset"); runner.assertEqual(true, this.range.collapsed, "collapsed"); - runner.assertEqual($('content'), this.range.commonAncestorContainer, "commonAncestorContainer") + runner.assertEqual($('content'), this.range.commonAncestorContainer, "commonAncestorContainer"); }, testSelectNode: function() { var runner = this; + this.range.selectNode($('content')); + + runner.assertEqual($('wrapper'), this.range.startContainer, "startContainer"); + runner.assertEqual(1, this.range.startOffset, "startOffset"); + runner.assertEqual($('wrapper'), this.range.endContainer, "startContainer"); + runner.assertEqual(2, this.range.endOffset, "endOffset"); + runner.assertEqual(false, this.range.collapsed, "collapsed"); + runner.assertEqual($('wrapper'), this.range.commonAncestorContainer, "commonAncestorContainer"); + this.range.selectNode($('lorem')); - runner.assertEqual(Node.ELEMENT_NODE, this.range.startContainer.nodeType, "startContainer.nodeType"); - runner.assertEqual("DIV", this.range.startContainer.tagName, "startContainer.tagName"); + runner.assertEqual($('content'), this.range.startContainer, "startContainer"); runner.assertEqual(0, this.range.startOffset, "startOffset"); - runner.assertEqual(Node.ELEMENT_NODE, this.range.endContainer.nodeType, "endContainer.nodeType"); - runner.assertEqual("DIV", this.range.endContainer.tagName, "endContainer.tagName"); + runner.assertEqual($('content'), this.range.endContainer, "startContainer"); runner.assertEqual(1, this.range.endOffset, "endOffset"); runner.assertEqual(false, this.range.collapsed, "collapsed"); - runner.assertEqual($('content'), this.range.commonAncestorContainer, "commonAncestorContainer") + runner.assertEqual($('content'), this.range.commonAncestorContainer, "commonAncestorContainer"); }, testSelectNodeContents: function() { var runner = this; + this.range.selectNodeContents($('content')); + + runner.assertEqual($('content'), this.range.startContainer, "startContainer"); + runner.assertEqual(0, this.range.startOffset, "startOffset"); + runner.assertEqual($('content'), this.range.endContainer, "startContainer"); + runner.assertEqual(4, this.range.endOffset, "endOffset"); + runner.assertEqual(false, this.range.collapsed, "collapsed"); + runner.assertEqual($('content'), this.range.commonAncestorContainer, "commonAncestorContainer"); + this.range.selectNodeContents($('lorem')); - runner.assertEqual(Node.ELEMENT_NODE, this.range.startContainer.nodeType, "startContainer.nodeType"); - runner.assertEqual("STRONG", this.range.startContainer.tagName, "startContainer.tagName"); + runner.assertEqual($('lorem'), this.range.startContainer, "startContainer"); runner.assertEqual(0, this.range.startOffset, "startOffset"); - runner.assertEqual(Node.ELEMENT_NODE, this.range.endContainer.nodeType, "endContainer.nodeType"); - runner.assertEqual("STRONG", this.range.endContainer.tagName, "endContainer.tagName"); + runner.assertEqual($('lorem'), this.range.endContainer, "startContainer"); runner.assertEqual(1, this.range.endOffset, "endOffset"); runner.assertEqual(false, this.range.collapsed, "collapsed"); - runner.assertEqual($('lorem'), this.range.commonAncestorContainer, "commonAncestorContainer") + runner.assertEqual($('lorem'), this.range.commonAncestorContainer, "commonAncestorContainer"); }, testDeleteContents: function() { @@ -248,15 +262,12 @@ new Test.Unit.Runner({ runner.assertEqual("inserted!", $('inserted').innerHTML, "innerHTML"); - // PENDING - // runner.assertEqual(Node.ELEMENT_NODE, this.range.startContainer.nodeType, "startContainer.nodeType"); - // runner.assertEqual("DIV", this.range.startContainer.tagName, "startContainer.tagName"); - // runner.assertEqual(0, this.range.startOffset, "startOffset"); - // runner.assertEqual(Node.ELEMENT_NODE, this.range.endContainer.nodeType, "endContainer.nodeType"); - // runner.assertEqual("DIV", this.range.endContainer.tagName, "endContainer.tagName"); - // runner.assertEqual(2, this.range.endOffset, "endOffset"); - // runner.assertEqual(false, this.range.collapsed, "collapsed"); - // runner.assertEqual($('content'), this.range.commonAncestorContainer, "commonAncestorContainer") + runner.assertEqual($('content'), this.range.startContainer, "startContainer"); + runner.assertEqual(0, this.range.startOffset, "startOffset"); + runner.assertEqual($('content'), this.range.endContainer, "startContainer"); + runner.assertEqual(2, this.range.endOffset, "endOffset"); + runner.assertEqual(false, this.range.collapsed, "collapsed"); + runner.assertEqual($('content'), this.range.commonAncestorContainer, "commonAncestorContainer"); }, testSurrondContents: function() { From 11df793b5a32cdb37a16089b43705f68138d8270 Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Wed, 17 Feb 2010 11:21:50 -0600 Subject: [PATCH 109/164] more range tests --- test/unit/range_test.js | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/test/unit/range_test.js b/test/unit/range_test.js index 0cb6895..275bac9 100644 --- a/test/unit/range_test.js +++ b/test/unit/range_test.js @@ -93,6 +93,16 @@ new Test.Unit.Runner({ runner.assertEqual(2, this.range.endOffset, "endOffset"); runner.assertEqual(false, this.range.collapsed, "collapsed"); runner.assertEqual($('content'), this.range.commonAncestorContainer, "commonAncestorContainer"); + + this.range.setStartAfter($('content')); + this.range.setEnd($('content'), 2); + + runner.assertEqual($('content'), this.range.startContainer, "startContainer"); + runner.assertEqual(2, this.range.startOffset, "startOffset"); + runner.assertEqual($('content'), this.range.endContainer, "startContainer"); + runner.assertEqual(2, this.range.endOffset, "endOffset"); + runner.assertEqual(true, this.range.collapsed, "collapsed"); + runner.assertEqual($('content'), this.range.commonAncestorContainer, "commonAncestorContainer"); }, testSetEndBefore: function() { @@ -107,6 +117,16 @@ new Test.Unit.Runner({ runner.assertEqual(0, this.range.endOffset, "endOffset"); runner.assertEqual(true, this.range.collapsed, "collapsed"); runner.assertEqual($('content'), this.range.commonAncestorContainer, "commonAncestorContainer"); + + this.range.setStart($('content'), 0); + this.range.setEndBefore($('content')); + + runner.assertEqual($('wrapper'), this.range.startContainer, "startContainer"); + runner.assertEqual(1, this.range.startOffset, "startOffset"); + runner.assertEqual($('wrapper'), this.range.endContainer, "startContainer"); + runner.assertEqual(1, this.range.endOffset, "endOffset"); + runner.assertEqual(true, this.range.collapsed, "collapsed"); + runner.assertEqual($('wrapper'), this.range.commonAncestorContainer, "commonAncestorContainer"); }, testSetEndAfter: function() { From e8e8c9ee25000f30d9f4ac00500aaafca43ba2c3 Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Wed, 17 Feb 2010 12:43:30 -0600 Subject: [PATCH 110/164] protect findChildPosition --- src/wysihat/dom/ierange.js | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/src/wysihat/dom/ierange.js b/src/wysihat/dom/ierange.js index de6797e..56afbbc 100644 --- a/src/wysihat/dom/ierange.js +++ b/src/wysihat/dom/ierange.js @@ -10,11 +10,6 @@ if (!window.getSelection) { // TODO: Move this object into a closure var DOMUtils = { - findChildPosition: function(node) { - for (var i = 0; node = node.previousSibling; i++) - continue; - return i; - }, isDataNode: function(node) { return node && node.nodeValue !== null && node.data !== null; }, @@ -120,6 +115,12 @@ if (!window.getSelection) { Range.END_TO_END = 2; Range.END_TO_START = 3; + function findChildPosition(node) { + for (var i = 0; node = node.previousSibling; i++) + continue; + return i; + } + Range.prototype = { startContainer: null, startOffset: 0, @@ -151,19 +152,19 @@ if (!window.getSelection) { }, setStartBefore: function(refNode) { // set start to beore this node - this.setStart(refNode.parentNode, DOMUtils.findChildPosition(refNode)); + this.setStart(refNode.parentNode, findChildPosition(refNode)); }, setStartAfter: function(refNode) { // select next sibling - this.setStart(refNode.parentNode, DOMUtils.findChildPosition(refNode) + 1); + this.setStart(refNode.parentNode, findChildPosition(refNode) + 1); }, setEndBefore: function(refNode) { // set end to beore this node - this.setEnd(refNode.parentNode, DOMUtils.findChildPosition(refNode)); + this.setEnd(refNode.parentNode, findChildPosition(refNode)); }, setEndAfter: function(refNode) { // select next sibling - this.setEnd(refNode.parentNode, DOMUtils.findChildPosition(refNode) + 1); + this.setEnd(refNode.parentNode, findChildPosition(refNode) + 1); }, selectNode: function(refNode) { this.setStartBefore(refNode); From 407a98177387bcbb8e1571a0c1dbd7cf5fd3e429 Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Wed, 17 Feb 2010 12:51:19 -0600 Subject: [PATCH 111/164] disable some tests, i think safari maybe wrong --- test/unit/range_test.js | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/test/unit/range_test.js b/test/unit/range_test.js index 275bac9..d4b8025 100644 --- a/test/unit/range_test.js +++ b/test/unit/range_test.js @@ -74,7 +74,7 @@ new Test.Unit.Runner({ this.range.setEnd($('content'), 2); runner.assertEqual($('wrapper'), this.range.startContainer, "startContainer"); - runner.assertEqual(1, this.range.startOffset, "startOffset"); + // runner.assertEqual(1, this.range.startOffset, "startOffset"); runner.assertEqual($('content'), this.range.endContainer, "endContainer"); runner.assertEqual(2, this.range.endOffset, "endOffset"); runner.assertEqual(false, this.range.collapsed, "collapsed"); @@ -97,12 +97,12 @@ new Test.Unit.Runner({ this.range.setStartAfter($('content')); this.range.setEnd($('content'), 2); - runner.assertEqual($('content'), this.range.startContainer, "startContainer"); - runner.assertEqual(2, this.range.startOffset, "startOffset"); - runner.assertEqual($('content'), this.range.endContainer, "startContainer"); - runner.assertEqual(2, this.range.endOffset, "endOffset"); - runner.assertEqual(true, this.range.collapsed, "collapsed"); - runner.assertEqual($('content'), this.range.commonAncestorContainer, "commonAncestorContainer"); + // runner.assertEqual($('content'), this.range.startContainer, "startContainer"); + // runner.assertEqual(2, this.range.startOffset, "startOffset"); + // runner.assertEqual($('content'), this.range.endContainer, "startContainer"); + // runner.assertEqual(2, this.range.endOffset, "endOffset"); + // runner.assertEqual(true, this.range.collapsed, "collapsed"); + // runner.assertEqual($('content'), this.range.commonAncestorContainer, "commonAncestorContainer"); }, testSetEndBefore: function() { @@ -121,12 +121,12 @@ new Test.Unit.Runner({ this.range.setStart($('content'), 0); this.range.setEndBefore($('content')); - runner.assertEqual($('wrapper'), this.range.startContainer, "startContainer"); - runner.assertEqual(1, this.range.startOffset, "startOffset"); - runner.assertEqual($('wrapper'), this.range.endContainer, "startContainer"); - runner.assertEqual(1, this.range.endOffset, "endOffset"); - runner.assertEqual(true, this.range.collapsed, "collapsed"); - runner.assertEqual($('wrapper'), this.range.commonAncestorContainer, "commonAncestorContainer"); + // runner.assertEqual($('wrapper'), this.range.startContainer, "startContainer"); + // runner.assertEqual(1, this.range.startOffset, "startOffset"); + // runner.assertEqual($('wrapper'), this.range.endContainer, "startContainer"); + // runner.assertEqual(1, this.range.endOffset, "endOffset"); + // runner.assertEqual(true, this.range.collapsed, "collapsed"); + // runner.assertEqual($('wrapper'), this.range.commonAncestorContainer, "commonAncestorContainer"); }, testSetEndAfter: function() { @@ -148,7 +148,7 @@ new Test.Unit.Runner({ runner.assertEqual($('content'), this.range.startContainer, "startContainer"); runner.assertEqual(0, this.range.startOffset, "startOffset"); runner.assertEqual($('wrapper'), this.range.endContainer, "startContainer"); - runner.assertEqual(2, this.range.endOffset, "endOffset"); + // runner.assertEqual(2, this.range.endOffset, "endOffset"); runner.assertEqual(false, this.range.collapsed, "collapsed"); runner.assertEqual($('wrapper'), this.range.commonAncestorContainer, "commonAncestorContainer"); }, @@ -185,9 +185,9 @@ new Test.Unit.Runner({ this.range.selectNode($('content')); runner.assertEqual($('wrapper'), this.range.startContainer, "startContainer"); - runner.assertEqual(1, this.range.startOffset, "startOffset"); + // runner.assertEqual(1, this.range.startOffset, "startOffset"); runner.assertEqual($('wrapper'), this.range.endContainer, "startContainer"); - runner.assertEqual(2, this.range.endOffset, "endOffset"); + // runner.assertEqual(2, this.range.endOffset, "endOffset"); runner.assertEqual(false, this.range.collapsed, "collapsed"); runner.assertEqual($('wrapper'), this.range.commonAncestorContainer, "commonAncestorContainer"); @@ -285,7 +285,7 @@ new Test.Unit.Runner({ runner.assertEqual($('content'), this.range.startContainer, "startContainer"); runner.assertEqual(0, this.range.startOffset, "startOffset"); runner.assertEqual($('content'), this.range.endContainer, "startContainer"); - runner.assertEqual(2, this.range.endOffset, "endOffset"); + // runner.assertEqual(2, this.range.endOffset, "endOffset"); runner.assertEqual(false, this.range.collapsed, "collapsed"); runner.assertEqual($('content'), this.range.commonAncestorContainer, "commonAncestorContainer"); }, From da407551dd43782f1854737e98a03c6a711a7108 Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Wed, 17 Feb 2010 14:05:20 -0600 Subject: [PATCH 112/164] clean test editor whitespace --- test/unit/range_test.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/unit/range_test.js b/test/unit/range_test.js index d4b8025..559c258 100644 --- a/test/unit/range_test.js +++ b/test/unit/range_test.js @@ -5,6 +5,8 @@ new Test.Unit.Runner({ "consectetuer adipiscing elit." ); + $('wrapper').cleanWhitespace(); + this.range = document.createRange(); this.range.selectNode($('content')); }, From 4f7baa7ad3fdd69935203bc50fc66d606b930935 Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Wed, 17 Feb 2010 14:08:40 -0600 Subject: [PATCH 113/164] correct tests that were dependent on whitespace --- test/unit/range_test.js | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/test/unit/range_test.js b/test/unit/range_test.js index 559c258..56c7d63 100644 --- a/test/unit/range_test.js +++ b/test/unit/range_test.js @@ -76,7 +76,7 @@ new Test.Unit.Runner({ this.range.setEnd($('content'), 2); runner.assertEqual($('wrapper'), this.range.startContainer, "startContainer"); - // runner.assertEqual(1, this.range.startOffset, "startOffset"); + runner.assertEqual(0, this.range.startOffset, "startOffset"); runner.assertEqual($('content'), this.range.endContainer, "endContainer"); runner.assertEqual(2, this.range.endOffset, "endOffset"); runner.assertEqual(false, this.range.collapsed, "collapsed"); @@ -101,8 +101,8 @@ new Test.Unit.Runner({ // runner.assertEqual($('content'), this.range.startContainer, "startContainer"); // runner.assertEqual(2, this.range.startOffset, "startOffset"); - // runner.assertEqual($('content'), this.range.endContainer, "startContainer"); - // runner.assertEqual(2, this.range.endOffset, "endOffset"); + runner.assertEqual($('content'), this.range.endContainer, "startContainer"); + runner.assertEqual(2, this.range.endOffset, "endOffset"); // runner.assertEqual(true, this.range.collapsed, "collapsed"); // runner.assertEqual($('content'), this.range.commonAncestorContainer, "commonAncestorContainer"); }, @@ -124,11 +124,11 @@ new Test.Unit.Runner({ this.range.setEndBefore($('content')); // runner.assertEqual($('wrapper'), this.range.startContainer, "startContainer"); - // runner.assertEqual(1, this.range.startOffset, "startOffset"); - // runner.assertEqual($('wrapper'), this.range.endContainer, "startContainer"); - // runner.assertEqual(1, this.range.endOffset, "endOffset"); + runner.assertEqual(0, this.range.startOffset, "startOffset"); + runner.assertEqual($('wrapper'), this.range.endContainer, "startContainer"); + runner.assertEqual(0, this.range.endOffset, "endOffset"); // runner.assertEqual(true, this.range.collapsed, "collapsed"); - // runner.assertEqual($('wrapper'), this.range.commonAncestorContainer, "commonAncestorContainer"); + runner.assertEqual($('wrapper'), this.range.commonAncestorContainer, "commonAncestorContainer"); }, testSetEndAfter: function() { @@ -150,7 +150,7 @@ new Test.Unit.Runner({ runner.assertEqual($('content'), this.range.startContainer, "startContainer"); runner.assertEqual(0, this.range.startOffset, "startOffset"); runner.assertEqual($('wrapper'), this.range.endContainer, "startContainer"); - // runner.assertEqual(2, this.range.endOffset, "endOffset"); + runner.assertEqual(1, this.range.endOffset, "endOffset"); runner.assertEqual(false, this.range.collapsed, "collapsed"); runner.assertEqual($('wrapper'), this.range.commonAncestorContainer, "commonAncestorContainer"); }, @@ -187,9 +187,9 @@ new Test.Unit.Runner({ this.range.selectNode($('content')); runner.assertEqual($('wrapper'), this.range.startContainer, "startContainer"); - // runner.assertEqual(1, this.range.startOffset, "startOffset"); + runner.assertEqual(0, this.range.startOffset, "startOffset"); runner.assertEqual($('wrapper'), this.range.endContainer, "startContainer"); - // runner.assertEqual(2, this.range.endOffset, "endOffset"); + runner.assertEqual(1, this.range.endOffset, "endOffset"); runner.assertEqual(false, this.range.collapsed, "collapsed"); runner.assertEqual($('wrapper'), this.range.commonAncestorContainer, "commonAncestorContainer"); From e754f89121910fa5ff12d5266759eb48ece8c11f Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Wed, 17 Feb 2010 14:14:42 -0600 Subject: [PATCH 114/164] correct some invalid tests --- test/unit/range_test.js | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/test/unit/range_test.js b/test/unit/range_test.js index 56c7d63..e028ea0 100644 --- a/test/unit/range_test.js +++ b/test/unit/range_test.js @@ -97,14 +97,14 @@ new Test.Unit.Runner({ runner.assertEqual($('content'), this.range.commonAncestorContainer, "commonAncestorContainer"); this.range.setStartAfter($('content')); - this.range.setEnd($('content'), 2); + this.range.setEnd($('wrapper'), 1); - // runner.assertEqual($('content'), this.range.startContainer, "startContainer"); - // runner.assertEqual(2, this.range.startOffset, "startOffset"); - runner.assertEqual($('content'), this.range.endContainer, "startContainer"); - runner.assertEqual(2, this.range.endOffset, "endOffset"); - // runner.assertEqual(true, this.range.collapsed, "collapsed"); - // runner.assertEqual($('content'), this.range.commonAncestorContainer, "commonAncestorContainer"); + runner.assertEqual($('wrapper'), this.range.startContainer, "startContainer"); + runner.assertEqual(1, this.range.startOffset, "startOffset"); + runner.assertEqual($('wrapper'), this.range.endContainer, "startContainer"); + runner.assertEqual(1, this.range.endOffset, "endOffset"); + runner.assertEqual(true, this.range.collapsed, "collapsed"); + runner.assertEqual($('wrapper'), this.range.commonAncestorContainer, "commonAncestorContainer"); }, testSetEndBefore: function() { @@ -120,14 +120,14 @@ new Test.Unit.Runner({ runner.assertEqual(true, this.range.collapsed, "collapsed"); runner.assertEqual($('content'), this.range.commonAncestorContainer, "commonAncestorContainer"); - this.range.setStart($('content'), 0); + this.range.setStart($('wrapper'), 0); this.range.setEndBefore($('content')); - // runner.assertEqual($('wrapper'), this.range.startContainer, "startContainer"); + runner.assertEqual($('wrapper'), this.range.startContainer, "startContainer"); runner.assertEqual(0, this.range.startOffset, "startOffset"); runner.assertEqual($('wrapper'), this.range.endContainer, "startContainer"); runner.assertEqual(0, this.range.endOffset, "endOffset"); - // runner.assertEqual(true, this.range.collapsed, "collapsed"); + runner.assertEqual(true, this.range.collapsed, "collapsed"); runner.assertEqual($('wrapper'), this.range.commonAncestorContainer, "commonAncestorContainer"); }, From 0dccc5c333d00ebe5f7d0c3e8f3ab7e5f23012ae Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Wed, 17 Feb 2010 14:24:48 -0600 Subject: [PATCH 115/164] pending test for toString --- test/unit/range_test.js | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/test/unit/range_test.js b/test/unit/range_test.js index e028ea0..272dbb0 100644 --- a/test/unit/range_test.js +++ b/test/unit/range_test.js @@ -23,6 +23,7 @@ new Test.Unit.Runner({ runner.assertEqual(2, this.range.endOffset, "endOffset"); runner.assertEqual(true, this.range.collapsed, "collapsed"); runner.assertEqual($('content'), this.range.commonAncestorContainer, "commonAncestorContainer"); + runner.assertEqual("", this.range.toString(), "toString"); this.range.setStart($('lorem'), 0); this.range.setEnd($('lorem'), 1); @@ -33,6 +34,7 @@ new Test.Unit.Runner({ runner.assertEqual(1, this.range.endOffset, "endOffset"); runner.assertEqual(false, this.range.collapsed, "collapsed"); runner.assertEqual($('lorem'), this.range.commonAncestorContainer, "commonAncestorContainer"); + // runner.assertEqual("Lorem ipsum", this.range.toString(), "toString"); }, testSetEnd: function() { @@ -47,6 +49,7 @@ new Test.Unit.Runner({ runner.assertEqual(2, this.range.endOffset, "endOffset"); runner.assertEqual(false, this.range.collapsed, "collapsed"); runner.assertEqual($('content'), this.range.commonAncestorContainer, "commonAncestorContainer"); + runner.assertEqual(" dolor sit amet, ", this.range.toString(), "toString"); this.range.setStart($('consectetuer'), 0); this.range.setEnd($('consectetuer'), 1); @@ -57,6 +60,7 @@ new Test.Unit.Runner({ runner.assertEqual(1, this.range.endOffset, "endOffset"); runner.assertEqual(false, this.range.collapsed, "collapsed"); runner.assertEqual($('consectetuer'), this.range.commonAncestorContainer, "commonAncestorContainer"); + // runner.assertEqual("consectetuer", this.range.toString(), "toString"); }, testSetStartBefore: function() { @@ -71,6 +75,7 @@ new Test.Unit.Runner({ runner.assertEqual(2, this.range.endOffset, "endOffset"); runner.assertEqual(false, this.range.collapsed, "collapsed"); runner.assertEqual($('content'), this.range.commonAncestorContainer, "commonAncestorContainer"); + runner.assertEqual("Lorem ipsum dolor sit amet, ", this.range.toString(), "toString"); this.range.setStartBefore($('content')); this.range.setEnd($('content'), 2); @@ -81,6 +86,7 @@ new Test.Unit.Runner({ runner.assertEqual(2, this.range.endOffset, "endOffset"); runner.assertEqual(false, this.range.collapsed, "collapsed"); runner.assertEqual($('wrapper'), this.range.commonAncestorContainer, "commonAncestorContainer"); + runner.assertEqual("Lorem ipsum dolor sit amet, ", this.range.toString(), "toString"); }, testSetStartAfter: function() { @@ -95,6 +101,7 @@ new Test.Unit.Runner({ runner.assertEqual(2, this.range.endOffset, "endOffset"); runner.assertEqual(false, this.range.collapsed, "collapsed"); runner.assertEqual($('content'), this.range.commonAncestorContainer, "commonAncestorContainer"); + runner.assertEqual(" dolor sit amet, ", this.range.toString(), "toString"); this.range.setStartAfter($('content')); this.range.setEnd($('wrapper'), 1); @@ -105,6 +112,7 @@ new Test.Unit.Runner({ runner.assertEqual(1, this.range.endOffset, "endOffset"); runner.assertEqual(true, this.range.collapsed, "collapsed"); runner.assertEqual($('wrapper'), this.range.commonAncestorContainer, "commonAncestorContainer"); + // runner.assertEqual("", this.range.toString(), "toString"); }, testSetEndBefore: function() { @@ -119,6 +127,7 @@ new Test.Unit.Runner({ runner.assertEqual(0, this.range.endOffset, "endOffset"); runner.assertEqual(true, this.range.collapsed, "collapsed"); runner.assertEqual($('content'), this.range.commonAncestorContainer, "commonAncestorContainer"); + runner.assertEqual("", this.range.toString(), "toString"); this.range.setStart($('wrapper'), 0); this.range.setEndBefore($('content')); @@ -129,6 +138,7 @@ new Test.Unit.Runner({ runner.assertEqual(0, this.range.endOffset, "endOffset"); runner.assertEqual(true, this.range.collapsed, "collapsed"); runner.assertEqual($('wrapper'), this.range.commonAncestorContainer, "commonAncestorContainer"); + runner.assertEqual("", this.range.toString(), "toString"); }, testSetEndAfter: function() { @@ -143,6 +153,7 @@ new Test.Unit.Runner({ runner.assertEqual(1, this.range.endOffset, "endOffset"); runner.assertEqual(false, this.range.collapsed, "collapsed"); runner.assertEqual($('content'), this.range.commonAncestorContainer, "commonAncestorContainer"); + runner.assertEqual("Lorem ipsum", this.range.toString(), "toString"); this.range.setStart($('content'), 0); this.range.setEndAfter($('content')); @@ -153,6 +164,7 @@ new Test.Unit.Runner({ runner.assertEqual(1, this.range.endOffset, "endOffset"); runner.assertEqual(false, this.range.collapsed, "collapsed"); runner.assertEqual($('wrapper'), this.range.commonAncestorContainer, "commonAncestorContainer"); + // runner.assertEqual("Lorem ipsum dolor sit amet, consectetuer adipiscing elit.", this.range.toString(), "toString"); }, testCollapse: function() { @@ -168,6 +180,7 @@ new Test.Unit.Runner({ runner.assertEqual(1, this.range.endOffset, "endOffset"); runner.assertEqual(true, this.range.collapsed, "collapsed"); runner.assertEqual($('content'), this.range.commonAncestorContainer, "commonAncestorContainer"); + runner.assertEqual("", this.range.toString(), "toString"); this.range.setStart($('content'), 1); this.range.setEnd($('content'), 2); @@ -179,6 +192,7 @@ new Test.Unit.Runner({ runner.assertEqual(2, this.range.endOffset, "endOffset"); runner.assertEqual(true, this.range.collapsed, "collapsed"); runner.assertEqual($('content'), this.range.commonAncestorContainer, "commonAncestorContainer"); + runner.assertEqual("", this.range.toString(), "toString"); }, testSelectNode: function() { @@ -192,6 +206,7 @@ new Test.Unit.Runner({ runner.assertEqual(1, this.range.endOffset, "endOffset"); runner.assertEqual(false, this.range.collapsed, "collapsed"); runner.assertEqual($('wrapper'), this.range.commonAncestorContainer, "commonAncestorContainer"); + // runner.assertEqual("Lorem ipsum dolor sit amet, consectetuer adipiscing elit.", this.range.toString(), "toString"); this.range.selectNode($('lorem')); @@ -201,6 +216,7 @@ new Test.Unit.Runner({ runner.assertEqual(1, this.range.endOffset, "endOffset"); runner.assertEqual(false, this.range.collapsed, "collapsed"); runner.assertEqual($('content'), this.range.commonAncestorContainer, "commonAncestorContainer"); + runner.assertEqual("Lorem ipsum", this.range.toString(), "toString"); }, testSelectNodeContents: function() { @@ -214,6 +230,7 @@ new Test.Unit.Runner({ runner.assertEqual(4, this.range.endOffset, "endOffset"); runner.assertEqual(false, this.range.collapsed, "collapsed"); runner.assertEqual($('content'), this.range.commonAncestorContainer, "commonAncestorContainer"); + // runner.assertEqual("Lorem ipsum dolor sit amet, consectetuer adipiscing elit.", this.range.toString(), "toString"); this.range.selectNodeContents($('lorem')); @@ -223,6 +240,7 @@ new Test.Unit.Runner({ runner.assertEqual(1, this.range.endOffset, "endOffset"); runner.assertEqual(false, this.range.collapsed, "collapsed"); runner.assertEqual($('lorem'), this.range.commonAncestorContainer, "commonAncestorContainer"); + // runner.assertEqual("Lorem ipsum", this.range.toString(), "toString"); }, testDeleteContents: function() { From 77e1b10dad74e31f5fdc310133fe9e3742d44435 Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Wed, 17 Feb 2010 15:50:41 -0600 Subject: [PATCH 116/164] fix textrange end point adopt when offset is beyond child nodes --- src/wysihat/dom/ierange.js | 66 +++++++++++++++++++++----------------- test/unit/range_test.js | 18 +++++------ 2 files changed, 45 insertions(+), 39 deletions(-) diff --git a/src/wysihat/dom/ierange.js b/src/wysihat/dom/ierange.js index 56afbbc..b845b35 100644 --- a/src/wysihat/dom/ierange.js +++ b/src/wysihat/dom/ierange.js @@ -70,34 +70,6 @@ if (!window.getSelection) { adoptBoundary(domRange, textRange, true); adoptBoundary(domRange, textRange, false); return domRange; - }, - convertFromDOMRange: function(domRange) { - function adoptEndPoint(textRange, domRange, bStart) { - // find anchor node and offset - var container = domRange[bStart ? 'startContainer' : 'endContainer']; - var offset = domRange[bStart ? 'startOffset' : 'endOffset'], textOffset = 0; - var anchorNode = DOMUtils.isDataNode(container) ? container : container.childNodes[offset]; - var anchorParent = DOMUtils.isDataNode(container) ? container.parentNode : container; - // visible data nodes need a text offset - if (container.nodeType == 3 || container.nodeType == 4) - textOffset = offset; - - // create a cursor element node to position range (since we can't select text nodes) - var cursorNode = domRange._document.createElement('a'); - anchorParent.insertBefore(cursorNode, anchorNode); - var cursor = domRange._document.body.createTextRange(); - cursor.moveToElementText(cursorNode); - cursorNode.parentNode.removeChild(cursorNode); - // move range - textRange.setEndPoint(bStart ? 'StartToStart' : 'EndToStart', cursor); - textRange[bStart ? 'moveStart' : 'moveEnd']('character', textOffset); - } - - // return an IE text range - var textRange = domRange._document.body.createTextRange(); - adoptEndPoint(textRange, domRange, true); - adoptEndPoint(textRange, domRange, false); - return textRange; } }; @@ -130,6 +102,40 @@ if (!window.getSelection) { collapsed: false, _document: null, + _toTextRange: function() { + function adoptEndPoint(textRange, domRange, bStart) { + // find anchor node and offset + var container = domRange[bStart ? 'startContainer' : 'endContainer']; + var offset = domRange[bStart ? 'startOffset' : 'endOffset'], textOffset = 0; + var anchorNode = DOMUtils.isDataNode(container) ? container : container.childNodes[offset]; + var anchorParent = DOMUtils.isDataNode(container) ? container.parentNode : container; + + // visible data nodes need a text offset + if (container.nodeType == 3 || container.nodeType == 4) + textOffset = offset; + + // create a cursor element node to position range (since we can't select text nodes) + var cursorNode = domRange._document.createElement('a'); + if (anchorNode) + anchorParent.insertBefore(cursorNode, anchorNode); + else + anchorParent.appendChild(cursorNode); + var cursor = domRange._document.body.createTextRange(); + cursor.moveToElementText(cursorNode); + cursorNode.parentNode.removeChild(cursorNode); + + // move range + textRange.setEndPoint(bStart ? 'StartToStart' : 'EndToStart', cursor); + textRange[bStart ? 'moveStart' : 'moveEnd']('character', textOffset); + } + + // return an IE text range + var textRange = this._document.body.createTextRange(); + adoptEndPoint(textRange, this, true); + adoptEndPoint(textRange, this, false); + return textRange; + }, + _refreshProperties: function() { // collapsed attribute this.collapsed = (this.startContainer == this.endContainer && this.startOffset == this.endOffset); @@ -285,7 +291,7 @@ if (!window.getSelection) { detach: function() { }, toString: function() { - return TextRangeUtils.convertFromDOMRange(this).text; + return this._toTextRange().text; }, createContextualFragment: function(tagString) { // parse the tag string in a context node @@ -396,7 +402,7 @@ if (!window.getSelection) { textRange.parentElement().isContentEditable; }, addRange: function(range) { - var selection = this._document.selection.createRange(), textRange = TextRangeUtils.convertFromDOMRange(range); + var selection = this._document.selection.createRange(), textRange = range._toTextRange(); if (!this._selectionExists(selection)) { textRange.select(); } else { diff --git a/test/unit/range_test.js b/test/unit/range_test.js index 272dbb0..2e6a5e2 100644 --- a/test/unit/range_test.js +++ b/test/unit/range_test.js @@ -34,7 +34,7 @@ new Test.Unit.Runner({ runner.assertEqual(1, this.range.endOffset, "endOffset"); runner.assertEqual(false, this.range.collapsed, "collapsed"); runner.assertEqual($('lorem'), this.range.commonAncestorContainer, "commonAncestorContainer"); - // runner.assertEqual("Lorem ipsum", this.range.toString(), "toString"); + runner.assertEqual("Lorem ipsum", this.range.toString(), "toString"); }, testSetEnd: function() { @@ -60,7 +60,7 @@ new Test.Unit.Runner({ runner.assertEqual(1, this.range.endOffset, "endOffset"); runner.assertEqual(false, this.range.collapsed, "collapsed"); runner.assertEqual($('consectetuer'), this.range.commonAncestorContainer, "commonAncestorContainer"); - // runner.assertEqual("consectetuer", this.range.toString(), "toString"); + runner.assertEqual("consectetuer", this.range.toString(), "toString"); }, testSetStartBefore: function() { @@ -112,7 +112,7 @@ new Test.Unit.Runner({ runner.assertEqual(1, this.range.endOffset, "endOffset"); runner.assertEqual(true, this.range.collapsed, "collapsed"); runner.assertEqual($('wrapper'), this.range.commonAncestorContainer, "commonAncestorContainer"); - // runner.assertEqual("", this.range.toString(), "toString"); + runner.assertEqual("", this.range.toString(), "toString"); }, testSetEndBefore: function() { @@ -164,7 +164,7 @@ new Test.Unit.Runner({ runner.assertEqual(1, this.range.endOffset, "endOffset"); runner.assertEqual(false, this.range.collapsed, "collapsed"); runner.assertEqual($('wrapper'), this.range.commonAncestorContainer, "commonAncestorContainer"); - // runner.assertEqual("Lorem ipsum dolor sit amet, consectetuer adipiscing elit.", this.range.toString(), "toString"); + runner.assertEqual("Lorem ipsum dolor sit amet, consectetuer adipiscing elit.", this.range.toString(), "toString"); }, testCollapse: function() { @@ -206,7 +206,7 @@ new Test.Unit.Runner({ runner.assertEqual(1, this.range.endOffset, "endOffset"); runner.assertEqual(false, this.range.collapsed, "collapsed"); runner.assertEqual($('wrapper'), this.range.commonAncestorContainer, "commonAncestorContainer"); - // runner.assertEqual("Lorem ipsum dolor sit amet, consectetuer adipiscing elit.", this.range.toString(), "toString"); + runner.assertEqual("Lorem ipsum dolor sit amet, consectetuer adipiscing elit.", this.range.toString(), "toString"); this.range.selectNode($('lorem')); @@ -230,7 +230,7 @@ new Test.Unit.Runner({ runner.assertEqual(4, this.range.endOffset, "endOffset"); runner.assertEqual(false, this.range.collapsed, "collapsed"); runner.assertEqual($('content'), this.range.commonAncestorContainer, "commonAncestorContainer"); - // runner.assertEqual("Lorem ipsum dolor sit amet, consectetuer adipiscing elit.", this.range.toString(), "toString"); + runner.assertEqual("Lorem ipsum dolor sit amet, consectetuer adipiscing elit.", this.range.toString(), "toString"); this.range.selectNodeContents($('lorem')); @@ -240,7 +240,7 @@ new Test.Unit.Runner({ runner.assertEqual(1, this.range.endOffset, "endOffset"); runner.assertEqual(false, this.range.collapsed, "collapsed"); runner.assertEqual($('lorem'), this.range.commonAncestorContainer, "commonAncestorContainer"); - // runner.assertEqual("Lorem ipsum", this.range.toString(), "toString"); + runner.assertEqual("Lorem ipsum", this.range.toString(), "toString"); }, testDeleteContents: function() { @@ -260,7 +260,7 @@ new Test.Unit.Runner({ runner.assertEqual("", $('lorem').innerHTML, "innerHTML"); - // IE document does not have any useful methods. Everyone else can just + //IE document does not have any useful methods. Everyone else can just // read textContent, IE needs to append the fragment to another element // and read its innerHTML if (contents.textContent) { @@ -305,7 +305,7 @@ new Test.Unit.Runner({ runner.assertEqual($('content'), this.range.startContainer, "startContainer"); runner.assertEqual(0, this.range.startOffset, "startOffset"); runner.assertEqual($('content'), this.range.endContainer, "startContainer"); - // runner.assertEqual(2, this.range.endOffset, "endOffset"); + runner.assertEqual(2, this.range.endOffset, "endOffset"); runner.assertEqual(false, this.range.collapsed, "collapsed"); runner.assertEqual($('content'), this.range.commonAncestorContainer, "commonAncestorContainer"); }, From e0c8a242771e76433368c77247a321b1ac92481d Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Wed, 17 Feb 2010 23:49:07 -0600 Subject: [PATCH 117/164] return false if no lists are selected --- src/wysihat/commands.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/wysihat/commands.js b/src/wysihat/commands.js index d933a1e..758e23c 100644 --- a/src/wysihat/commands.js +++ b/src/wysihat/commands.js @@ -222,7 +222,8 @@ var $E = WysiHat.Commands = (function(window) { **/ function orderedListSelected() { var element = window.getSelection().getNode(); - return element.match("[contenteditable=true] ol, [contenteditable=true] ol *"); + if (element) return element.match("[contenteditable=true] ol, [contenteditable=true] ol *"); + return false; } /** @@ -251,7 +252,8 @@ var $E = WysiHat.Commands = (function(window) { **/ function unorderedListSelected() { var element = window.getSelection().getNode(); - return element.match("[contenteditable=true] ul, [contenteditable=true] ul *"); + if (element) return element.match("[contenteditable=true] ul, [contenteditable=true] ul *"); + return false; } /** From 316a1c5c58efbdc978eb7c1d2507e66cd505446a Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Thu, 18 Feb 2010 15:13:58 -0600 Subject: [PATCH 118/164] evidence --- .gitmodules | 3 - Rakefile | 33 +- test/sprockets.yml | 6 + test/unit/editor_test.js | 50 +-- test/unit/formatter_test.js | 326 ++++++++------- test/unit/range_test.js | 704 ++++++++++++++++---------------- test/unit/sanitizer_test.js | 112 +++-- test/unit/selection_test.js | 296 +++++++------- test/unit/templates/default.erb | 21 - vendor/unittest_js | 1 - 10 files changed, 748 insertions(+), 804 deletions(-) create mode 100644 test/sprockets.yml delete mode 100644 test/unit/templates/default.erb delete mode 160000 vendor/unittest_js diff --git a/.gitmodules b/.gitmodules index d9dce75..d6b2acb 100644 --- a/.gitmodules +++ b/.gitmodules @@ -7,6 +7,3 @@ [submodule "sprockets"] path = vendor/sprockets url = git://github.com/sstephenson/sprockets.git -[submodule "unittest_js"] - path = vendor/unittest_js - url = git://github.com/tobie/unittest_js.git diff --git a/Rakefile b/Rakefile index e2bb1c0..83d9b8b 100644 --- a/Rakefile +++ b/Rakefile @@ -2,13 +2,11 @@ require 'rake' require 'rake/testtask' require 'rake/rdoctask' -WYSIHAT_ROOT = File.expand_path(File.dirname(__FILE__)) -WYSIHAT_SRC_DIR = File.join(WYSIHAT_ROOT, 'src') -WYSIHAT_DIST_DIR = File.join(WYSIHAT_ROOT, 'dist') -WYSIHAT_DOC_DIR = File.join(WYSIHAT_ROOT, 'doc') -WYSIHAT_TEST_DIR = File.join(WYSIHAT_ROOT, 'test') -WYSIHAT_TEST_UNIT_DIR = File.join(WYSIHAT_TEST_DIR, 'unit') -WYSIHAT_TMP_DIR = File.join(WYSIHAT_TEST_UNIT_DIR, 'tmp') +WYSIHAT_ROOT = File.expand_path(File.dirname(__FILE__)) +WYSIHAT_SRC_DIR = File.join(WYSIHAT_ROOT, 'src') +WYSIHAT_DIST_DIR = File.join(WYSIHAT_ROOT, 'dist') +WYSIHAT_DOC_DIR = File.join(WYSIHAT_ROOT, 'doc') +WYSIHAT_TEST_DIR = File.join(WYSIHAT_ROOT, 'test') desc "Update git submodules" task :update_submodules do @@ -84,24 +82,3 @@ namespace :doc do rm_rf WYSIHAT_DOC_DIR end end - -desc "Builds the distribution, runs the JavaScript unit tests and collects their results." -task :test => ['test:build'] - -namespace :test do - task :build => [:clean, :dist] do - require File.join(WYSIHAT_ROOT, "vendor", "unittest_js", "lib", "unittest_js") - builder = UnittestJS::Builder::SuiteBuilder.new({ - :input_dir => WYSIHAT_TEST_UNIT_DIR, - :assets_dir => WYSIHAT_DIST_DIR - }) - selected_tests = (ENV['TESTS'] || '').split(',') - builder.collect(*selected_tests) - builder.render - end - - task :clean do - require File.join(WYSIHAT_ROOT, "vendor", "unittest_js", "lib", "unittest_js") - UnittestJS::Builder.empty_dir!(WYSIHAT_TMP_DIR) - end -end diff --git a/test/sprockets.yml b/test/sprockets.yml new file mode 100644 index 0000000..e1f2a85 --- /dev/null +++ b/test/sprockets.yml @@ -0,0 +1,6 @@ +:load_path: + - src + - vendor/*/src +:source_files: + - vendor/prototype/src/prototype.js + - src/wysihat.js diff --git a/test/unit/editor_test.js b/test/unit/editor_test.js index 9ec7f99..329c147 100644 --- a/test/unit/editor_test.js +++ b/test/unit/editor_test.js @@ -1,32 +1,34 @@ -new Test.Unit.Runner({ - setup: function() { - this.textarea = $('content'); - this.editor = WysiHat.Editor.attach(this.textarea); - this.editor.focus(); - }, +function setup() { + this.textarea = $('content'); + this.editor = WysiHat.Editor.attach(this.textarea); + this.editor.focus(); +} - teardown: function() { - this.editor.innerHTML = ""; - this.textarea.value = ""; - }, +function teardown() { + this.editor.innerHTML = ""; + this.textarea.value = ""; +} - testInsertHTML: function() { - var runner = this; +test("inertHTML", function() { + setup.bind(this)(); - this.editor.insertHTML("

    Hello.

    "); - runner.assertEqual("

    Hello.

    ", this.editor.innerHTML.formatHTMLOutput()); - }, + this.editor.insertHTML("

    Hello.

    "); + this.assertEqual("

    Hello.

    ", this.editor.innerHTML.formatHTMLOutput()); - testBoldSelection: function() { - var runner = this; + teardown.bind(this)(); +}); + +test("boldSelection", function() { + setup.bind(this)(); + + // this.editor.insertHTML("

    Hello.

    "); + this.editor.innerHTML = '

    Hello.

    '.formatHTMLInput(); - // this.editor.insertHTML("

    Hello.

    "); - this.editor.innerHTML = '

    Hello.

    '.formatHTMLInput(); + window.getSelection().selectNode(this.editor.down('#hello')); + this.editor.boldSelection(); - window.getSelection().selectNode(this.editor.down('#hello')); - this.editor.boldSelection(); + this.assert(this.editor.boldSelected()); + this.assertEqual('

    Hello.

    ', this.editor.innerHTML.formatHTMLOutput()); - runner.assert(this.editor.boldSelected()); - runner.assertEqual('

    Hello.

    ', this.editor.innerHTML.formatHTMLOutput()); - } + teardown.bind(this)(); }); diff --git a/test/unit/formatter_test.js b/test/unit/formatter_test.js index f047718..2c51c5d 100644 --- a/test/unit/formatter_test.js +++ b/test/unit/formatter_test.js @@ -1,182 +1,174 @@ -new Test.Unit.Runner({ - testNormalizeTags: function() { - var runner = this; +test("normalize tags", function() { + [ + ['Hello', 'Hello'], + ['Hello\nWorld\n', 'Hello\r\nWorld\r\n'], + ['

    Hello

    \n

    World

    ', '

    Hello

    \n

    World

    '], + ['

    Hello

    \n

    World

    ', '

    Hello

    \n

    World

    '], + ['

    Hello

    \n

    World

    ', '

    Hello

    \n

    World

    '], + ['Hello
    \nWorld', 'Hello
    \r\nWorld'] + ].each(function(assertion) { + this.assertEqual(assertion[0], assertion[1].tidyXHTML()); + }.bind(this)); +}); +test("format html", function() { + if (Prototype.Browser.WebKit) { [ - ['Hello', 'Hello'], - ['Hello\nWorld\n', 'Hello\r\nWorld\r\n'], - ['

    Hello

    \n

    World

    ', '

    Hello

    \n

    World

    '], - ['

    Hello

    \n

    World

    ', '

    Hello

    \n

    World

    '], - ['

    Hello

    \n

    World

    ', '

    Hello

    \n

    World

    '], - ['Hello
    \nWorld', 'Hello
    \r\nWorld'] - ].each(function(assertion) { - runner.assertEqual(assertion[0], assertion[1].tidyXHTML()); - }); - }, - - testFormatHtml: function() { - var runner = this; + ['

    Here is some basic text
    \nwith a line break.

    \n\n

    And maybe another paragraph

    ', + '
    Here is some basic text
    with a line break.

    And maybe another paragraph
    '], + ['

    Hello

    \n\n

    World!

    ', '
    Hello

    World!
    '], + ['

    Hello

    \n\n

    World!

    ', '

    Hello


    World!
    '], + ['

    Hello

    \n\n

    World!

    ', '
    Hello

    World!

    '], + ['

    Hello
    \nWorld!
    \nGoodbye!

    ', 'Hello
    World!
    Goodbye!
    '], + ['

    Hello
    \nWorld!
    \nGoodbye!

    ', 'Hello
    World!
    Goodbye!
    '], + ['

    Hello

    \n\n

    World!
    \nGoodbye!

    ', 'Hello

    World!
    Goodbye!
    '], + ['

    Hello
    \nWorld!

    \n\n

    Goodbye!

    ', 'Hello
    World!

    Goodbye!
    '], - if (Prototype.Browser.WebKit) { - [ - ['

    Here is some basic text
    \nwith a line break.

    \n\n

    And maybe another paragraph

    ', - '
    Here is some basic text
    with a line break.

    And maybe another paragraph
    '], - ['

    Hello

    \n\n

    World!

    ', '
    Hello

    World!
    '], - ['

    Hello

    \n\n

    World!

    ', '

    Hello


    World!
    '], - ['

    Hello

    \n\n

    World!

    ', '
    Hello

    World!

    '], - ['

    Hello
    \nWorld!
    \nGoodbye!

    ', 'Hello
    World!
    Goodbye!
    '], - ['

    Hello
    \nWorld!
    \nGoodbye!

    ', 'Hello
    World!
    Goodbye!
    '], - ['

    Hello

    \n\n

    World!
    \nGoodbye!

    ', 'Hello

    World!
    Goodbye!
    '], - ['

    Hello
    \nWorld!

    \n\n

    Goodbye!

    ', 'Hello
    World!

    Goodbye!
    '], + ['

    Some bold text

    ', + 'Some bold text'], + ['

    Some italic text

    ', + 'Some italic text'], + ['

    Some underlined text

    ', + 'Some underlined text'], + ['

    Some bold and italic text

    ', + 'Some bold and italic text'], + ['

    Some italic and bold text

    ', + 'Some italic and bold text'], + ['

    Some bold, underlined, and italic text

    ', + 'Some bold, underlined, and italic text'], + ['

    Hello
    \n

    ', '
    Hello
    '], + ['

    Hello

    ', '
    Hello
    '], - ['

    Some bold text

    ', - 'Some bold text'], - ['

    Some italic text

    ', - 'Some italic text'], - ['

    Some underlined text

    ', - 'Some underlined text'], - ['

    Some bold and italic text

    ', - 'Some bold and italic text'], - ['

    Some italic and bold text

    ', - 'Some italic and bold text'], - ['

    Some bold, underlined, and italic text

    ', - 'Some bold, underlined, and italic text'], - ['

    Hello
    \n

    ', '
    Hello
    '], - ['

    Hello

    ', '
    Hello
    '], + ['

    ', ''], - ['

    ', ''], - - ['
    1. one
    2. two
    \n\n

    not

    ', '
    1. one
    2. two
    not
    '], - ['
    • one
    • two
    \n\n

    not

    ', '
    • one
    • two
    not
    '] - ].each(function(assertion) { - runner.assertEqual(assertion[0], assertion[1].formatHTMLOutput()); - }); - } else if (Prototype.Browser.Gecko) { - [ - ['

    Here is some basic text
    \nwith a line break.

    \n\n

    And maybe another paragraph

    ', - 'Here is some basic text
    with a line break.

    And maybe another paragraph
    '], - ['

    Here is some basic text
    \nwith a line break.

    \n\n

    And maybe another paragraph

    ', - 'Here is some basic text
    with a line break.


    And maybe another paragraph
    '], - ['

    Hello

    \n\n

    World!

    ', 'Hello

    World!'], - ['

    Hello
    \nWorld!
    \nGoodbye!

    ', 'Hello
    World!
    Goodbye!
    '], - ['

    Hello
    \nWorld!

    \n\n

    Goodbye!

    ', 'Hello
    World!

    Goodbye!
    '], - - ['

    Some bold text

    ', - 'Some bold text'], - ['

    Some italic text

    ', - 'Some italic text'], - ['

    Some underlined text

    ', - 'Some underlined text'], - ['

    Some bold and italic text

    ', - 'Some bold and italic text'], - ['

    Some italic and bold text

    ', - 'Some italic and bold text'], - ['

    Some bold, underline, and italic text

    ', - 'Some bold, underline, and italic text'], + ['
    1. one
    2. two
    \n\n

    not

    ', '
    1. one
    2. two
    not
    '], + ['
    • one
    • two
    \n\n

    not

    ', '
    • one
    • two
    not
    '] + ].each(function(assertion) { + this.assertEqual(assertion[0], assertion[1].formatHTMLOutput()); + }.bind(this)); + } else if (Prototype.Browser.Gecko) { + [ + ['

    Here is some basic text
    \nwith a line break.

    \n\n

    And maybe another paragraph

    ', + 'Here is some basic text
    with a line break.

    And maybe another paragraph
    '], + ['

    Here is some basic text
    \nwith a line break.

    \n\n

    And maybe another paragraph

    ', + 'Here is some basic text
    with a line break.


    And maybe another paragraph
    '], + ['

    Hello

    \n\n

    World!

    ', 'Hello

    World!'], + ['

    Hello
    \nWorld!
    \nGoodbye!

    ', 'Hello
    World!
    Goodbye!
    '], + ['

    Hello
    \nWorld!

    \n\n

    Goodbye!

    ', 'Hello
    World!

    Goodbye!
    '], - ['

    ', ''], + ['

    Some bold text

    ', + 'Some bold text'], + ['

    Some italic text

    ', + 'Some italic text'], + ['

    Some underlined text

    ', + 'Some underlined text'], + ['

    Some bold and italic text

    ', + 'Some bold and italic text'], + ['

    Some italic and bold text

    ', + 'Some italic and bold text'], + ['

    Some bold, underline, and italic text

    ', + 'Some bold, underline, and italic text'], - ['
    1. one
    2. two
    \n\n

    not

    ', '
    1. one
    2. two
    not
    '], - ['
    • one
    • two
    \n\n

    not

    ', '
    • one
    • two
    not
    '] - ].each(function(assertion) { - runner.assertEqual(assertion[0], assertion[1].formatHTMLOutput()); - }); - } else if (Prototype.Browser.IE) { - [ - ['', '

    '], - ['', '

    \n

    '], - ['', '

    \n

     

    \n

    '], - ['', '

     

    \n

    '], - ['

    One
    \nTwo

    \n\n

    Break

    ', - '

    \n

    One


    \n

    Two

    \n

    \n

    \n

    Break

    \n

    '], - ['

    Here is some basic text
    \nwith a line break.

    \n\n

    And maybe another paragraph

    ', - '

    \n

    Here is some basic text


    \n

    with a line break.


    \n

     


    \n

    And maybe another paragraph

    \n

    '], - ['

    Hello

    \n\n

    World!

    ', - '

    \n

    Hello


    \n

     


    \n

    World!

    \n

    '], - ['

    Hello
    \nWorld!
    \nGoodbye!

    ', - '

    \n

    Hello


    \n

    World!


    \n

    Goodbye!

    \n

    '], - ['

    Hello
    \nWorld!

    \n\n

    Goodbye!

    ', - '

    \n

    Hello


    \n

    World!


    \n

     


    \n

    Goodbye!

    \n

    '], + ['

    ', ''], - ['

    Some bold text

    ', - 'Some bold text'], - ['

    Some italic text

    ', - 'Some italic text'], - ['

    Some underlined text

    ', - 'Some underlined text'], + ['
    1. one
    2. two
    \n\n

    not

    ', '
    1. one
    2. two
    not
    '], + ['
    • one
    • two
    \n\n

    not

    ', '
    • one
    • two
    not
    '] + ].each(function(assertion) { + this.assertEqual(assertion[0], assertion[1].formatHTMLOutput()); + }.bind(this)); + } else if (Prototype.Browser.IE) { + [ + ['', '

    '], + ['', '

    \n

    '], + ['', '

    \n

     

    \n

    '], + ['', '

     

    \n

    '], + ['

    One
    \nTwo

    \n\n

    Break

    ', + '

    \n

    One


    \n

    Two

    \n

    \n

    \n

    Break

    \n

    '], + ['

    Here is some basic text
    \nwith a line break.

    \n\n

    And maybe another paragraph

    ', + '

    \n

    Here is some basic text


    \n

    with a line break.


    \n

     


    \n

    And maybe another paragraph

    \n

    '], + ['

    Hello

    \n\n

    World!

    ', + '

    \n

    Hello


    \n

     


    \n

    World!

    \n

    '], + ['

    Hello
    \nWorld!
    \nGoodbye!

    ', + '

    \n

    Hello


    \n

    World!


    \n

    Goodbye!

    \n

    '], + ['

    Hello
    \nWorld!

    \n\n

    Goodbye!

    ', + '

    \n

    Hello


    \n

    World!


    \n

     


    \n

    Goodbye!

    \n

    '], - ['
      \n
    1. one
    2. \n
    3. two
    \n

    not

    ', '
      \n
    1. one
    2. \n
    3. two
    \n

    not

    '], - ['
      \n
    • one
    • \n
    • two
    \n

    not

    ', '
      \n
    • one
    • \n
    • two
    \n

    not

    '] - ].each(function(assertion) { - runner.assertEqual(assertion[0], assertion[1].formatHTMLOutput()); - }); - } else if (Prototype.Browser.Opera) { - [ - ['

    One
    \nTwo

    \n\n

    Break

    ', - '

    One

    Two

    Break

    '], - ['

    Here is some basic text...
    \n...with a line break.

    \n\n

    We want to put a paragraph...

    \n\n

    ...right there.

    ', - '

    Here is some basic text...

    \n

    ...with a line break.

    \n

     

    \n

    We want to put a paragraph...

    \n

     

    \n

    ...right there.

    '], - ['

    Here is some basic text...
    \n...with a line break.

    \n\n

    We want to put a paragraph...

    \n\n

    ...right there.

    ', - '

    Here is some basic text...

    \n

    ...with a line break.

    \n

    \n

    We want to put a paragraph...

    \n

    \n

    ...right there.

    '] - ].each(function(assertion) { - runner.assertEqual(assertion[0], assertion[1].formatHTMLOutput()); - }); - } - }, + ['

    Some bold text

    ', + 'Some bold text'], + ['

    Some italic text

    ', + 'Some italic text'], + ['

    Some underlined text

    ', + 'Some underlined text'], - testFormatForEditor: function() { - var runner = this; + ['
      \n
    1. one
    2. \n
    3. two
    \n

    not

    ', '
      \n
    1. one
    2. \n
    3. two
    \n

    not

    '], + ['
      \n
    • one
    • \n
    • two
    \n

    not

    ', '
      \n
    • one
    • \n
    • two
    \n

    not

    '] + ].each(function(assertion) { + this.assertEqual(assertion[0], assertion[1].formatHTMLOutput()); + }.bind(this)); + } else if (Prototype.Browser.Opera) { + [ + ['

    One
    \nTwo

    \n\n

    Break

    ', + '

    One

    Two

    Break

    '], + ['

    Here is some basic text...
    \n...with a line break.

    \n\n

    We want to put a paragraph...

    \n\n

    ...right there.

    ', + '

    Here is some basic text...

    \n

    ...with a line break.

    \n

     

    \n

    We want to put a paragraph...

    \n

     

    \n

    ...right there.

    '], + ['

    Here is some basic text...
    \n...with a line break.

    \n\n

    We want to put a paragraph...

    \n\n

    ...right there.

    ', + '

    Here is some basic text...

    \n

    ...with a line break.

    \n

    \n

    We want to put a paragraph...

    \n

    \n

    ...right there.

    '] + ].each(function(assertion) { + this.assertEqual(assertion[0], assertion[1].formatHTMLOutput()); + }.bind(this)); + } +}); - if (Prototype.Browser.WebKit) { - [ - ['
    Here is some basic text...
    ...with a line break.

    We want to put a paragraph...

    ...right there.
    ', - '

    Here is some basic text...
    \n...with a line break.

    \n\n

    We want to put a paragraph...

    \n\n

    ...right there.

    '], +test("format for editor", function() { + if (Prototype.Browser.WebKit) { + [ + ['
    Here is some basic text...
    ...with a line break.

    We want to put a paragraph...

    ...right there.
    ', + '

    Here is some basic text...
    \n...with a line break.

    \n\n

    We want to put a paragraph...

    \n\n

    ...right there.

    '], - ['
    Some bold text
    ', - '

    Some bold text

    '], - ['
    Some italic text
    ', - '

    Some italic text

    '], - ['
    Some underlined text
    ', - '

    Some underlined text

    '], - ['
    Some bold and italic text
    ', - '

    Some bold and italic text

    '], - ['
    Some italic and bold text
    ', - '

    Some italic and bold text

    '] - ].each(function(assertion) { - runner.assertEqual(assertion[0], assertion[1].formatHTMLInput()); - }); - } else if (Prototype.Browser.Gecko) { - [ - ['Here is some basic text...
    ...with a line break.

    We want to put a paragraph...

    ...right there.
    ', - '

    Here is some basic text...
    \n...with a line break.

    \n\n

    We want to put a paragraph...

    \n\n

    ...right there.

    '], + ['
    Some bold text
    ', + '

    Some bold text

    '], + ['
    Some italic text
    ', + '

    Some italic text

    '], + ['
    Some underlined text
    ', + '

    Some underlined text

    '], + ['
    Some bold and italic text
    ', + '

    Some bold and italic text

    '], + ['
    Some italic and bold text
    ', + '

    Some italic and bold text

    '] + ].each(function(assertion) { + this.assertEqual(assertion[0], assertion[1].formatHTMLInput()); + }.bind(this)); + } else if (Prototype.Browser.Gecko) { + [ + ['Here is some basic text...
    ...with a line break.

    We want to put a paragraph...

    ...right there.
    ', + '

    Here is some basic text...
    \n...with a line break.

    \n\n

    We want to put a paragraph...

    \n\n

    ...right there.

    '], - ['Some bold text
    ', - '

    Some bold text

    '], - ['Some italic text
    ', - '

    Some italic text

    '], - ['Some underlined text
    ', - '

    Some underlined text

    '], - ['Some bold and italic text
    ', - '

    Some bold and italic text

    '], - ['Some italic and bold text
    ', - '

    Some italic and bold text

    '] - ].each(function(assertion) { - runner.assertEqual(assertion[0], assertion[1].formatHTMLInput()); - }); - } else if (Prototype.Browser.IE) { - [ - [ '

    Here is some basic text...

    \n

    ...with a line break.

    \n

     

    \n

    We want to put a paragraph...

    \n

     

    \n

    ...right there.

    ', - '

    Here is some basic text...
    \n...with a line break.

    \n\n

    We want to put a paragraph...

    \n\n

    ...right there.

    '] - ].each(function(assertion) { - runner.assertEqual(assertion[0], assertion[1].formatHTMLInput()); - }); - } else if (Prototype.Browser.Opera) { - [ - [ '

    Here is some basic text...

    \n

    ...with a line break.

    \n

     

    \n

    We want to put a paragraph...

    \n

     

    \n

    ...right there.

    ', - '

    Here is some basic text...
    \n...with a line break.

    \n\n

    We want to put a paragraph...

    \n\n

    ...right there.

    '] - ].each(function(assertion) { - runner.assertEqual(assertion[0], assertion[1].formatHTMLInput()); - }); - } + ['Some bold text
    ', + '

    Some bold text

    '], + ['Some italic text
    ', + '

    Some italic text

    '], + ['Some underlined text
    ', + '

    Some underlined text

    '], + ['Some bold and italic text
    ', + '

    Some bold and italic text

    '], + ['Some italic and bold text
    ', + '

    Some italic and bold text

    '] + ].each(function(assertion) { + this.assertEqual(assertion[0], assertion[1].formatHTMLInput()); + }.bind(this)); + } else if (Prototype.Browser.IE) { + [ + [ '

    Here is some basic text...

    \n

    ...with a line break.

    \n

     

    \n

    We want to put a paragraph...

    \n

     

    \n

    ...right there.

    ', + '

    Here is some basic text...
    \n...with a line break.

    \n\n

    We want to put a paragraph...

    \n\n

    ...right there.

    '] + ].each(function(assertion) { + this.assertEqual(assertion[0], assertion[1].formatHTMLInput()); + }.bind(this)); + } else if (Prototype.Browser.Opera) { + [ + [ '

    Here is some basic text...

    \n

    ...with a line break.

    \n

     

    \n

    We want to put a paragraph...

    \n

     

    \n

    ...right there.

    ', + '

    Here is some basic text...
    \n...with a line break.

    \n\n

    We want to put a paragraph...

    \n\n

    ...right there.

    '] + ].each(function(assertion) { + this.assertEqual(assertion[0], assertion[1].formatHTMLInput()); + }.bind(this)); } }); diff --git a/test/unit/range_test.js b/test/unit/range_test.js index 2e6a5e2..2eeb10c 100644 --- a/test/unit/range_test.js +++ b/test/unit/range_test.js @@ -1,378 +1,376 @@ -new Test.Unit.Runner({ - setup: function() { - $('content').update( - "Lorem ipsum dolor sit amet, " + - "consectetuer adipiscing elit." - ); - - $('wrapper').cleanWhitespace(); - - this.range = document.createRange(); - this.range.selectNode($('content')); - }, - - testSetStart: function() { - var runner = this; - - this.range.setStart($('content'), 2); - this.range.setEnd($('content'), 2); - - runner.assertEqual($('content'), this.range.startContainer, "startContainer"); - runner.assertEqual(2, this.range.startOffset, "startOffset"); - runner.assertEqual($('content'), this.range.endContainer, "endContainer"); - runner.assertEqual(2, this.range.endOffset, "endOffset"); - runner.assertEqual(true, this.range.collapsed, "collapsed"); - runner.assertEqual($('content'), this.range.commonAncestorContainer, "commonAncestorContainer"); - runner.assertEqual("", this.range.toString(), "toString"); - - this.range.setStart($('lorem'), 0); - this.range.setEnd($('lorem'), 1); - - runner.assertEqual($('lorem'), this.range.startContainer, "startContainer"); - runner.assertEqual(0, this.range.startOffset, "startOffset"); - runner.assertEqual($('lorem'), this.range.endContainer, "endContainer"); - runner.assertEqual(1, this.range.endOffset, "endOffset"); - runner.assertEqual(false, this.range.collapsed, "collapsed"); - runner.assertEqual($('lorem'), this.range.commonAncestorContainer, "commonAncestorContainer"); - runner.assertEqual("Lorem ipsum", this.range.toString(), "toString"); - }, - - testSetEnd: function() { - var runner = this; - - this.range.setStart($('content'), 1); - this.range.setEnd($('content'), 2); - - runner.assertEqual($('content'), this.range.startContainer, "startContainer"); - runner.assertEqual(1, this.range.startOffset, "startOffset"); - runner.assertEqual($('content'), this.range.endContainer, "startContainer"); - runner.assertEqual(2, this.range.endOffset, "endOffset"); - runner.assertEqual(false, this.range.collapsed, "collapsed"); - runner.assertEqual($('content'), this.range.commonAncestorContainer, "commonAncestorContainer"); - runner.assertEqual(" dolor sit amet, ", this.range.toString(), "toString"); - - this.range.setStart($('consectetuer'), 0); - this.range.setEnd($('consectetuer'), 1); - - runner.assertEqual($('consectetuer'), this.range.startContainer, "startContainer"); - runner.assertEqual(0, this.range.startOffset, "startOffset"); - runner.assertEqual($('consectetuer'), this.range.endContainer, "startContainer"); - runner.assertEqual(1, this.range.endOffset, "endOffset"); - runner.assertEqual(false, this.range.collapsed, "collapsed"); - runner.assertEqual($('consectetuer'), this.range.commonAncestorContainer, "commonAncestorContainer"); - runner.assertEqual("consectetuer", this.range.toString(), "toString"); - }, - - testSetStartBefore: function() { - var runner = this; - - this.range.setStartBefore($('lorem')); - this.range.setEnd($('content'), 2); - - runner.assertEqual($('content'), this.range.startContainer, "startContainer"); - runner.assertEqual(0, this.range.startOffset, "startOffset"); - runner.assertEqual($('content'), this.range.endContainer, "endContainer"); - runner.assertEqual(2, this.range.endOffset, "endOffset"); - runner.assertEqual(false, this.range.collapsed, "collapsed"); - runner.assertEqual($('content'), this.range.commonAncestorContainer, "commonAncestorContainer"); - runner.assertEqual("Lorem ipsum dolor sit amet, ", this.range.toString(), "toString"); - - this.range.setStartBefore($('content')); - this.range.setEnd($('content'), 2); - - runner.assertEqual($('wrapper'), this.range.startContainer, "startContainer"); - runner.assertEqual(0, this.range.startOffset, "startOffset"); - runner.assertEqual($('content'), this.range.endContainer, "endContainer"); - runner.assertEqual(2, this.range.endOffset, "endOffset"); - runner.assertEqual(false, this.range.collapsed, "collapsed"); - runner.assertEqual($('wrapper'), this.range.commonAncestorContainer, "commonAncestorContainer"); - runner.assertEqual("Lorem ipsum dolor sit amet, ", this.range.toString(), "toString"); - }, - - testSetStartAfter: function() { - var runner = this; - - this.range.setStartAfter($('lorem')); - this.range.setEnd($('content'), 2); - - runner.assertEqual($('content'), this.range.startContainer, "startContainer"); - runner.assertEqual(1, this.range.startOffset, "startOffset"); - runner.assertEqual($('content'), this.range.endContainer, "startContainer"); - runner.assertEqual(2, this.range.endOffset, "endOffset"); - runner.assertEqual(false, this.range.collapsed, "collapsed"); - runner.assertEqual($('content'), this.range.commonAncestorContainer, "commonAncestorContainer"); - runner.assertEqual(" dolor sit amet, ", this.range.toString(), "toString"); - - this.range.setStartAfter($('content')); - this.range.setEnd($('wrapper'), 1); - - runner.assertEqual($('wrapper'), this.range.startContainer, "startContainer"); - runner.assertEqual(1, this.range.startOffset, "startOffset"); - runner.assertEqual($('wrapper'), this.range.endContainer, "startContainer"); - runner.assertEqual(1, this.range.endOffset, "endOffset"); - runner.assertEqual(true, this.range.collapsed, "collapsed"); - runner.assertEqual($('wrapper'), this.range.commonAncestorContainer, "commonAncestorContainer"); - runner.assertEqual("", this.range.toString(), "toString"); - }, - - testSetEndBefore: function() { - var runner = this; - - this.range.setStart($('content'), 0); - this.range.setEndBefore($('lorem')); - - runner.assertEqual($('content'), this.range.startContainer, "startContainer"); - runner.assertEqual(0, this.range.startOffset, "startOffset"); - runner.assertEqual($('content'), this.range.endContainer, "startContainer"); - runner.assertEqual(0, this.range.endOffset, "endOffset"); - runner.assertEqual(true, this.range.collapsed, "collapsed"); - runner.assertEqual($('content'), this.range.commonAncestorContainer, "commonAncestorContainer"); - runner.assertEqual("", this.range.toString(), "toString"); - - this.range.setStart($('wrapper'), 0); - this.range.setEndBefore($('content')); - - runner.assertEqual($('wrapper'), this.range.startContainer, "startContainer"); - runner.assertEqual(0, this.range.startOffset, "startOffset"); - runner.assertEqual($('wrapper'), this.range.endContainer, "startContainer"); - runner.assertEqual(0, this.range.endOffset, "endOffset"); - runner.assertEqual(true, this.range.collapsed, "collapsed"); - runner.assertEqual($('wrapper'), this.range.commonAncestorContainer, "commonAncestorContainer"); - runner.assertEqual("", this.range.toString(), "toString"); - }, - - testSetEndAfter: function() { - var runner = this; - - this.range.setStart($('content'), 0); - this.range.setEndAfter($('lorem')); - - runner.assertEqual($('content'), this.range.startContainer, "startContainer"); - runner.assertEqual(0, this.range.startOffset, "startOffset"); - runner.assertEqual($('content'), this.range.endContainer, "startContainer"); - runner.assertEqual(1, this.range.endOffset, "endOffset"); - runner.assertEqual(false, this.range.collapsed, "collapsed"); - runner.assertEqual($('content'), this.range.commonAncestorContainer, "commonAncestorContainer"); - runner.assertEqual("Lorem ipsum", this.range.toString(), "toString"); - - this.range.setStart($('content'), 0); - this.range.setEndAfter($('content')); - - runner.assertEqual($('content'), this.range.startContainer, "startContainer"); - runner.assertEqual(0, this.range.startOffset, "startOffset"); - runner.assertEqual($('wrapper'), this.range.endContainer, "startContainer"); - runner.assertEqual(1, this.range.endOffset, "endOffset"); - runner.assertEqual(false, this.range.collapsed, "collapsed"); - runner.assertEqual($('wrapper'), this.range.commonAncestorContainer, "commonAncestorContainer"); - runner.assertEqual("Lorem ipsum dolor sit amet, consectetuer adipiscing elit.", this.range.toString(), "toString"); - }, - - testCollapse: function() { - var runner = this; - - this.range.setStart($('content'), 1); - this.range.setEnd($('content'), 2); - this.range.collapse(true); - - runner.assertEqual($('content'), this.range.startContainer, "startContainer"); - runner.assertEqual(1, this.range.startOffset, "startOffset"); - runner.assertEqual($('content'), this.range.endContainer, "startContainer"); - runner.assertEqual(1, this.range.endOffset, "endOffset"); - runner.assertEqual(true, this.range.collapsed, "collapsed"); - runner.assertEqual($('content'), this.range.commonAncestorContainer, "commonAncestorContainer"); - runner.assertEqual("", this.range.toString(), "toString"); - - this.range.setStart($('content'), 1); - this.range.setEnd($('content'), 2); - this.range.collapse(false); - - runner.assertEqual($('content'), this.range.startContainer, "startContainer"); - runner.assertEqual(2, this.range.startOffset, "startOffset"); - runner.assertEqual($('content'), this.range.endContainer, "startContainer"); - runner.assertEqual(2, this.range.endOffset, "endOffset"); - runner.assertEqual(true, this.range.collapsed, "collapsed"); - runner.assertEqual($('content'), this.range.commonAncestorContainer, "commonAncestorContainer"); - runner.assertEqual("", this.range.toString(), "toString"); - }, - - testSelectNode: function() { - var runner = this; - - this.range.selectNode($('content')); - - runner.assertEqual($('wrapper'), this.range.startContainer, "startContainer"); - runner.assertEqual(0, this.range.startOffset, "startOffset"); - runner.assertEqual($('wrapper'), this.range.endContainer, "startContainer"); - runner.assertEqual(1, this.range.endOffset, "endOffset"); - runner.assertEqual(false, this.range.collapsed, "collapsed"); - runner.assertEqual($('wrapper'), this.range.commonAncestorContainer, "commonAncestorContainer"); - runner.assertEqual("Lorem ipsum dolor sit amet, consectetuer adipiscing elit.", this.range.toString(), "toString"); - - this.range.selectNode($('lorem')); - - runner.assertEqual($('content'), this.range.startContainer, "startContainer"); - runner.assertEqual(0, this.range.startOffset, "startOffset"); - runner.assertEqual($('content'), this.range.endContainer, "startContainer"); - runner.assertEqual(1, this.range.endOffset, "endOffset"); - runner.assertEqual(false, this.range.collapsed, "collapsed"); - runner.assertEqual($('content'), this.range.commonAncestorContainer, "commonAncestorContainer"); - runner.assertEqual("Lorem ipsum", this.range.toString(), "toString"); - }, - - testSelectNodeContents: function() { - var runner = this; - - this.range.selectNodeContents($('content')); - - runner.assertEqual($('content'), this.range.startContainer, "startContainer"); - runner.assertEqual(0, this.range.startOffset, "startOffset"); - runner.assertEqual($('content'), this.range.endContainer, "startContainer"); - runner.assertEqual(4, this.range.endOffset, "endOffset"); - runner.assertEqual(false, this.range.collapsed, "collapsed"); - runner.assertEqual($('content'), this.range.commonAncestorContainer, "commonAncestorContainer"); - runner.assertEqual("Lorem ipsum dolor sit amet, consectetuer adipiscing elit.", this.range.toString(), "toString"); - - this.range.selectNodeContents($('lorem')); - - runner.assertEqual($('lorem'), this.range.startContainer, "startContainer"); - runner.assertEqual(0, this.range.startOffset, "startOffset"); - runner.assertEqual($('lorem'), this.range.endContainer, "startContainer"); - runner.assertEqual(1, this.range.endOffset, "endOffset"); - runner.assertEqual(false, this.range.collapsed, "collapsed"); - runner.assertEqual($('lorem'), this.range.commonAncestorContainer, "commonAncestorContainer"); - runner.assertEqual("Lorem ipsum", this.range.toString(), "toString"); - }, - - testDeleteContents: function() { - var runner = this; - - this.range.selectNodeContents($('lorem')); - this.range.deleteContents(); - - runner.assertEqual("", $('lorem').innerHTML, "innerHTML"); - }, - - testExtractContents: function() { - var runner = this; - - this.range.selectNodeContents($('lorem')); - var contents = this.range.extractContents(); - - runner.assertEqual("", $('lorem').innerHTML, "innerHTML"); - - //IE document does not have any useful methods. Everyone else can just - // read textContent, IE needs to append the fragment to another element - // and read its innerHTML - if (contents.textContent) { - runner.assertEqual("Lorem ipsum", contents.textContent, "textContent"); - } else { - var e = new Element('div'); - e.appendChild(contents); - runner.assertEqual("Lorem ipsum", e.innerHTML, "textContent"); - } - }, - - testCloneContents: function() { - var runner = this; - - this.range.selectNodeContents($('lorem')); - var contents = this.range.cloneContents(); - - runner.assertEqual("Lorem ipsum", $('lorem').innerHTML, "innerHTML"); - - // IE document does not have any useful methods. Everyone else can just - // read textContent, IE needs to append the fragment to another element - // and read its innerHTML - if (contents.textContent) { - runner.assertEqual("Lorem ipsum", contents.textContent, "textContent"); - } else { - var e = new Element('div'); - e.appendChild(contents); - runner.assertEqual("Lorem ipsum", e.innerHTML, "textContent"); - } - }, +function setup() { + $('content').update( + "Lorem ipsum dolor sit amet, " + + "consectetuer adipiscing elit." + ); + + $('wrapper').cleanWhitespace(); + + this.range = document.createRange(); + this.range.selectNode($('content')); +} + +test("SetStart", function() { + setup.bind(this)(); + + this.range.setStart($('content'), 2); + this.range.setEnd($('content'), 2); + + this.assertEqual($('content'), this.range.startContainer, "startContainer"); + this.assertEqual(2, this.range.startOffset, "startOffset"); + this.assertEqual($('content'), this.range.endContainer, "endContainer"); + this.assertEqual(2, this.range.endOffset, "endOffset"); + this.assertEqual(true, this.range.collapsed, "collapsed"); + this.assertEqual($('content'), this.range.commonAncestorContainer, "commonAncestorContainer"); + this.assertEqual("", this.range.toString(), "toString"); + + this.range.setStart($('lorem'), 0); + this.range.setEnd($('lorem'), 1); + + this.assertEqual($('lorem'), this.range.startContainer, "startContainer"); + this.assertEqual(0, this.range.startOffset, "startOffset"); + this.assertEqual($('lorem'), this.range.endContainer, "endContainer"); + this.assertEqual(1, this.range.endOffset, "endOffset"); + this.assertEqual(false, this.range.collapsed, "collapsed"); + this.assertEqual($('lorem'), this.range.commonAncestorContainer, "commonAncestorContainer"); + this.assertEqual("Lorem ipsum", this.range.toString(), "toString"); +}); + +test("SetEnd", function() { + setup.bind(this)(); + + this.range.setStart($('content'), 1); + this.range.setEnd($('content'), 2); + + this.assertEqual($('content'), this.range.startContainer, "startContainer"); + this.assertEqual(1, this.range.startOffset, "startOffset"); + this.assertEqual($('content'), this.range.endContainer, "startContainer"); + this.assertEqual(2, this.range.endOffset, "endOffset"); + this.assertEqual(false, this.range.collapsed, "collapsed"); + this.assertEqual($('content'), this.range.commonAncestorContainer, "commonAncestorContainer"); + this.assertEqual(" dolor sit amet, ", this.range.toString(), "toString"); + + this.range.setStart($('consectetuer'), 0); + this.range.setEnd($('consectetuer'), 1); + + this.assertEqual($('consectetuer'), this.range.startContainer, "startContainer"); + this.assertEqual(0, this.range.startOffset, "startOffset"); + this.assertEqual($('consectetuer'), this.range.endContainer, "startContainer"); + this.assertEqual(1, this.range.endOffset, "endOffset"); + this.assertEqual(false, this.range.collapsed, "collapsed"); + this.assertEqual($('consectetuer'), this.range.commonAncestorContainer, "commonAncestorContainer"); + this.assertEqual("consectetuer", this.range.toString(), "toString"); +}); + +test("SetStartBefore", function() { + setup.bind(this)(); + + this.range.setStartBefore($('lorem')); + this.range.setEnd($('content'), 2); + + this.assertEqual($('content'), this.range.startContainer, "startContainer"); + this.assertEqual(0, this.range.startOffset, "startOffset"); + this.assertEqual($('content'), this.range.endContainer, "endContainer"); + this.assertEqual(2, this.range.endOffset, "endOffset"); + this.assertEqual(false, this.range.collapsed, "collapsed"); + this.assertEqual($('content'), this.range.commonAncestorContainer, "commonAncestorContainer"); + this.assertEqual("Lorem ipsum dolor sit amet, ", this.range.toString(), "toString"); + + this.range.setStartBefore($('content')); + this.range.setEnd($('content'), 2); + + this.assertEqual($('wrapper'), this.range.startContainer, "startContainer"); + this.assertEqual(0, this.range.startOffset, "startOffset"); + this.assertEqual($('content'), this.range.endContainer, "endContainer"); + this.assertEqual(2, this.range.endOffset, "endOffset"); + this.assertEqual(false, this.range.collapsed, "collapsed"); + this.assertEqual($('wrapper'), this.range.commonAncestorContainer, "commonAncestorContainer"); + this.assertEqual("Lorem ipsum dolor sit amet, ", this.range.toString(), "toString"); +}); + +test("SetStartAfter", function() { + setup.bind(this)(); + + this.range.setStartAfter($('lorem')); + this.range.setEnd($('content'), 2); + + this.assertEqual($('content'), this.range.startContainer, "startContainer"); + this.assertEqual(1, this.range.startOffset, "startOffset"); + this.assertEqual($('content'), this.range.endContainer, "startContainer"); + this.assertEqual(2, this.range.endOffset, "endOffset"); + this.assertEqual(false, this.range.collapsed, "collapsed"); + this.assertEqual($('content'), this.range.commonAncestorContainer, "commonAncestorContainer"); + this.assertEqual(" dolor sit amet, ", this.range.toString(), "toString"); + + this.range.setStartAfter($('content')); + this.range.setEnd($('wrapper'), 1); + + this.assertEqual($('wrapper'), this.range.startContainer, "startContainer"); + this.assertEqual(1, this.range.startOffset, "startOffset"); + this.assertEqual($('wrapper'), this.range.endContainer, "startContainer"); + this.assertEqual(1, this.range.endOffset, "endOffset"); + this.assertEqual(true, this.range.collapsed, "collapsed"); + this.assertEqual($('wrapper'), this.range.commonAncestorContainer, "commonAncestorContainer"); + this.assertEqual("", this.range.toString(), "toString"); +}); + +test("SetEndBefore", function() { + setup.bind(this)(); + + this.range.setStart($('content'), 0); + this.range.setEndBefore($('lorem')); + + this.assertEqual($('content'), this.range.startContainer, "startContainer"); + this.assertEqual(0, this.range.startOffset, "startOffset"); + this.assertEqual($('content'), this.range.endContainer, "startContainer"); + this.assertEqual(0, this.range.endOffset, "endOffset"); + this.assertEqual(true, this.range.collapsed, "collapsed"); + this.assertEqual($('content'), this.range.commonAncestorContainer, "commonAncestorContainer"); + this.assertEqual("", this.range.toString(), "toString"); + + this.range.setStart($('wrapper'), 0); + this.range.setEndBefore($('content')); + + this.assertEqual($('wrapper'), this.range.startContainer, "startContainer"); + this.assertEqual(0, this.range.startOffset, "startOffset"); + this.assertEqual($('wrapper'), this.range.endContainer, "startContainer"); + this.assertEqual(0, this.range.endOffset, "endOffset"); + this.assertEqual(true, this.range.collapsed, "collapsed"); + this.assertEqual($('wrapper'), this.range.commonAncestorContainer, "commonAncestorContainer"); + this.assertEqual("", this.range.toString(), "toString"); +}); + +test("SetEndAfter", function() { + setup.bind(this)(); + + this.range.setStart($('content'), 0); + this.range.setEndAfter($('lorem')); + + this.assertEqual($('content'), this.range.startContainer, "startContainer"); + this.assertEqual(0, this.range.startOffset, "startOffset"); + this.assertEqual($('content'), this.range.endContainer, "startContainer"); + this.assertEqual(1, this.range.endOffset, "endOffset"); + this.assertEqual(false, this.range.collapsed, "collapsed"); + this.assertEqual($('content'), this.range.commonAncestorContainer, "commonAncestorContainer"); + this.assertEqual("Lorem ipsum", this.range.toString(), "toString"); + + this.range.setStart($('content'), 0); + this.range.setEndAfter($('content')); + + this.assertEqual($('content'), this.range.startContainer, "startContainer"); + this.assertEqual(0, this.range.startOffset, "startOffset"); + this.assertEqual($('wrapper'), this.range.endContainer, "startContainer"); + this.assertEqual(1, this.range.endOffset, "endOffset"); + this.assertEqual(false, this.range.collapsed, "collapsed"); + this.assertEqual($('wrapper'), this.range.commonAncestorContainer, "commonAncestorContainer"); + this.assertEqual("Lorem ipsum dolor sit amet, consectetuer adipiscing elit.", this.range.toString(), "toString"); +}); + +test("Collapse", function() { + setup.bind(this)(); + + this.range.setStart($('content'), 1); + this.range.setEnd($('content'), 2); + this.range.collapse(true); + + this.assertEqual($('content'), this.range.startContainer, "startContainer"); + this.assertEqual(1, this.range.startOffset, "startOffset"); + this.assertEqual($('content'), this.range.endContainer, "startContainer"); + this.assertEqual(1, this.range.endOffset, "endOffset"); + this.assertEqual(true, this.range.collapsed, "collapsed"); + this.assertEqual($('content'), this.range.commonAncestorContainer, "commonAncestorContainer"); + this.assertEqual("", this.range.toString(), "toString"); + + this.range.setStart($('content'), 1); + this.range.setEnd($('content'), 2); + this.range.collapse(false); + + this.assertEqual($('content'), this.range.startContainer, "startContainer"); + this.assertEqual(2, this.range.startOffset, "startOffset"); + this.assertEqual($('content'), this.range.endContainer, "startContainer"); + this.assertEqual(2, this.range.endOffset, "endOffset"); + this.assertEqual(true, this.range.collapsed, "collapsed"); + this.assertEqual($('content'), this.range.commonAncestorContainer, "commonAncestorContainer"); + this.assertEqual("", this.range.toString(), "toString"); +}); + +test("SelectNode", function() { + setup.bind(this)(); + + this.range.selectNode($('content')); + + this.assertEqual($('wrapper'), this.range.startContainer, "startContainer"); + this.assertEqual(0, this.range.startOffset, "startOffset"); + this.assertEqual($('wrapper'), this.range.endContainer, "startContainer"); + this.assertEqual(1, this.range.endOffset, "endOffset"); + this.assertEqual(false, this.range.collapsed, "collapsed"); + this.assertEqual($('wrapper'), this.range.commonAncestorContainer, "commonAncestorContainer"); + this.assertEqual("Lorem ipsum dolor sit amet, consectetuer adipiscing elit.", this.range.toString(), "toString"); + + this.range.selectNode($('lorem')); + + this.assertEqual($('content'), this.range.startContainer, "startContainer"); + this.assertEqual(0, this.range.startOffset, "startOffset"); + this.assertEqual($('content'), this.range.endContainer, "startContainer"); + this.assertEqual(1, this.range.endOffset, "endOffset"); + this.assertEqual(false, this.range.collapsed, "collapsed"); + this.assertEqual($('content'), this.range.commonAncestorContainer, "commonAncestorContainer"); + this.assertEqual("Lorem ipsum", this.range.toString(), "toString"); +}); + +test("SelectNodeContents", function() { + setup.bind(this)(); + + this.range.selectNodeContents($('content')); + + this.assertEqual($('content'), this.range.startContainer, "startContainer"); + this.assertEqual(0, this.range.startOffset, "startOffset"); + this.assertEqual($('content'), this.range.endContainer, "startContainer"); + this.assertEqual(4, this.range.endOffset, "endOffset"); + this.assertEqual(false, this.range.collapsed, "collapsed"); + this.assertEqual($('content'), this.range.commonAncestorContainer, "commonAncestorContainer"); + this.assertEqual("Lorem ipsum dolor sit amet, consectetuer adipiscing elit.", this.range.toString(), "toString"); + + this.range.selectNodeContents($('lorem')); - testInsertNode: function() { - var runner = this; + this.assertEqual($('lorem'), this.range.startContainer, "startContainer"); + this.assertEqual(0, this.range.startOffset, "startOffset"); + this.assertEqual($('lorem'), this.range.endContainer, "startContainer"); + this.assertEqual(1, this.range.endOffset, "endOffset"); + this.assertEqual(false, this.range.collapsed, "collapsed"); + this.assertEqual($('lorem'), this.range.commonAncestorContainer, "commonAncestorContainer"); + this.assertEqual("Lorem ipsum", this.range.toString(), "toString"); +}); + +test("DeleteContents", function() { + setup.bind(this)(); + + this.range.selectNodeContents($('lorem')); + this.range.deleteContents(); - var node = new Element('span', {id: 'inserted'}).update("inserted!"); + this.assertEqual("", $('lorem').innerHTML, "innerHTML"); +}); - this.range.selectNode($('lorem')); - this.range.insertNode(node); +test("ExtractContents", function() { + setup.bind(this)(); - runner.assertEqual("inserted!", $('inserted').innerHTML, "innerHTML"); - - runner.assertEqual($('content'), this.range.startContainer, "startContainer"); - runner.assertEqual(0, this.range.startOffset, "startOffset"); - runner.assertEqual($('content'), this.range.endContainer, "startContainer"); - runner.assertEqual(2, this.range.endOffset, "endOffset"); - runner.assertEqual(false, this.range.collapsed, "collapsed"); - runner.assertEqual($('content'), this.range.commonAncestorContainer, "commonAncestorContainer"); - }, + this.range.selectNodeContents($('lorem')); + var contents = this.range.extractContents(); - testSurrondContents: function() { - var runner = this; + this.assertEqual("", $('lorem').innerHTML, "innerHTML"); - var node; + //IE document does not have any useful methods. Everyone else can just + // read textContent, IE needs to append the fragment to another element + // and read its innerHTML + if (contents.textContent) { + this.assertEqual("Lorem ipsum", contents.textContent, "textContent"); + } else { + var e = new Element('div'); + e.appendChild(contents); + this.assertEqual("Lorem ipsum", e.innerHTML, "textContent"); + } +}); - node = new Element('span', {id: 'wrapper'}); +test("CloneContents", function() { + setup.bind(this)(); - this.range.selectNodeContents($('lorem')); - this.range.surroundContents(node); + this.range.selectNodeContents($('lorem')); + var contents = this.range.cloneContents(); - expected = new Element('div'); - expected.appendChild(new Element('span', {id: 'wrapper'}).update("Lorem ipsum")); + this.assertEqual("Lorem ipsum", $('lorem').innerHTML, "innerHTML"); - runner.assertEqual(expected.innerHTML, $('lorem').innerHTML, "innerHTML"); - }, + // IE document does not have any useful methods. Everyone else can just + // read textContent, IE needs to append the fragment to another element + // and read its innerHTML + if (contents.textContent) { + this.assertEqual("Lorem ipsum", contents.textContent, "textContent"); + } else { + var e = new Element('div'); + e.appendChild(contents); + this.assertEqual("Lorem ipsum", e.innerHTML, "textContent"); + } +}); - testEqualRange: function() { - var runner = this; +test("InsertNode", function() { + setup.bind(this)(); - if (!this.range.equalRange) { - runner.flunk("equalRange is not implemented"); - return false; - } + var node = new Element('span', {id: 'inserted'}).update("inserted!"); - var r1 = document.createRange(); - r1.selectNodeContents($('lorem')); + this.range.selectNode($('lorem')); + this.range.insertNode(node); - var r2 = document.createRange(); - r2.selectNodeContents($('lorem')); + this.assertEqual("inserted!", $('inserted').innerHTML, "innerHTML"); - var r3 = document.createRange(); - r3.selectNodeContents($('consectetuer')); + this.assertEqual($('content'), this.range.startContainer, "startContainer"); + this.assertEqual(0, this.range.startOffset, "startOffset"); + this.assertEqual($('content'), this.range.endContainer, "startContainer"); + this.assertEqual(2, this.range.endOffset, "endOffset"); + this.assertEqual(false, this.range.collapsed, "collapsed"); + this.assertEqual($('content'), this.range.commonAncestorContainer, "commonAncestorContainer"); +}); - runner.assert(r1.equalRange(r1), "r1.equalRange(r1)"); - runner.assert(r2.equalRange(r2), "r2.equalRange(r2)"); - runner.assert(r3.equalRange(r3), "r3.equalRange(r3)"); +test("SurrondContents", function() { + setup.bind(this)(); - runner.assert(r1.equalRange(r2), "r1.equalRange(r2)"); - runner.assert(r2.equalRange(r1), "r2.equalRange(r1)"); - runner.assert(!r1.equalRange(r3), "r1.equalRange(r3)"); - runner.assert(!r3.equalRange(r1), "r3.equalRange(r1)"); + var node; - runner.assert(!r1.equalRange(null), "r1.equalRange(null)"); - runner.assert(!r2.equalRange(null), "r2.equalRange(null)"); - runner.assert(!r3.equalRange(null), "r3.equalRange(null)"); - }, + node = new Element('span', {id: 'wrapper'}); - testGetNode: function() { - var runner = this; + this.range.selectNodeContents($('lorem')); + this.range.surroundContents(node); - if (!this.range.getNode) { - runner.flunk("getNode is not implemented"); - return false; - } + expected = new Element('div'); + expected.appendChild(new Element('span', {id: 'wrapper'}).update("Lorem ipsum")); - this.range.selectNodeContents($('lorem')); - runner.assertEqual($('lorem'), this.range.getNode(), "getNode"); + this.assertEqual(expected.innerHTML, $('lorem').innerHTML, "innerHTML"); +}); - this.range.selectNode($('lorem')); - runner.assertEqual($('lorem'), this.range.getNode(), "getNode"); +test("EqualRange", function() { + setup.bind(this)(); - this.range.setStart($('lorem'), 0); - this.range.setEnd($('lorem'), 1); - runner.assertEqual($('lorem'), this.range.getNode(), "getNode"); + if (!this.range.equalRange) { + this.flunk("equalRange is not implemented"); + return false; } + + var r1 = document.createRange(); + r1.selectNodeContents($('lorem')); + + var r2 = document.createRange(); + r2.selectNodeContents($('lorem')); + + var r3 = document.createRange(); + r3.selectNodeContents($('consectetuer')); + + this.assert(r1.equalRange(r1), "r1.equalRange(r1)"); + this.assert(r2.equalRange(r2), "r2.equalRange(r2)"); + this.assert(r3.equalRange(r3), "r3.equalRange(r3)"); + + this.assert(r1.equalRange(r2), "r1.equalRange(r2)"); + this.assert(r2.equalRange(r1), "r2.equalRange(r1)"); + this.assert(!r1.equalRange(r3), "r1.equalRange(r3)"); + this.assert(!r3.equalRange(r1), "r3.equalRange(r1)"); + + this.assert(!r1.equalRange(null), "r1.equalRange(null)"); + this.assert(!r2.equalRange(null), "r2.equalRange(null)"); + this.assert(!r3.equalRange(null), "r3.equalRange(null)"); +}); + +test("GetNode", function() { + setup.bind(this)(); + + if (!this.range.getNode) { + this.flunk("getNode is not implemented"); + return false; + } + + this.range.selectNodeContents($('lorem')); + this.assertEqual($('lorem'), this.range.getNode(), "getNode"); + + this.range.selectNode($('lorem')); + this.assertEqual($('lorem'), this.range.getNode(), "getNode"); + + this.range.setStart($('lorem'), 0); + this.range.setEnd($('lorem'), 1); + this.assertEqual($('lorem'), this.range.getNode(), "getNode"); }); diff --git a/test/unit/sanitizer_test.js b/test/unit/sanitizer_test.js index fc511eb..69452a9 100644 --- a/test/unit/sanitizer_test.js +++ b/test/unit/sanitizer_test.js @@ -1,72 +1,68 @@ -new Test.Unit.Runner({ - testSanitize: function() { - var runner = this; +test("sanitize", function() { + this.assertEqual( + "Hello", + "Hello".sanitize() + ); - runner.assertEqual( - "Hello", - "Hello".sanitize() - ); + this.assertEqual( + "Hello", + "Hello".sanitize() + ); - runner.assertEqual( - "Hello", - "Hello".sanitize() - ); + this.assertEqual( + "Hello", + "Hello".sanitize() + ); - runner.assertEqual( - "Hello", - "Hello".sanitize() - ); + this.assertEqual( + "Hello", + "Hello".sanitize({tags: ["strong"]}) + ); - runner.assertEqual( - "Hello", - "Hello".sanitize({tags: ["strong"]}) - ); + this.assertEqual( + "Hello", + "Hello".sanitize({tags: ["strong"]}) + ); - runner.assertEqual( - "Hello", - "Hello".sanitize({tags: ["strong"]}) - ); + this.assertEqual( + "Hello", + "Hello".sanitize({tags: ["strong"]}) + ); - runner.assertEqual( - "Hello", - "Hello".sanitize({tags: ["strong"]}) - ); + this.assertEqual( + "Hello World!", + "

    Hello World!

    ".sanitize({tags: ["strong"]}) + ); - runner.assertEqual( - "Hello World!", - "

    Hello World!

    ".sanitize({tags: ["strong"]}) - ); + this.assertEqual( + "Hello", + "Hello".sanitize({tags: ["em", "strong"]}) + ); - runner.assertEqual( - "Hello", - "Hello".sanitize({tags: ["em", "strong"]}) - ); + this.assertEqual( + "Google", + "Google".sanitize({tags: ["a"]}) + ); - runner.assertEqual( - "Google", - "Google".sanitize({tags: ["a"]}) - ); + this.assertEqual( + "Google", + "Google".sanitize({tags: ["a"], attributes: ["href"]}) + ); - runner.assertEqual( - "Google", - "Google".sanitize({tags: ["a"], attributes: ["href"]}) + if (Prototype.Browser.IE) { + this.assertEqual( + "Hello ", + "Hello ".sanitize({tags: ["span"], attributes: ["id"]}) ); - - if (Prototype.Browser.IE) { - runner.assertEqual( - "Hello ", - "Hello ".sanitize({tags: ["span"], attributes: ["id"]}) - ); - } else { - runner.assertEqual( - "Hello ", - "Hello ".sanitize({tags: ["span"], attributes: ["id"]}) - ); - } - - runner.assertEqual( - "", - "".sanitize({tags: ['img'], attributes: ["href", "src"]}) + } else { + this.assertEqual( + "Hello ", + "Hello ".sanitize({tags: ["span"], attributes: ["id"]}) ); } + + this.assertEqual( + "", + "".sanitize({tags: ['img'], attributes: ["href", "src"]}) + ); }); diff --git a/test/unit/selection_test.js b/test/unit/selection_test.js index e63d92a..e675138 100644 --- a/test/unit/selection_test.js +++ b/test/unit/selection_test.js @@ -1,151 +1,149 @@ -new Test.Unit.Runner({ - setup: function() { - $('content').update( - "Lorem ipsum dolor sit amet, " + - "consectetuer adipiscing elit." - ); - - this.selection = window.getSelection(); - this.selection.removeAllRanges(); - }, - - testSelectNode: function() { - var runner = this; - - this.selection.selectNode($('lorem')); - - runner.assertEqual("Lorem ipsum", this.selection.anchorNode.textContent, "anchorNode.textContent"); - runner.assertEqual(0, this.selection.anchorOffset, "anchorOffset"); - runner.assertEqual("Lorem ipsum", this.selection.focusNode.textContent, "focusNode.textContent"); - runner.assertEqual(11, this.selection.focusOffset, "focusOffset"); - runner.assertEqual(false, this.selection.isCollapsed, "isCollapsed"); - runner.assertEqual(1, this.selection.rangeCount, "rangeCount"); - - // Webkit extensions - if (this.selection.baseNode) { - runner.assertEqual("Lorem ipsum", this.selection.baseNode.textContent, "baseNode.textContent"); - runner.assertEqual(0, this.selection.baseOffset, "baseOffset"); - } - if (this.selection.extentNode) { - runner.assertEqual("Lorem ipsum", this.selection.extentNode.textContent, "extentNode.textContent"); - runner.assertEqual(11, this.selection.extentOffset, "extentOffset"); - } - }, - - testCollapse: function() { - var runner = this; - - this.selection.collapse($('lorem'), 0); - - runner.assertEqual("Lorem ipsum", this.selection.anchorNode.textContent, "anchorNode.textContent"); - runner.assertEqual(0, this.selection.anchorOffset, "anchorOffset"); - runner.assertEqual("Lorem ipsum", this.selection.focusNode.textContent, "focusNode.textContent"); - runner.assertEqual(0, this.selection.focusOffset, "focusOffset"); - runner.assertEqual(true, this.selection.isCollapsed, "isCollapsed"); - runner.assertEqual(1, this.selection.rangeCount, "rangeCount"); - }, - - testCollapseToStart: function() { - var runner = this; - - range = document.createRange(); - range.selectNodeContents($('lorem')); - this.selection.addRange(range); - this.selection.collapseToStart(); - - runner.assertEqual("Lorem ipsum", this.selection.anchorNode.textContent, "anchorNode.textContent"); - runner.assertEqual(0, this.selection.anchorOffset, "anchorOffset"); - runner.assertEqual("Lorem ipsum", this.selection.focusNode.textContent, "focusNode.textContent"); - runner.assertEqual(0, this.selection.focusOffset, "focusOffset"); - runner.assertEqual(true, this.selection.isCollapsed, "isCollapsed"); - runner.assertEqual(1, this.selection.rangeCount, "rangeCount"); - }, - - testCollapseToEnd: function() { - var runner = this; - - range = document.createRange(); - range.selectNodeContents($('lorem')); - this.selection.addRange(range); - this.selection.collapseToEnd(); - - runner.assertEqual("Lorem ipsum", this.selection.anchorNode.textContent, "anchorNode.textContent"); - runner.assertEqual(11, this.selection.anchorOffset, "anchorOffset"); - runner.assertEqual("Lorem ipsum", this.selection.focusNode.textContent, "focusNode.textContent"); - runner.assertEqual(11, this.selection.focusOffset, "focusOffset"); - runner.assertEqual(true, this.selection.isCollapsed, "isCollapsed"); - runner.assertEqual(1, this.selection.rangeCount, "rangeCount"); - }, - - testSelectAllChildren: function() { - var runner = this; - - this.selection.selectAllChildren($('lorem')); - - runner.assertEqual("Lorem ipsum", this.selection.anchorNode.textContent, "anchorNode.textContent"); - runner.assertEqual(0, this.selection.anchorOffset, "anchorOffset"); - runner.assertEqual("Lorem ipsum", this.selection.focusNode.textContent, "focusNode.textContent"); - runner.assertEqual(11, this.selection.focusOffset, "focusOffset"); - runner.assertEqual(false, this.selection.isCollapsed, "isCollapsed"); - runner.assertEqual(1, this.selection.rangeCount, "rangeCount"); - - this.selection.selectAllChildren($('content')); - - runner.assertEqual("Lorem ipsum", this.selection.anchorNode.textContent, "anchorNode.textContent"); - runner.assertEqual(0, this.selection.anchorOffset, "anchorOffset"); - runner.assertEqual(" adipiscing elit.", this.selection.focusNode.textContent, "focusNode.textContent"); - runner.assertEqual(17, this.selection.focusOffset, "focusOffset"); - runner.assertEqual(false, this.selection.isCollapsed, "isCollapsed"); - runner.assertEqual(1, this.selection.rangeCount, "rangeCount"); - }, - - testDeleteFromDocument: function() { - var runner = this; - - range = document.createRange(); - range.selectNodeContents($('lorem')); - this.selection.addRange(range); - this.selection.deleteFromDocument(); - - runner.assertEqual("", $('lorem').innerHTML); - }, - - testGetRangeAt: function() { - var runner = this; - - range = document.createRange(); - range.selectNodeContents($('lorem')); - this.selection.addRange(range); - range = this.selection.getRangeAt(0); - - runner.assertEqual(Node.TEXT_NODE, range.startContainer.nodeType, "startContainer.nodeType"); - runner.assertEqual(null, range.startContainer.tagName, "startContainer.tagName"); - runner.assertEqual(0, range.startOffset, "startOffset"); - runner.assertEqual(Node.TEXT_NODE, range.endContainer.nodeType, "endContainer.nodeType"); - runner.assertEqual(null, range.endContainer.tagName, "endContainer.tagName"); - runner.assertEqual(11, range.endOffset, "endOffset"); - runner.assertEqual(false, range.collapsed, "collapsed"); - runner.assertEqual("Lorem ipsum", range.commonAncestorContainer.textContent, "commonAncestorContainer.textContent") - }, - - testSelectFocusNode: function() { - var runner = this; - - var range = document.createRange(); - range.selectNodeContents($('lorem')); - this.selection.addRange(range); - this.selection.collapseToStart(); - - var range = document.createRange(); - range.selectNode(this.selection.focusNode); - this.selection.removeAllRanges(); - this.selection.addRange(range); - - runner.assertEqual("Lorem ipsum", this.selection.anchorNode.textContent, "anchorNode.textContent"); - runner.assertEqual(0, this.selection.anchorOffset, "anchorOffset"); - runner.assertEqual("Lorem ipsum", this.selection.focusNode.textContent, "focusNode.textContent"); - runner.assertEqual(11, this.selection.focusOffset, "focusOffset"); - runner.assertEqual(false, this.selection.isCollapsed, "isCollapsed"); - runner.assertEqual(1, this.selection.rangeCount, "rangeCount"); +function setup() { + $('content').update( + "Lorem ipsum dolor sit amet, " + + "consectetuer adipiscing elit." + ); + + this.selection = window.getSelection(); + this.selection.removeAllRanges(); +} + +test("selectNode", function() { + setup.bind(this)(); + + this.selection.selectNode($('lorem')); + + this.assertEqual("Lorem ipsum", this.selection.anchorNode.textContent, "anchorNode.textContent"); + this.assertEqual(0, this.selection.anchorOffset, "anchorOffset"); + this.assertEqual("Lorem ipsum", this.selection.focusNode.textContent, "focusNode.textContent"); + this.assertEqual(11, this.selection.focusOffset, "focusOffset"); + this.assertEqual(false, this.selection.isCollapsed, "isCollapsed"); + this.assertEqual(1, this.selection.rangeCount, "rangeCount"); + + // Webkit extensions + if (this.selection.baseNode) { + this.assertEqual("Lorem ipsum", this.selection.baseNode.textContent, "baseNode.textContent"); + this.assertEqual(0, this.selection.baseOffset, "baseOffset"); } + if (this.selection.extentNode) { + this.assertEqual("Lorem ipsum", this.selection.extentNode.textContent, "extentNode.textContent"); + this.assertEqual(11, this.selection.extentOffset, "extentOffset"); + } +}); + +test("Collapse", function() { + setup.bind(this)(); + + this.selection.collapse($('lorem'), 0); + + this.assertEqual("Lorem ipsum", this.selection.anchorNode.textContent, "anchorNode.textContent"); + this.assertEqual(0, this.selection.anchorOffset, "anchorOffset"); + this.assertEqual("Lorem ipsum", this.selection.focusNode.textContent, "focusNode.textContent"); + this.assertEqual(0, this.selection.focusOffset, "focusOffset"); + this.assertEqual(true, this.selection.isCollapsed, "isCollapsed"); + this.assertEqual(1, this.selection.rangeCount, "rangeCount"); +}); + +test("CollapseToStart", function() { + setup.bind(this)(); + + range = document.createRange(); + range.selectNodeContents($('lorem')); + this.selection.addRange(range); + this.selection.collapseToStart(); + + this.assertEqual("Lorem ipsum", this.selection.anchorNode.textContent, "anchorNode.textContent"); + this.assertEqual(0, this.selection.anchorOffset, "anchorOffset"); + this.assertEqual("Lorem ipsum", this.selection.focusNode.textContent, "focusNode.textContent"); + this.assertEqual(0, this.selection.focusOffset, "focusOffset"); + this.assertEqual(true, this.selection.isCollapsed, "isCollapsed"); + this.assertEqual(1, this.selection.rangeCount, "rangeCount"); +}); + +test("CollapseToEnd", function() { + setup.bind(this)(); + + range = document.createRange(); + range.selectNodeContents($('lorem')); + this.selection.addRange(range); + this.selection.collapseToEnd(); + + this.assertEqual("Lorem ipsum", this.selection.anchorNode.textContent, "anchorNode.textContent"); + this.assertEqual(11, this.selection.anchorOffset, "anchorOffset"); + this.assertEqual("Lorem ipsum", this.selection.focusNode.textContent, "focusNode.textContent"); + this.assertEqual(11, this.selection.focusOffset, "focusOffset"); + this.assertEqual(true, this.selection.isCollapsed, "isCollapsed"); + this.assertEqual(1, this.selection.rangeCount, "rangeCount"); +}); + +test("SelectAllChildren", function() { + setup.bind(this)(); + + this.selection.selectAllChildren($('lorem')); + + this.assertEqual("Lorem ipsum", this.selection.anchorNode.textContent, "anchorNode.textContent"); + this.assertEqual(0, this.selection.anchorOffset, "anchorOffset"); + this.assertEqual("Lorem ipsum", this.selection.focusNode.textContent, "focusNode.textContent"); + this.assertEqual(11, this.selection.focusOffset, "focusOffset"); + this.assertEqual(false, this.selection.isCollapsed, "isCollapsed"); + this.assertEqual(1, this.selection.rangeCount, "rangeCount"); + + this.selection.selectAllChildren($('content')); + + this.assertEqual("Lorem ipsum", this.selection.anchorNode.textContent, "anchorNode.textContent"); + this.assertEqual(0, this.selection.anchorOffset, "anchorOffset"); + this.assertEqual(" adipiscing elit.", this.selection.focusNode.textContent, "focusNode.textContent"); + this.assertEqual(17, this.selection.focusOffset, "focusOffset"); + this.assertEqual(false, this.selection.isCollapsed, "isCollapsed"); + this.assertEqual(1, this.selection.rangeCount, "rangeCount"); +}); + +test("DeleteFromDocument", function() { + setup.bind(this)(); + + range = document.createRange(); + range.selectNodeContents($('lorem')); + this.selection.addRange(range); + this.selection.deleteFromDocument(); + + this.assertEqual("", $('lorem').innerHTML); +}); + +test("GetRangeAt", function() { + setup.bind(this)(); + + range = document.createRange(); + range.selectNodeContents($('lorem')); + this.selection.addRange(range); + range = this.selection.getRangeAt(0); + + this.assertEqual(Node.TEXT_NODE, range.startContainer.nodeType, "startContainer.nodeType"); + this.assertEqual(null, range.startContainer.tagName, "startContainer.tagName"); + this.assertEqual(0, range.startOffset, "startOffset"); + this.assertEqual(Node.TEXT_NODE, range.endContainer.nodeType, "endContainer.nodeType"); + this.assertEqual(null, range.endContainer.tagName, "endContainer.tagName"); + this.assertEqual(11, range.endOffset, "endOffset"); + this.assertEqual(false, range.collapsed, "collapsed"); + this.assertEqual("Lorem ipsum", range.commonAncestorContainer.textContent, "commonAncestorContainer.textContent") +}); + +test("SelectFocusNode", function() { + setup.bind(this)(); + + var range = document.createRange(); + range.selectNodeContents($('lorem')); + this.selection.addRange(range); + this.selection.collapseToStart(); + + var range = document.createRange(); + range.selectNode(this.selection.focusNode); + this.selection.removeAllRanges(); + this.selection.addRange(range); + + this.assertEqual("Lorem ipsum", this.selection.anchorNode.textContent, "anchorNode.textContent"); + this.assertEqual(0, this.selection.anchorOffset, "anchorOffset"); + this.assertEqual("Lorem ipsum", this.selection.focusNode.textContent, "focusNode.textContent"); + this.assertEqual(11, this.selection.focusOffset, "focusOffset"); + this.assertEqual(false, this.selection.isCollapsed, "isCollapsed"); + this.assertEqual(1, this.selection.rangeCount, "rangeCount"); }); diff --git a/test/unit/templates/default.erb b/test/unit/templates/default.erb deleted file mode 100644 index 59ee537..0000000 --- a/test/unit/templates/default.erb +++ /dev/null @@ -1,21 +0,0 @@ - - - - Unit test file | <%= title %> | <%= template_name %> template | <%= timestamp %> - - <%= lib_files %> - - <%= css_fixtures %> - <%= js_fixtures %> - <%= test_file %> - - - - - -
    - -<%= html_fixtures %> - - \ No newline at end of file diff --git a/vendor/unittest_js b/vendor/unittest_js deleted file mode 160000 index b811722..0000000 --- a/vendor/unittest_js +++ /dev/null @@ -1 +0,0 @@ -Subproject commit b81172275ea4a6530a5bac4912f2f18ecbfe9feb From f79aa781c2b9a1a3936007719cceacafe7793609 Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Thu, 18 Feb 2010 22:08:54 -0600 Subject: [PATCH 119/164] kill ie firebug console --- test/editor.html | 4 ---- 1 file changed, 4 deletions(-) diff --git a/test/editor.html b/test/editor.html index b4250fc..98ff7df 100644 --- a/test/editor.html +++ b/test/editor.html @@ -4,10 +4,6 @@ WYSIWYG - - + - - - - - - -

    Oops, you need to build the package before running this example. It's easy: just run rake in the project's directory.

    - -

    This example shows you how to create a simple UI for adding HTML

    - -
    -  var HTMLHelper = {
    -    promptHTML: function() {
    -      var value = prompt("Enter some HTML", '<h1>:)</h1>')
    -      if(value)
    -        this.insertHTML(value);
    -    }
    -  }
    -
    -  WysiHat.Editor.include(HTMLHelper);
    -
    -  Event.observe(window, 'load', function() {
    -    var editor = WysiHat.Editor.attach('content');
    -    var toolbar = new WysiHat.Toolbar(editor);
    -
    -    toolbar.addButton({
    -      label: "HTML",
    -      handler: function(editor) { return editor.promptHTML(); }
    -    });
    -  });
    -
    - -
    - -
    - - diff --git a/examples/image.html b/examples/image.html deleted file mode 100644 index 98479f5..0000000 --- a/examples/image.html +++ /dev/null @@ -1,89 +0,0 @@ - - - - WYSIWYG - - - - - - - - - - -

    Oops, you need to build the package before running this example. It's easy: just run rake in the project's directory.

    - -

    This example shows you how to create a simple UI for adding images

    - -
    -  var ImageHelper = {
    -    promptImage: function() {
    -      var value = prompt("Enter a URL", "http://www.google.com/intl/en_ALL/images/logo.gif")
    -      if(value)
    -        this.insertImage(value);
    -    }
    -  }
    -
    -  WysiHat.Editor.include(ImageHelper);
    -
    -  Event.observe(window, 'load', function() {
    -    var editor = WysiHat.Editor.attach('content');
    -    var toolbar = new WysiHat.Toolbar(editor);
    -
    -    toolbar.addButton({
    -      label: "Image",
    -      handler: function(editor) { return editor.promptImage(); }
    -    });
    -  });
    -
    - -
    - -
    - - diff --git a/examples/link_selection.html b/examples/link_selection.html index 4cc1747..1cd3331 100644 --- a/examples/link_selection.html +++ b/examples/link_selection.html @@ -1,51 +1,26 @@ - - + + WYSIWYG - + @@ -54,7 +33,7 @@ } }); - Event.observe(window, 'load', function() { + document.on("dom:loaded", function() { var editor = WysiHat.Editor.attach('content'); var toolbar = new Toolbar(editor); toolbar.addButtonSet(WysiHat.Toolbar.ButtonSets.Basic); @@ -95,7 +74,7 @@ } }); - Event.observe(window, 'load', function() { + document.on("dom:loaded", function() { var editor = WysiHat.Editor.attach('content'); var toolbar = new Toolbar(editor); toolbar.addButtonSet(WysiHat.Toolbar.ButtonSets.Basic); @@ -109,7 +88,7 @@ Underline Italic -