liquid_feedback_frontend

diff static/wysihtml/wysihtml.toolbar.js @ 1309:32cc544d5a5b

Cumulative patch for upcoming frontend version 4
author bsw/jbe
date Sun Jul 15 14:07:29 2018 +0200 (2018-07-15)
parents
children
line diff
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/static/wysihtml/wysihtml.toolbar.js	Sun Jul 15 14:07:29 2018 +0200
     1.3 @@ -0,0 +1,850 @@
     1.4 +/**
     1.5 + * Toolbar Dialog
     1.6 + *
     1.7 + * @param {Element} link The toolbar link which causes the dialog to show up
     1.8 + * @param {Element} container The dialog container
     1.9 + *
    1.10 + * @example
    1.11 + *    <!-- Toolbar link -->
    1.12 + *    <a data-wysihtml-command="insertImage">insert an image</a>
    1.13 + *
    1.14 + *    <!-- Dialog -->
    1.15 + *    <div data-wysihtml-dialog="insertImage" style="display: none;">
    1.16 + *      <label>
    1.17 + *        URL: <input data-wysihtml-dialog-field="src" value="http://">
    1.18 + *      </label>
    1.19 + *      <label>
    1.20 + *        Alternative text: <input data-wysihtml-dialog-field="alt" value="">
    1.21 + *      </label>
    1.22 + *    </div>
    1.23 + *
    1.24 + *    <script>
    1.25 + *      var dialog = new wysihtml.toolbar.Dialog(
    1.26 + *        document.querySelector("[data-wysihtml-command='insertImage']"),
    1.27 + *        document.querySelector("[data-wysihtml-dialog='insertImage']")
    1.28 + *      );
    1.29 + *      dialog.observe("save", function(attributes) {
    1.30 + *        // do something
    1.31 + *      });
    1.32 + *    </script>
    1.33 + */
    1.34 +(function(wysihtml) {
    1.35 +  var dom                     = wysihtml.dom,
    1.36 +      CLASS_NAME_OPENED       = "wysihtml-command-dialog-opened",
    1.37 +      SELECTOR_FORM_ELEMENTS  = "input, select, textarea",
    1.38 +      SELECTOR_FIELDS         = "[data-wysihtml-dialog-field]",
    1.39 +      ATTRIBUTE_FIELDS        = "data-wysihtml-dialog-field";
    1.40 +
    1.41 +
    1.42 +  wysihtml.toolbar.Dialog = wysihtml.lang.Dispatcher.extend(
    1.43 +    /** @scope wysihtml.toolbar.Dialog.prototype */ {
    1.44 +    constructor: function(link, container) {
    1.45 +      this.link       = link;
    1.46 +      this.container  = container;
    1.47 +    },
    1.48 +
    1.49 +    _observe: function() {
    1.50 +      if (this._observed) {
    1.51 +        return;
    1.52 +      }
    1.53 +
    1.54 +      var that = this,
    1.55 +          callbackWrapper = function(event) {
    1.56 +            var attributes = that._serialize();
    1.57 +            that.fire("save", attributes);
    1.58 +            that.hide();
    1.59 +            event.preventDefault();
    1.60 +            event.stopPropagation();
    1.61 +          };
    1.62 +
    1.63 +      dom.observe(that.link, "click", function() {
    1.64 +        if (dom.hasClass(that.link, CLASS_NAME_OPENED)) {
    1.65 +          setTimeout(function() { that.hide(); }, 0);
    1.66 +        }
    1.67 +      });
    1.68 +
    1.69 +      dom.observe(this.container, "keydown", function(event) {
    1.70 +        var keyCode = event.keyCode;
    1.71 +        if (keyCode === wysihtml.ENTER_KEY) {
    1.72 +          callbackWrapper(event);
    1.73 +        }
    1.74 +        if (keyCode === wysihtml.ESCAPE_KEY) {
    1.75 +          that.cancel();
    1.76 +        }
    1.77 +      });
    1.78 +
    1.79 +      dom.delegate(this.container, "[data-wysihtml-dialog-action=save]", "click", callbackWrapper);
    1.80 +
    1.81 +      dom.delegate(this.container, "[data-wysihtml-dialog-action=cancel]", "click", function(event) {
    1.82 +        that.cancel();
    1.83 +        event.preventDefault();
    1.84 +        event.stopPropagation();
    1.85 +      });
    1.86 +
    1.87 +      this._observed = true;
    1.88 +    },
    1.89 +
    1.90 +    /**
    1.91 +     * Grabs all fields in the dialog and puts them in key=>value style in an object which
    1.92 +     * then gets returned
    1.93 +     */
    1.94 +    _serialize: function() {
    1.95 +      var data    = {},
    1.96 +          fields  = this.container.querySelectorAll(SELECTOR_FIELDS),
    1.97 +          length  = fields.length,
    1.98 +          i       = 0;
    1.99 +
   1.100 +      for (; i<length; i++) {
   1.101 +        data[fields[i].getAttribute(ATTRIBUTE_FIELDS)] = fields[i].value;
   1.102 +      }
   1.103 +      return data;
   1.104 +    },
   1.105 +
   1.106 +    /**
   1.107 +     * Takes the attributes of the "elementToChange"
   1.108 +     * and inserts them in their corresponding dialog input fields
   1.109 +     *
   1.110 +     * Assume the "elementToChange" looks like this:
   1.111 +     *    <a href="http://www.google.com" target="_blank">foo</a>
   1.112 +     *
   1.113 +     * and we have the following dialog:
   1.114 +     *    <input type="text" data-wysihtml-dialog-field="href" value="">
   1.115 +     *    <input type="text" data-wysihtml-dialog-field="target" value="">
   1.116 +     *
   1.117 +     * after calling _interpolate() the dialog will look like this
   1.118 +     *    <input type="text" data-wysihtml-dialog-field="href" value="http://www.google.com">
   1.119 +     *    <input type="text" data-wysihtml-dialog-field="target" value="_blank">
   1.120 +     *
   1.121 +     * Basically it adopted the attribute values into the corresponding input fields
   1.122 +     *
   1.123 +     */
   1.124 +    _interpolate: function(avoidHiddenFields) {
   1.125 +      var field,
   1.126 +          fieldName,
   1.127 +          newValue,
   1.128 +          focusedElement = document.querySelector(":focus"),
   1.129 +          fields         = this.container.querySelectorAll(SELECTOR_FIELDS),
   1.130 +          length         = fields.length,
   1.131 +          i              = 0;
   1.132 +      for (; i<length; i++) {
   1.133 +        field = fields[i];
   1.134 +
   1.135 +        // Never change elements where the user is currently typing in
   1.136 +        if (field === focusedElement) {
   1.137 +          continue;
   1.138 +        }
   1.139 +
   1.140 +        // Don't update hidden fields
   1.141 +        // See https://github.com/xing/wysihtml5/pull/14
   1.142 +        if (avoidHiddenFields && field.type === "hidden") {
   1.143 +          continue;
   1.144 +        }
   1.145 +
   1.146 +        fieldName = field.getAttribute(ATTRIBUTE_FIELDS);
   1.147 +        newValue  = (this.elementToChange && typeof(this.elementToChange) !== 'boolean') ? (this.elementToChange.getAttribute(fieldName) || "") : field.defaultValue;
   1.148 +        field.value = newValue;
   1.149 +      }
   1.150 +    },
   1.151 +
   1.152 +    update: function (elementToChange) {
   1.153 +      this.elementToChange = elementToChange ? elementToChange : this.elementToChange;
   1.154 +      this._interpolate();
   1.155 +    },
   1.156 +
   1.157 +    /**
   1.158 +     * Show the dialog element
   1.159 +     */
   1.160 +    show: function(elementToChange) {
   1.161 +      var firstField  = this.container.querySelector(SELECTOR_FORM_ELEMENTS);
   1.162 +
   1.163 +      this._observe();
   1.164 +      this.update(elementToChange);
   1.165 +
   1.166 +      dom.addClass(this.link, CLASS_NAME_OPENED);
   1.167 +      this.container.style.display = "";
   1.168 +      this.isOpen = true;
   1.169 +      this.fire("show");
   1.170 +
   1.171 +      if (firstField && !elementToChange) {
   1.172 +        try {
   1.173 +          firstField.focus();
   1.174 +        } catch(e) {}
   1.175 +      }
   1.176 +    },
   1.177 +
   1.178 +    /**
   1.179 +     * Hide the dialog element
   1.180 +     */
   1.181 +    _hide: function(focus) {
   1.182 +      this.elementToChange = null;
   1.183 +      dom.removeClass(this.link, CLASS_NAME_OPENED);
   1.184 +      this.container.style.display = "none";
   1.185 +      this.isOpen = false;
   1.186 +    },
   1.187 +
   1.188 +    hide: function() {
   1.189 +      this._hide();
   1.190 +      this.fire("hide");
   1.191 +    },
   1.192 +
   1.193 +    cancel: function() {
   1.194 +      this._hide();
   1.195 +      this.fire("cancel");
   1.196 +    }
   1.197 +  });
   1.198 +})(wysihtml); //jshint ignore:line
   1.199 +
   1.200 +(function(wysihtml) {
   1.201 +  var dom                     = wysihtml.dom,
   1.202 +      SELECTOR_FIELDS         = "[data-wysihtml-dialog-field]",
   1.203 +      ATTRIBUTE_FIELDS        = "data-wysihtml-dialog-field";
   1.204 +
   1.205 +  wysihtml.toolbar.Dialog_bgColorStyle = wysihtml.toolbar.Dialog.extend({
   1.206 +    multiselect: true,
   1.207 +
   1.208 +    _serialize: function() {
   1.209 +      var data    = {},
   1.210 +          fields  = this.container.querySelectorAll(SELECTOR_FIELDS),
   1.211 +          length  = fields.length,
   1.212 +          i       = 0;
   1.213 +
   1.214 +      for (; i<length; i++) {
   1.215 +        data[fields[i].getAttribute(ATTRIBUTE_FIELDS)] = fields[i].value;
   1.216 +      }
   1.217 +      return data;
   1.218 +    },
   1.219 +
   1.220 +    _interpolate: function(avoidHiddenFields) {
   1.221 +      var field,
   1.222 +          fieldName,
   1.223 +          newValue,
   1.224 +          focusedElement = document.querySelector(":focus"),
   1.225 +          fields         = this.container.querySelectorAll(SELECTOR_FIELDS),
   1.226 +          length         = fields.length,
   1.227 +          i              = 0,
   1.228 +          firstElement   = (this.elementToChange) ? ((wysihtml.lang.object(this.elementToChange).isArray()) ? this.elementToChange[0] : this.elementToChange) : null,
   1.229 +          colorStr       = (firstElement) ? firstElement.getAttribute('style') : null,
   1.230 +          color          = (colorStr) ? wysihtml.quirks.styleParser.parseColor(colorStr, "background-color") : null;
   1.231 +
   1.232 +      for (; i<length; i++) {
   1.233 +        field = fields[i];
   1.234 +        // Never change elements where the user is currently typing in
   1.235 +        if (field === focusedElement) {
   1.236 +          continue;
   1.237 +        }
   1.238 +        // Don't update hidden fields3
   1.239 +        if (avoidHiddenFields && field.type === "hidden") {
   1.240 +          continue;
   1.241 +        }
   1.242 +        if (field.getAttribute(ATTRIBUTE_FIELDS) === "color") {
   1.243 +          if (color) {
   1.244 +            if (color[3] && color[3] != 1) {
   1.245 +              field.value = "rgba(" + color[0] + "," + color[1] + "," + color[2] + "," + color[3] + ");";
   1.246 +            } else {
   1.247 +              field.value = "rgb(" + color[0] + "," + color[1] + "," + color[2] + ");";
   1.248 +            }
   1.249 +          } else {
   1.250 +            field.value = "rgb(0,0,0);";
   1.251 +          }
   1.252 +        }
   1.253 +      }
   1.254 +    }
   1.255 +
   1.256 +  });
   1.257 +})(wysihtml);
   1.258 +
   1.259 +(function(wysihtml) {
   1.260 +  wysihtml.toolbar.Dialog_createTable = wysihtml.toolbar.Dialog.extend({
   1.261 +    show: function(elementToChange) {
   1.262 +      this.base(elementToChange);
   1.263 +    }
   1.264 +  });
   1.265 +})(wysihtml);
   1.266 +
   1.267 +(function(wysihtml) {
   1.268 +  var dom                     = wysihtml.dom,
   1.269 +      SELECTOR_FIELDS         = "[data-wysihtml-dialog-field]",
   1.270 +      ATTRIBUTE_FIELDS        = "data-wysihtml-dialog-field";
   1.271 +
   1.272 +  wysihtml.toolbar.Dialog_fontSizeStyle = wysihtml.toolbar.Dialog.extend({
   1.273 +    multiselect: true,
   1.274 +
   1.275 +    _serialize: function() {
   1.276 +      return {"size" : this.container.querySelector('[data-wysihtml-dialog-field="size"]').value};
   1.277 +    },
   1.278 +
   1.279 +    _interpolate: function(avoidHiddenFields) {
   1.280 +      var focusedElement = document.querySelector(":focus"),
   1.281 +          field          = this.container.querySelector("[data-wysihtml-dialog-field='size']"),
   1.282 +          firstElement   = (this.elementToChange) ? ((wysihtml.lang.object(this.elementToChange).isArray()) ? this.elementToChange[0] : this.elementToChange) : null,
   1.283 +          styleStr       = (firstElement) ? firstElement.getAttribute('style') : null,
   1.284 +          size           = (styleStr) ? wysihtml.quirks.styleParser.parseFontSize(styleStr) : null;
   1.285 +
   1.286 +      if (field && field !== focusedElement && size && !(/^\s*$/).test(size)) {
   1.287 +        field.value = size;
   1.288 +      }
   1.289 +    }
   1.290 +  });
   1.291 +})(wysihtml);
   1.292 +
   1.293 +(function(wysihtml) {
   1.294 +  var SELECTOR_FIELDS         = "[data-wysihtml-dialog-field]",
   1.295 +      ATTRIBUTE_FIELDS        = "data-wysihtml-dialog-field";
   1.296 +
   1.297 +  wysihtml.toolbar.Dialog_foreColorStyle = wysihtml.toolbar.Dialog.extend({
   1.298 +    multiselect: true,
   1.299 +
   1.300 +    _serialize: function() {
   1.301 +      var data    = {},
   1.302 +          fields  = this.container.querySelectorAll(SELECTOR_FIELDS),
   1.303 +          length  = fields.length,
   1.304 +          i       = 0;
   1.305 +
   1.306 +      for (; i<length; i++) {
   1.307 +        data[fields[i].getAttribute(ATTRIBUTE_FIELDS)] = fields[i].value;
   1.308 +      }
   1.309 +      return data;
   1.310 +    },
   1.311 +
   1.312 +    _interpolate: function(avoidHiddenFields) {
   1.313 +      var field, colourMode,
   1.314 +          styleParser = wysihtml.quirks.styleParser,
   1.315 +          focusedElement = document.querySelector(":focus"),
   1.316 +          fields         = this.container.querySelectorAll(SELECTOR_FIELDS),
   1.317 +          length         = fields.length,
   1.318 +          i              = 0,
   1.319 +          firstElement   = (this.elementToChange) ? ((wysihtml.lang.object(this.elementToChange).isArray()) ? this.elementToChange[0] : this.elementToChange) : null,
   1.320 +          colourStr       = (firstElement) ? firstElement.getAttribute("style") : null,
   1.321 +          colour          = (colourStr) ? styleParser.parseColor(colourStr, "color") : null;
   1.322 +
   1.323 +      for (; i<length; i++) {
   1.324 +        field = fields[i];
   1.325 +        // Never change elements where the user is currently typing in
   1.326 +        if (field === focusedElement) {
   1.327 +          continue;
   1.328 +        }
   1.329 +        // Don't update hidden fields3
   1.330 +        if (avoidHiddenFields && field.type === "hidden") {
   1.331 +          continue;
   1.332 +        }
   1.333 +        if (field.getAttribute(ATTRIBUTE_FIELDS) === "color") {
   1.334 +          colourMode = (field.dataset.colormode || "rgb").toLowerCase();
   1.335 +          colourMode = colourMode === "hex" ? "hash" : colourMode;
   1.336 +
   1.337 +          if (colour) {
   1.338 +            field.value = styleParser.unparseColor(colour, colourMode);
   1.339 +          } else {
   1.340 +            field.value = styleParser.unparseColor([0, 0, 0], colourMode);
   1.341 +          }
   1.342 +        }
   1.343 +      }
   1.344 +    }
   1.345 +
   1.346 +  });
   1.347 +})(wysihtml);
   1.348 +
   1.349 +/**
   1.350 + * Converts speech-to-text and inserts this into the editor
   1.351 + * As of now (2011/03/25) this only is supported in Chrome >= 11
   1.352 + *
   1.353 + * Note that it sends the recorded audio to the google speech recognition api:
   1.354 + * http://stackoverflow.com/questions/4361826/does-chrome-have-buil-in-speech-recognition-for-input-type-text-x-webkit-speec
   1.355 + *
   1.356 + * Current HTML5 draft can be found here
   1.357 + * http://lists.w3.org/Archives/Public/public-xg-htmlspeech/2011Feb/att-0020/api-draft.html
   1.358 + *
   1.359 + * "Accessing Google Speech API Chrome 11"
   1.360 + * http://mikepultz.com/2011/03/accessing-google-speech-api-chrome-11/
   1.361 + */
   1.362 +(function(wysihtml) {
   1.363 +  var dom = wysihtml.dom;
   1.364 +
   1.365 +  var linkStyles = {
   1.366 +    position: "relative"
   1.367 +  };
   1.368 +
   1.369 +  var wrapperStyles = {
   1.370 +    left:     0,
   1.371 +    margin:   0,
   1.372 +    opacity:  0,
   1.373 +    overflow: "hidden",
   1.374 +    padding:  0,
   1.375 +    position: "absolute",
   1.376 +    top:      0,
   1.377 +    zIndex:   1
   1.378 +  };
   1.379 +
   1.380 +  var inputStyles = {
   1.381 +    cursor:     "inherit",
   1.382 +    fontSize:   "50px",
   1.383 +    height:     "50px",
   1.384 +    marginTop:  "-25px",
   1.385 +    outline:    0,
   1.386 +    padding:    0,
   1.387 +    position:   "absolute",
   1.388 +    right:      "-4px",
   1.389 +    top:        "50%"
   1.390 +  };
   1.391 +
   1.392 +  var inputAttributes = {
   1.393 +    "x-webkit-speech": "",
   1.394 +    "speech":          ""
   1.395 +  };
   1.396 +
   1.397 +  wysihtml.toolbar.Speech = function(parent, link) {
   1.398 +    var input = document.createElement("input");
   1.399 +    if (!wysihtml.browser.supportsSpeechApiOn(input)) {
   1.400 +      link.style.display = "none";
   1.401 +      return;
   1.402 +    }
   1.403 +    var lang = parent.editor.textarea.element.getAttribute("lang");
   1.404 +    if (lang) {
   1.405 +      inputAttributes.lang = lang;
   1.406 +    }
   1.407 +
   1.408 +    var wrapper = document.createElement("div");
   1.409 +
   1.410 +    wysihtml.lang.object(wrapperStyles).merge({
   1.411 +      width:  link.offsetWidth  + "px",
   1.412 +      height: link.offsetHeight + "px"
   1.413 +    });
   1.414 +
   1.415 +    dom.insert(input).into(wrapper);
   1.416 +    dom.insert(wrapper).into(link);
   1.417 +
   1.418 +    dom.setStyles(inputStyles).on(input);
   1.419 +    dom.setAttributes(inputAttributes).on(input);
   1.420 +
   1.421 +    dom.setStyles(wrapperStyles).on(wrapper);
   1.422 +    dom.setStyles(linkStyles).on(link);
   1.423 +
   1.424 +    var eventName = "onwebkitspeechchange" in input ? "webkitspeechchange" : "speechchange";
   1.425 +    dom.observe(input, eventName, function() {
   1.426 +      parent.execCommand("insertText", input.value);
   1.427 +      input.value = "";
   1.428 +    });
   1.429 +
   1.430 +    dom.observe(input, "click", function(event) {
   1.431 +      if (dom.hasClass(link, "wysihtml-command-disabled")) {
   1.432 +        event.preventDefault();
   1.433 +      }
   1.434 +
   1.435 +      event.stopPropagation();
   1.436 +    });
   1.437 +  };
   1.438 +})(wysihtml);
   1.439 +
   1.440 +/**
   1.441 + * Toolbar
   1.442 + *
   1.443 + * @param {Object} parent Reference to instance of Editor instance
   1.444 + * @param {Element} container Reference to the toolbar container element
   1.445 + *
   1.446 + * @example
   1.447 + *    <div id="toolbar">
   1.448 + *      <a data-wysihtml-command="createLink">insert link</a>
   1.449 + *      <a data-wysihtml-command="formatBlock" data-wysihtml-command-value="h1">insert h1</a>
   1.450 + *    </div>
   1.451 + *
   1.452 + *    <script>
   1.453 + *      var toolbar = new wysihtml.toolbar.Toolbar(editor, document.getElementById("toolbar"));
   1.454 + *    </script>
   1.455 + */
   1.456 +(function(wysihtml) {
   1.457 +  var CLASS_NAME_COMMAND_DISABLED   = "wysihtml-command-disabled",
   1.458 +      CLASS_NAME_COMMANDS_DISABLED  = "wysihtml-commands-disabled",
   1.459 +      CLASS_NAME_COMMAND_ACTIVE     = "wysihtml-command-active",
   1.460 +      CLASS_NAME_ACTION_ACTIVE      = "wysihtml-action-active",
   1.461 +      dom                           = wysihtml.dom;
   1.462 +
   1.463 +  wysihtml.toolbar.Toolbar = Base.extend(
   1.464 +    /** @scope wysihtml.toolbar.Toolbar.prototype */ {
   1.465 +    constructor: function(editor, container, showOnInit) {
   1.466 +      this.editor     = editor;
   1.467 +      this.container  = typeof(container) === "string" ? document.getElementById(container) : container;
   1.468 +      this.composer   = editor.composer;
   1.469 +
   1.470 +      this._getLinks("command");
   1.471 +      this._getLinks("action");
   1.472 +
   1.473 +      this._observe();
   1.474 +      if (showOnInit) { this.show(); }
   1.475 +
   1.476 +      if (editor.config.classNameCommandDisabled != null) {
   1.477 +        CLASS_NAME_COMMAND_DISABLED = editor.config.classNameCommandDisabled;
   1.478 +      }
   1.479 +      if (editor.config.classNameCommandsDisabled != null) {
   1.480 +        CLASS_NAME_COMMANDS_DISABLED = editor.config.classNameCommandsDisabled;
   1.481 +      }
   1.482 +      if (editor.config.classNameCommandActive != null) {
   1.483 +        CLASS_NAME_COMMAND_ACTIVE = editor.config.classNameCommandActive;
   1.484 +      }
   1.485 +      if (editor.config.classNameActionActive != null) {
   1.486 +        CLASS_NAME_ACTION_ACTIVE = editor.config.classNameActionActive;
   1.487 +      }
   1.488 +
   1.489 +      var speechInputLinks  = this.container.querySelectorAll("[data-wysihtml-command=insertSpeech]"),
   1.490 +          length            = speechInputLinks.length,
   1.491 +          i                 = 0;
   1.492 +      for (; i<length; i++) {
   1.493 +        new wysihtml.toolbar.Speech(this, speechInputLinks[i]);
   1.494 +      }
   1.495 +    },
   1.496 +
   1.497 +    _getLinks: function(type) {
   1.498 +      var links   = this[type + "Links"] = wysihtml.lang.array(this.container.querySelectorAll("[data-wysihtml-" + type + "]")).get(),
   1.499 +          length  = links.length,
   1.500 +          i       = 0,
   1.501 +          mapping = this[type + "Mapping"] = {},
   1.502 +          link,
   1.503 +          group,
   1.504 +          name,
   1.505 +          value,
   1.506 +          dialog,
   1.507 +          tracksBlankValue;
   1.508 +
   1.509 +      for (; i<length; i++) {
   1.510 +        link    = links[i];
   1.511 +        name    = link.getAttribute("data-wysihtml-" + type);
   1.512 +        value   = link.getAttribute("data-wysihtml-" + type + "-value");
   1.513 +        tracksBlankValue   = link.getAttribute("data-wysihtml-" + type + "-blank-value");
   1.514 +        group   = this.container.querySelector("[data-wysihtml-" + type + "-group='" + name + "']");
   1.515 +        dialog  = this._getDialog(link, name);
   1.516 +
   1.517 +        mapping[name + ":" + value] = {
   1.518 +          link:   link,
   1.519 +          group:  group,
   1.520 +          name:   name,
   1.521 +          value:  value,
   1.522 +          tracksBlankValue: tracksBlankValue,
   1.523 +          dialog: dialog,
   1.524 +          state:  false
   1.525 +        };
   1.526 +      }
   1.527 +    },
   1.528 +
   1.529 +    _getDialog: function(link, command) {
   1.530 +      var that          = this,
   1.531 +          dialogElement = this.container.querySelector("[data-wysihtml-dialog='" + command + "']"),
   1.532 +          dialog, caretBookmark;
   1.533 +
   1.534 +      if (dialogElement) {
   1.535 +        if (wysihtml.toolbar["Dialog_" + command]) {
   1.536 +            dialog = new wysihtml.toolbar["Dialog_" + command](link, dialogElement);
   1.537 +        } else {
   1.538 +            dialog = new wysihtml.toolbar.Dialog(link, dialogElement);
   1.539 +        }
   1.540 +
   1.541 +        dialog.on("show", function() {
   1.542 +          caretBookmark = that.composer.selection.getBookmark();
   1.543 +          that.editor.fire("show:dialog", { command: command, dialogContainer: dialogElement, commandLink: link });
   1.544 +        });
   1.545 +
   1.546 +        dialog.on("save", function(attributes) {
   1.547 +          if (caretBookmark) {
   1.548 +            that.composer.selection.setBookmark(caretBookmark);
   1.549 +          }
   1.550 +          that._execCommand(command, attributes);
   1.551 +          that.editor.fire("save:dialog", { command: command, dialogContainer: dialogElement, commandLink: link });
   1.552 +          that._hideAllDialogs();
   1.553 +          that._preventInstantFocus();
   1.554 +          caretBookmark = undefined;
   1.555 +
   1.556 +        });
   1.557 +
   1.558 +        dialog.on("cancel", function() {
   1.559 +          if (caretBookmark) {
   1.560 +            that.composer.selection.setBookmark(caretBookmark);
   1.561 +          }
   1.562 +          that.editor.fire("cancel:dialog", { command: command, dialogContainer: dialogElement, commandLink: link });
   1.563 +          caretBookmark = undefined;
   1.564 +          that._preventInstantFocus();
   1.565 +        });
   1.566 +
   1.567 +        dialog.on("hide", function() {
   1.568 +          that.editor.fire("hide:dialog", { command: command, dialogContainer: dialogElement, commandLink: link });
   1.569 +          caretBookmark = undefined;
   1.570 +        });
   1.571 +
   1.572 +      }
   1.573 +      return dialog;
   1.574 +    },
   1.575 +
   1.576 +    /**
   1.577 +     * @example
   1.578 +     *    var toolbar = new wysihtml.Toolbar();
   1.579 +     *    // Insert a <blockquote> element or wrap current selection in <blockquote>
   1.580 +     *    toolbar.execCommand("formatBlock", "blockquote");
   1.581 +     */
   1.582 +    execCommand: function(command, commandValue) {
   1.583 +      if (this.commandsDisabled) {
   1.584 +        return;
   1.585 +      }
   1.586 +
   1.587 +      this._execCommand(command, commandValue);
   1.588 +    },
   1.589 +
   1.590 +    _execCommand: function(command, commandValue) {
   1.591 +      // Make sure that composer is focussed (false => don't move caret to the end)
   1.592 +      this.editor.focus(false);
   1.593 +
   1.594 +      this.composer.commands.exec(command, commandValue);
   1.595 +      this._updateLinkStates();
   1.596 +    },
   1.597 +
   1.598 +    execAction: function(action) {
   1.599 +      var editor = this.editor;
   1.600 +      if (action === "change_view") {
   1.601 +        if (editor.currentView === editor.textarea || editor.currentView === "source") {
   1.602 +          editor.fire("change_view", "composer");
   1.603 +        } else {
   1.604 +          editor.fire("change_view", "textarea");
   1.605 +        }
   1.606 +      }
   1.607 +      if (action == "showSource") {
   1.608 +          editor.fire("showSource");
   1.609 +      }
   1.610 +    },
   1.611 +
   1.612 +    _observe: function() {
   1.613 +      var that      = this,
   1.614 +          editor    = this.editor,
   1.615 +          container = this.container,
   1.616 +          links     = this.commandLinks.concat(this.actionLinks),
   1.617 +          length    = links.length,
   1.618 +          i         = 0;
   1.619 +
   1.620 +      for (; i<length; i++) {
   1.621 +        // 'javascript:;' and unselectable=on Needed for IE, but done in all browsers to make sure that all get the same css applied
   1.622 +        // (you know, a:link { ... } doesn't match anchors with missing href attribute)
   1.623 +        if (links[i].nodeName === "A") {
   1.624 +          dom.setAttributes({
   1.625 +            href:         "javascript:;",
   1.626 +            unselectable: "on"
   1.627 +          }).on(links[i]);
   1.628 +        } else {
   1.629 +          dom.setAttributes({ unselectable: "on" }).on(links[i]);
   1.630 +        }
   1.631 +      }
   1.632 +
   1.633 +      // Needed for opera and chrome
   1.634 +      dom.delegate(container, "[data-wysihtml-command], [data-wysihtml-action]", "mousedown", function(event) { event.preventDefault(); });
   1.635 +
   1.636 +      dom.delegate(container, "[data-wysihtml-command]", "click", function(event) {
   1.637 +        var state,
   1.638 +            link          = this,
   1.639 +            command       = link.getAttribute("data-wysihtml-command"),
   1.640 +            commandValue  = link.getAttribute("data-wysihtml-command-value"),
   1.641 +            commandObj = that.commandMapping[command + ":" + commandValue];
   1.642 +
   1.643 +        if (commandValue || !commandObj.dialog) {
   1.644 +          that.execCommand(command, commandValue);
   1.645 +        } else {
   1.646 +          state = getCommandState(that.composer, commandObj);
   1.647 +          commandObj.dialog.show(state);
   1.648 +        }
   1.649 +
   1.650 +        event.preventDefault();
   1.651 +      });
   1.652 +
   1.653 +      dom.delegate(container, "[data-wysihtml-action]", "click", function(event) {
   1.654 +        var action = this.getAttribute("data-wysihtml-action");
   1.655 +        that.execAction(action);
   1.656 +        event.preventDefault();
   1.657 +      });
   1.658 +
   1.659 +      editor.on("interaction:composer", function(event) {
   1.660 +        if (!that.preventFocus) {
   1.661 +          that._updateLinkStates();
   1.662 +        }
   1.663 +      });
   1.664 +
   1.665 +      this._ownerDocumentClick = function(event) {
   1.666 +        if (!wysihtml.dom.contains(that.container, event.target) && !wysihtml.dom.contains(that.composer.element, event.target)) {
   1.667 +          that._updateLinkStates();
   1.668 +          that._preventInstantFocus();
   1.669 +        }
   1.670 +      };
   1.671 +
   1.672 +      this.container.ownerDocument.addEventListener("click", this._ownerDocumentClick, false);
   1.673 +      this.editor.on("destroy:composer", this.destroy.bind(this));
   1.674 +
   1.675 +      if (this.editor.config.handleTables) {
   1.676 +        editor.on("tableselect:composer", function() {
   1.677 +            that.container.querySelectorAll('[data-wysihtml-hiddentools="table"]')[0].style.display = "";
   1.678 +        });
   1.679 +        editor.on("tableunselect:composer", function() {
   1.680 +            that.container.querySelectorAll('[data-wysihtml-hiddentools="table"]')[0].style.display = "none";
   1.681 +        });
   1.682 +      }
   1.683 +
   1.684 +      editor.on("change_view", function(currentView) {
   1.685 +        // Set timeout needed in order to let the blur event fire first
   1.686 +          setTimeout(function() {
   1.687 +            that.commandsDisabled = (currentView !== "composer");
   1.688 +            that._updateLinkStates();
   1.689 +            if (that.commandsDisabled) {
   1.690 +              dom.addClass(container, CLASS_NAME_COMMANDS_DISABLED);
   1.691 +            } else {
   1.692 +              dom.removeClass(container, CLASS_NAME_COMMANDS_DISABLED);
   1.693 +            }
   1.694 +          }, 0);
   1.695 +      });
   1.696 +    },
   1.697 +
   1.698 +    destroy: function() {
   1.699 +      this.container.ownerDocument.removeEventListener("click", this._ownerDocumentClick, false);
   1.700 +    },
   1.701 +
   1.702 +    _hideAllDialogs: function() {
   1.703 +      var commandMapping      = this.commandMapping;
   1.704 +      for (var i in commandMapping) {
   1.705 +        if (commandMapping[i].dialog) {
   1.706 +          commandMapping[i].dialog.hide();
   1.707 +        }
   1.708 +      }
   1.709 +    },
   1.710 +
   1.711 +    _preventInstantFocus: function() {
   1.712 +      this.preventFocus = true;
   1.713 +      setTimeout(function() {
   1.714 +        this.preventFocus = false;
   1.715 +      }.bind(this),0);
   1.716 +    },
   1.717 +
   1.718 +    _updateLinkStates: function() {
   1.719 +
   1.720 +      var i, state, action, command, displayDialogAttributeValue,
   1.721 +          commandMapping      = this.commandMapping,
   1.722 +          composer            = this.composer,
   1.723 +          actionMapping       = this.actionMapping;
   1.724 +      // every millisecond counts... this is executed quite often
   1.725 +      for (i in commandMapping) {
   1.726 +        command = commandMapping[i];
   1.727 +        if (this.commandsDisabled) {
   1.728 +          state = false;
   1.729 +          dom.removeClass(command.link, CLASS_NAME_COMMAND_ACTIVE);
   1.730 +          if (command.group) {
   1.731 +            dom.removeClass(command.group, CLASS_NAME_COMMAND_ACTIVE);
   1.732 +          }
   1.733 +          if (command.dialog) {
   1.734 +            command.dialog.hide();
   1.735 +          }
   1.736 +        } else {
   1.737 +          state = this.composer.commands.state(command.name, command.value);
   1.738 +          dom.removeClass(command.link, CLASS_NAME_COMMAND_DISABLED);
   1.739 +          if (command.group) {
   1.740 +            dom.removeClass(command.group, CLASS_NAME_COMMAND_DISABLED);
   1.741 +          }
   1.742 +        }
   1.743 +        if (command.state === state && !command.tracksBlankValue) {
   1.744 +          continue;
   1.745 +        }
   1.746 +
   1.747 +        command.state = state;
   1.748 +        if (state) {
   1.749 +          if (command.tracksBlankValue) {
   1.750 +            dom.removeClass(command.link, CLASS_NAME_COMMAND_ACTIVE);
   1.751 +          } else {
   1.752 +            dom.addClass(command.link, CLASS_NAME_COMMAND_ACTIVE);
   1.753 +            if (command.group) {
   1.754 +              dom.addClass(command.group, CLASS_NAME_COMMAND_ACTIVE);
   1.755 +            }
   1.756 +            // commands with fixed value can not have a dialog.
   1.757 +            if (command.dialog && (typeof command.value === "undefined" || command.value === null)) {
   1.758 +              if (state && typeof state === "object") {
   1.759 +                state = getCommandState(composer, command);
   1.760 +                command.state = state;
   1.761 +
   1.762 +                // If dialog has dataset.showdialogonselection set as true,
   1.763 +                // Dialog displays on text state becoming active regardless of clobal showToolbarDialogsOnSelection options value
   1.764 +                displayDialogAttributeValue = command.dialog.container.dataset ? command.dialog.container.dataset.showdialogonselection : false;
   1.765 +
   1.766 +                if (composer.config.showToolbarDialogsOnSelection || displayDialogAttributeValue) {
   1.767 +                  command.dialog.show(state);
   1.768 +                } else {
   1.769 +                  command.dialog.update(state);
   1.770 +                }
   1.771 +              } else {
   1.772 +                command.dialog.hide();
   1.773 +              }
   1.774 +            }
   1.775 +          }
   1.776 +        } else {
   1.777 +          if (command.tracksBlankValue) {
   1.778 +            dom.addClass(command.link, CLASS_NAME_COMMAND_ACTIVE);
   1.779 +          } else {
   1.780 +            dom.removeClass(command.link, CLASS_NAME_COMMAND_ACTIVE);
   1.781 +            if (command.group) {
   1.782 +              dom.removeClass(command.group, CLASS_NAME_COMMAND_ACTIVE);
   1.783 +            }
   1.784 +            // commands with fixed value can not have a dialog.
   1.785 +            if (command.dialog && !command.value) {
   1.786 +              command.dialog.hide();
   1.787 +            }
   1.788 +          }
   1.789 +        }
   1.790 +      }
   1.791 +
   1.792 +      for (i in actionMapping) {
   1.793 +        action = actionMapping[i];
   1.794 +
   1.795 +        if (action.name === "change_view") {
   1.796 +          action.state = this.editor.currentView === this.editor.textarea || this.editor.currentView === "source";
   1.797 +          if (action.state) {
   1.798 +            dom.addClass(action.link, CLASS_NAME_ACTION_ACTIVE);
   1.799 +          } else {
   1.800 +            dom.removeClass(action.link, CLASS_NAME_ACTION_ACTIVE);
   1.801 +          }
   1.802 +        }
   1.803 +      }
   1.804 +    },
   1.805 +
   1.806 +    show: function() {
   1.807 +      this.container.style.display = "";
   1.808 +    },
   1.809 +
   1.810 +    hide: function() {
   1.811 +      this.container.style.display = "none";
   1.812 +    }
   1.813 +  });
   1.814 +
   1.815 +  function getCommandState (composer, command) {
   1.816 +    var state = composer.commands.state(command.name, command.value);
   1.817 +
   1.818 +    // Grab first and only object/element in state array, otherwise convert state into boolean
   1.819 +    // to avoid showing a dialog for multiple selected elements which may have different attributes
   1.820 +    // eg. when two links with different href are selected, the state will be an array consisting of both link elements
   1.821 +    // but the dialog interface can only update one
   1.822 +    if (!command.dialog.multiselect && wysihtml.lang.object(state).isArray()) {
   1.823 +      state = state.length === 1 ? state[0] : true;
   1.824 +    }
   1.825 +
   1.826 +    return state;
   1.827 +  }
   1.828 +
   1.829 +  // Extend defaults
   1.830 +
   1.831 +  // Id of the toolbar element, pass falsey value if you don't want any toolbar logic
   1.832 +  wysihtml.Editor.prototype.defaults.toolbar = undefined;
   1.833 +
   1.834 +  // Whether toolbar is displayed after init by script automatically.
   1.835 +  // Can be set to false if toolobar is set to display only on editable area focus
   1.836 +  wysihtml.Editor.prototype.defaults.showToolbarAfterInit = true;
   1.837 +
   1.838 +  // With default toolbar it shows dialogs in toolbar when their related text format state becomes active (click on link in text opens link dialogue)
   1.839 +  wysihtml.Editor.prototype.defaults.showToolbarDialogsOnSelection= true;
   1.840 +
   1.841 +  // Bind toolbar initiation on editor instance creation
   1.842 +  wysihtml.extendEditor(function(editor) {
   1.843 +    if (editor.config.toolbar) {
   1.844 +      editor.toolbar = new wysihtml.toolbar.Toolbar(editor, editor.config.toolbar, editor.config.showToolbarAfterInit);
   1.845 +      editor.on('destroy:composer', function() {
   1.846 +        if (editor && editor.toolbar) {
   1.847 +          editor.toolbar.destroy();
   1.848 +        }
   1.849 +      });
   1.850 +    }
   1.851 +  });
   1.852 +
   1.853 +})(wysihtml);

Impressum / About Us