liquid_feedback_frontend
view static/wysihtml/wysihtml.toolbar.js @ 1357:3b6575d156bc
Fixed automatic unit assignment 2
| author | bsw | 
|---|---|
| date | Mon Aug 06 13:39:56 2018 +0200 (2018-08-06) | 
| parents | 32cc544d5a5b | 
| children | 
 line source
     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  *    <!-- Toolbar link -->
     9  *    <a data-wysihtml-command="insertImage">insert an image</a>
    10  *
    11  *    <!-- Dialog -->
    12  *    <div data-wysihtml-dialog="insertImage" style="display: none;">
    13  *      <label>
    14  *        URL: <input data-wysihtml-dialog-field="src" value="http://">
    15  *      </label>
    16  *      <label>
    17  *        Alternative text: <input data-wysihtml-dialog-field="alt" value="">
    18  *      </label>
    19  *    </div>
    20  *
    21  *    <script>
    22  *      var dialog = new wysihtml.toolbar.Dialog(
    23  *        document.querySelector("[data-wysihtml-command='insertImage']"),
    24  *        document.querySelector("[data-wysihtml-dialog='insertImage']")
    25  *      );
    26  *      dialog.observe("save", function(attributes) {
    27  *        // do something
    28  *      });
    29  *    </script>
    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";
    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     },
    46     _observe: function() {
    47       if (this._observed) {
    48         return;
    49       }
    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           };
    60       dom.observe(that.link, "click", function() {
    61         if (dom.hasClass(that.link, CLASS_NAME_OPENED)) {
    62           setTimeout(function() { that.hide(); }, 0);
    63         }
    64       });
    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       });
    76       dom.delegate(this.container, "[data-wysihtml-dialog-action=save]", "click", callbackWrapper);
    78       dom.delegate(this.container, "[data-wysihtml-dialog-action=cancel]", "click", function(event) {
    79         that.cancel();
    80         event.preventDefault();
    81         event.stopPropagation();
    82       });
    84       this._observed = true;
    85     },
    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;
    97       for (; i<length; i++) {
    98         data[fields[i].getAttribute(ATTRIBUTE_FIELDS)] = fields[i].value;
    99       }
   100       return data;
   101     },
   103     /**
   104      * Takes the attributes of the "elementToChange"
   105      * and inserts them in their corresponding dialog input fields
   106      *
   107      * Assume the "elementToChange" looks like this:
   108      *    <a href="http://www.google.com" target="_blank">foo</a>
   109      *
   110      * and we have the following dialog:
   111      *    <input type="text" data-wysihtml-dialog-field="href" value="">
   112      *    <input type="text" data-wysihtml-dialog-field="target" value="">
   113      *
   114      * after calling _interpolate() the dialog will look like this
   115      *    <input type="text" data-wysihtml-dialog-field="href" value="http://www.google.com">
   116      *    <input type="text" data-wysihtml-dialog-field="target" value="_blank">
   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<length; i++) {
   130         field = fields[i];
   132         // Never change elements where the user is currently typing in
   133         if (field === focusedElement) {
   134           continue;
   135         }
   137         // Don't update hidden fields
   138         // See https://github.com/xing/wysihtml5/pull/14
   139         if (avoidHiddenFields && field.type === "hidden") {
   140           continue;
   141         }
   143         fieldName = field.getAttribute(ATTRIBUTE_FIELDS);
   144         newValue  = (this.elementToChange && typeof(this.elementToChange) !== 'boolean') ? (this.elementToChange.getAttribute(fieldName) || "") : field.defaultValue;
   145         field.value = newValue;
   146       }
   147     },
   149     update: function (elementToChange) {
   150       this.elementToChange = elementToChange ? elementToChange : this.elementToChange;
   151       this._interpolate();
   152     },
   154     /**
   155      * Show the dialog element
   156      */
   157     show: function(elementToChange) {
   158       var firstField  = this.container.querySelector(SELECTOR_FORM_ELEMENTS);
   160       this._observe();
   161       this.update(elementToChange);
   163       dom.addClass(this.link, CLASS_NAME_OPENED);
   164       this.container.style.display = "";
   165       this.isOpen = true;
   166       this.fire("show");
   168       if (firstField && !elementToChange) {
   169         try {
   170           firstField.focus();
   171         } catch(e) {}
   172       }
   173     },
   175     /**
   176      * Hide the dialog element
   177      */
   178     _hide: function(focus) {
   179       this.elementToChange = null;
   180       dom.removeClass(this.link, CLASS_NAME_OPENED);
   181       this.container.style.display = "none";
   182       this.isOpen = false;
   183     },
   185     hide: function() {
   186       this._hide();
   187       this.fire("hide");
   188     },
   190     cancel: function() {
   191       this._hide();
   192       this.fire("cancel");
   193     }
   194   });
   195 })(wysihtml); //jshint ignore:line
   197 (function(wysihtml) {
   198   var dom                     = wysihtml.dom,
   199       SELECTOR_FIELDS         = "[data-wysihtml-dialog-field]",
   200       ATTRIBUTE_FIELDS        = "data-wysihtml-dialog-field";
   202   wysihtml.toolbar.Dialog_bgColorStyle = wysihtml.toolbar.Dialog.extend({
   203     multiselect: true,
   205     _serialize: function() {
   206       var data    = {},
   207           fields  = this.container.querySelectorAll(SELECTOR_FIELDS),
   208           length  = fields.length,
   209           i       = 0;
   211       for (; i<length; i++) {
   212         data[fields[i].getAttribute(ATTRIBUTE_FIELDS)] = fields[i].value;
   213       }
   214       return data;
   215     },
   217     _interpolate: function(avoidHiddenFields) {
   218       var field,
   219           fieldName,
   220           newValue,
   221           focusedElement = document.querySelector(":focus"),
   222           fields         = this.container.querySelectorAll(SELECTOR_FIELDS),
   223           length         = fields.length,
   224           i              = 0,
   225           firstElement   = (this.elementToChange) ? ((wysihtml.lang.object(this.elementToChange).isArray()) ? this.elementToChange[0] : this.elementToChange) : null,
   226           colorStr       = (firstElement) ? firstElement.getAttribute('style') : null,
   227           color          = (colorStr) ? wysihtml.quirks.styleParser.parseColor(colorStr, "background-color") : null;
   229       for (; i<length; i++) {
   230         field = fields[i];
   231         // Never change elements where the user is currently typing in
   232         if (field === focusedElement) {
   233           continue;
   234         }
   235         // Don't update hidden fields3
   236         if (avoidHiddenFields && field.type === "hidden") {
   237           continue;
   238         }
   239         if (field.getAttribute(ATTRIBUTE_FIELDS) === "color") {
   240           if (color) {
   241             if (color[3] && color[3] != 1) {
   242               field.value = "rgba(" + color[0] + "," + color[1] + "," + color[2] + "," + color[3] + ");";
   243             } else {
   244               field.value = "rgb(" + color[0] + "," + color[1] + "," + color[2] + ");";
   245             }
   246           } else {
   247             field.value = "rgb(0,0,0);";
   248           }
   249         }
   250       }
   251     }
   253   });
   254 })(wysihtml);
   256 (function(wysihtml) {
   257   wysihtml.toolbar.Dialog_createTable = wysihtml.toolbar.Dialog.extend({
   258     show: function(elementToChange) {
   259       this.base(elementToChange);
   260     }
   261   });
   262 })(wysihtml);
   264 (function(wysihtml) {
   265   var dom                     = wysihtml.dom,
   266       SELECTOR_FIELDS         = "[data-wysihtml-dialog-field]",
   267       ATTRIBUTE_FIELDS        = "data-wysihtml-dialog-field";
   269   wysihtml.toolbar.Dialog_fontSizeStyle = wysihtml.toolbar.Dialog.extend({
   270     multiselect: true,
   272     _serialize: function() {
   273       return {"size" : this.container.querySelector('[data-wysihtml-dialog-field="size"]').value};
   274     },
   276     _interpolate: function(avoidHiddenFields) {
   277       var focusedElement = document.querySelector(":focus"),
   278           field          = this.container.querySelector("[data-wysihtml-dialog-field='size']"),
   279           firstElement   = (this.elementToChange) ? ((wysihtml.lang.object(this.elementToChange).isArray()) ? this.elementToChange[0] : this.elementToChange) : null,
   280           styleStr       = (firstElement) ? firstElement.getAttribute('style') : null,
   281           size           = (styleStr) ? wysihtml.quirks.styleParser.parseFontSize(styleStr) : null;
   283       if (field && field !== focusedElement && size && !(/^\s*$/).test(size)) {
   284         field.value = size;
   285       }
   286     }
   287   });
   288 })(wysihtml);
   290 (function(wysihtml) {
   291   var SELECTOR_FIELDS         = "[data-wysihtml-dialog-field]",
   292       ATTRIBUTE_FIELDS        = "data-wysihtml-dialog-field";
   294   wysihtml.toolbar.Dialog_foreColorStyle = wysihtml.toolbar.Dialog.extend({
   295     multiselect: true,
   297     _serialize: function() {
   298       var data    = {},
   299           fields  = this.container.querySelectorAll(SELECTOR_FIELDS),
   300           length  = fields.length,
   301           i       = 0;
   303       for (; i<length; i++) {
   304         data[fields[i].getAttribute(ATTRIBUTE_FIELDS)] = fields[i].value;
   305       }
   306       return data;
   307     },
   309     _interpolate: function(avoidHiddenFields) {
   310       var field, colourMode,
   311           styleParser = wysihtml.quirks.styleParser,
   312           focusedElement = document.querySelector(":focus"),
   313           fields         = this.container.querySelectorAll(SELECTOR_FIELDS),
   314           length         = fields.length,
   315           i              = 0,
   316           firstElement   = (this.elementToChange) ? ((wysihtml.lang.object(this.elementToChange).isArray()) ? this.elementToChange[0] : this.elementToChange) : null,
   317           colourStr       = (firstElement) ? firstElement.getAttribute("style") : null,
   318           colour          = (colourStr) ? styleParser.parseColor(colourStr, "color") : null;
   320       for (; i<length; i++) {
   321         field = fields[i];
   322         // Never change elements where the user is currently typing in
   323         if (field === focusedElement) {
   324           continue;
   325         }
   326         // Don't update hidden fields3
   327         if (avoidHiddenFields && field.type === "hidden") {
   328           continue;
   329         }
   330         if (field.getAttribute(ATTRIBUTE_FIELDS) === "color") {
   331           colourMode = (field.dataset.colormode || "rgb").toLowerCase();
   332           colourMode = colourMode === "hex" ? "hash" : colourMode;
   334           if (colour) {
   335             field.value = styleParser.unparseColor(colour, colourMode);
   336           } else {
   337             field.value = styleParser.unparseColor([0, 0, 0], colourMode);
   338           }
   339         }
   340       }
   341     }
   343   });
   344 })(wysihtml);
   346 /**
   347  * Converts speech-to-text and inserts this into the editor
   348  * As of now (2011/03/25) this only is supported in Chrome >= 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;
   362   var linkStyles = {
   363     position: "relative"
   364   };
   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   };
   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   };
   389   var inputAttributes = {
   390     "x-webkit-speech": "",
   391     "speech":          ""
   392   };
   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     }
   405     var wrapper = document.createElement("div");
   407     wysihtml.lang.object(wrapperStyles).merge({
   408       width:  link.offsetWidth  + "px",
   409       height: link.offsetHeight + "px"
   410     });
   412     dom.insert(input).into(wrapper);
   413     dom.insert(wrapper).into(link);
   415     dom.setStyles(inputStyles).on(input);
   416     dom.setAttributes(inputAttributes).on(input);
   418     dom.setStyles(wrapperStyles).on(wrapper);
   419     dom.setStyles(linkStyles).on(link);
   421     var eventName = "onwebkitspeechchange" in input ? "webkitspeechchange" : "speechchange";
   422     dom.observe(input, eventName, function() {
   423       parent.execCommand("insertText", input.value);
   424       input.value = "";
   425     });
   427     dom.observe(input, "click", function(event) {
   428       if (dom.hasClass(link, "wysihtml-command-disabled")) {
   429         event.preventDefault();
   430       }
   432       event.stopPropagation();
   433     });
   434   };
   435 })(wysihtml);
   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  *    <div id="toolbar">
   445  *      <a data-wysihtml-command="createLink">insert link</a>
   446  *      <a data-wysihtml-command="formatBlock" data-wysihtml-command-value="h1">insert h1</a>
   447  *    </div>
   448  *
   449  *    <script>
   450  *      var toolbar = new wysihtml.toolbar.Toolbar(editor, document.getElementById("toolbar"));
   451  *    </script>
   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;
   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;
   467       this._getLinks("command");
   468       this._getLinks("action");
   470       this._observe();
   471       if (showOnInit) { this.show(); }
   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       }
   486       var speechInputLinks  = this.container.querySelectorAll("[data-wysihtml-command=insertSpeech]"),
   487           length            = speechInputLinks.length,
   488           i                 = 0;
   489       for (; i<length; i++) {
   490         new wysihtml.toolbar.Speech(this, speechInputLinks[i]);
   491       }
   492     },
   494     _getLinks: function(type) {
   495       var links   = this[type + "Links"] = wysihtml.lang.array(this.container.querySelectorAll("[data-wysihtml-" + type + "]")).get(),
   496           length  = links.length,
   497           i       = 0,
   498           mapping = this[type + "Mapping"] = {},
   499           link,
   500           group,
   501           name,
   502           value,
   503           dialog,
   504           tracksBlankValue;
   506       for (; i<length; i++) {
   507         link    = links[i];
   508         name    = link.getAttribute("data-wysihtml-" + type);
   509         value   = link.getAttribute("data-wysihtml-" + type + "-value");
   510         tracksBlankValue   = link.getAttribute("data-wysihtml-" + type + "-blank-value");
   511         group   = this.container.querySelector("[data-wysihtml-" + type + "-group='" + name + "']");
   512         dialog  = this._getDialog(link, name);
   514         mapping[name + ":" + value] = {
   515           link:   link,
   516           group:  group,
   517           name:   name,
   518           value:  value,
   519           tracksBlankValue: tracksBlankValue,
   520           dialog: dialog,
   521           state:  false
   522         };
   523       }
   524     },
   526     _getDialog: function(link, command) {
   527       var that          = this,
   528           dialogElement = this.container.querySelector("[data-wysihtml-dialog='" + command + "']"),
   529           dialog, caretBookmark;
   531       if (dialogElement) {
   532         if (wysihtml.toolbar["Dialog_" + command]) {
   533             dialog = new wysihtml.toolbar["Dialog_" + command](link, dialogElement);
   534         } else {
   535             dialog = new wysihtml.toolbar.Dialog(link, dialogElement);
   536         }
   538         dialog.on("show", function() {
   539           caretBookmark = that.composer.selection.getBookmark();
   540           that.editor.fire("show:dialog", { command: command, dialogContainer: dialogElement, commandLink: link });
   541         });
   543         dialog.on("save", function(attributes) {
   544           if (caretBookmark) {
   545             that.composer.selection.setBookmark(caretBookmark);
   546           }
   547           that._execCommand(command, attributes);
   548           that.editor.fire("save:dialog", { command: command, dialogContainer: dialogElement, commandLink: link });
   549           that._hideAllDialogs();
   550           that._preventInstantFocus();
   551           caretBookmark = undefined;
   553         });
   555         dialog.on("cancel", function() {
   556           if (caretBookmark) {
   557             that.composer.selection.setBookmark(caretBookmark);
   558           }
   559           that.editor.fire("cancel:dialog", { command: command, dialogContainer: dialogElement, commandLink: link });
   560           caretBookmark = undefined;
   561           that._preventInstantFocus();
   562         });
   564         dialog.on("hide", function() {
   565           that.editor.fire("hide:dialog", { command: command, dialogContainer: dialogElement, commandLink: link });
   566           caretBookmark = undefined;
   567         });
   569       }
   570       return dialog;
   571     },
   573     /**
   574      * @example
   575      *    var toolbar = new wysihtml.Toolbar();
   576      *    // Insert a <blockquote> element or wrap current selection in <blockquote>
   577      *    toolbar.execCommand("formatBlock", "blockquote");
   578      */
   579     execCommand: function(command, commandValue) {
   580       if (this.commandsDisabled) {
   581         return;
   582       }
   584       this._execCommand(command, commandValue);
   585     },
   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);
   591       this.composer.commands.exec(command, commandValue);
   592       this._updateLinkStates();
   593     },
   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     },
   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;
   617       for (; i<length; i++) {
   618         // 'javascript:;' and unselectable=on Needed for IE, but done in all browsers to make sure that all get the same css applied
   619         // (you know, a:link { ... } doesn't match anchors with missing href attribute)
   620         if (links[i].nodeName === "A") {
   621           dom.setAttributes({
   622             href:         "javascript:;",
   623             unselectable: "on"
   624           }).on(links[i]);
   625         } else {
   626           dom.setAttributes({ unselectable: "on" }).on(links[i]);
   627         }
   628       }
   630       // Needed for opera and chrome
   631       dom.delegate(container, "[data-wysihtml-command], [data-wysihtml-action]", "mousedown", function(event) { event.preventDefault(); });
   633       dom.delegate(container, "[data-wysihtml-command]", "click", function(event) {
   634         var state,
   635             link          = this,
   636             command       = link.getAttribute("data-wysihtml-command"),
   637             commandValue  = link.getAttribute("data-wysihtml-command-value"),
   638             commandObj = that.commandMapping[command + ":" + commandValue];
   640         if (commandValue || !commandObj.dialog) {
   641           that.execCommand(command, commandValue);
   642         } else {
   643           state = getCommandState(that.composer, commandObj);
   644           commandObj.dialog.show(state);
   645         }
   647         event.preventDefault();
   648       });
   650       dom.delegate(container, "[data-wysihtml-action]", "click", function(event) {
   651         var action = this.getAttribute("data-wysihtml-action");
   652         that.execAction(action);
   653         event.preventDefault();
   654       });
   656       editor.on("interaction:composer", function(event) {
   657         if (!that.preventFocus) {
   658           that._updateLinkStates();
   659         }
   660       });
   662       this._ownerDocumentClick = function(event) {
   663         if (!wysihtml.dom.contains(that.container, event.target) && !wysihtml.dom.contains(that.composer.element, event.target)) {
   664           that._updateLinkStates();
   665           that._preventInstantFocus();
   666         }
   667       };
   669       this.container.ownerDocument.addEventListener("click", this._ownerDocumentClick, false);
   670       this.editor.on("destroy:composer", this.destroy.bind(this));
   672       if (this.editor.config.handleTables) {
   673         editor.on("tableselect:composer", function() {
   674             that.container.querySelectorAll('[data-wysihtml-hiddentools="table"]')[0].style.display = "";
   675         });
   676         editor.on("tableunselect:composer", function() {
   677             that.container.querySelectorAll('[data-wysihtml-hiddentools="table"]')[0].style.display = "none";
   678         });
   679       }
   681       editor.on("change_view", function(currentView) {
   682         // Set timeout needed in order to let the blur event fire first
   683           setTimeout(function() {
   684             that.commandsDisabled = (currentView !== "composer");
   685             that._updateLinkStates();
   686             if (that.commandsDisabled) {
   687               dom.addClass(container, CLASS_NAME_COMMANDS_DISABLED);
   688             } else {
   689               dom.removeClass(container, CLASS_NAME_COMMANDS_DISABLED);
   690             }
   691           }, 0);
   692       });
   693     },
   695     destroy: function() {
   696       this.container.ownerDocument.removeEventListener("click", this._ownerDocumentClick, false);
   697     },
   699     _hideAllDialogs: function() {
   700       var commandMapping      = this.commandMapping;
   701       for (var i in commandMapping) {
   702         if (commandMapping[i].dialog) {
   703           commandMapping[i].dialog.hide();
   704         }
   705       }
   706     },
   708     _preventInstantFocus: function() {
   709       this.preventFocus = true;
   710       setTimeout(function() {
   711         this.preventFocus = false;
   712       }.bind(this),0);
   713     },
   715     _updateLinkStates: function() {
   717       var i, state, action, command, displayDialogAttributeValue,
   718           commandMapping      = this.commandMapping,
   719           composer            = this.composer,
   720           actionMapping       = this.actionMapping;
   721       // every millisecond counts... this is executed quite often
   722       for (i in commandMapping) {
   723         command = commandMapping[i];
   724         if (this.commandsDisabled) {
   725           state = false;
   726           dom.removeClass(command.link, CLASS_NAME_COMMAND_ACTIVE);
   727           if (command.group) {
   728             dom.removeClass(command.group, CLASS_NAME_COMMAND_ACTIVE);
   729           }
   730           if (command.dialog) {
   731             command.dialog.hide();
   732           }
   733         } else {
   734           state = this.composer.commands.state(command.name, command.value);
   735           dom.removeClass(command.link, CLASS_NAME_COMMAND_DISABLED);
   736           if (command.group) {
   737             dom.removeClass(command.group, CLASS_NAME_COMMAND_DISABLED);
   738           }
   739         }
   740         if (command.state === state && !command.tracksBlankValue) {
   741           continue;
   742         }
   744         command.state = state;
   745         if (state) {
   746           if (command.tracksBlankValue) {
   747             dom.removeClass(command.link, CLASS_NAME_COMMAND_ACTIVE);
   748           } else {
   749             dom.addClass(command.link, CLASS_NAME_COMMAND_ACTIVE);
   750             if (command.group) {
   751               dom.addClass(command.group, CLASS_NAME_COMMAND_ACTIVE);
   752             }
   753             // commands with fixed value can not have a dialog.
   754             if (command.dialog && (typeof command.value === "undefined" || command.value === null)) {
   755               if (state && typeof state === "object") {
   756                 state = getCommandState(composer, command);
   757                 command.state = state;
   759                 // If dialog has dataset.showdialogonselection set as true,
   760                 // Dialog displays on text state becoming active regardless of clobal showToolbarDialogsOnSelection options value
   761                 displayDialogAttributeValue = command.dialog.container.dataset ? command.dialog.container.dataset.showdialogonselection : false;
   763                 if (composer.config.showToolbarDialogsOnSelection || displayDialogAttributeValue) {
   764                   command.dialog.show(state);
   765                 } else {
   766                   command.dialog.update(state);
   767                 }
   768               } else {
   769                 command.dialog.hide();
   770               }
   771             }
   772           }
   773         } else {
   774           if (command.tracksBlankValue) {
   775             dom.addClass(command.link, CLASS_NAME_COMMAND_ACTIVE);
   776           } else {
   777             dom.removeClass(command.link, CLASS_NAME_COMMAND_ACTIVE);
   778             if (command.group) {
   779               dom.removeClass(command.group, CLASS_NAME_COMMAND_ACTIVE);
   780             }
   781             // commands with fixed value can not have a dialog.
   782             if (command.dialog && !command.value) {
   783               command.dialog.hide();
   784             }
   785           }
   786         }
   787       }
   789       for (i in actionMapping) {
   790         action = actionMapping[i];
   792         if (action.name === "change_view") {
   793           action.state = this.editor.currentView === this.editor.textarea || this.editor.currentView === "source";
   794           if (action.state) {
   795             dom.addClass(action.link, CLASS_NAME_ACTION_ACTIVE);
   796           } else {
   797             dom.removeClass(action.link, CLASS_NAME_ACTION_ACTIVE);
   798           }
   799         }
   800       }
   801     },
   803     show: function() {
   804       this.container.style.display = "";
   805     },
   807     hide: function() {
   808       this.container.style.display = "none";
   809     }
   810   });
   812   function getCommandState (composer, command) {
   813     var state = composer.commands.state(command.name, command.value);
   815     // Grab first and only object/element in state array, otherwise convert state into boolean
   816     // to avoid showing a dialog for multiple selected elements which may have different attributes
   817     // eg. when two links with different href are selected, the state will be an array consisting of both link elements
   818     // but the dialog interface can only update one
   819     if (!command.dialog.multiselect && wysihtml.lang.object(state).isArray()) {
   820       state = state.length === 1 ? state[0] : true;
   821     }
   823     return state;
   824   }
   826   // Extend defaults
   828   // Id of the toolbar element, pass falsey value if you don't want any toolbar logic
   829   wysihtml.Editor.prototype.defaults.toolbar = undefined;
   831   // Whether toolbar is displayed after init by script automatically.
   832   // Can be set to false if toolobar is set to display only on editable area focus
   833   wysihtml.Editor.prototype.defaults.showToolbarAfterInit = true;
   835   // With default toolbar it shows dialogs in toolbar when their related text format state becomes active (click on link in text opens link dialogue)
   836   wysihtml.Editor.prototype.defaults.showToolbarDialogsOnSelection= true;
   838   // Bind toolbar initiation on editor instance creation
   839   wysihtml.extendEditor(function(editor) {
   840     if (editor.config.toolbar) {
   841       editor.toolbar = new wysihtml.toolbar.Toolbar(editor, editor.config.toolbar, editor.config.showToolbarAfterInit);
   842       editor.on('destroy:composer', function() {
   843         if (editor && editor.toolbar) {
   844           editor.toolbar.destroy();
   845         }
   846       });
   847     }
   848   });
   850 })(wysihtml);
