├── .gitignore
├── Gemfile
├── LICENSE.txt
├── README.md
├── Rakefile
├── lib
└── wysihtml
│ ├── rails.rb
│ └── rails
│ └── version.rb
├── vendor
└── assets
│ ├── javascripts
│ ├── wysihtml.js
│ └── wysihtml
│ │ ├── all_commands.js
│ │ ├── extra_commands
│ │ ├── alignCenterStyle.js
│ │ ├── alignJustifyStyle.js
│ │ ├── alignLeftStyle.js
│ │ ├── alignRightStyle.js
│ │ ├── bgColorStyle.js
│ │ ├── bold.js
│ │ ├── command_formatCode.js
│ │ ├── command_insertImage.js
│ │ ├── fontSize.js
│ │ ├── fontSizeStyle.js
│ │ ├── foreColor.js
│ │ ├── foreColorStyle.js
│ │ ├── insertBlockQuote.js
│ │ ├── insertHorizontalRule.js
│ │ ├── insertOrderedList.js
│ │ ├── insertUnorderedList.js
│ │ ├── italic.js
│ │ ├── justifyCenter.js
│ │ ├── justifyFull.js
│ │ ├── justifyLeft.js
│ │ ├── justifyRight.js
│ │ ├── subscript.js
│ │ ├── superscript.js
│ │ └── underline.js
│ │ ├── parser_rules
│ │ ├── advanced.js
│ │ ├── advanced_and_extended.js
│ │ ├── advanced_unwrap.js
│ │ └── simple.js
│ │ ├── table_editing.js
│ │ └── toolbar.js
│ └── stylesheets
│ └── wysihtml.css
└── wysihtml-rails.gemspec
/.gitignore:
--------------------------------------------------------------------------------
1 | *.gem
2 | *.rbc
3 | .bundle
4 | .config
5 | .yardoc
6 | Gemfile.lock
7 | InstalledFiles
8 | _yardoc
9 | coverage
10 | doc/
11 | lib/bundler/man
12 | pkg
13 | rdoc
14 | spec/reports
15 | test/tmp
16 | test/version_tmp
17 | tmp
18 | .DS_Store
19 |
--------------------------------------------------------------------------------
/Gemfile:
--------------------------------------------------------------------------------
1 | source 'https://rubygems.org'
2 |
3 | # Specify your gem's dependencies in wysihtml-rails.gemspec
4 | gemspec
5 |
--------------------------------------------------------------------------------
/LICENSE.txt:
--------------------------------------------------------------------------------
1 | Copyright (c) 2013 Tanel Jakobsoo
2 |
3 | MIT License
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining
6 | a copy of this software and associated documentation files (the
7 | "Software"), to deal in the Software without restriction, including
8 | without limitation the rights to use, copy, modify, merge, publish,
9 | distribute, sublicense, and/or sell copies of the Software, and to
10 | permit persons to whom the Software is furnished to do so, subject to
11 | the following conditions:
12 |
13 | The above copyright notice and this permission notice shall be
14 | included in all copies or substantial portions of the Software.
15 |
16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # wysihtml for Rails
2 |
3 | [Voog/wysihtml](https://github.com/Voog/wysihtml) is an extended and less strict approach on [xing/wysihtml5](http://xing.github.io/wysihtml5/) open source rich text editor based on HTML5 technology.
4 |
5 | This gem adds wysihtml to Rails assets pipeline.
6 |
7 | ## Installation
8 |
9 | Add this line to your application's Gemfile:
10 |
11 | ```ruby
12 | gem 'wysihtml-rails'
13 | ```
14 |
15 | Or you can install from latest build:
16 |
17 | ```ruby
18 | gem 'wysihtml-rails', :git => 'https://github.com/Voog/wysihtml-rails.git'
19 | ```
20 |
21 | And then execute:
22 |
23 | ```sh
24 | bundle
25 | ```
26 |
27 | Or install it yourself as:
28 |
29 | ```sh
30 | $ gem install wysihtml-rails
31 | ```
32 |
33 | ## Usage
34 |
35 | Require it in your JS manifest's file `application.js`:
36 |
37 | ```js
38 | //= require wysihtml
39 | ```
40 |
41 | or if you also need toolbar, table editing features or all commands:
42 |
43 | ```js
44 | //= require wysihtml
45 | //= require wysihtml/toolbar
46 | //= require wysihtml/all_commands
47 | //= require wysihtml/table_editing
48 | ```
49 |
50 | Additionally include predefined `simple`, `advanced` or `advanced_unwrap` parsing rules in your `application.js`:
51 |
52 | ```js
53 | //= require wysihtml/parser_rules/advanced_unwrap
54 | ```
55 |
56 | Additionally include predefined `wysihtml` stiles in your `application.css.scss` file:
57 |
58 | ```scss
59 | *= require wysihtml
60 | ```
61 |
62 | The simple initialise:
63 |
64 | ```html
65 |
73 | ```
74 |
75 | ## Contributing
76 |
77 | 1. Fork it
78 | 2. Create your feature branch (`git checkout -b my-new-feature`)
79 | 3. Commit your changes (`git commit -am 'Add some feature'`)
80 | 4. Push to the branch (`git push origin my-new-feature`)
81 | 5. Create new Pull Request
82 |
--------------------------------------------------------------------------------
/Rakefile:
--------------------------------------------------------------------------------
1 | require "bundler/gem_tasks"
2 |
--------------------------------------------------------------------------------
/lib/wysihtml/rails.rb:
--------------------------------------------------------------------------------
1 | require "wysihtml/rails/version"
2 |
3 | module Wysihtml
4 | module Rails
5 | class Engine < ::Rails::Engine
6 | isolate_namespace Wysihtml::Rails
7 | end
8 | end
9 | end
10 |
--------------------------------------------------------------------------------
/lib/wysihtml/rails/version.rb:
--------------------------------------------------------------------------------
1 | module Wysihtml
2 | module Rails
3 | VERSION = "0.6.0.beta2"
4 | end
5 | end
6 |
--------------------------------------------------------------------------------
/vendor/assets/javascripts/wysihtml/all_commands.js:
--------------------------------------------------------------------------------
1 | //= require ./extra_commands/alignCenterStyle.js
2 | //= require ./extra_commands/alignJustifyStyle.js
3 | //= require ./extra_commands/alignLeftStyle.js
4 | //= require ./extra_commands/alignRightStyle.js
5 | //= require ./extra_commands/bgColorStyle.js
6 | //= require ./extra_commands/bold.js
7 | //= require ./extra_commands/command_formatCode.js
8 | //= require ./extra_commands/command_insertImage.js
9 | //= require ./extra_commands/fontSize.js
10 | //= require ./extra_commands/fontSizeStyle.js
11 | //= require ./extra_commands/foreColor.js
12 | //= require ./extra_commands/foreColorStyle.js
13 | //= require ./extra_commands/insertBlockQuote.js
14 | //= require ./extra_commands/insertHorizontalRule.js
15 | //= require ./extra_commands/insertOrderedList.js
16 | //= require ./extra_commands/insertUnorderedList.js
17 | //= require ./extra_commands/italic.js
18 | //= require ./extra_commands/justifyCenter.js
19 | //= require ./extra_commands/justifyFull.js
20 | //= require ./extra_commands/justifyLeft.js
21 | //= require ./extra_commands/justifyRight.js
22 | //= require ./extra_commands/subscript.js
23 | //= require ./extra_commands/superscript.js
24 | //= require ./extra_commands/underline.js
25 |
--------------------------------------------------------------------------------
/vendor/assets/javascripts/wysihtml/extra_commands/alignCenterStyle.js:
--------------------------------------------------------------------------------
1 | wysihtml.commands.alignCenterStyle = (function() {
2 | var nodeOptions = {
3 | styleProperty: "textAlign",
4 | styleValue: "center",
5 | toggle: true
6 | };
7 |
8 | return {
9 | exec: function(composer, command) {
10 | return wysihtml.commands.formatBlock.exec(composer, "formatBlock", nodeOptions);
11 | },
12 |
13 | state: function(composer, command) {
14 | return wysihtml.commands.formatBlock.state(composer, "formatBlock", nodeOptions);
15 | }
16 | };
17 | })();
18 |
--------------------------------------------------------------------------------
/vendor/assets/javascripts/wysihtml/extra_commands/alignJustifyStyle.js:
--------------------------------------------------------------------------------
1 | wysihtml.commands.alignJustifyStyle = (function() {
2 | var nodeOptions = {
3 | styleProperty: "textAlign",
4 | styleValue: "justify",
5 | toggle: true
6 | };
7 |
8 | return {
9 | exec: function(composer, command) {
10 | return wysihtml.commands.formatBlock.exec(composer, "formatBlock", nodeOptions);
11 | },
12 |
13 | state: function(composer, command) {
14 | return wysihtml.commands.formatBlock.state(composer, "formatBlock", nodeOptions);
15 | }
16 | };
17 | })();
18 |
--------------------------------------------------------------------------------
/vendor/assets/javascripts/wysihtml/extra_commands/alignLeftStyle.js:
--------------------------------------------------------------------------------
1 | wysihtml.commands.alignLeftStyle = (function() {
2 | var nodeOptions = {
3 | styleProperty: "textAlign",
4 | styleValue: "left",
5 | toggle: true
6 | };
7 |
8 | return {
9 | exec: function(composer, command) {
10 | return wysihtml.commands.formatBlock.exec(composer, "formatBlock", nodeOptions);
11 | },
12 |
13 | state: function(composer, command) {
14 | return wysihtml.commands.formatBlock.state(composer, "formatBlock", nodeOptions);
15 | }
16 | };
17 | })();
18 |
--------------------------------------------------------------------------------
/vendor/assets/javascripts/wysihtml/extra_commands/alignRightStyle.js:
--------------------------------------------------------------------------------
1 | wysihtml.commands.alignRightStyle = (function() {
2 | var nodeOptions = {
3 | styleProperty: "textAlign",
4 | styleValue: "right",
5 | toggle: true
6 | };
7 |
8 | return {
9 | exec: function(composer, command) {
10 | return wysihtml.commands.formatBlock.exec(composer, "formatBlock", nodeOptions);
11 | },
12 |
13 | state: function(composer, command) {
14 | return wysihtml.commands.formatBlock.state(composer, "formatBlock", nodeOptions);
15 | }
16 | };
17 | })();
18 |
--------------------------------------------------------------------------------
/vendor/assets/javascripts/wysihtml/extra_commands/bgColorStyle.js:
--------------------------------------------------------------------------------
1 | /* Sets text background color by inline styles */
2 | wysihtml.commands.bgColorStyle = (function() {
3 | return {
4 | exec: function(composer, command, color) {
5 | var colorVals = wysihtml.quirks.styleParser.parseColor("background-color:" + (color.color || color), "background-color"),
6 | colString;
7 |
8 | if (colorVals) {
9 | colString = (colorVals[3] === 1 ? "rgb(" + [colorVals[0], colorVals[1], colorVals[2]].join(', ') : "rgba(" + colorVals.join(', ')) + ')';
10 | wysihtml.commands.formatInline.exec(composer, command, {styleProperty: 'backgroundColor', styleValue: colString});
11 | }
12 | },
13 |
14 | state: function(composer, command, color) {
15 | var colorVals = color ? wysihtml.quirks.styleParser.parseColor("background-color:" + (color.color || color), "background-color") : null,
16 | colString;
17 |
18 | if (colorVals) {
19 | colString = (colorVals[3] === 1 ? "rgb(" + [colorVals[0], colorVals[1], colorVals[2]].join(', ') : "rgba(" + colorVals.join(', ')) + ')';
20 | }
21 |
22 | return wysihtml.commands.formatInline.state(composer, command, {styleProperty: 'backgroundColor', styleValue: colString});
23 | },
24 |
25 | remove: function(composer, command) {
26 | return wysihtml.commands.formatInline.remove(composer, command, {styleProperty: 'backgroundColor'});
27 | },
28 |
29 | stateValue: function(composer, command, props) {
30 | var st = this.state(composer, command),
31 | colorStr,
32 | val = false;
33 |
34 | if (st && wysihtml.lang.object(st).isArray()) {
35 | st = st[0];
36 | }
37 |
38 | if (st) {
39 | colorStr = st.getAttribute('style');
40 | if (colorStr) {
41 | val = wysihtml.quirks.styleParser.parseColor(colorStr, "background-color");
42 | return wysihtml.quirks.styleParser.unparseColor(val, props);
43 | }
44 | }
45 | return false;
46 | }
47 | };
48 | })();
49 |
--------------------------------------------------------------------------------
/vendor/assets/javascripts/wysihtml/extra_commands/bold.js:
--------------------------------------------------------------------------------
1 | wysihtml.commands.bold = (function() {
2 | var nodeOptions = {
3 | nodeName: "B",
4 | toggle: true
5 | };
6 |
7 | return {
8 | exec: function(composer, command) {
9 | wysihtml.commands.formatInline.exec(composer, command, nodeOptions);
10 | },
11 |
12 | state: function(composer, command) {
13 | return wysihtml.commands.formatInline.state(composer, command, nodeOptions);
14 | }
15 | };
16 | })();
17 |
--------------------------------------------------------------------------------
/vendor/assets/javascripts/wysihtml/extra_commands/command_formatCode.js:
--------------------------------------------------------------------------------
1 | /* Formats block for as a
block
2 | * Useful in conjuction for sytax highlight utility: highlight.js
3 | *
4 | * Usage:
5 | *
6 | * editorInstance.composer.commands.exec("formatCode", "language-html");
7 | */
8 | wysihtml.commands.formatCode = (function() {
9 | return {
10 | exec: function(composer, command, classname) {
11 | var pre = this.state(composer)[0],
12 | code, range, selectedNodes;
13 |
14 | if (pre) {
15 | // caret is already within a ...
16 | composer.selection.executeAndRestore(function() {
17 | code = pre.querySelector("code");
18 | wysihtml.dom.replaceWithChildNodes(pre);
19 | if (code) {
20 | wysihtml.dom.replaceWithChildNodes(code);
21 | }
22 | });
23 | } else {
24 | // Wrap in ...
25 | range = composer.selection.getRange();
26 | selectedNodes = range.extractContents();
27 | pre = composer.doc.createElement("pre");
28 | code = composer.doc.createElement("code");
29 |
30 | if (classname) {
31 | code.className = classname;
32 | }
33 |
34 | pre.appendChild(code);
35 | code.appendChild(selectedNodes);
36 | range.insertNode(pre);
37 | composer.selection.selectNode(pre);
38 | }
39 | },
40 |
41 | state: function(composer) {
42 | var selectedNode = composer.selection.getSelectedNode(), node;
43 | if (selectedNode && selectedNode.nodeName && selectedNode.nodeName == "PRE"&&
44 | selectedNode.firstChild && selectedNode.firstChild.nodeName && selectedNode.firstChild.nodeName == "CODE") {
45 | return [selectedNode];
46 | } else {
47 | node = wysihtml.dom.getParentElement(selectedNode, { query: "pre code" });
48 | return node ? [node.parentNode] : false;
49 | }
50 | }
51 | };
52 | })();
53 |
--------------------------------------------------------------------------------
/vendor/assets/javascripts/wysihtml/extra_commands/command_insertImage.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Inserts an
3 | * If selection is already an image link, it removes it
4 | *
5 | * @example
6 | * // either ...
7 | * wysihtml.commands.insertImage.exec(composer, "insertImage", "http://www.google.de/logo.jpg");
8 | * // ... or ...
9 | * wysihtml.commands.insertImage.exec(composer, "insertImage", { src: "http://www.google.de/logo.jpg", title: "foo" });
10 | */
11 | wysihtml.commands.insertImage = (function() {
12 | var NODE_NAME = "IMG";
13 | return {
14 | exec: function(composer, command, value) {
15 | value = typeof(value) === "object" ? value : { src: value };
16 |
17 | var doc = composer.doc,
18 | image = this.state(composer),
19 | textNode,
20 | parent;
21 |
22 | // If image is selected and src ie empty, set the caret before it and delete the image
23 | if (image && !value.src) {
24 | composer.selection.setBefore(image);
25 | parent = image.parentNode;
26 | parent.removeChild(image);
27 |
28 | // and it's parent too if it hasn't got any other relevant child nodes
29 | wysihtml.dom.removeEmptyTextNodes(parent);
30 | if (parent.nodeName === "A" && !parent.firstChild) {
31 | composer.selection.setAfter(parent);
32 | parent.parentNode.removeChild(parent);
33 | }
34 |
35 | // firefox and ie sometimes don't remove the image handles, even though the image got removed
36 | wysihtml.quirks.redraw(composer.element);
37 | return;
38 | }
39 |
40 | // If image selected change attributes accordingly
41 | if (image) {
42 | for (var key in value) {
43 | if (value.hasOwnProperty(key)) {
44 | image.setAttribute(key === "className" ? "class" : key, value[key]);
45 | }
46 | }
47 | return;
48 | }
49 |
50 | // Otherwise lets create the image
51 | image = doc.createElement(NODE_NAME);
52 |
53 | for (var i in value) {
54 | image.setAttribute(i === "className" ? "class" : i, value[i]);
55 | }
56 |
57 | composer.selection.insertNode(image);
58 | if (wysihtml.browser.hasProblemsSettingCaretAfterImg()) {
59 | textNode = doc.createTextNode(wysihtml.INVISIBLE_SPACE);
60 | composer.selection.insertNode(textNode);
61 | composer.selection.setAfter(textNode);
62 | } else {
63 | composer.selection.setAfter(image);
64 | }
65 | },
66 |
67 | state: function(composer) {
68 | var doc = composer.doc,
69 | selectedNode,
70 | text,
71 | imagesInSelection;
72 |
73 | if (!wysihtml.dom.hasElementWithTagName(doc, NODE_NAME)) {
74 | return false;
75 | }
76 |
77 | selectedNode = composer.selection.getSelectedNode();
78 | if (!selectedNode) {
79 | return false;
80 | }
81 |
82 | if (selectedNode.nodeName === NODE_NAME) {
83 | // This works perfectly in IE
84 | return selectedNode;
85 | }
86 |
87 | if (selectedNode.nodeType !== wysihtml.ELEMENT_NODE) {
88 | return false;
89 | }
90 |
91 | text = composer.selection.getText();
92 | text = wysihtml.lang.string(text).trim();
93 | if (text) {
94 | return false;
95 | }
96 |
97 | imagesInSelection = composer.selection.getNodes(wysihtml.ELEMENT_NODE, function(node) {
98 | return node.nodeName === "IMG";
99 | });
100 |
101 | if (imagesInSelection.length !== 1) {
102 | return false;
103 | }
104 |
105 | return imagesInSelection[0];
106 | }
107 | };
108 | })();
109 |
--------------------------------------------------------------------------------
/vendor/assets/javascripts/wysihtml/extra_commands/fontSize.js:
--------------------------------------------------------------------------------
1 | wysihtml.commands.fontSize = (function() {
2 | var REG_EXP = /wysiwyg-font-size-[0-9a-z\-]+/g;
3 |
4 | return {
5 | exec: function(composer, command, size) {
6 | wysihtml.commands.formatInline.exec(composer, command, {className: "wysiwyg-font-size-" + size, classRegExp: REG_EXP, toggle: true});
7 | },
8 |
9 | state: function(composer, command, size) {
10 | return wysihtml.commands.formatInline.state(composer, command, {className: "wysiwyg-font-size-" + size});
11 | }
12 | };
13 | })();
14 |
--------------------------------------------------------------------------------
/vendor/assets/javascripts/wysihtml/extra_commands/fontSizeStyle.js:
--------------------------------------------------------------------------------
1 | /* Set font size by inline style */
2 | wysihtml.commands.fontSizeStyle = (function() {
3 | return {
4 | exec: function(composer, command, size) {
5 | size = size.size || size;
6 | if (!(/^\s*$/).test(size)) {
7 | wysihtml.commands.formatInline.exec(composer, command, {styleProperty: "fontSize", styleValue: size, toggle: false});
8 | }
9 | },
10 |
11 | state: function(composer, command, size) {
12 | return wysihtml.commands.formatInline.state(composer, command, {styleProperty: "fontSize", styleValue: size || undefined});
13 | },
14 |
15 | remove: function(composer, command) {
16 | return wysihtml.commands.formatInline.remove(composer, command, {styleProperty: "fontSize"});
17 | },
18 |
19 | stateValue: function(composer, command) {
20 | var styleStr,
21 | st = this.state(composer, command);
22 |
23 | if (st && wysihtml.lang.object(st).isArray()) {
24 | st = st[0];
25 | }
26 | if (st) {
27 | styleStr = st.getAttribute("style");
28 | if (styleStr) {
29 | return wysihtml.quirks.styleParser.parseFontSize(styleStr);
30 | }
31 | }
32 | return false;
33 | }
34 | };
35 | })();
36 |
--------------------------------------------------------------------------------
/vendor/assets/javascripts/wysihtml/extra_commands/foreColor.js:
--------------------------------------------------------------------------------
1 | wysihtml.commands.foreColor = (function() {
2 | var REG_EXP = /wysiwyg-color-[0-9a-z]+/g;
3 |
4 | return {
5 | exec: function(composer, command, color) {
6 | wysihtml.commands.formatInline.exec(composer, command, {className: "wysiwyg-color-" + color, classRegExp: REG_EXP, toggle: true});
7 | },
8 |
9 | state: function(composer, command, color) {
10 | return wysihtml.commands.formatInline.state(composer, command, {className: "wysiwyg-color-" + color});
11 | }
12 | };
13 | })();
14 |
--------------------------------------------------------------------------------
/vendor/assets/javascripts/wysihtml/extra_commands/foreColorStyle.js:
--------------------------------------------------------------------------------
1 | /* Sets text color by inline styles */
2 | wysihtml.commands.foreColorStyle = (function() {
3 | return {
4 | exec: function(composer, command, color) {
5 | var colorVals, colString;
6 |
7 | if (!color) { return; }
8 |
9 | colorVals = wysihtml.quirks.styleParser.parseColor("color:" + (color.color || color), "color");
10 |
11 | if (colorVals) {
12 | colString = (colorVals[3] === 1 ? "rgb(" + [colorVals[0], colorVals[1], colorVals[2]].join(", ") : "rgba(" + colorVals.join(', ')) + ')';
13 | wysihtml.commands.formatInline.exec(composer, command, {styleProperty: "color", styleValue: colString});
14 | }
15 | },
16 |
17 | state: function(composer, command, color) {
18 | var colorVals = color ? wysihtml.quirks.styleParser.parseColor("color:" + (color.color || color), "color") : null,
19 | colString;
20 |
21 |
22 | if (colorVals) {
23 | colString = (colorVals[3] === 1 ? "rgb(" + [colorVals[0], colorVals[1], colorVals[2]].join(", ") : "rgba(" + colorVals.join(', ')) + ')';
24 | }
25 |
26 | return wysihtml.commands.formatInline.state(composer, command, {styleProperty: "color", styleValue: colString});
27 | },
28 |
29 | remove: function(composer, command) {
30 | return wysihtml.commands.formatInline.remove(composer, command, {styleProperty: "color"});
31 | },
32 |
33 | stateValue: function(composer, command, props) {
34 | var st = this.state(composer, command),
35 | colorStr,
36 | val = false;
37 |
38 | if (st && wysihtml.lang.object(st).isArray()) {
39 | st = st[0];
40 | }
41 |
42 | if (st) {
43 | colorStr = st.getAttribute("style");
44 | if (colorStr) {
45 | val = wysihtml.quirks.styleParser.parseColor(colorStr, "color");
46 | return wysihtml.quirks.styleParser.unparseColor(val, props);
47 | }
48 | }
49 | return false;
50 | }
51 | };
52 | })();
53 |
--------------------------------------------------------------------------------
/vendor/assets/javascripts/wysihtml/extra_commands/insertBlockQuote.js:
--------------------------------------------------------------------------------
1 | wysihtml.commands.insertBlockQuote = (function() {
2 | var nodeOptions = {
3 | nodeName: "BLOCKQUOTE",
4 | toggle: true
5 | };
6 |
7 | return {
8 | exec: function(composer, command) {
9 | return wysihtml.commands.formatBlock.exec(composer, "formatBlock", nodeOptions);
10 | },
11 |
12 | state: function(composer, command) {
13 | return wysihtml.commands.formatBlock.state(composer, "formatBlock", nodeOptions);
14 | }
15 | };
16 | })();
17 |
--------------------------------------------------------------------------------
/vendor/assets/javascripts/wysihtml/extra_commands/insertHorizontalRule.js:
--------------------------------------------------------------------------------
1 | wysihtml.commands.insertHorizontalRule = (function() {
2 | return {
3 | exec: function(composer) {
4 | var node = composer.selection.getSelectedNode(),
5 | phrasingOnlyParent = wysihtml.dom.getParentElement(node, { query: wysihtml.PERMITTED_PHRASING_CONTENT_ONLY }, null, composer.editableArea),
6 | elem = document.createElement('hr'),
7 | range, idx;
8 |
9 | // HR is not allowed into some elements (where only phrasing content is allowed)
10 | // thus the HR insertion must break out of those https://developer.mozilla.org/en-US/docs/Web/Guide/HTML/Content_categories
11 | if (phrasingOnlyParent) {
12 | composer.selection.splitElementAtCaret(phrasingOnlyParent, elem);
13 | } else {
14 | composer.selection.insertNode(elem);
15 | }
16 |
17 | if (elem.nextSibling) {
18 | composer.selection.setBefore(elem.nextSibling);
19 | } else {
20 | composer.selection.setAfter(elem);
21 | }
22 | },
23 | state: function() {
24 | return false; // :(
25 | }
26 | };
27 | })();
28 |
--------------------------------------------------------------------------------
/vendor/assets/javascripts/wysihtml/extra_commands/insertOrderedList.js:
--------------------------------------------------------------------------------
1 | wysihtml.commands.insertOrderedList = (function() {
2 | return {
3 | exec: function(composer, command) {
4 | wysihtml.commands.insertList.exec(composer, command, "OL");
5 | },
6 |
7 | state: function(composer, command) {
8 | return wysihtml.commands.insertList.state(composer, command, "OL");
9 | }
10 | };
11 | })();
12 |
--------------------------------------------------------------------------------
/vendor/assets/javascripts/wysihtml/extra_commands/insertUnorderedList.js:
--------------------------------------------------------------------------------
1 | wysihtml.commands.insertUnorderedList = (function() {
2 | return {
3 | exec: function(composer, command) {
4 | wysihtml.commands.insertList.exec(composer, command, "UL");
5 | },
6 |
7 | state: function(composer, command) {
8 | return wysihtml.commands.insertList.state(composer, command, "UL");
9 | }
10 | };
11 | })();
12 |
--------------------------------------------------------------------------------
/vendor/assets/javascripts/wysihtml/extra_commands/italic.js:
--------------------------------------------------------------------------------
1 | wysihtml.commands.italic = (function() {
2 | var nodeOptions = {
3 | nodeName: "I",
4 | toggle: true
5 | };
6 |
7 | return {
8 | exec: function(composer, command) {
9 | wysihtml.commands.formatInline.exec(composer, command, nodeOptions);
10 | },
11 |
12 | state: function(composer, command) {
13 | return wysihtml.commands.formatInline.state(composer, command, nodeOptions);
14 | }
15 | };
16 |
17 | })();
18 |
--------------------------------------------------------------------------------
/vendor/assets/javascripts/wysihtml/extra_commands/justifyCenter.js:
--------------------------------------------------------------------------------
1 | wysihtml.commands.justifyCenter = (function() {
2 | var nodeOptions = {
3 | className: "wysiwyg-text-align-center",
4 | classRegExp: /wysiwyg-text-align-[0-9a-z]+/g,
5 | toggle: true
6 | };
7 |
8 | return {
9 | exec: function(composer, command) {
10 | return wysihtml.commands.formatBlock.exec(composer, "formatBlock", nodeOptions);
11 | },
12 |
13 | state: function(composer, command) {
14 | return wysihtml.commands.formatBlock.state(composer, "formatBlock", nodeOptions);
15 | }
16 | };
17 |
18 | })();
19 |
--------------------------------------------------------------------------------
/vendor/assets/javascripts/wysihtml/extra_commands/justifyFull.js:
--------------------------------------------------------------------------------
1 | wysihtml.commands.justifyFull = (function() {
2 | var nodeOptions = {
3 | className: "wysiwyg-text-align-justify",
4 | classRegExp: /wysiwyg-text-align-[0-9a-z]+/g,
5 | toggle: true
6 | };
7 |
8 | return {
9 | exec: function(composer, command) {
10 | return wysihtml.commands.formatBlock.exec(composer, "formatBlock", nodeOptions);
11 | },
12 |
13 | state: function(composer, command) {
14 | return wysihtml.commands.formatBlock.state(composer, "formatBlock", nodeOptions);
15 | }
16 | };
17 | })();
18 |
--------------------------------------------------------------------------------
/vendor/assets/javascripts/wysihtml/extra_commands/justifyLeft.js:
--------------------------------------------------------------------------------
1 | wysihtml.commands.justifyLeft = (function() {
2 | var nodeOptions = {
3 | className: "wysiwyg-text-align-left",
4 | classRegExp: /wysiwyg-text-align-[0-9a-z]+/g,
5 | toggle: true
6 | };
7 |
8 | return {
9 | exec: function(composer, command) {
10 | return wysihtml.commands.formatBlock.exec(composer, "formatBlock", nodeOptions);
11 | },
12 |
13 | state: function(composer, command) {
14 | return wysihtml.commands.formatBlock.state(composer, "formatBlock", nodeOptions);
15 | }
16 | };
17 | })();
18 |
--------------------------------------------------------------------------------
/vendor/assets/javascripts/wysihtml/extra_commands/justifyRight.js:
--------------------------------------------------------------------------------
1 | wysihtml.commands.justifyRight = (function() {
2 | var nodeOptions = {
3 | className: "wysiwyg-text-align-right",
4 | classRegExp: /wysiwyg-text-align-[0-9a-z]+/g,
5 | toggle: true
6 | };
7 |
8 | return {
9 | exec: function(composer, command) {
10 | return wysihtml.commands.formatBlock.exec(composer, "formatBlock", nodeOptions);
11 | },
12 |
13 | state: function(composer, command) {
14 | return wysihtml.commands.formatBlock.state(composer, "formatBlock", nodeOptions);
15 | }
16 | };
17 | })();
18 |
--------------------------------------------------------------------------------
/vendor/assets/javascripts/wysihtml/extra_commands/subscript.js:
--------------------------------------------------------------------------------
1 | wysihtml.commands.subscript = (function() {
2 | var nodeOptions = {
3 | nodeName: "SUB",
4 | toggle: true
5 | };
6 |
7 | return {
8 | exec: function(composer, command) {
9 | wysihtml.commands.formatInline.exec(composer, command, nodeOptions);
10 | },
11 |
12 | state: function(composer, command) {
13 | return wysihtml.commands.formatInline.state(composer, command, nodeOptions);
14 | }
15 | };
16 |
17 | })();
18 |
--------------------------------------------------------------------------------
/vendor/assets/javascripts/wysihtml/extra_commands/superscript.js:
--------------------------------------------------------------------------------
1 | wysihtml.commands.superscript = (function() {
2 | var nodeOptions = {
3 | nodeName: "SUP",
4 | toggle: true
5 | };
6 |
7 | return {
8 | exec: function(composer, command) {
9 | wysihtml.commands.formatInline.exec(composer, command, nodeOptions);
10 | },
11 |
12 | state: function(composer, command) {
13 | return wysihtml.commands.formatInline.state(composer, command, nodeOptions);
14 | }
15 | };
16 |
17 | })();
18 |
--------------------------------------------------------------------------------
/vendor/assets/javascripts/wysihtml/extra_commands/underline.js:
--------------------------------------------------------------------------------
1 | wysihtml.commands.underline = (function() {
2 | var nodeOptions = {
3 | nodeName: "U",
4 | toggle: true
5 | };
6 |
7 | return {
8 | exec: function(composer, command) {
9 | wysihtml.commands.formatInline.exec(composer, command, nodeOptions);
10 | },
11 |
12 | state: function(composer, command) {
13 | return wysihtml.commands.formatInline.state(composer, command, nodeOptions);
14 | }
15 | };
16 |
17 | })();
18 |
--------------------------------------------------------------------------------
/vendor/assets/javascripts/wysihtml/parser_rules/advanced.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Full HTML5 compatibility rule set
3 | * These rules define which tags and CSS classes are supported and which tags should be specially treated.
4 | *
5 | * Examples based on this rule set:
6 | *
7 | * foo
8 | * ... becomes ...
9 | * foo
10 | *
11 | *
12 | * ... becomes ...
13 | *
14 | *
15 | * foo
16 | * ... becomes ...
17 | * foo
18 | *
19 | * foo
20 | * ... becomes ...
21 | * foo
22 | *
23 | * foo bar
24 | * ... becomes ...
25 | * foo bar
26 | *
27 | * hello
28 | * ... becomes ...
29 | * hello
30 | *
31 | * hello
32 | * ... becomes ...
33 | * hello
34 | */
35 | var wysihtmlParserRules = {
36 | /**
37 | * CSS Class white-list
38 | * Following CSS classes won't be removed when parsed by the wysihtml HTML parser
39 | */
40 | "classes": {
41 | "wysiwyg-clear-both": 1,
42 | "wysiwyg-clear-left": 1,
43 | "wysiwyg-clear-right": 1,
44 | "wysiwyg-color-aqua": 1,
45 | "wysiwyg-color-black": 1,
46 | "wysiwyg-color-blue": 1,
47 | "wysiwyg-color-fuchsia": 1,
48 | "wysiwyg-color-gray": 1,
49 | "wysiwyg-color-green": 1,
50 | "wysiwyg-color-lime": 1,
51 | "wysiwyg-color-maroon": 1,
52 | "wysiwyg-color-navy": 1,
53 | "wysiwyg-color-olive": 1,
54 | "wysiwyg-color-purple": 1,
55 | "wysiwyg-color-red": 1,
56 | "wysiwyg-color-silver": 1,
57 | "wysiwyg-color-teal": 1,
58 | "wysiwyg-color-white": 1,
59 | "wysiwyg-color-yellow": 1,
60 | "wysiwyg-float-left": 1,
61 | "wysiwyg-float-right": 1,
62 | "wysiwyg-font-size-large": 1,
63 | "wysiwyg-font-size-larger": 1,
64 | "wysiwyg-font-size-medium": 1,
65 | "wysiwyg-font-size-small": 1,
66 | "wysiwyg-font-size-smaller": 1,
67 | "wysiwyg-font-size-x-large": 1,
68 | "wysiwyg-font-size-x-small": 1,
69 | "wysiwyg-font-size-xx-large": 1,
70 | "wysiwyg-font-size-xx-small": 1,
71 | "wysiwyg-text-align-center": 1,
72 | "wysiwyg-text-align-justify": 1,
73 | "wysiwyg-text-align-left": 1,
74 | "wysiwyg-text-align-right": 1
75 | },
76 | /**
77 | * Tag list
78 | *
79 | * The following options are available:
80 | *
81 | * - add_class: converts and deletes the given HTML4 attribute (align, clear, ...) via the given method to a css class
82 | * The following methods are implemented in wysihtml.dom.parse:
83 | * - align_text: converts align attribute values (right/left/center/justify) to their corresponding css class "wysiwyg-text-align-*")
84 | * foo
... becomes ... foo
85 | * - clear_br: converts clear attribute values left/right/all/both to their corresponding css class "wysiwyg-clear-*"
86 | * ... becomes ...
87 | * - align_img: converts align attribute values (right/left) on to their corresponding css class "wysiwyg-float-*"
88 | *
89 | * - add_style: converts and deletes the given HTML4 attribute (align) via the given method to a css style
90 | * The following methods are implemented in wysihtml.dom.parse:
91 | * - align_text: converts align attribute values (right/left/center) to their corresponding css style)
92 | * foo
... becomes ... foo
93 | *
94 | * - remove: removes the element and its content
95 | *
96 | * - unwrap removes element but leaves content
97 | *
98 | * - rename_tag: renames the element to the given tag
99 | *
100 | * - set_class: adds the given class to the element (note: make sure that the class is in the "classes" white list above)
101 | *
102 | * - set_attributes: sets/overrides the given attributes
103 | *
104 | * - check_attributes: checks the given HTML attribute via the given method
105 | * - url: allows only valid urls (starting with http:// or https://)
106 | * - src: allows something like "/foobar.jpg", "http://google.com", ...
107 | * - href: allows something like "mailto:bert@foo.com", "http://google.com", "/foobar.jpg"
108 | * - alt: strips unwanted characters. if the attribute is not set, then it gets set (to ensure valid and compatible HTML)
109 | * - numbers: ensures that the attribute only contains numeric (integer) characters (no float values or units)
110 | * - dimension: for with/height attributes where floating point numbrs and percentages are allowed
111 | * - any: allows anything to pass
112 | */
113 | "tags": {
114 | "tr": {
115 | "add_class": {
116 | "align": "align_text"
117 | }
118 | },
119 | "strike": {
120 | "remove": 1
121 | },
122 | "form": {
123 | "rename_tag": "div"
124 | },
125 | "rt": {
126 | "rename_tag": "span"
127 | },
128 | "code": {},
129 | "acronym": {
130 | "rename_tag": "span"
131 | },
132 | "br": {
133 | "add_class": {
134 | "clear": "clear_br"
135 | }
136 | },
137 | "details": {
138 | "rename_tag": "div"
139 | },
140 | "h4": {
141 | "add_class": {
142 | "align": "align_text"
143 | }
144 | },
145 | "em": {},
146 | "title": {
147 | "remove": 1
148 | },
149 | "multicol": {
150 | "rename_tag": "div"
151 | },
152 | "figure": {
153 | "rename_tag": "div"
154 | },
155 | "xmp": {
156 | "rename_tag": "span"
157 | },
158 | "small": {
159 | "rename_tag": "span",
160 | "set_class": "wysiwyg-font-size-smaller"
161 | },
162 | "area": {
163 | "remove": 1
164 | },
165 | "time": {
166 | "rename_tag": "span"
167 | },
168 | "dir": {
169 | "rename_tag": "ul"
170 | },
171 | "bdi": {
172 | "rename_tag": "span"
173 | },
174 | "command": {
175 | "remove": 1
176 | },
177 | "ul": {},
178 | "progress": {
179 | "rename_tag": "span"
180 | },
181 | "dfn": {
182 | "rename_tag": "span"
183 | },
184 | "iframe": {
185 | "remove": 1
186 | },
187 | "figcaption": {
188 | "rename_tag": "div"
189 | },
190 | "a": {
191 | "check_attributes": {
192 | "target": "any",
193 | "href": "url" // if you compiled master manually then change this from 'url' to 'href'
194 | },
195 | "set_attributes": {
196 | "rel": "nofollow"
197 | }
198 | },
199 | "img": {
200 | "check_attributes": {
201 | "width": "dimension",
202 | "alt": "alt",
203 | "src": "url", // if you compiled master manually then change this from 'url' to 'src'
204 | "height": "dimension"
205 | },
206 | "add_class": {
207 | "align": "align_img"
208 | }
209 | },
210 | "rb": {
211 | "rename_tag": "span"
212 | },
213 | "footer": {
214 | "rename_tag": "div"
215 | },
216 | "noframes": {
217 | "remove": 1
218 | },
219 | "abbr": {
220 | "rename_tag": "span"
221 | },
222 | "u": {},
223 | "bgsound": {
224 | "remove": 1
225 | },
226 | "address": {
227 | "rename_tag": "div"
228 | },
229 | "basefont": {
230 | "remove": 1
231 | },
232 | "nav": {
233 | "rename_tag": "div"
234 | },
235 | "h1": {
236 | "add_class": {
237 | "align": "align_text"
238 | }
239 | },
240 | "head": {
241 | "remove": 1
242 | },
243 | "tbody": {
244 | "add_class": {
245 | "align": "align_text"
246 | }
247 | },
248 | "dd": {
249 | "rename_tag": "div"
250 | },
251 | "s": {
252 | "rename_tag": "span"
253 | },
254 | "li": {},
255 | "td": {
256 | "check_attributes": {
257 | "rowspan": "numbers",
258 | "colspan": "numbers"
259 | },
260 | "add_class": {
261 | "align": "align_text"
262 | }
263 | },
264 | "object": {
265 | "remove": 1
266 | },
267 | "div": {
268 | "add_class": {
269 | "align": "align_text"
270 | }
271 | },
272 | "option": {
273 | "rename_tag": "span"
274 | },
275 | "select": {
276 | "rename_tag": "span"
277 | },
278 | "i": {},
279 | "track": {
280 | "remove": 1
281 | },
282 | "wbr": {
283 | "remove": 1
284 | },
285 | "fieldset": {
286 | "rename_tag": "div"
287 | },
288 | "big": {
289 | "rename_tag": "span",
290 | "set_class": "wysiwyg-font-size-larger"
291 | },
292 | "button": {
293 | "rename_tag": "span"
294 | },
295 | "noscript": {
296 | "remove": 1
297 | },
298 | "svg": {
299 | "remove": 1
300 | },
301 | "input": {
302 | "remove": 1
303 | },
304 | "table": {},
305 | "keygen": {
306 | "remove": 1
307 | },
308 | "h5": {
309 | "add_class": {
310 | "align": "align_text"
311 | }
312 | },
313 | "meta": {
314 | "remove": 1
315 | },
316 | "map": {
317 | "rename_tag": "div"
318 | },
319 | "isindex": {
320 | "remove": 1
321 | },
322 | "mark": {
323 | "rename_tag": "span"
324 | },
325 | "caption": {
326 | "add_class": {
327 | "align": "align_text"
328 | }
329 | },
330 | "tfoot": {
331 | "add_class": {
332 | "align": "align_text"
333 | }
334 | },
335 | "base": {
336 | "remove": 1
337 | },
338 | "video": {
339 | "remove": 1
340 | },
341 | "strong": {},
342 | "canvas": {
343 | "remove": 1
344 | },
345 | "output": {
346 | "rename_tag": "span"
347 | },
348 | "marquee": {
349 | "rename_tag": "span"
350 | },
351 | "b": {},
352 | "q": {
353 | "check_attributes": {
354 | "cite": "url"
355 | }
356 | },
357 | "applet": {
358 | "remove": 1
359 | },
360 | "span": {},
361 | "rp": {
362 | "rename_tag": "span"
363 | },
364 | "spacer": {
365 | "remove": 1
366 | },
367 | "source": {
368 | "remove": 1
369 | },
370 | "aside": {
371 | "rename_tag": "div"
372 | },
373 | "frame": {
374 | "remove": 1
375 | },
376 | "section": {
377 | "rename_tag": "div"
378 | },
379 | "body": {
380 | "rename_tag": "div"
381 | },
382 | "ol": {},
383 | "nobr": {
384 | "rename_tag": "span"
385 | },
386 | "html": {
387 | "rename_tag": "div"
388 | },
389 | "summary": {
390 | "rename_tag": "span"
391 | },
392 | "var": {
393 | "rename_tag": "span"
394 | },
395 | "del": {
396 | "remove": 1
397 | },
398 | "blockquote": {
399 | "check_attributes": {
400 | "cite": "url"
401 | }
402 | },
403 | "style": {
404 | "remove": 1
405 | },
406 | "device": {
407 | "remove": 1
408 | },
409 | "meter": {
410 | "rename_tag": "span"
411 | },
412 | "h3": {
413 | "add_class": {
414 | "align": "align_text"
415 | }
416 | },
417 | "textarea": {
418 | "rename_tag": "span"
419 | },
420 | "embed": {
421 | "remove": 1
422 | },
423 | "hgroup": {
424 | "rename_tag": "div"
425 | },
426 | "font": {
427 | "rename_tag": "span",
428 | "add_class": {
429 | "size": "size_font"
430 | }
431 | },
432 | "tt": {
433 | "rename_tag": "span"
434 | },
435 | "noembed": {
436 | "remove": 1
437 | },
438 | "thead": {
439 | "add_class": {
440 | "align": "align_text"
441 | }
442 | },
443 | "blink": {
444 | "rename_tag": "span"
445 | },
446 | "plaintext": {
447 | "rename_tag": "span"
448 | },
449 | "xml": {
450 | "remove": 1
451 | },
452 | "h6": {
453 | "add_class": {
454 | "align": "align_text"
455 | }
456 | },
457 | "param": {
458 | "remove": 1
459 | },
460 | "th": {
461 | "check_attributes": {
462 | "rowspan": "numbers",
463 | "colspan": "numbers"
464 | },
465 | "add_class": {
466 | "align": "align_text"
467 | }
468 | },
469 | "legend": {
470 | "rename_tag": "span"
471 | },
472 | "hr": {},
473 | "label": {
474 | "rename_tag": "span"
475 | },
476 | "dl": {
477 | "rename_tag": "div"
478 | },
479 | "kbd": {
480 | "rename_tag": "span"
481 | },
482 | "listing": {
483 | "rename_tag": "div"
484 | },
485 | "dt": {
486 | "rename_tag": "span"
487 | },
488 | "nextid": {
489 | "remove": 1
490 | },
491 | "pre": {},
492 | "center": {
493 | "rename_tag": "div",
494 | "set_class": "wysiwyg-text-align-center"
495 | },
496 | "audio": {
497 | "remove": 1
498 | },
499 | "datalist": {
500 | "rename_tag": "span"
501 | },
502 | "samp": {
503 | "rename_tag": "span"
504 | },
505 | "col": {
506 | "remove": 1
507 | },
508 | "article": {
509 | "rename_tag": "div"
510 | },
511 | "cite": {},
512 | "link": {
513 | "remove": 1
514 | },
515 | "script": {
516 | "remove": 1
517 | },
518 | "bdo": {
519 | "rename_tag": "span"
520 | },
521 | "menu": {
522 | "rename_tag": "ul"
523 | },
524 | "colgroup": {
525 | "remove": 1
526 | },
527 | "ruby": {
528 | "rename_tag": "span"
529 | },
530 | "h2": {
531 | "add_class": {
532 | "align": "align_text"
533 | }
534 | },
535 | "ins": {
536 | "rename_tag": "span"
537 | },
538 | "p": {
539 | "add_class": {
540 | "align": "align_text"
541 | }
542 | },
543 | "sub": {},
544 | "comment": {
545 | "remove": 1
546 | },
547 | "frameset": {
548 | "remove": 1
549 | },
550 | "optgroup": {
551 | "rename_tag": "span"
552 | },
553 | "header": {
554 | "rename_tag": "div"
555 | },
556 | "sup": {}
557 | }
558 | };
559 |
--------------------------------------------------------------------------------
/vendor/assets/javascripts/wysihtml/parser_rules/advanced_and_extended.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Full HTML5 compatibility rule set
3 | * Loosened and extended ruleset. Allows more freedom on user side
4 | * These rules define which tags and CSS classes are supported and which tags should be specially treated.
5 | */
6 |
7 | var wysihtmlParserRulesDefaults = {
8 | "blockLevelEl": {
9 | "keep_styles": {
10 | "textAlign": /^((left)|(right)|(center)|(justify))$/i,
11 | "float": 1
12 | },
13 | "add_style": {
14 | "align": "align_text"
15 | },
16 | "check_attributes": {
17 | "id": "any"
18 | }
19 | },
20 |
21 | "makeDiv": {
22 | "rename_tag": "div",
23 | "one_of_type": {
24 | "alignment_object": 1
25 | },
26 | "remove_action": "unwrap",
27 | "keep_styles": {
28 | "textAlign": 1,
29 | "float": 1
30 | },
31 | "add_style": {
32 | "align": "align_text"
33 | },
34 | "check_attributes": {
35 | "id": "any"
36 | }
37 | }
38 | };
39 |
40 | var wysihtmlParserRules = {
41 | /**
42 | * CSS Class white-list
43 | * Following CSS classes won't be removed when parsed by the wysihtml HTML parser
44 | * If all classes should pass "any" as classes value. Ex: "classes": "any"
45 | */
46 | "classes": "any",
47 |
48 | /* blacklist of classes is only available if classes is set to any */
49 | "classes_blacklist": {
50 | "Apple-interchange-newline": 1,
51 | "MsoNormal": 1,
52 | "MsoPlainText": 1
53 | },
54 |
55 | "type_definitions": {
56 |
57 | "alignment_object": {
58 | "classes": {
59 | "wysiwyg-text-align-center": 1,
60 | "wysiwyg-text-align-justify": 1,
61 | "wysiwyg-text-align-left": 1,
62 | "wysiwyg-text-align-right": 1,
63 | "wysiwyg-float-left": 1,
64 | "wysiwyg-float-right": 1
65 | },
66 | "styles": {
67 | "float": ["left", "right"],
68 | "text-align": ["left", "right", "center"]
69 | }
70 | },
71 |
72 | "valid_image_src": {
73 | "attrs": {
74 | "src": /^[^data\:]/i
75 | }
76 | },
77 |
78 | "text_color_object": {
79 | "styles": {
80 | "color": true,
81 | "background-color": true
82 | }
83 | },
84 |
85 | "text_fontsize_object": {
86 | "styles": {
87 | "font-size": true
88 | }
89 | },
90 |
91 | "text_formatting_object": {
92 | "classes": {
93 | "wysiwyg-color-aqua": 1,
94 | "wysiwyg-color-black": 1,
95 | "wysiwyg-color-blue": 1,
96 | "wysiwyg-color-fuchsia": 1,
97 | "wysiwyg-color-gray": 1,
98 | "wysiwyg-color-green": 1,
99 | "wysiwyg-color-lime": 1,
100 | "wysiwyg-color-maroon": 1,
101 | "wysiwyg-color-navy": 1,
102 | "wysiwyg-color-olive": 1,
103 | "wysiwyg-color-purple": 1,
104 | "wysiwyg-color-red": 1,
105 | "wysiwyg-color-silver": 1,
106 | "wysiwyg-color-teal": 1,
107 | "wysiwyg-color-white": 1,
108 | "wysiwyg-color-yellow": 1,
109 | "wysiwyg-font-size-large": 1,
110 | "wysiwyg-font-size-larger": 1,
111 | "wysiwyg-font-size-medium": 1,
112 | "wysiwyg-font-size-small": 1,
113 | "wysiwyg-font-size-smaller": 1,
114 | "wysiwyg-font-size-x-large": 1,
115 | "wysiwyg-font-size-x-small": 1,
116 | "wysiwyg-font-size-xx-large": 1,
117 | "wysiwyg-font-size-xx-small": 1
118 | }
119 | }
120 | },
121 |
122 | "comments": 1, // if set allows comments to pass
123 |
124 | /**
125 | * Tag list
126 | *
127 | * The following options are available:
128 | *
129 | * - add_class: converts and deletes the given HTML4 attribute (align, clear, ...) via the given method to a css class
130 | * The following methods are implemented in wysihtml.dom.parse:
131 | * - align_text: converts align attribute values (right/left/center/justify) to their corresponding css class "wysiwyg-text-align-*")
132 | * foo
... becomes ... class="wysiwyg-text-align-center">foo
133 | * - clear_br: converts clear attribute values left/right/all/both to their corresponding css class "wysiwyg-clear-*"
134 | * ... becomes ...
135 | * - align_img: converts align attribute values (right/left) on to their corresponding css class "wysiwyg-float-*"
136 | *
137 | * - remove: removes the element and its content
138 | *
139 | * - unwrap removes element but leaves content
140 | *
141 | * - rename_tag: renames the element to the given tag
142 | *
143 | * - set_class: adds the given class to the element (note: make sure that the class is in the "classes" white list above)
144 | *
145 | * - set_attributes: sets/overrides the given attributes
146 | *
147 | * - check_attributes: checks the given HTML attribute via the given method
148 | * - url: allows only valid urls (starting with http:// or https://)
149 | * - src: allows something like "/foobar.jpg", "http://google.com", ...
150 | * - href: allows something like "mailto:bert@foo.com", "http://google.com", "/foobar.jpg"
151 | * - alt: strips unwanted characters. if the attribute is not set, then it gets set (to ensure valid and compatible HTML)
152 | * - numbers: ensures that the attribute only contains numeric (integer) characters (no float values or units)
153 | * - dimension: for with/height attributes where floating point numbrs and percentages are allowed
154 | * - any: allows anything to pass
155 | */
156 | "tags": {
157 | "tr": {
158 | "add_style": {
159 | "align": "align_text"
160 | },
161 | "check_attributes": {
162 | "id": "any"
163 | }
164 | },
165 | "strike": {
166 | "unwrap": 1
167 | },
168 | "form": {
169 | "unwrap": 1
170 | },
171 | "rt": {
172 | "rename_tag": "span"
173 | },
174 | "code": {},
175 | "acronym": {
176 | "rename_tag": "span"
177 | },
178 | "br": {
179 | "add_class": {
180 | "clear": "clear_br"
181 | }
182 | },
183 | "details": {
184 | "unwrap": 1
185 | },
186 | "h4": wysihtmlParserRulesDefaults.blockLevelEl,
187 | "em": {},
188 | "title": {
189 | "remove": 1
190 | },
191 | "multicol": {
192 | "unwrap": 1
193 | },
194 | "figure": {
195 | "unwrap": 1
196 | },
197 | "xmp": {
198 | "unwrap": 1
199 | },
200 | "small": {
201 | "rename_tag": "span",
202 | "set_class": "wysiwyg-font-size-smaller"
203 | },
204 | "area": {
205 | "remove": 1
206 | },
207 | "time": {
208 | "unwrap": 1
209 | },
210 | "dir": {
211 | "rename_tag": "ul"
212 | },
213 | "bdi": {
214 | "unwrap": 1
215 | },
216 | "command": {
217 | "unwrap": 1
218 | },
219 | "ul": {
220 | "check_attributes": {
221 | "id": "any"
222 | }
223 | },
224 | "progress": {
225 | "rename_tag": "span"
226 | },
227 | "dfn": {
228 | "unwrap": 1
229 | },
230 | "iframe": {
231 | "check_attributes": {
232 | "src": "any",
233 | "width": "any",
234 | "height": "any",
235 | "frameborder": "any",
236 | "style": "any",
237 | "id": "any"
238 | }
239 | },
240 | "figcaption": {
241 | "unwrap": 1
242 | },
243 | "a": {
244 | "check_attributes": {
245 | "href": "href", // if you compiled master manually then change this from 'url' to 'href'
246 | "rel": "any",
247 | "target": "any",
248 | "id": "any"
249 | }
250 | },
251 | "img": {
252 | "one_of_type": {
253 | "valid_image_src": 1
254 | },
255 | "check_attributes": {
256 | "width": "dimension",
257 | "alt": "alt",
258 | "src": "src", // if you compiled master manually then change this from 'url' to 'src'
259 | "height": "dimension",
260 | "id": "any"
261 | },
262 | "add_class": {
263 | "align": "align_img"
264 | }
265 | },
266 | "rb": {
267 | "unwrap": 1
268 | },
269 | "footer": wysihtmlParserRulesDefaults.makeDiv,
270 | "noframes": {
271 | "remove": 1
272 | },
273 | "abbr": {
274 | "unwrap": 1
275 | },
276 | "u": {},
277 | "bgsound": {
278 | "remove": 1
279 | },
280 | "sup": {},
281 | "address": {
282 | "unwrap": 1
283 | },
284 | "basefont": {
285 | "remove": 1
286 | },
287 | "nav": {
288 | "unwrap": 1
289 | },
290 | "h1": wysihtmlParserRulesDefaults.blockLevelEl,
291 | "head": {
292 | "unwrap": 1
293 | },
294 | "tbody": wysihtmlParserRulesDefaults.blockLevelEl,
295 | "dd": {
296 | "unwrap": 1
297 | },
298 | "s": {
299 | "unwrap": 1
300 | },
301 | "li": {},
302 | "td": {
303 | "check_attributes": {
304 | "rowspan": "numbers",
305 | "colspan": "numbers",
306 | "valign": "any",
307 | "align": "any",
308 | "id": "any",
309 | "class": "any"
310 | },
311 | "keep_styles": {
312 | "backgroundColor": 1,
313 | "width": 1,
314 | "height": 1
315 | },
316 | "add_style": {
317 | "align": "align_text"
318 | }
319 | },
320 | "object": {
321 | "remove": 1
322 | },
323 |
324 | "div": {
325 | "one_of_type": {
326 | "alignment_object": 1
327 | },
328 | "remove_action": "unwrap",
329 | "keep_styles": {
330 | "textAlign": 1,
331 | "float": 1
332 | },
333 | "add_style": {
334 | "align": "align_text"
335 | },
336 | "check_attributes": {
337 | "id": "any",
338 | "contenteditable": "any"
339 | }
340 | },
341 |
342 | "option": {
343 | "remove":1
344 | },
345 | "select": {
346 | "remove":1
347 | },
348 | "i": {},
349 | "track": {
350 | "remove": 1
351 | },
352 | "wbr": {
353 | "remove": 1
354 | },
355 | "fieldset": {
356 | "unwrap": 1
357 | },
358 | "big": {
359 | "rename_tag": "span",
360 | "set_class": "wysiwyg-font-size-larger"
361 | },
362 | "button": {
363 | "unwrap": 1
364 | },
365 | "noscript": {
366 | "remove": 1
367 | },
368 | "svg": {
369 | "remove": 1
370 | },
371 | "input": {
372 | "remove": 1
373 | },
374 | "table": {
375 | "keep_styles": {
376 | "width": 1,
377 | "textAlign": 1,
378 | "float": 1
379 | },
380 | "check_attributes": {
381 | "id": "any"
382 | }
383 | },
384 | "keygen": {
385 | "remove": 1
386 | },
387 | "h5": wysihtmlParserRulesDefaults.blockLevelEl,
388 | "meta": {
389 | "remove": 1
390 | },
391 | "map": {
392 | "remove": 1
393 | },
394 | "isindex": {
395 | "remove": 1
396 | },
397 | "mark": {
398 | "unwrap": 1
399 | },
400 | "caption": wysihtmlParserRulesDefaults.blockLevelEl,
401 | "tfoot": wysihtmlParserRulesDefaults.blockLevelEl,
402 | "base": {
403 | "remove": 1
404 | },
405 | "video": {
406 | "remove": 1
407 | },
408 | "strong": {},
409 | "canvas": {
410 | "remove": 1
411 | },
412 | "output": {
413 | "unwrap": 1
414 | },
415 | "marquee": {
416 | "unwrap": 1
417 | },
418 | "b": {},
419 | "q": {
420 | "check_attributes": {
421 | "cite": "url",
422 | "id": "any"
423 | }
424 | },
425 | "applet": {
426 | "remove": 1
427 | },
428 | "span": {
429 | "one_of_type": {
430 | "text_formatting_object": 1,
431 | "text_color_object": 1,
432 | "text_fontsize_object": 1
433 | },
434 | "keep_styles": {
435 | "color": 1,
436 | "backgroundColor": 1,
437 | "fontSize": 1
438 | },
439 | "remove_action": "unwrap",
440 | "check_attributes": {
441 | "id": "any"
442 | }
443 | },
444 | "rp": {
445 | "unwrap": 1
446 | },
447 | "spacer": {
448 | "remove": 1
449 | },
450 | "source": {
451 | "remove": 1
452 | },
453 | "aside": wysihtmlParserRulesDefaults.makeDiv,
454 | "frame": {
455 | "remove": 1
456 | },
457 | "section": wysihtmlParserRulesDefaults.makeDiv,
458 | "body": {
459 | "unwrap": 1
460 | },
461 | "ol": {},
462 | "nobr": {
463 | "unwrap": 1
464 | },
465 | "html": {
466 | "unwrap": 1
467 | },
468 | "summary": {
469 | "unwrap": 1
470 | },
471 | "var": {
472 | "unwrap": 1
473 | },
474 | "del": {
475 | "unwrap": 1
476 | },
477 | "blockquote": {
478 | "keep_styles": {
479 | "textAlign": 1,
480 | "float": 1
481 | },
482 | "add_style": {
483 | "align": "align_text"
484 | },
485 | "check_attributes": {
486 | "cite": "url",
487 | "id": "any"
488 | }
489 | },
490 | "style": {
491 | "check_attributes": {
492 | "type": "any",
493 | "src": "any",
494 | "charset": "any"
495 | }
496 | },
497 | "device": {
498 | "remove": 1
499 | },
500 | "meter": {
501 | "unwrap": 1
502 | },
503 | "h3": wysihtmlParserRulesDefaults.blockLevelEl,
504 | "textarea": {
505 | "unwrap": 1
506 | },
507 | "embed": {
508 | "remove": 1
509 | },
510 | "hgroup": {
511 | "unwrap": 1
512 | },
513 | "font": {
514 | "rename_tag": "span",
515 | "add_class": {
516 | "size": "size_font"
517 | }
518 | },
519 | "tt": {
520 | "unwrap": 1
521 | },
522 | "noembed": {
523 | "remove": 1
524 | },
525 | "thead": {
526 | "add_style": {
527 | "align": "align_text"
528 | },
529 | "check_attributes": {
530 | "id": "any"
531 | }
532 | },
533 | "blink": {
534 | "unwrap": 1
535 | },
536 | "plaintext": {
537 | "unwrap": 1
538 | },
539 | "xml": {
540 | "remove": 1
541 | },
542 | "h6": wysihtmlParserRulesDefaults.blockLevelEl,
543 | "param": {
544 | "remove": 1
545 | },
546 | "th": {
547 | "check_attributes": {
548 | "rowspan": "numbers",
549 | "colspan": "numbers",
550 | "valign": "any",
551 | "align": "any",
552 | "id": "any"
553 | },
554 | "keep_styles": {
555 | "backgroundColor": 1,
556 | "width": 1,
557 | "height": 1
558 | },
559 | "add_style": {
560 | "align": "align_text"
561 | }
562 | },
563 | "legend": {
564 | "unwrap": 1
565 | },
566 | "hr": {},
567 | "label": {
568 | "unwrap": 1
569 | },
570 | "dl": {
571 | "unwrap": 1
572 | },
573 | "kbd": {
574 | "unwrap": 1
575 | },
576 | "listing": {
577 | "unwrap": 1
578 | },
579 | "dt": {
580 | "unwrap": 1
581 | },
582 | "nextid": {
583 | "remove": 1
584 | },
585 | "pre": {},
586 | "center": wysihtmlParserRulesDefaults.makeDiv,
587 | "audio": {
588 | "remove": 1
589 | },
590 | "datalist": {
591 | "unwrap": 1
592 | },
593 | "samp": {
594 | "unwrap": 1
595 | },
596 | "col": {
597 | "remove": 1
598 | },
599 | "article": wysihtmlParserRulesDefaults.makeDiv,
600 | "cite": {},
601 | "link": {
602 | "remove": 1
603 | },
604 | "script": {
605 | "check_attributes": {
606 | "type": "any",
607 | "src": "any",
608 | "charset": "any"
609 | }
610 | },
611 | "bdo": {
612 | "unwrap": 1
613 | },
614 | "menu": {
615 | "rename_tag": "ul"
616 | },
617 | "colgroup": {
618 | "remove": 1
619 | },
620 | "ruby": {
621 | "unwrap": 1
622 | },
623 | "h2": wysihtmlParserRulesDefaults.blockLevelEl,
624 | "ins": {
625 | "unwrap": 1
626 | },
627 | "p": wysihtmlParserRulesDefaults.blockLevelEl,
628 | "sub": {},
629 | "comment": {
630 | "remove": 1
631 | },
632 | "frameset": {
633 | "remove": 1
634 | },
635 | "optgroup": {
636 | "unwrap": 1
637 | },
638 | "header": wysihtmlParserRulesDefaults.makeDiv
639 | }
640 | };
641 |
642 |
643 | (function() {
644 | // Paste cleanup rules universal for all rules (also applied to content copied from editor)
645 | var commonRules = wysihtml.lang.object(wysihtmlParserRules).clone(true);
646 | commonRules.comments = false;
647 | commonRules.selectors = { "a u": "unwrap"};
648 | commonRules.tags.style = { "remove": 1 };
649 | commonRules.tags.script = { "remove": 1 };
650 | commonRules.tags.head = { "remove": 1 };
651 |
652 | // Paste cleanup for unindentified source
653 | var universalRules = wysihtml.lang.object(commonRules).clone(true);
654 | universalRules.tags.div.one_of_type.alignment_object = 1;
655 | universalRules.tags.div.remove_action = "unwrap";
656 | universalRules.tags.div.check_attributes.style = false;
657 | universalRules.tags.div.keep_styles = {
658 | "textAlign": /^((left)|(right)|(center)|(justify))$/i,
659 | "float": 1
660 | };
661 | universalRules.tags.span.keep_styles = false;
662 |
663 | // Paste cleanup for MS Office
664 | // TODO: should be extended to stricter ruleset, as current set will probably not cover all Office bizarreness
665 | var msOfficeRules = wysihtml.lang.object(universalRules).clone(true);
666 | msOfficeRules.classes = {};
667 |
668 | window.wysihtmlParserPasteRulesets = [
669 | {
670 | condition: //i,
674 | set: commonRules
675 | },{
676 | set: universalRules
677 | }
678 | ];
679 |
680 | })();
681 |
--------------------------------------------------------------------------------
/vendor/assets/javascripts/wysihtml/parser_rules/advanced_unwrap.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Full HTML5 compatibility rule set
3 | * These rules define which tags and CSS classes are supported and which tags should be specially treated.
4 | *
5 | * Examples based on this rule set:
6 | *
7 | * foo
8 | * ... becomes ...
9 | * foo
10 | *
11 | *
12 | * ... becomes ...
13 | *
14 | *
15 | * foo
16 | * ... becomes ...
17 | * foo
18 | *
19 | * foo
20 | * ... becomes ...
21 | * foo
22 | *
23 | * foo bar
24 | * ... becomes ...
25 | * foo bar
26 | *
27 | * hello
28 | * ... becomes ...
29 | * hello
30 | *
31 | * hello
32 | * ... becomes ...
33 | * hello
34 | */
35 | var wysihtmlParserRules = {
36 | /**
37 | * CSS Class white-list
38 | * Following CSS classes won't be removed when parsed by the wysihtml HTML parser
39 | * If all classes should pass "any" as classes value. Ex: "classes": "any"
40 | */
41 | "classes": {
42 | "wysiwyg-clear-both": 1,
43 | "wysiwyg-clear-left": 1,
44 | "wysiwyg-clear-right": 1,
45 | "wysiwyg-color-aqua": 1,
46 | "wysiwyg-color-black": 1,
47 | "wysiwyg-color-blue": 1,
48 | "wysiwyg-color-fuchsia": 1,
49 | "wysiwyg-color-gray": 1,
50 | "wysiwyg-color-green": 1,
51 | "wysiwyg-color-lime": 1,
52 | "wysiwyg-color-maroon": 1,
53 | "wysiwyg-color-navy": 1,
54 | "wysiwyg-color-olive": 1,
55 | "wysiwyg-color-purple": 1,
56 | "wysiwyg-color-red": 1,
57 | "wysiwyg-color-silver": 1,
58 | "wysiwyg-color-teal": 1,
59 | "wysiwyg-color-white": 1,
60 | "wysiwyg-color-yellow": 1,
61 | "wysiwyg-float-left": 1,
62 | "wysiwyg-float-right": 1,
63 | "wysiwyg-font-size-large": 1,
64 | "wysiwyg-font-size-larger": 1,
65 | "wysiwyg-font-size-medium": 1,
66 | "wysiwyg-font-size-small": 1,
67 | "wysiwyg-font-size-smaller": 1,
68 | "wysiwyg-font-size-x-large": 1,
69 | "wysiwyg-font-size-x-small": 1,
70 | "wysiwyg-font-size-xx-large": 1,
71 | "wysiwyg-font-size-xx-small": 1,
72 | "wysiwyg-text-align-center": 1,
73 | "wysiwyg-text-align-justify": 1,
74 | "wysiwyg-text-align-left": 1,
75 | "wysiwyg-text-align-right": 1
76 | },
77 |
78 |
79 | "type_definitions": {
80 |
81 | "visible_content_object": {
82 | "methods": {
83 | "has_visible_contet": 1
84 | }
85 | },
86 |
87 | "alignment_object": {
88 | "classes": {
89 | "wysiwyg-text-align-center": 1,
90 | "wysiwyg-text-align-justify": 1,
91 | "wysiwyg-text-align-left": 1,
92 | "wysiwyg-text-align-right": 1,
93 | "wysiwyg-float-left": 1,
94 | "wysiwyg-float-right": 1
95 | },
96 | "styles": {
97 | "float": ["left", "right"],
98 | "text-align": ["left", "right", "center"]
99 | }
100 | },
101 |
102 | "valid_image_src": {
103 | "attrs": {
104 | "src": /^[^data\:]/i
105 | }
106 | },
107 |
108 | "text_color_object": {
109 | "styles": {
110 | "color": true,
111 | "background-color": true
112 | }
113 | },
114 |
115 | "text_fontsize_object": {
116 | "styles": {
117 | "font-size": true
118 | }
119 | },
120 |
121 | "text_formatting_object": {
122 | "classes": {
123 | "wysiwyg-color-aqua": 1,
124 | "wysiwyg-color-black": 1,
125 | "wysiwyg-color-blue": 1,
126 | "wysiwyg-color-fuchsia": 1,
127 | "wysiwyg-color-gray": 1,
128 | "wysiwyg-color-green": 1,
129 | "wysiwyg-color-lime": 1,
130 | "wysiwyg-color-maroon": 1,
131 | "wysiwyg-color-navy": 1,
132 | "wysiwyg-color-olive": 1,
133 | "wysiwyg-color-purple": 1,
134 | "wysiwyg-color-red": 1,
135 | "wysiwyg-color-silver": 1,
136 | "wysiwyg-color-teal": 1,
137 | "wysiwyg-color-white": 1,
138 | "wysiwyg-color-yellow": 1,
139 | "wysiwyg-font-size-large": 1,
140 | "wysiwyg-font-size-larger": 1,
141 | "wysiwyg-font-size-medium": 1,
142 | "wysiwyg-font-size-small": 1,
143 | "wysiwyg-font-size-smaller": 1,
144 | "wysiwyg-font-size-x-large": 1,
145 | "wysiwyg-font-size-x-small": 1,
146 | "wysiwyg-font-size-xx-large": 1,
147 | "wysiwyg-font-size-xx-small": 1
148 | }
149 | }
150 | },
151 |
152 | "comments": 1, // if set allows comments to pass
153 |
154 | /**
155 | * Tag list
156 | *
157 | * The following options are available:
158 | *
159 | * - add_class: converts and deletes the given HTML4 attribute (align, clear, ...) via the given method to a css class
160 | * The following methods are implemented in wysihtml.dom.parse:
161 | * - align_text: converts align attribute values (right/left/center/justify) to their corresponding css class "wysiwyg-text-align-*")
162 | * foo
... becomes ... foo
163 | * - clear_br: converts clear attribute values left/right/all/both to their corresponding css class "wysiwyg-clear-*"
164 | * ... becomes ...
165 | * - align_img: converts align attribute values (right/left) on to their corresponding css class "wysiwyg-float-*"
166 | *
167 | * - add_style: converts and deletes the given HTML4 attribute (align) via the given method to a css style
168 | * The following methods are implemented in wysihtml.dom.parse:
169 | * - align_text: converts align attribute values (right/left/center) to their corresponding css style)
170 | * foo
... becomes ... foo
171 | *
172 | * - remove: removes the element and its content
173 | *
174 | * - unwrap removes element but leaves content
175 | *
176 | * - rename_tag: renames the element to the given tag
177 | *
178 | * - set_class: adds the given class to the element (note: make sure that the class is in the "classes" white list above)
179 | *
180 | * - set_attributes: sets/overrides the given attributes
181 | *
182 | * - check_attributes: checks the given HTML attribute via the given method
183 | * - url: allows only valid urls (starting with http:// or https://)
184 | * - src: allows something like "/foobar.jpg", "http://google.com", ...
185 | * - href: allows something like "mailto:bert@foo.com", "http://google.com", "/foobar.jpg"
186 | * - alt: strips unwanted characters. if the attribute is not set, then it gets set (to ensure valid and compatible HTML)
187 | * - numbers: ensures that the attribute only contains numeric (integer) characters (no float values or units)
188 | * - dimension: for with/height attributes where floating point numbrs and percentages are allowed
189 | * - any: allows anything to pass
190 | */
191 | "tags": {
192 | "tr": {
193 | "add_class": {
194 | "align": "align_text"
195 | }
196 | },
197 | "strike": {
198 | "unwrap": 1
199 | },
200 | "form": {
201 | "unwrap": 1
202 | },
203 | "rt": {
204 | "rename_tag": "span"
205 | },
206 | "code": {},
207 | "acronym": {
208 | "rename_tag": "span"
209 | },
210 | "br": {
211 | "add_class": {
212 | "clear": "clear_br"
213 | }
214 | },
215 | "details": {
216 | "unwrap": 1
217 | },
218 | "h4": {
219 | "add_class": {
220 | "align": "align_text"
221 | }
222 | },
223 | "em": {},
224 | "title": {
225 | "remove": 1
226 | },
227 | "multicol": {
228 | "unwrap": 1
229 | },
230 | "figure": {
231 | "unwrap": 1
232 | },
233 | "xmp": {
234 | "unwrap": 1
235 | },
236 | "small": {
237 | "rename_tag": "span",
238 | "set_class": "wysiwyg-font-size-smaller"
239 | },
240 | "area": {
241 | "remove": 1
242 | },
243 | "time": {
244 | "unwrap": 1
245 | },
246 | "dir": {
247 | "rename_tag": "ul"
248 | },
249 | "bdi": {
250 | "unwrap": 1
251 | },
252 | "command": {
253 | "unwrap": 1
254 | },
255 | "ul": {},
256 | "progress": {
257 | "rename_tag": "span"
258 | },
259 | "dfn": {
260 | "unwrap": 1
261 | },
262 | "iframe": {
263 | "remove": 1
264 | },
265 | "figcaption": {
266 | "unwrap": 1
267 | },
268 | "a": {
269 | "check_attributes": {
270 | "href": "href", // if you compiled master manually then change this from 'url' to 'href'
271 | "target": "any"
272 | },
273 | "set_attributes": {
274 | "rel": "nofollow"
275 | }
276 | },
277 | "img": {
278 | "one_of_type": {
279 | "valid_image_src": 1
280 | },
281 | "check_attributes": {
282 | "width": "dimension",
283 | "alt": "alt",
284 | "src": "src", // if you compiled master manually then change this from 'url' to 'src'
285 | "height": "dimension"
286 | },
287 | "add_class": {
288 | "align": "align_img"
289 | }
290 | },
291 | "rb": {
292 | "unwrap": 1
293 | },
294 | "footer": {
295 | "rename_tag": "div"
296 | },
297 | "noframes": {
298 | "remove": 1
299 | },
300 | "abbr": {
301 | "unwrap": 1
302 | },
303 | "u": {},
304 | "bgsound": {
305 | "remove": 1
306 | },
307 | "sup": {},
308 | "address": {
309 | "unwrap": 1
310 | },
311 | "basefont": {
312 | "remove": 1
313 | },
314 | "nav": {
315 | "unwrap": 1
316 | },
317 | "h1": {
318 | "add_class": {
319 | "align": "align_text"
320 | }
321 | },
322 | "head": {
323 | "unwrap": 1
324 | },
325 | "tbody": {
326 | "add_class": {
327 | "align": "align_text"
328 | }
329 | },
330 | "dd": {
331 | "unwrap": 1
332 | },
333 | "s": {
334 | "unwrap": 1
335 | },
336 | "li": {},
337 | "td": {
338 | "check_attributes": {
339 | "rowspan": "numbers",
340 | "colspan": "numbers",
341 | "valign": "any",
342 | "align": "any"
343 | },
344 | "add_class": {
345 | "align": "align_text"
346 | }
347 | },
348 | "object": {
349 | "remove": 1
350 | },
351 |
352 | "div": {
353 | "one_of_type": {
354 | "visible_content_object": 1
355 | },
356 | "remove_action": "unwrap",
357 | "keep_styles": {
358 | "textAlign": 1,
359 | "float": 1
360 | },
361 | "add_class": {
362 | "align": "align_text"
363 | }
364 | },
365 |
366 | "option": {
367 | "remove":1
368 | },
369 | "select": {
370 | "remove":1
371 | },
372 | "i": {},
373 | "track": {
374 | "remove": 1
375 | },
376 | "wbr": {
377 | "remove": 1
378 | },
379 | "fieldset": {
380 | "unwrap": 1
381 | },
382 | "big": {
383 | "rename_tag": "span",
384 | "set_class": "wysiwyg-font-size-larger"
385 | },
386 | "button": {
387 | "unwrap": 1
388 | },
389 | "noscript": {
390 | "remove": 1
391 | },
392 | "svg": {
393 | "remove": 1
394 | },
395 | "input": {
396 | "remove": 1
397 | },
398 | "table": {},
399 | "keygen": {
400 | "remove": 1
401 | },
402 | "h5": {
403 | "add_class": {
404 | "align": "align_text"
405 | }
406 | },
407 | "meta": {
408 | "remove": 1
409 | },
410 | "map": {
411 | "remove": 1
412 | },
413 | "isindex": {
414 | "remove": 1
415 | },
416 | "mark": {
417 | "unwrap": 1
418 | },
419 | "caption": {
420 | "add_class": {
421 | "align": "align_text"
422 | }
423 | },
424 | "tfoot": {
425 | "add_class": {
426 | "align": "align_text"
427 | }
428 | },
429 | "base": {
430 | "remove": 1
431 | },
432 | "video": {
433 | "remove": 1
434 | },
435 | "strong": {},
436 | "canvas": {
437 | "remove": 1
438 | },
439 | "output": {
440 | "unwrap": 1
441 | },
442 | "marquee": {
443 | "unwrap": 1
444 | },
445 | "b": {},
446 | "q": {
447 | "check_attributes": {
448 | "cite": "url"
449 | }
450 | },
451 | "applet": {
452 | "remove": 1
453 | },
454 | "span": {
455 | "one_of_type": {
456 | "text_formatting_object": 1,
457 | "text_color_object": 1,
458 | "text_fontsize_object": 1
459 | },
460 | "keep_styles": {
461 | "color": 1,
462 | "backgroundColor": 1,
463 | "fontSize": 1
464 | },
465 | "remove_action": "unwrap"
466 | },
467 | "rp": {
468 | "unwrap": 1
469 | },
470 | "spacer": {
471 | "remove": 1
472 | },
473 | "source": {
474 | "remove": 1
475 | },
476 | "aside": {
477 | "rename_tag": "div"
478 | },
479 | "frame": {
480 | "remove": 1
481 | },
482 | "section": {
483 | "rename_tag": "div"
484 | },
485 | "body": {
486 | "unwrap": 1
487 | },
488 | "ol": {},
489 | "nobr": {
490 | "unwrap": 1
491 | },
492 | "html": {
493 | "unwrap": 1
494 | },
495 | "summary": {
496 | "unwrap": 1
497 | },
498 | "var": {
499 | "unwrap": 1
500 | },
501 | "del": {
502 | "unwrap": 1
503 | },
504 | "blockquote": {
505 | "check_attributes": {
506 | "cite": "url"
507 | }
508 | },
509 | "style": {
510 | "remove": 1
511 | },
512 | "device": {
513 | "remove": 1
514 | },
515 | "meter": {
516 | "unwrap": 1
517 | },
518 | "h3": {
519 | "add_class": {
520 | "align": "align_text"
521 | }
522 | },
523 | "textarea": {
524 | "unwrap": 1
525 | },
526 | "embed": {
527 | "remove": 1
528 | },
529 | "hgroup": {
530 | "unwrap": 1
531 | },
532 | "font": {
533 | "rename_tag": "span",
534 | "add_class": {
535 | "size": "size_font"
536 | }
537 | },
538 | "tt": {
539 | "unwrap": 1
540 | },
541 | "noembed": {
542 | "remove": 1
543 | },
544 | "thead": {
545 | "add_class": {
546 | "align": "align_text"
547 | }
548 | },
549 | "blink": {
550 | "unwrap": 1
551 | },
552 | "plaintext": {
553 | "unwrap": 1
554 | },
555 | "xml": {
556 | "remove": 1
557 | },
558 | "h6": {
559 | "add_class": {
560 | "align": "align_text"
561 | }
562 | },
563 | "param": {
564 | "remove": 1
565 | },
566 | "th": {
567 | "check_attributes": {
568 | "rowspan": "numbers",
569 | "colspan": "numbers"
570 | },
571 | "add_class": {
572 | "align": "align_text"
573 | }
574 | },
575 | "legend": {
576 | "unwrap": 1
577 | },
578 | "hr": {},
579 | "label": {
580 | "unwrap": 1
581 | },
582 | "dl": {
583 | "unwrap": 1
584 | },
585 | "kbd": {
586 | "unwrap": 1
587 | },
588 | "listing": {
589 | "unwrap": 1
590 | },
591 | "dt": {
592 | "unwrap": 1
593 | },
594 | "nextid": {
595 | "remove": 1
596 | },
597 | "pre": {},
598 | "center": {
599 | "rename_tag": "div",
600 | "set_class": "wysiwyg-text-align-center"
601 | },
602 | "audio": {
603 | "remove": 1
604 | },
605 | "datalist": {
606 | "unwrap": 1
607 | },
608 | "samp": {
609 | "unwrap": 1
610 | },
611 | "col": {
612 | "remove": 1
613 | },
614 | "article": {
615 | "rename_tag": "div"
616 | },
617 | "cite": {},
618 | "link": {
619 | "remove": 1
620 | },
621 | "script": {
622 | "remove": 1
623 | },
624 | "bdo": {
625 | "unwrap": 1
626 | },
627 | "menu": {
628 | "rename_tag": "ul"
629 | },
630 | "colgroup": {
631 | "remove": 1
632 | },
633 | "ruby": {
634 | "unwrap": 1
635 | },
636 | "h2": {
637 | "add_class": {
638 | "align": "align_text"
639 | }
640 | },
641 | "ins": {
642 | "unwrap": 1
643 | },
644 | "p": {
645 | "add_class": {
646 | "align": "align_text"
647 | }
648 | },
649 | "sub": {},
650 | "comment": {
651 | "remove": 1
652 | },
653 | "frameset": {
654 | "remove": 1
655 | },
656 | "optgroup": {
657 | "unwrap": 1
658 | },
659 | "header": {
660 | "rename_tag": "div"
661 | }
662 | }
663 | };
664 |
--------------------------------------------------------------------------------
/vendor/assets/javascripts/wysihtml/parser_rules/simple.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Very simple basic rule set
3 | *
4 | * Allows
5 | * , , , , ,
,
,
,
, , ,
6 | *
7 | * For a proper documentation of the format check advanced.js
8 | */
9 | var wysihtmlParserRules = {
10 | tags: {
11 | strong: {},
12 | b: {},
13 | i: {},
14 | em: {},
15 | br: {},
16 | p: {},
17 | div: {},
18 | span: {},
19 | ul: {},
20 | ol: {},
21 | li: {},
22 | a: {
23 | set_attributes: {
24 | target: "_blank",
25 | rel: "nofollow"
26 | },
27 | check_attributes: {
28 | href: "url" // important to avoid XSS
29 | }
30 | }
31 | }
32 | };
33 |
--------------------------------------------------------------------------------
/vendor/assets/javascripts/wysihtml/table_editing.js:
--------------------------------------------------------------------------------
1 | wysihtml.commands.addTableCells = {
2 | exec: function(composer, command, value) {
3 | if (composer.tableSelection && composer.tableSelection.start && composer.tableSelection.end) {
4 |
5 | // switches start and end if start is bigger than end (reverse selection)
6 | var tableSelect = wysihtml.dom.table.orderSelectionEnds(composer.tableSelection.start, composer.tableSelection.end);
7 | if (value == 'before' || value == 'above') {
8 | wysihtml.dom.table.addCells(tableSelect.start, value);
9 | } else if (value == 'after' || value == 'below') {
10 | wysihtml.dom.table.addCells(tableSelect.end, value);
11 | }
12 | setTimeout(function() {
13 | composer.tableSelection.select(tableSelect.start, tableSelect.end);
14 | },0);
15 | }
16 | },
17 |
18 | state: function(composer, command) {
19 | return false;
20 | }
21 | };
22 |
23 | wysihtml.commands.createTable = {
24 | exec: function(composer, command, value) {
25 | var col, row, html;
26 | if (value && value.cols && value.rows && parseInt(value.cols, 10) > 0 && parseInt(value.rows, 10) > 0) {
27 | if (value.tableStyle) {
28 | html = '';
29 | } else {
30 | html = '';
31 | }
32 | html += '';
33 | for (row = 0; row < value.rows; row ++) {
34 | html += '';
35 | for (col = 0; col < value.cols; col ++) {
36 | html += ' ';
37 | }
38 | html += ' ';
39 | }
40 | html += '
';
41 | composer.commands.exec('insertHTML', html);
42 | }
43 | },
44 |
45 | state: function(composer, command) {
46 | return false;
47 | }
48 | };
49 |
50 | wysihtml.commands.deleteTableCells = {
51 | exec: function(composer, command, value) {
52 | if (composer.tableSelection && composer.tableSelection.start && composer.tableSelection.end) {
53 | var tableSelect = wysihtml.dom.table.orderSelectionEnds(composer.tableSelection.start, composer.tableSelection.end),
54 | idx = wysihtml.dom.table.indexOf(tableSelect.start),
55 | selCell,
56 | table = composer.tableSelection.table;
57 |
58 | wysihtml.dom.table.removeCells(tableSelect.start, value);
59 | setTimeout(function() {
60 | // move selection to next or previous if not present
61 | selCell = wysihtml.dom.table.findCell(table, idx);
62 |
63 | if (!selCell) {
64 | if (value == 'row') {
65 | selCell = wysihtml.dom.table.findCell(table, {
66 | 'row': idx.row - 1,
67 | 'col': idx.col
68 | });
69 | }
70 |
71 | if (value == 'column') {
72 | selCell = wysihtml.dom.table.findCell(table, {
73 | 'row': idx.row,
74 | 'col': idx.col - 1
75 | });
76 | }
77 | }
78 | if (selCell) {
79 | composer.tableSelection.select(selCell, selCell);
80 | }
81 | }, 0);
82 | }
83 | },
84 |
85 | state: function(composer, command) {
86 | return false;
87 | }
88 | };
89 |
90 | wysihtml.commands.mergeTableCells = {
91 | exec: function(composer, command) {
92 | if (composer.tableSelection && composer.tableSelection.start && composer.tableSelection.end) {
93 | if (this.state(composer, command)) {
94 | wysihtml.dom.table.unmergeCell(composer.tableSelection.start);
95 | } else {
96 | wysihtml.dom.table.mergeCellsBetween(composer.tableSelection.start, composer.tableSelection.end);
97 | }
98 | }
99 | },
100 |
101 | state: function(composer, command) {
102 | if (composer.tableSelection) {
103 | var start = composer.tableSelection.start,
104 | end = composer.tableSelection.end;
105 | if (start && end && start == end &&
106 | ((
107 | wysihtml.dom.getAttribute(start, 'colspan') &&
108 | parseInt(wysihtml.dom.getAttribute(start, 'colspan'), 10) > 1
109 | ) || (
110 | wysihtml.dom.getAttribute(start, 'rowspan') &&
111 | parseInt(wysihtml.dom.getAttribute(start, 'rowspan'), 10) > 1
112 | ))
113 | ) {
114 | return [start];
115 | }
116 | }
117 | return false;
118 | }
119 | };
120 |
121 | (function() {
122 |
123 | var api = wysihtml.dom;
124 |
125 | var MapCell = function(cell) {
126 | this.el = cell;
127 | this.isColspan= false;
128 | this.isRowspan= false;
129 | this.firstCol= true;
130 | this.lastCol= true;
131 | this.firstRow= true;
132 | this.lastRow= true;
133 | this.isReal= true;
134 | this.spanCollection= [];
135 | this.modified = false;
136 | };
137 |
138 | var TableModifyerByCell = function (cell, table) {
139 | if (cell) {
140 | this.cell = cell;
141 | this.table = api.getParentElement(cell, { query: "table" });
142 | } else if (table) {
143 | this.table = table;
144 | this.cell = this.table.querySelectorAll('th, td')[0];
145 | }
146 | };
147 |
148 | function queryInList(list, query) {
149 | var ret = [],
150 | q;
151 | for (var e = 0, len = list.length; e < len; e++) {
152 | q = list[e].querySelectorAll(query);
153 | if (q) {
154 | for(var i = q.length; i--; ret.unshift(q[i]));
155 | }
156 | }
157 | return ret;
158 | }
159 |
160 | function removeElement(el) {
161 | el.parentNode.removeChild(el);
162 | }
163 |
164 | function insertAfter(referenceNode, newNode) {
165 | referenceNode.parentNode.insertBefore(newNode, referenceNode.nextSibling);
166 | }
167 |
168 | function nextNode(node, tag) {
169 | var element = node.nextSibling;
170 | while (element.nodeType !=1) {
171 | element = element.nextSibling;
172 | if (!tag || tag == element.tagName.toLowerCase()) {
173 | return element;
174 | }
175 | }
176 | return null;
177 | }
178 |
179 | TableModifyerByCell.prototype = {
180 |
181 | addSpannedCellToMap: function(cell, map, r, c, cspan, rspan) {
182 | var spanCollect = [],
183 | rmax = r + ((rspan) ? parseInt(rspan, 10) - 1 : 0),
184 | cmax = c + ((cspan) ? parseInt(cspan, 10) - 1 : 0);
185 |
186 | for (var rr = r; rr <= rmax; rr++) {
187 | if (typeof map[rr] == "undefined") { map[rr] = []; }
188 | for (var cc = c; cc <= cmax; cc++) {
189 | map[rr][cc] = new MapCell(cell);
190 | map[rr][cc].isColspan = (cspan && parseInt(cspan, 10) > 1);
191 | map[rr][cc].isRowspan = (rspan && parseInt(rspan, 10) > 1);
192 | map[rr][cc].firstCol = cc == c;
193 | map[rr][cc].lastCol = cc == cmax;
194 | map[rr][cc].firstRow = rr == r;
195 | map[rr][cc].lastRow = rr == rmax;
196 | map[rr][cc].isReal = cc == c && rr == r;
197 | map[rr][cc].spanCollection = spanCollect;
198 |
199 | spanCollect.push(map[rr][cc]);
200 | }
201 | }
202 | },
203 |
204 | setCellAsModified: function(cell) {
205 | cell.modified = true;
206 | if (cell.spanCollection.length > 0) {
207 | for (var s = 0, smax = cell.spanCollection.length; s < smax; s++) {
208 | cell.spanCollection[s].modified = true;
209 | }
210 | }
211 | },
212 |
213 | setTableMap: function() {
214 | var map = [];
215 | var tableRows = this.getTableRows(),
216 | ridx, row, cells, cidx, cell,
217 | c,
218 | cspan, rspan;
219 |
220 | for (ridx = 0; ridx < tableRows.length; ridx++) {
221 | row = tableRows[ridx];
222 | cells = this.getRowCells(row);
223 | c = 0;
224 | if (typeof map[ridx] == "undefined") { map[ridx] = []; }
225 | for (cidx = 0; cidx < cells.length; cidx++) {
226 | cell = cells[cidx];
227 |
228 | // If cell allready set means it is set by col or rowspan,
229 | // so increase cols index until free col is found
230 | while (typeof map[ridx][c] != "undefined") { c++; }
231 |
232 | cspan = api.getAttribute(cell, 'colspan');
233 | rspan = api.getAttribute(cell, 'rowspan');
234 |
235 | if (cspan || rspan) {
236 | this.addSpannedCellToMap(cell, map, ridx, c, cspan, rspan);
237 | c = c + ((cspan) ? parseInt(cspan, 10) : 1);
238 | } else {
239 | map[ridx][c] = new MapCell(cell);
240 | c++;
241 | }
242 | }
243 | }
244 | this.map = map;
245 | return map;
246 | },
247 |
248 | getRowCells: function(row) {
249 | var inlineTables = this.table.querySelectorAll('table'),
250 | inlineCells = (inlineTables) ? queryInList(inlineTables, 'th, td') : [],
251 | allCells = row.querySelectorAll('th, td'),
252 | tableCells = (inlineCells.length > 0) ? wysihtml.lang.array(allCells).without(inlineCells) : allCells;
253 |
254 | return tableCells;
255 | },
256 |
257 | getTableRows: function() {
258 | var inlineTables = this.table.querySelectorAll('table'),
259 | inlineRows = (inlineTables) ? queryInList(inlineTables, 'tr') : [],
260 | allRows = this.table.querySelectorAll('tr'),
261 | tableRows = (inlineRows.length > 0) ? wysihtml.lang.array(allRows).without(inlineRows) : allRows;
262 |
263 | return tableRows;
264 | },
265 |
266 | getMapIndex: function(cell) {
267 | var r_length = this.map.length,
268 | c_length = (this.map && this.map[0]) ? this.map[0].length : 0;
269 |
270 | for (var r_idx = 0;r_idx < r_length; r_idx++) {
271 | for (var c_idx = 0;c_idx < c_length; c_idx++) {
272 | if (this.map[r_idx][c_idx].el === cell) {
273 | return {'row': r_idx, 'col': c_idx};
274 | }
275 | }
276 | }
277 | return false;
278 | },
279 |
280 | getElementAtIndex: function(idx) {
281 | this.setTableMap();
282 | if (this.map[idx.row] && this.map[idx.row][idx.col] && this.map[idx.row][idx.col].el) {
283 | return this.map[idx.row][idx.col].el;
284 | }
285 | return null;
286 | },
287 |
288 | getMapElsTo: function(to_cell) {
289 | var els = [];
290 | this.setTableMap();
291 | this.idx_start = this.getMapIndex(this.cell);
292 | this.idx_end = this.getMapIndex(to_cell);
293 |
294 | // switch indexes if start is bigger than end
295 | if (this.idx_start.row > this.idx_end.row || (this.idx_start.row == this.idx_end.row && this.idx_start.col > this.idx_end.col)) {
296 | var temp_idx = this.idx_start;
297 | this.idx_start = this.idx_end;
298 | this.idx_end = temp_idx;
299 | }
300 | if (this.idx_start.col > this.idx_end.col) {
301 | var temp_cidx = this.idx_start.col;
302 | this.idx_start.col = this.idx_end.col;
303 | this.idx_end.col = temp_cidx;
304 | }
305 |
306 | if (this.idx_start != null && this.idx_end != null) {
307 | for (var row = this.idx_start.row, maxr = this.idx_end.row; row <= maxr; row++) {
308 | for (var col = this.idx_start.col, maxc = this.idx_end.col; col <= maxc; col++) {
309 | els.push(this.map[row][col].el);
310 | }
311 | }
312 | }
313 | return els;
314 | },
315 |
316 | orderSelectionEnds: function(secondcell) {
317 | this.setTableMap();
318 | this.idx_start = this.getMapIndex(this.cell);
319 | this.idx_end = this.getMapIndex(secondcell);
320 |
321 | // switch indexes if start is bigger than end
322 | if (this.idx_start.row > this.idx_end.row || (this.idx_start.row == this.idx_end.row && this.idx_start.col > this.idx_end.col)) {
323 | var temp_idx = this.idx_start;
324 | this.idx_start = this.idx_end;
325 | this.idx_end = temp_idx;
326 | }
327 | if (this.idx_start.col > this.idx_end.col) {
328 | var temp_cidx = this.idx_start.col;
329 | this.idx_start.col = this.idx_end.col;
330 | this.idx_end.col = temp_cidx;
331 | }
332 |
333 | return {
334 | "start": this.map[this.idx_start.row][this.idx_start.col].el,
335 | "end": this.map[this.idx_end.row][this.idx_end.col].el
336 | };
337 | },
338 |
339 | createCells: function(tag, nr, attrs) {
340 | var doc = this.table.ownerDocument,
341 | frag = doc.createDocumentFragment(),
342 | cell;
343 | for (var i = 0; i < nr; i++) {
344 | cell = doc.createElement(tag);
345 |
346 | if (attrs) {
347 | for (var attr in attrs) {
348 | if (attrs.hasOwnProperty(attr)) {
349 | cell.setAttribute(attr, attrs[attr]);
350 | }
351 | }
352 | }
353 |
354 | // add non breaking space
355 | cell.appendChild(document.createTextNode("\u00a0"));
356 | frag.appendChild(cell);
357 | }
358 | return frag;
359 | },
360 |
361 | // Returns next real cell (not part of spanned cell unless first) on row if selected index is not real. I no real cells -1 will be returned
362 | correctColIndexForUnreals: function(col, row) {
363 | var r = this.map[row],
364 | corrIdx = -1;
365 | for (var i = 0, max = col; i < col; i++) {
366 | if (r[i].isReal){
367 | corrIdx++;
368 | }
369 | }
370 | return corrIdx;
371 | },
372 |
373 | getLastNewCellOnRow: function(row, rowLimit) {
374 | var cells = this.getRowCells(row),
375 | cell, idx;
376 |
377 | for (var cidx = 0, cmax = cells.length; cidx < cmax; cidx++) {
378 | cell = cells[cidx];
379 | idx = this.getMapIndex(cell);
380 | if (idx === false || (typeof rowLimit != "undefined" && idx.row != rowLimit)) {
381 | return cell;
382 | }
383 | }
384 | return null;
385 | },
386 |
387 | removeEmptyTable: function() {
388 | var cells = this.table.querySelectorAll('td, th');
389 | if (!cells || cells.length == 0) {
390 | removeElement(this.table);
391 | return true;
392 | } else {
393 | return false;
394 | }
395 | },
396 |
397 | // Splits merged cell on row to unique cells
398 | splitRowToCells: function(cell) {
399 | if (cell.isColspan) {
400 | var colspan = parseInt(api.getAttribute(cell.el, 'colspan') || 1, 10),
401 | cType = cell.el.tagName.toLowerCase();
402 | if (colspan > 1) {
403 | var newCells = this.createCells(cType, colspan -1);
404 | insertAfter(cell.el, newCells);
405 | }
406 | cell.el.removeAttribute('colspan');
407 | }
408 | },
409 |
410 | getRealRowEl: function(force, idx) {
411 | var r = null,
412 | c = null;
413 |
414 | idx = idx || this.idx;
415 |
416 | for (var cidx = 0, cmax = this.map[idx.row].length; cidx < cmax; cidx++) {
417 | c = this.map[idx.row][cidx];
418 | if (c.isReal) {
419 | r = api.getParentElement(c.el, { query: "tr" });
420 | if (r) {
421 | return r;
422 | }
423 | }
424 | }
425 |
426 | if (r === null && force) {
427 | r = api.getParentElement(this.map[idx.row][idx.col].el, { query: "tr" }) || null;
428 | }
429 |
430 | return r;
431 | },
432 |
433 | injectRowAt: function(row, col, colspan, cType, c) {
434 | var r = this.getRealRowEl(false, {'row': row, 'col': col}),
435 | new_cells = this.createCells(cType, colspan);
436 |
437 | if (r) {
438 | var n_cidx = this.correctColIndexForUnreals(col, row);
439 | if (n_cidx >= 0) {
440 | insertAfter(this.getRowCells(r)[n_cidx], new_cells);
441 | } else {
442 | r.insertBefore(new_cells, r.firstChild);
443 | }
444 | } else {
445 | var rr = this.table.ownerDocument.createElement('tr');
446 | rr.appendChild(new_cells);
447 | insertAfter(api.getParentElement(c.el, { query: "tr" }), rr);
448 | }
449 | },
450 |
451 | canMerge: function(to) {
452 | this.to = to;
453 | this.setTableMap();
454 | this.idx_start = this.getMapIndex(this.cell);
455 | this.idx_end = this.getMapIndex(this.to);
456 |
457 | // switch indexes if start is bigger than end
458 | if (this.idx_start.row > this.idx_end.row || (this.idx_start.row == this.idx_end.row && this.idx_start.col > this.idx_end.col)) {
459 | var temp_idx = this.idx_start;
460 | this.idx_start = this.idx_end;
461 | this.idx_end = temp_idx;
462 | }
463 | if (this.idx_start.col > this.idx_end.col) {
464 | var temp_cidx = this.idx_start.col;
465 | this.idx_start.col = this.idx_end.col;
466 | this.idx_end.col = temp_cidx;
467 | }
468 |
469 | for (var row = this.idx_start.row, maxr = this.idx_end.row; row <= maxr; row++) {
470 | for (var col = this.idx_start.col, maxc = this.idx_end.col; col <= maxc; col++) {
471 | if (this.map[row][col].isColspan || this.map[row][col].isRowspan) {
472 | return false;
473 | }
474 | }
475 | }
476 | return true;
477 | },
478 |
479 | decreaseCellSpan: function(cell, span) {
480 | var nr = parseInt(api.getAttribute(cell.el, span), 10) - 1;
481 | if (nr >= 1) {
482 | cell.el.setAttribute(span, nr);
483 | } else {
484 | cell.el.removeAttribute(span);
485 | if (span == 'colspan') {
486 | cell.isColspan = false;
487 | }
488 | if (span == 'rowspan') {
489 | cell.isRowspan = false;
490 | }
491 | cell.firstCol = true;
492 | cell.lastCol = true;
493 | cell.firstRow = true;
494 | cell.lastRow = true;
495 | cell.isReal = true;
496 | }
497 | },
498 |
499 | removeSurplusLines: function() {
500 | var row, cell, ridx, rmax, cidx, cmax, allRowspan;
501 |
502 | this.setTableMap();
503 | if (this.map) {
504 | ridx = 0;
505 | rmax = this.map.length;
506 | for (;ridx < rmax; ridx++) {
507 | row = this.map[ridx];
508 | allRowspan = true;
509 | cidx = 0;
510 | cmax = row.length;
511 | for (; cidx < cmax; cidx++) {
512 | cell = row[cidx];
513 | if (!(api.getAttribute(cell.el, "rowspan") && parseInt(api.getAttribute(cell.el, "rowspan"), 10) > 1 && cell.firstRow !== true)) {
514 | allRowspan = false;
515 | break;
516 | }
517 | }
518 | if (allRowspan) {
519 | cidx = 0;
520 | for (; cidx < cmax; cidx++) {
521 | this.decreaseCellSpan(row[cidx], 'rowspan');
522 | }
523 | }
524 | }
525 |
526 | // remove rows without cells
527 | var tableRows = this.getTableRows();
528 | ridx = 0;
529 | rmax = tableRows.length;
530 | for (;ridx < rmax; ridx++) {
531 | row = tableRows[ridx];
532 | if (row.childNodes.length == 0 && (/^\s*$/.test(row.textContent || row.innerText))) {
533 | removeElement(row);
534 | }
535 | }
536 | }
537 | },
538 |
539 | fillMissingCells: function() {
540 | var r_max = 0,
541 | c_max = 0,
542 | prevcell = null;
543 |
544 | this.setTableMap();
545 | if (this.map) {
546 |
547 | // find maximal dimensions of broken table
548 | r_max = this.map.length;
549 | for (var ridx = 0; ridx < r_max; ridx++) {
550 | if (this.map[ridx].length > c_max) { c_max = this.map[ridx].length; }
551 | }
552 |
553 | for (var row = 0; row < r_max; row++) {
554 | for (var col = 0; col < c_max; col++) {
555 | if (this.map[row] && !this.map[row][col]) {
556 | if (col > 0) {
557 | this.map[row][col] = new MapCell(this.createCells('td', 1));
558 | prevcell = this.map[row][col-1];
559 | if (prevcell && prevcell.el && prevcell.el.parent) { // if parent does not exist element is removed from dom
560 | insertAfter(this.map[row][col-1].el, this.map[row][col].el);
561 | }
562 | }
563 | }
564 | }
565 | }
566 | }
567 | },
568 |
569 | rectify: function() {
570 | if (!this.removeEmptyTable()) {
571 | this.removeSurplusLines();
572 | this.fillMissingCells();
573 | return true;
574 | } else {
575 | return false;
576 | }
577 | },
578 |
579 | unmerge: function() {
580 | if (this.rectify()) {
581 | this.setTableMap();
582 | this.idx = this.getMapIndex(this.cell);
583 |
584 | if (this.idx) {
585 | var thisCell = this.map[this.idx.row][this.idx.col],
586 | colspan = (api.getAttribute(thisCell.el, "colspan")) ? parseInt(api.getAttribute(thisCell.el, "colspan"), 10) : 1,
587 | cType = thisCell.el.tagName.toLowerCase();
588 |
589 | if (thisCell.isRowspan) {
590 | var rowspan = parseInt(api.getAttribute(thisCell.el, "rowspan"), 10);
591 | if (rowspan > 1) {
592 | for (var nr = 1, maxr = rowspan - 1; nr <= maxr; nr++){
593 | this.injectRowAt(this.idx.row + nr, this.idx.col, colspan, cType, thisCell);
594 | }
595 | }
596 | thisCell.el.removeAttribute('rowspan');
597 | }
598 | this.splitRowToCells(thisCell);
599 | }
600 | }
601 | },
602 |
603 | // merges cells from start cell (defined in creating obj) to "to" cell
604 | merge: function(to) {
605 | if (this.rectify()) {
606 | if (this.canMerge(to)) {
607 | var rowspan = this.idx_end.row - this.idx_start.row + 1,
608 | colspan = this.idx_end.col - this.idx_start.col + 1;
609 |
610 | for (var row = this.idx_start.row, maxr = this.idx_end.row; row <= maxr; row++) {
611 | for (var col = this.idx_start.col, maxc = this.idx_end.col; col <= maxc; col++) {
612 |
613 | if (row == this.idx_start.row && col == this.idx_start.col) {
614 | if (rowspan > 1) {
615 | this.map[row][col].el.setAttribute('rowspan', rowspan);
616 | }
617 | if (colspan > 1) {
618 | this.map[row][col].el.setAttribute('colspan', colspan);
619 | }
620 | } else {
621 | // transfer content
622 | if (!(/^\s* \s*$/.test(this.map[row][col].el.innerHTML.toLowerCase()))) {
623 | this.map[this.idx_start.row][this.idx_start.col].el.innerHTML += ' ' + this.map[row][col].el.innerHTML;
624 | }
625 | removeElement(this.map[row][col].el);
626 | }
627 |
628 | }
629 | }
630 | this.rectify();
631 | } else {
632 | if (window.console) {
633 | console.log('Do not know how to merge allready merged cells.');
634 | }
635 | }
636 | }
637 | },
638 |
639 | // Decreases rowspan of a cell if it is done on first cell of rowspan row (real cell)
640 | // Cell is moved to next row (if it is real)
641 | collapseCellToNextRow: function(cell) {
642 | var cellIdx = this.getMapIndex(cell.el),
643 | newRowIdx = cellIdx.row + 1,
644 | newIdx = {'row': newRowIdx, 'col': cellIdx.col};
645 |
646 | if (newRowIdx < this.map.length) {
647 |
648 | var row = this.getRealRowEl(false, newIdx);
649 | if (row !== null) {
650 | var n_cidx = this.correctColIndexForUnreals(newIdx.col, newIdx.row);
651 | if (n_cidx >= 0) {
652 | insertAfter(this.getRowCells(row)[n_cidx], cell.el);
653 | } else {
654 | var lastCell = this.getLastNewCellOnRow(row, newRowIdx);
655 | if (lastCell !== null) {
656 | insertAfter(lastCell, cell.el);
657 | } else {
658 | row.insertBefore(cell.el, row.firstChild);
659 | }
660 | }
661 | if (parseInt(api.getAttribute(cell.el, 'rowspan'), 10) > 2) {
662 | cell.el.setAttribute('rowspan', parseInt(api.getAttribute(cell.el, 'rowspan'), 10) - 1);
663 | } else {
664 | cell.el.removeAttribute('rowspan');
665 | }
666 | }
667 | }
668 | },
669 |
670 | // Removes a cell when removing a row
671 | // If is rowspan cell then decreases the rowspan
672 | // and moves cell to next row if needed (is first cell of rowspan)
673 | removeRowCell: function(cell) {
674 | if (cell.isReal) {
675 | if (cell.isRowspan) {
676 | this.collapseCellToNextRow(cell);
677 | } else {
678 | removeElement(cell.el);
679 | }
680 | } else {
681 | if (parseInt(api.getAttribute(cell.el, 'rowspan'), 10) > 2) {
682 | cell.el.setAttribute('rowspan', parseInt(api.getAttribute(cell.el, 'rowspan'), 10) - 1);
683 | } else {
684 | cell.el.removeAttribute('rowspan');
685 | }
686 | }
687 | },
688 |
689 | getRowElementsByCell: function() {
690 | var cells = [];
691 | this.setTableMap();
692 | this.idx = this.getMapIndex(this.cell);
693 | if (this.idx !== false) {
694 | var modRow = this.map[this.idx.row];
695 | for (var cidx = 0, cmax = modRow.length; cidx < cmax; cidx++) {
696 | if (modRow[cidx].isReal) {
697 | cells.push(modRow[cidx].el);
698 | }
699 | }
700 | }
701 | return cells;
702 | },
703 |
704 | getColumnElementsByCell: function() {
705 | var cells = [];
706 | this.setTableMap();
707 | this.idx = this.getMapIndex(this.cell);
708 | if (this.idx !== false) {
709 | for (var ridx = 0, rmax = this.map.length; ridx < rmax; ridx++) {
710 | if (this.map[ridx][this.idx.col] && this.map[ridx][this.idx.col].isReal) {
711 | cells.push(this.map[ridx][this.idx.col].el);
712 | }
713 | }
714 | }
715 | return cells;
716 | },
717 |
718 | // Removes the row of selected cell
719 | removeRow: function() {
720 | var oldRow = api.getParentElement(this.cell, { query: "tr" });
721 | if (oldRow) {
722 | this.setTableMap();
723 | this.idx = this.getMapIndex(this.cell);
724 | if (this.idx !== false) {
725 | var modRow = this.map[this.idx.row];
726 | for (var cidx = 0, cmax = modRow.length; cidx < cmax; cidx++) {
727 | if (!modRow[cidx].modified) {
728 | this.setCellAsModified(modRow[cidx]);
729 | this.removeRowCell(modRow[cidx]);
730 | }
731 | }
732 | }
733 | removeElement(oldRow);
734 | }
735 | },
736 |
737 | removeColCell: function(cell) {
738 | if (cell.isColspan) {
739 | if (parseInt(api.getAttribute(cell.el, 'colspan'), 10) > 2) {
740 | cell.el.setAttribute('colspan', parseInt(api.getAttribute(cell.el, 'colspan'), 10) - 1);
741 | } else {
742 | cell.el.removeAttribute('colspan');
743 | }
744 | } else if (cell.isReal) {
745 | removeElement(cell.el);
746 | }
747 | },
748 |
749 | removeColumn: function() {
750 | this.setTableMap();
751 | this.idx = this.getMapIndex(this.cell);
752 | if (this.idx !== false) {
753 | for (var ridx = 0, rmax = this.map.length; ridx < rmax; ridx++) {
754 | if (!this.map[ridx][this.idx.col].modified) {
755 | this.setCellAsModified(this.map[ridx][this.idx.col]);
756 | this.removeColCell(this.map[ridx][this.idx.col]);
757 | }
758 | }
759 | }
760 | },
761 |
762 | // removes row or column by selected cell element
763 | remove: function(what) {
764 | if (this.rectify()) {
765 | switch (what) {
766 | case 'row':
767 | this.removeRow();
768 | break;
769 | case 'column':
770 | this.removeColumn();
771 | break;
772 | }
773 | this.rectify();
774 | }
775 | },
776 |
777 | addRow: function(where) {
778 | var doc = this.table.ownerDocument;
779 |
780 | this.setTableMap();
781 | this.idx = this.getMapIndex(this.cell);
782 | if (where == "below" && api.getAttribute(this.cell, 'rowspan')) {
783 | this.idx.row = this.idx.row + parseInt(api.getAttribute(this.cell, 'rowspan'), 10) - 1;
784 | }
785 |
786 | if (this.idx !== false) {
787 | var modRow = this.map[this.idx.row],
788 | newRow = doc.createElement('tr');
789 |
790 | for (var ridx = 0, rmax = modRow.length; ridx < rmax; ridx++) {
791 | if (!modRow[ridx].modified) {
792 | this.setCellAsModified(modRow[ridx]);
793 | this.addRowCell(modRow[ridx], newRow, where);
794 | }
795 | }
796 |
797 | switch (where) {
798 | case 'below':
799 | insertAfter(this.getRealRowEl(true), newRow);
800 | break;
801 | case 'above':
802 | var cr = api.getParentElement(this.map[this.idx.row][this.idx.col].el, { query: "tr" });
803 | if (cr) {
804 | cr.parentNode.insertBefore(newRow, cr);
805 | }
806 | break;
807 | }
808 | }
809 | },
810 |
811 | addRowCell: function(cell, row, where) {
812 | var colSpanAttr = (cell.isColspan) ? {"colspan" : api.getAttribute(cell.el, 'colspan')} : null;
813 | if (cell.isReal) {
814 | if (where != 'above' && cell.isRowspan) {
815 | cell.el.setAttribute('rowspan', parseInt(api.getAttribute(cell.el,'rowspan'), 10) + 1);
816 | } else {
817 | row.appendChild(this.createCells('td', 1, colSpanAttr));
818 | }
819 | } else {
820 | if (where != 'above' && cell.isRowspan && cell.lastRow) {
821 | row.appendChild(this.createCells('td', 1, colSpanAttr));
822 | } else if (c.isRowspan) {
823 | cell.el.attr('rowspan', parseInt(api.getAttribute(cell.el, 'rowspan'), 10) + 1);
824 | }
825 | }
826 | },
827 |
828 | add: function(where) {
829 | if (this.rectify()) {
830 | if (where == 'below' || where == 'above') {
831 | this.addRow(where);
832 | }
833 | if (where == 'before' || where == 'after') {
834 | this.addColumn(where);
835 | }
836 | }
837 | },
838 |
839 | addColCell: function (cell, ridx, where) {
840 | var doAdd,
841 | cType = cell.el.tagName.toLowerCase();
842 |
843 | // defines add cell vs expand cell conditions
844 | // true means add
845 | switch (where) {
846 | case "before":
847 | doAdd = (!cell.isColspan || cell.firstCol);
848 | break;
849 | case "after":
850 | doAdd = (!cell.isColspan || cell.lastCol || (cell.isColspan && cell.el == this.cell));
851 | break;
852 | }
853 |
854 | if (doAdd){
855 | // adds a cell before or after current cell element
856 | switch (where) {
857 | case "before":
858 | cell.el.parentNode.insertBefore(this.createCells(cType, 1), cell.el);
859 | break;
860 | case "after":
861 | insertAfter(cell.el, this.createCells(cType, 1));
862 | break;
863 | }
864 |
865 | // handles if cell has rowspan
866 | if (cell.isRowspan) {
867 | this.handleCellAddWithRowspan(cell, ridx+1, where);
868 | }
869 |
870 | } else {
871 | // expands cell
872 | cell.el.setAttribute('colspan', parseInt(api.getAttribute(cell.el, 'colspan'), 10) + 1);
873 | }
874 | },
875 |
876 | addColumn: function(where) {
877 | var row, modCell;
878 |
879 | this.setTableMap();
880 | this.idx = this.getMapIndex(this.cell);
881 | if (where == "after" && api.getAttribute(this.cell, 'colspan')) {
882 | this.idx.col = this.idx.col + parseInt(api.getAttribute(this.cell, 'colspan'), 10) - 1;
883 | }
884 |
885 | if (this.idx !== false) {
886 | for (var ridx = 0, rmax = this.map.length; ridx < rmax; ridx++ ) {
887 | row = this.map[ridx];
888 | if (row[this.idx.col]) {
889 | modCell = row[this.idx.col];
890 | if (!modCell.modified) {
891 | this.setCellAsModified(modCell);
892 | this.addColCell(modCell, ridx , where);
893 | }
894 | }
895 | }
896 | }
897 | },
898 |
899 | handleCellAddWithRowspan: function (cell, ridx, where) {
900 | var addRowsNr = parseInt(api.getAttribute(this.cell, 'rowspan'), 10) - 1,
901 | crow = api.getParentElement(cell.el, { query: "tr" }),
902 | cType = cell.el.tagName.toLowerCase(),
903 | cidx, temp_r_cells,
904 | doc = this.table.ownerDocument,
905 | nrow;
906 |
907 | for (var i = 0; i < addRowsNr; i++) {
908 | cidx = this.correctColIndexForUnreals(this.idx.col, (ridx + i));
909 | crow = nextNode(crow, 'tr');
910 | if (crow) {
911 | if (cidx > 0) {
912 | switch (where) {
913 | case "before":
914 | temp_r_cells = this.getRowCells(crow);
915 | if (cidx > 0 && this.map[ridx + i][this.idx.col].el != temp_r_cells[cidx] && cidx == temp_r_cells.length - 1) {
916 | insertAfter(temp_r_cells[cidx], this.createCells(cType, 1));
917 | } else {
918 | temp_r_cells[cidx].parentNode.insertBefore(this.createCells(cType, 1), temp_r_cells[cidx]);
919 | }
920 |
921 | break;
922 | case "after":
923 | insertAfter(this.getRowCells(crow)[cidx], this.createCells(cType, 1));
924 | break;
925 | }
926 | } else {
927 | crow.insertBefore(this.createCells(cType, 1), crow.firstChild);
928 | }
929 | } else {
930 | nrow = doc.createElement('tr');
931 | nrow.appendChild(this.createCells(cType, 1));
932 | this.table.appendChild(nrow);
933 | }
934 | }
935 | }
936 | };
937 |
938 | api.table = {
939 | getCellsBetween: function(cell1, cell2) {
940 | var c1 = new TableModifyerByCell(cell1);
941 | return c1.getMapElsTo(cell2);
942 | },
943 |
944 | addCells: function(cell, where) {
945 | var c = new TableModifyerByCell(cell);
946 | c.add(where);
947 | },
948 |
949 | removeCells: function(cell, what) {
950 | var c = new TableModifyerByCell(cell);
951 | c.remove(what);
952 | },
953 |
954 | mergeCellsBetween: function(cell1, cell2) {
955 | var c1 = new TableModifyerByCell(cell1);
956 | c1.merge(cell2);
957 | },
958 |
959 | unmergeCell: function(cell) {
960 | var c = new TableModifyerByCell(cell);
961 | c.unmerge();
962 | },
963 |
964 | orderSelectionEnds: function(cell, cell2) {
965 | var c = new TableModifyerByCell(cell);
966 | return c.orderSelectionEnds(cell2);
967 | },
968 |
969 | indexOf: function(cell) {
970 | var c = new TableModifyerByCell(cell);
971 | c.setTableMap();
972 | return c.getMapIndex(cell);
973 | },
974 |
975 | findCell: function(table, idx) {
976 | var c = new TableModifyerByCell(null, table);
977 | return c.getElementAtIndex(idx);
978 | },
979 |
980 | findRowByCell: function(cell) {
981 | var c = new TableModifyerByCell(cell);
982 | return c.getRowElementsByCell();
983 | },
984 |
985 | findColumnByCell: function(cell) {
986 | var c = new TableModifyerByCell(cell);
987 | return c.getColumnElementsByCell();
988 | },
989 |
990 | canMerge: function(cell1, cell2) {
991 | var c = new TableModifyerByCell(cell1);
992 | return c.canMerge(cell2);
993 | }
994 | };
995 |
996 | })();
997 |
998 | (function() {
999 |
1000 | // Keep the old composer.observe function.
1001 | var oldObserverFunction = wysihtml.views.Composer.prototype.observe;
1002 |
1003 | var extendedObserverFunction = function() {
1004 | oldObserverFunction.call(this);
1005 | // Bind the table user interaction tracking
1006 | if (this.config.handleTables) {
1007 | // If handleTables option is true, table handling functions are bound
1008 | initTableHandling.call(this);
1009 | }
1010 | };
1011 |
1012 | // Table management.
1013 | // If present enableObjectResizing and enableInlineTableEditing command
1014 | // should be called with false to prevent native table handlers.
1015 | var initTableHandling = function() {
1016 | var hideHandlers = function() {
1017 | this.win.removeEventListener('load', hideHandlers);
1018 | this.doc.execCommand('enableObjectResizing', false, 'false');
1019 | this.doc.execCommand('enableInlineTableEditing', false, 'false');
1020 | }.bind(this),
1021 | iframeInitiator = (function() {
1022 | hideHandlers.call(this);
1023 | this.actions.removeListeners(this.sandbox.getIframe(), ['focus', 'mouseup', 'mouseover'], iframeInitiator);
1024 | }).bind(this);
1025 |
1026 | if (
1027 | this.doc.execCommand &&
1028 | wysihtml.browser.supportsCommand(this.doc, 'enableObjectResizing') &&
1029 | wysihtml.browser.supportsCommand(this.doc, 'enableInlineTableEditing')
1030 | ) {
1031 | if (this.sandbox.getIframe) {
1032 | this.actions.addListeners(this.sandbox.getIframe(), ['focus', 'mouseup', 'mouseover'], iframeInitiator);
1033 | } else {
1034 | this.win.addEventListener('load', hideHandlers);
1035 | }
1036 | }
1037 | this.tableSelection = wysihtml.quirks.tableCellsSelection(this.element, this.parent);
1038 | };
1039 |
1040 | // Cell selections handling
1041 | var tableCellsSelection = function(editable, editor) {
1042 |
1043 | var init = function() {
1044 | editable.addEventListener('mousedown', handleMouseDown);
1045 | return select;
1046 | };
1047 |
1048 | var handleMouseDown = function(event) {
1049 | var target = wysihtml.dom.getParentElement(event.target, {query: 'td, th'}, false, editable);
1050 | if (target) {
1051 | handleSelectionMousedown(target);
1052 | }
1053 | };
1054 |
1055 | var handleSelectionMousedown = function(target) {
1056 | select.start = target;
1057 | select.end = target;
1058 | select.cells = [target];
1059 | select.table = dom.getParentElement(select.start, {query: 'table'}, false, editable);
1060 |
1061 | if (select.table) {
1062 | removeCellSelections();
1063 | dom.addClass(target, selectionClass);
1064 | editable.addEventListener('mousemove', handleMouseMove);
1065 | editable.addEventListener('mouseup', handleMouseUp);
1066 | editor.fire('tableselectstart').fire('tableselectstart:composer');
1067 | }
1068 | };
1069 |
1070 | // remove all selection classes
1071 | var removeCellSelections = function() {
1072 | if (editable) {
1073 | var selectedCells = editable.querySelectorAll('.' + selectionClass);
1074 | if (selectedCells.length > 0) {
1075 | for (var i = 0; i < selectedCells.length; i++) {
1076 | dom.removeClass(selectedCells[i], selectionClass);
1077 | }
1078 | }
1079 | }
1080 | };
1081 |
1082 | var addSelections = function(cells) {
1083 | for (var i = 0; i < cells.length; i++) {
1084 | dom.addClass(cells[i], selectionClass);
1085 | }
1086 | };
1087 |
1088 | var handleMouseMove = function(event) {
1089 | var curTable = null,
1090 | cell = dom.getParentElement(event.target, {query: 'td, th'}, false, editable),
1091 | oldEnd;
1092 |
1093 | if (cell && select.table && select.start) {
1094 | curTable = dom.getParentElement(cell, {query: 'table'}, false, editable);
1095 | if (curTable && curTable === select.table) {
1096 | removeCellSelections();
1097 | oldEnd = select.end;
1098 | select.end = cell;
1099 | select.cells = dom.table.getCellsBetween(select.start, cell);
1100 | if (select.cells.length > 1) {
1101 | editor.composer.selection.deselect();
1102 | }
1103 | addSelections(select.cells);
1104 | if (select.end !== oldEnd) {
1105 | editor.fire('tableselectchange').fire('tableselectchange:composer');
1106 | }
1107 | }
1108 | }
1109 | };
1110 |
1111 | var handleMouseUp = function(event) {
1112 | editable.removeEventListener('mousemove', handleMouseMove);
1113 | editable.removeEventListener('mouseup', handleMouseUp);
1114 | editor.fire('tableselect').fire('tableselect:composer');
1115 | setTimeout(function() {
1116 | bindSideclick();
1117 | }, 0);
1118 | };
1119 |
1120 | var sideClickHandler = function(event) {
1121 | editable.ownerDocument.removeEventListener('click', sideClickHandler);
1122 | if (dom.getParentElement(event.target, {query: 'table'}, false, editable) != select.table) {
1123 | removeCellSelections();
1124 | select.table = null;
1125 | select.start = null;
1126 | select.end = null;
1127 | editor.fire('tableunselect').fire('tableunselect:composer');
1128 | }
1129 | };
1130 |
1131 | var bindSideclick = function() {
1132 | editable.ownerDocument.addEventListener('click', sideClickHandler);
1133 | };
1134 |
1135 | var selectCells = function(start, end) {
1136 | select.start = start;
1137 | select.end = end;
1138 | select.table = dom.getParentElement(select.start, {query: 'table'}, false, editable);
1139 | selectedCells = dom.table.getCellsBetween(select.start, select.end);
1140 | addSelections(selectedCells);
1141 | bindSideclick();
1142 | editor.fire('tableselect').fire('tableselect:composer');
1143 | };
1144 |
1145 | var dom = wysihtml.dom,
1146 | select = {
1147 | table: null,
1148 | start: null,
1149 | end: null,
1150 | cells: null,
1151 | select: selectCells
1152 | },
1153 | selectionClass = 'wysiwyg-tmp-selected-cell';
1154 |
1155 | return init();
1156 | };
1157 |
1158 | // Bind to wysihtml
1159 | wysihtml.Editor.prototype.defaults.handleTables = true;
1160 | wysihtml.quirks.tableCellsSelection = tableCellsSelection;
1161 | wysihtml.views.Composer.prototype.observe = extendedObserverFunction;
1162 |
1163 | })();
1164 |
--------------------------------------------------------------------------------
/vendor/assets/javascripts/wysihtml/toolbar.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Toolbar Dialog
3 | *
4 | * @param {Element} link The toolbar link which causes the dialog to show up
5 | * @param {Element} container The dialog container
6 | *
7 | * @example
8 | *
9 | * insert an image
10 | *
11 | *
12 | *
13 | *
14 | * URL:
15 | *
16 | *
17 | * Alternative text:
18 | *
19 | *
20 | *
21 | *
30 | */
31 | (function(wysihtml) {
32 | var dom = wysihtml.dom,
33 | CLASS_NAME_OPENED = "wysihtml-command-dialog-opened",
34 | SELECTOR_FORM_ELEMENTS = "input, select, textarea",
35 | SELECTOR_FIELDS = "[data-wysihtml-dialog-field]",
36 | ATTRIBUTE_FIELDS = "data-wysihtml-dialog-field";
37 |
38 |
39 | wysihtml.toolbar.Dialog = wysihtml.lang.Dispatcher.extend(
40 | /** @scope wysihtml.toolbar.Dialog.prototype */ {
41 | constructor: function(link, container) {
42 | this.link = link;
43 | this.container = container;
44 | },
45 |
46 | _observe: function() {
47 | if (this._observed) {
48 | return;
49 | }
50 |
51 | var that = this,
52 | callbackWrapper = function(event) {
53 | var attributes = that._serialize();
54 | that.fire("save", attributes);
55 | that.hide();
56 | event.preventDefault();
57 | event.stopPropagation();
58 | };
59 |
60 | dom.observe(that.link, "click", function() {
61 | if (dom.hasClass(that.link, CLASS_NAME_OPENED)) {
62 | setTimeout(function() { that.hide(); }, 0);
63 | }
64 | });
65 |
66 | dom.observe(this.container, "keydown", function(event) {
67 | var keyCode = event.keyCode;
68 | if (keyCode === wysihtml.ENTER_KEY) {
69 | callbackWrapper(event);
70 | }
71 | if (keyCode === wysihtml.ESCAPE_KEY) {
72 | that.cancel();
73 | }
74 | });
75 |
76 | dom.delegate(this.container, "[data-wysihtml-dialog-action=save]", "click", callbackWrapper);
77 |
78 | dom.delegate(this.container, "[data-wysihtml-dialog-action=cancel]", "click", function(event) {
79 | that.cancel();
80 | event.preventDefault();
81 | event.stopPropagation();
82 | });
83 |
84 | this._observed = true;
85 | },
86 |
87 | /**
88 | * Grabs all fields in the dialog and puts them in key=>value style in an object which
89 | * then gets returned
90 | */
91 | _serialize: function() {
92 | var data = {},
93 | fields = this.container.querySelectorAll(SELECTOR_FIELDS),
94 | length = fields.length,
95 | i = 0;
96 |
97 | for (; ifoo
109 | *
110 | * and we have the following dialog:
111 | *
112 | *
113 | *
114 | * after calling _interpolate() the dialog will look like this
115 | *
116 | *
117 | *
118 | * Basically it adopted the attribute values into the corresponding input fields
119 | *
120 | */
121 | _interpolate: function(avoidHiddenFields) {
122 | var field,
123 | fieldName,
124 | newValue,
125 | focusedElement = document.querySelector(":focus"),
126 | fields = this.container.querySelectorAll(SELECTOR_FIELDS),
127 | length = fields.length,
128 | i = 0;
129 | for (; i= 11
349 | *
350 | * Note that it sends the recorded audio to the google speech recognition api:
351 | * http://stackoverflow.com/questions/4361826/does-chrome-have-buil-in-speech-recognition-for-input-type-text-x-webkit-speec
352 | *
353 | * Current HTML5 draft can be found here
354 | * http://lists.w3.org/Archives/Public/public-xg-htmlspeech/2011Feb/att-0020/api-draft.html
355 | *
356 | * "Accessing Google Speech API Chrome 11"
357 | * http://mikepultz.com/2011/03/accessing-google-speech-api-chrome-11/
358 | */
359 | (function(wysihtml) {
360 | var dom = wysihtml.dom;
361 |
362 | var linkStyles = {
363 | position: "relative"
364 | };
365 |
366 | var wrapperStyles = {
367 | left: 0,
368 | margin: 0,
369 | opacity: 0,
370 | overflow: "hidden",
371 | padding: 0,
372 | position: "absolute",
373 | top: 0,
374 | zIndex: 1
375 | };
376 |
377 | var inputStyles = {
378 | cursor: "inherit",
379 | fontSize: "50px",
380 | height: "50px",
381 | marginTop: "-25px",
382 | outline: 0,
383 | padding: 0,
384 | position: "absolute",
385 | right: "-4px",
386 | top: "50%"
387 | };
388 |
389 | var inputAttributes = {
390 | "x-webkit-speech": "",
391 | "speech": ""
392 | };
393 |
394 | wysihtml.toolbar.Speech = function(parent, link) {
395 | var input = document.createElement("input");
396 | if (!wysihtml.browser.supportsSpeechApiOn(input)) {
397 | link.style.display = "none";
398 | return;
399 | }
400 | var lang = parent.editor.textarea.element.getAttribute("lang");
401 | if (lang) {
402 | inputAttributes.lang = lang;
403 | }
404 |
405 | var wrapper = document.createElement("div");
406 |
407 | wysihtml.lang.object(wrapperStyles).merge({
408 | width: link.offsetWidth + "px",
409 | height: link.offsetHeight + "px"
410 | });
411 |
412 | dom.insert(input).into(wrapper);
413 | dom.insert(wrapper).into(link);
414 |
415 | dom.setStyles(inputStyles).on(input);
416 | dom.setAttributes(inputAttributes).on(input);
417 |
418 | dom.setStyles(wrapperStyles).on(wrapper);
419 | dom.setStyles(linkStyles).on(link);
420 |
421 | var eventName = "onwebkitspeechchange" in input ? "webkitspeechchange" : "speechchange";
422 | dom.observe(input, eventName, function() {
423 | parent.execCommand("insertText", input.value);
424 | input.value = "";
425 | });
426 |
427 | dom.observe(input, "click", function(event) {
428 | if (dom.hasClass(link, "wysihtml-command-disabled")) {
429 | event.preventDefault();
430 | }
431 |
432 | event.stopPropagation();
433 | });
434 | };
435 | })(wysihtml);
436 |
437 | /**
438 | * Toolbar
439 | *
440 | * @param {Object} parent Reference to instance of Editor instance
441 | * @param {Element} container Reference to the toolbar container element
442 | *
443 | * @example
444 | *
448 | *
449 | *
452 | */
453 | (function(wysihtml) {
454 | var CLASS_NAME_COMMAND_DISABLED = "wysihtml-command-disabled",
455 | CLASS_NAME_COMMANDS_DISABLED = "wysihtml-commands-disabled",
456 | CLASS_NAME_COMMAND_ACTIVE = "wysihtml-command-active",
457 | CLASS_NAME_ACTION_ACTIVE = "wysihtml-action-active",
458 | dom = wysihtml.dom;
459 |
460 | wysihtml.toolbar.Toolbar = Base.extend(
461 | /** @scope wysihtml.toolbar.Toolbar.prototype */ {
462 | constructor: function(editor, container, showOnInit) {
463 | this.editor = editor;
464 | this.container = typeof(container) === "string" ? document.getElementById(container) : container;
465 | this.composer = editor.composer;
466 |
467 | this._getLinks("command");
468 | this._getLinks("action");
469 |
470 | this._observe();
471 | if (showOnInit) { this.show(); }
472 |
473 | if (editor.config.classNameCommandDisabled != null) {
474 | CLASS_NAME_COMMAND_DISABLED = editor.config.classNameCommandDisabled;
475 | }
476 | if (editor.config.classNameCommandsDisabled != null) {
477 | CLASS_NAME_COMMANDS_DISABLED = editor.config.classNameCommandsDisabled;
478 | }
479 | if (editor.config.classNameCommandActive != null) {
480 | CLASS_NAME_COMMAND_ACTIVE = editor.config.classNameCommandActive;
481 | }
482 | if (editor.config.classNameActionActive != null) {
483 | CLASS_NAME_ACTION_ACTIVE = editor.config.classNameActionActive;
484 | }
485 |
486 | var speechInputLinks = this.container.querySelectorAll("[data-wysihtml-command=insertSpeech]"),
487 | length = speechInputLinks.length,
488 | i = 0;
489 | for (; i element or wrap current selection in
577 | * toolbar.execCommand("formatBlock", "blockquote");
578 | */
579 | execCommand: function(command, commandValue) {
580 | if (this.commandsDisabled) {
581 | return;
582 | }
583 |
584 | this._execCommand(command, commandValue);
585 | },
586 |
587 | _execCommand: function(command, commandValue) {
588 | // Make sure that composer is focussed (false => don't move caret to the end)
589 | this.editor.focus(false);
590 |
591 | this.composer.commands.exec(command, commandValue);
592 | this._updateLinkStates();
593 | },
594 |
595 | execAction: function(action) {
596 | var editor = this.editor;
597 | if (action === "change_view") {
598 | if (editor.currentView === editor.textarea || editor.currentView === "source") {
599 | editor.fire("change_view", "composer");
600 | } else {
601 | editor.fire("change_view", "textarea");
602 | }
603 | }
604 | if (action == "showSource") {
605 | editor.fire("showSource");
606 | }
607 | },
608 |
609 | _observe: function() {
610 | var that = this,
611 | editor = this.editor,
612 | container = this.container,
613 | links = this.commandLinks.concat(this.actionLinks),
614 | length = links.length,
615 | i = 0;
616 |
617 | for (; i= 3.1.0'
22 |
23 | spec.add_development_dependency 'bundler', '~> 1.3'
24 | spec.add_development_dependency 'rake', '~> 0'
25 | end
26 |
--------------------------------------------------------------------------------