(function () { 'use strict'; var Button = MediumEditor.Extension.extend({ /* Button Options */ /* action: [string] * The action argument to pass to MediumEditor.execAction() * when the button is clicked */ action: undefined, /* aria: [string] * The value to add as the aria-label attribute of the button * element displayed in the toolbar. * This is also used as the tooltip for the button */ aria: undefined, /* tagNames: [Array] * NOTE: This is not used if useQueryState is set to true. * * Array of element tag names that would indicate that this * button has already been applied. If this action has already * been applied, the button will be displayed as 'active' in the toolbar * * Example: * For 'bold', if the text is ever within a or * tag that indicates the text is already bold. So the array * of tagNames for bold would be: ['b', 'strong'] */ tagNames: undefined, /* style: [Object] * NOTE: This is not used if useQueryState is set to true. * * A pair of css property & value(s) that indicate that this * button has already been applied. If this action has already * been applied, the button will be displayed as 'active' in the toolbar * Properties of the object: * prop [String]: name of the css property * value [String]: value(s) of the css property * multiple values can be separated by a '|' * * Example: * For 'bold', if the text is ever within an element with a 'font-weight' * style property set to '700' or 'bold', that indicates the text * is already bold. So the style object for bold would be: * { prop: 'font-weight', value: '700|bold' } */ style: undefined, /* useQueryState: [boolean] * Enables/disables whether this button should use the built-in * document.queryCommandState() method to determine whether * the action has already been applied. If the action has already * been applied, the button will be displayed as 'active' in the toolbar * * Example: * For 'bold', if this is set to true, the code will call: * document.queryCommandState('bold') which will return true if the * browser thinks the text is already bold, and false otherwise */ useQueryState: undefined, /* contentDefault: [string] * Default innerHTML to put inside the button */ contentDefault: undefined, /* contentFA: [string] * The innerHTML to use for the content of the button * if the `buttonLabels` option for MediumEditor is set to 'fontawesome' */ contentFA: undefined, /* classList: [Array] * An array of classNames (strings) to be added to the button */ classList: undefined, /* attrs: [object] * A set of key-value pairs to add to the button as custom attributes */ attrs: undefined, // The button constructor can optionally accept the name of a built-in button // (ie 'bold', 'italic', etc.) // When the name of a button is passed, it will initialize itself with the // configuration for that button constructor: function (options) { if (Button.isBuiltInButton(options)) { MediumEditor.Extension.call(this, this.defaults[options]); } else { MediumEditor.Extension.call(this, options); } }, init: function () { MediumEditor.Extension.prototype.init.apply(this, arguments); this.button = this.createButton(); this.on(this.button, 'click', this.handleClick.bind(this)); }, /* getButton: [function ()] * * If implemented, this function will be called when * the toolbar is being created. The DOM Element returned * by this function will be appended to the toolbar along * with any other buttons. */ getButton: function () { return this.button; }, getAction: function () { return (typeof this.action === 'function') ? this.action(this.base.options) : this.action; }, getAria: function () { return (typeof this.aria === 'function') ? this.aria(this.base.options) : this.aria; }, getTagNames: function () { return (typeof this.tagNames === 'function') ? this.tagNames(this.base.options) : this.tagNames; }, createButton: function () { var button = this.document.createElement('button'), content = this.contentDefault, ariaLabel = this.getAria(), buttonLabels = this.getEditorOption('buttonLabels'); // Add class names button.classList.add('medium-editor-action'); button.classList.add('medium-editor-action-' + this.name); if (this.classList) { this.classList.forEach(function (className) { button.classList.add(className); }); } // Add attributes button.setAttribute('data-action', this.getAction()); if (ariaLabel) { button.setAttribute('title', ariaLabel); button.setAttribute('aria-label', ariaLabel); } if (this.attrs) { Object.keys(this.attrs).forEach(function (attr) { button.setAttribute(attr, this.attrs[attr]); }, this); } if (buttonLabels === 'fontawesome' && this.contentFA) { content = this.contentFA; } button.innerHTML = content; return button; }, handleClick: function (event) { event.preventDefault(); event.stopPropagation(); var action = this.getAction(); if (action) { this.execAction(action); } }, isActive: function () { return this.button.classList.contains(this.getEditorOption('activeButtonClass')); }, setInactive: function () { this.button.classList.remove(this.getEditorOption('activeButtonClass')); delete this.knownState; }, setActive: function () { this.button.classList.add(this.getEditorOption('activeButtonClass')); delete this.knownState; }, queryCommandState: function () { var queryState = null; if (this.useQueryState) { queryState = this.base.queryCommandState(this.getAction()); } return queryState; }, isAlreadyApplied: function (node) { var isMatch = false, tagNames = this.getTagNames(), styleVals, computedStyle; if (this.knownState === false || this.knownState === true) { return this.knownState; } if (tagNames && tagNames.length > 0) { isMatch = tagNames.indexOf(node.nodeName.toLowerCase()) !== -1; } if (!isMatch && this.style) { styleVals = this.style.value.split('|'); computedStyle = this.window.getComputedStyle(node, null).getPropertyValue(this.style.prop); styleVals.forEach(function (val) { if (!this.knownState) { isMatch = (computedStyle.indexOf(val) !== -1); // text-decoration is not inherited by default // so if the computed style for text-decoration doesn't match // don't write to knownState so we can fallback to other checks if (isMatch || this.style.prop !== 'text-decoration') { this.knownState = isMatch; } } }, this); } return isMatch; } }); Button.isBuiltInButton = function (name) { return (typeof name === 'string') && MediumEditor.extensions.button.prototype.defaults.hasOwnProperty(name); }; MediumEditor.extensions.button = Button; }());