diff --git a/README.md b/README.md index 7632af1..ea77392 100644 --- a/README.md +++ b/README.md @@ -38,10 +38,10 @@ Require it in your JS manifest's file `application.js`: //= require wysihtml5x ``` -or if you need pure wysihtml functionality without toolbar: +or if you need wysihtml with built-in toolbar: ```js -//= require wysihtml5x-wotools +//= require wysihtml5x-toolbar ``` Additionally include predefined `simple`, `advanced` or `advanced_unwrap` parsing rules in your `application.js`: diff --git a/vendor/assets/javascripts/wysihtml5x-wotools.js b/vendor/assets/javascripts/wysihtml5x-toolbar.js similarity index 92% rename from vendor/assets/javascripts/wysihtml5x-wotools.js rename to vendor/assets/javascripts/wysihtml5x-toolbar.js index bb46fd7..0eb4bd7 100644 --- a/vendor/assets/javascripts/wysihtml5x-wotools.js +++ b/vendor/assets/javascripts/wysihtml5x-toolbar.js @@ -5494,28 +5494,6 @@ wysihtml5.commands.bold = { var undef, NODE_NAME = "A", dom = wysihtml5.dom; - - function _removeFormat(composer, anchors) { - var length = anchors.length, - i = 0, - anchor, - codeElement, - textContent; - for (; i contains url-like text content, rename it to to prevent re-autolinking - // else replace with its childNodes - if (textContent.match(dom.autoLink.URL_REG_EXP) && !codeElement) { - // element is used to prevent later auto-linking of the content - codeElement = dom.renameElement(anchor, "code"); - } else { - dom.replaceWithChildNodes(anchor); - } - } - } function _format(composer, attributes) { var doc = composer.doc, @@ -5555,17 +5533,36 @@ wysihtml5.commands.bold = { elementToSetCaretAfter = whiteSpace; } } - console.log(elementToSetCaretAfter); composer.selection.setAfter(elementToSetCaretAfter); } + // Changes attributes of links + function _changeLinks(composer, anchors, attributes) { + var oldAttrs; + for (var a = anchors.length; a--;) { + + // Remove all old attributes + oldAttrs = anchors[a].attributes; + for (var oa = oldAttrs.length; oa--;) { + anchors[a].removeAttribute(oldAttrs.item(oa).name); + } + + // Set new attributes + for (var j in attributes) { + if (attributes.hasOwnProperty(j)) { + anchors[a].setAttribute(j, attributes[j]); + } + } + + } + } + wysihtml5.commands.createLink = { /** * TODO: Use HTMLApplier or formatInline here * * Turns selection into a link - * If selection is already a link, it removes the link and wraps it with a element - * The element is needed to avoid auto linking + * If selection is already a link, it just changes the attributes * * @example * // either ... @@ -5576,9 +5573,9 @@ wysihtml5.commands.bold = { exec: function(composer, command, value) { var anchors = this.state(composer, command); if (anchors) { - // Selection contains links + // Selection contains links then change attributes of these links composer.selection.executeAndRestore(function() { - _removeFormat(composer, anchors); + _changeLinks(composer, anchors, value); }); } else { // Create links @@ -5587,6 +5584,53 @@ wysihtml5.commands.bold = { } }, + state: function(composer, command) { + return wysihtml5.commands.formatInline.state(composer, command, "A"); + } + }; +})(wysihtml5);(function(wysihtml5) { + var dom = wysihtml5.dom; + + function _removeFormat(composer, anchors) { + var length = anchors.length, + i = 0, + anchor, + codeElement, + textContent; + for (; i contains url-like text content, rename it to to prevent re-autolinking + // else replace with its childNodes + if (textContent.match(dom.autoLink.URL_REG_EXP) && !codeElement) { + // element is used to prevent later auto-linking of the content + codeElement = dom.renameElement(anchor, "code"); + } else { + dom.replaceWithChildNodes(anchor); + } + } + } + + wysihtml5.commands.removeLink = { + /* + * If selection is a link, it removes the link and wraps it with a element + * The element is needed to avoid auto linking + * + * @example + * wysihtml5.commands.createLink.exec(composer, "removeLink"); + */ + + exec: function(composer, command) { + var anchors = this.state(composer, command); + if (anchors) { + composer.selection.executeAndRestore(function() { + _removeFormat(composer, anchors); + }); + } + }, + state: function(composer, command) { return wysihtml5.commands.formatInline.state(composer, command, "A"); } @@ -7985,6 +8029,697 @@ wysihtml5.views.Textarea = wysihtml5.views.View.extend( }); } });/** + * Toolbar Dialog + * + * @param {Element} link The toolbar link which causes the dialog to show up + * @param {Element} container The dialog container + * + * @example + * + * insert an image + * + * + *
+ * + * + *
+ * + * + */ +(function(wysihtml5) { + var dom = wysihtml5.dom, + CLASS_NAME_OPENED = "wysihtml5-command-dialog-opened", + SELECTOR_FORM_ELEMENTS = "input, select, textarea", + SELECTOR_FIELDS = "[data-wysihtml5-dialog-field]", + ATTRIBUTE_FIELDS = "data-wysihtml5-dialog-field"; + + + wysihtml5.toolbar.Dialog = wysihtml5.lang.Dispatcher.extend( + /** @scope wysihtml5.toolbar.Dialog.prototype */ { + constructor: function(link, container) { + this.link = link; + this.container = container; + }, + + _observe: function() { + if (this._observed) { + return; + } + + var that = this, + callbackWrapper = function(event) { + var attributes = that._serialize(); + if (attributes == that.elementToChange) { + that.fire("edit", attributes); + } else { + that.fire("save", attributes); + } + that.hide(); + event.preventDefault(); + event.stopPropagation(); + }; + + dom.observe(that.link, "click", function() { + if (dom.hasClass(that.link, CLASS_NAME_OPENED)) { + setTimeout(function() { that.hide(); }, 0); + } + }); + + dom.observe(this.container, "keydown", function(event) { + var keyCode = event.keyCode; + if (keyCode === wysihtml5.ENTER_KEY) { + callbackWrapper(event); + } + if (keyCode === wysihtml5.ESCAPE_KEY) { + that.fire("cancel"); + that.hide(); + } + }); + + dom.delegate(this.container, "[data-wysihtml5-dialog-action=save]", "click", callbackWrapper); + + dom.delegate(this.container, "[data-wysihtml5-dialog-action=cancel]", "click", function(event) { + that.fire("cancel"); + that.hide(); + event.preventDefault(); + event.stopPropagation(); + }); + + var formElements = this.container.querySelectorAll(SELECTOR_FORM_ELEMENTS), + i = 0, + length = formElements.length, + _clearInterval = function() { clearInterval(that.interval); }; + for (; ivalue style in an object which + * then gets returned + */ + _serialize: function() { + var data = this.elementToChange || {}, + fields = this.container.querySelectorAll(SELECTOR_FIELDS), + length = fields.length, + i = 0; + + for (; ifoo + * + * and we have the following dialog: + * + * + * + * after calling _interpolate() the dialog will look like this + * + * + * + * Basically it adopted the attribute values into the corresponding input fields + * + */ + _interpolate: function(avoidHiddenFields) { + var field, + fieldName, + newValue, + focusedElement = document.querySelector(":focus"), + fields = this.container.querySelectorAll(SELECTOR_FIELDS), + length = fields.length, + i = 0; + for (; i