Object.prototype.hasOwnProperty
.\n *\n * @private\n * @type {Function}\n */\n var hasOwnProperty = Object.prototype.hasOwnProperty;\n /**\n * A reference to Array.prototype.slice
.\n *\n * @private\n * @type {Function}\n */\n var slice = Array.prototype.slice;\n\n /**\n * Creates an object which inherits the given prototype
.\n *\n * Optionally, the created object can be extended further with the specified properties
.\n *\n * @param {Object} prototype - the prototype to be inherited by the created object\n * @param {Object} [properties] - the optional properties to be extended by the created object\n * @return {Object} The newly created object.\n * @private\n */\n function createObject(prototype, properties) {\n var result;\n /* istanbul ignore next */\n if (typeof Object.create === 'function') {\n result = Object.create(prototype);\n } else {\n Constructor.prototype = prototype;\n result = new Constructor();\n Constructor.prototype = null;\n }\n\n if (properties) {\n extendObject(true, result, properties);\n }\n\n return result;\n }\n\n /**\n * Extends the constructor to which this method is associated with the prototype
and/or\n * statics
provided.\n *\n * If name
is provided, it will be used as the class name and can be accessed via a special\n * class_
property on the child constructor, otherwise the class name of the super constructor will be used\n * instead. The class name may also be used string representation for instances of the child constructor (via\n * toString
), but this is not applicable to the lite version of Nevis.\n *\n * If constructor
is provided, it will be used as the constructor for the child, otherwise a simple\n * constructor which only calls the super constructor will be used instead.\n *\n * The super constructor can be accessed via a special super_
property on the child constructor.\n *\n * @param {string} [name=this.class_] - the class name to be used for the child constructor\n * @param {Function} [constructor] - the constructor for the child\n * @param {Object} [prototype] - the prototype properties to be defined for the child\n * @param {Object} [statics] - the static properties to be defined for the child\n * @return {Function} The child constructor
provided or the one created if none was given.\n * @public\n */\n function extend(name, constructor, prototype, statics) {\n var superConstructor = this;\n\n if (typeof name !== 'string') {\n statics = prototype;\n prototype = constructor;\n constructor = name;\n name = null;\n }\n\n if (typeof constructor !== 'function') {\n statics = prototype;\n prototype = constructor;\n constructor = function() {\n return superConstructor.apply(this, arguments);\n };\n }\n\n extendObject(false, constructor, superConstructor, statics);\n\n constructor.prototype = createObject(superConstructor.prototype, prototype);\n constructor.prototype.constructor = constructor;\n\n constructor.class_ = name || superConstructor.class_;\n constructor.super_ = superConstructor;\n\n return constructor;\n }\n\n /**\n * Extends the specified target
object with the properties in each of the sources
provided.\n *\n * if any source is null
it will be ignored.\n *\n * @param {boolean} own - true
to only copy own properties from sources
onto\n * target
; otherwise false
\n * @param {Object} target - the target object which should be extended\n * @param {...Object} [sources] - the source objects whose properties are to be copied onto target
\n * @return {void}\n * @private\n */\n function extendObject(own, target, sources) {\n sources = slice.call(arguments, 2);\n\n var property;\n var source;\n\n for (var i = 0, length = sources.length; i < length; i++) {\n source = sources[i];\n\n for (property in source) {\n if (!own || hasOwnProperty.call(source, property)) {\n target[property] = source[property];\n }\n }\n }\n }\n\n var extend_1 = extend;\n\n /**\n * The base class from which all others should extend.\n *\n * @public\n * @constructor\n */\n function Nevis() {}\n Nevis.class_ = 'Nevis';\n Nevis.super_ = Object;\n\n /**\n * Extends the constructor to which this method is associated with the prototype
and/or\n * statics
provided.\n *\n * If name
is provided, it will be used as the class name and can be accessed via a special\n * class_
property on the child constructor, otherwise the class name of the super constructor will be used\n * instead. The class name may also be used string representation for instances of the child constructor (via\n * toString
), but this is not applicable to the lite version of Nevis.\n *\n * If constructor
is provided, it will be used as the constructor for the child, otherwise a simple\n * constructor which only calls the super constructor will be used instead.\n *\n * The super constructor can be accessed via a special super_
property on the child constructor.\n *\n * @param {string} [name=this.class_] - the class name to be used for the child constructor\n * @param {Function} [constructor] - the constructor for the child\n * @param {Object} [prototype] - the prototype properties to be defined for the child\n * @param {Object} [statics] - the static properties to be defined for the child\n * @return {Function} The child constructor
provided or the one created if none was given.\n * @public\n * @static\n * @memberof Nevis\n */\n Nevis.extend = extend_1;\n\n var nevis = Nevis;\n\n var lite = nevis;\n\n /**\n * Contains utility methods that are useful throughout the library.\n *\n * @public\n * @class\n * @extends Nevis\n */\n var Utilities = lite.extend(null, {\n\n /**\n * Iterates over own (not inherited) enumerable properties on the specified object
.\n *\n * Nothing happens if object
is null
.\n *\n * @param {Object} object - the object whose own properties are to be iterated over\n * @param {Utilities~ForOwnCallback} callback - the function to be called with the value and key for each own property\n * on object
\n * @param {Object} [context] - the value to use this
when executing callback
\n * @return {void}\n * @public\n * @static\n * @memberof Utilities\n */\n forOwn: function(object, callback, context) {\n if (!object) {\n return;\n }\n\n for (var key in object) {\n if (Utilities.hasOwn(object, key)) {\n callback.call(context, object[key], key, object);\n }\n }\n },\n\n /**\n * Returns whether the specified object
has a property with the specified name
as an own\n * (not inherited) property.\n *\n * @param {Object} object - the object on which the property is to be checked\n * @param {string} name - the name of the property to be checked\n * @return {boolean} true
if object
has an own property with name
.\n * @public\n * @static\n * @memberof Utilities\n */\n hasOwn: function(object, name) {\n return Object.prototype.hasOwnProperty.call(object, name);\n },\n\n /**\n * Left pads the string
provided with the given padding string for the specified number of\n * times
.\n *\n * @param {string} [string=\"\"] - the string to be padded\n * @param {number} [times=0] - the number of times to pad string
\n * @param {string} [padding=\" \"] - the padding string\n * @return {string} The padded string
.\n * @public\n * @static\n * @memberof Utilities\n */\n leftPad: function(string, times, padding) {\n if (string == null) {\n string = '';\n }\n if (times == null) {\n times = 0;\n }\n if (padding == null) {\n padding = ' ';\n }\n if (!padding) {\n return string;\n }\n\n for (var i = 0; i < times; i++) {\n string = padding + string;\n }\n\n return string;\n }\n\n });\n\n var Utilities_1 = Utilities;\n\n /**\n * Called for each own enumerable property on object
.\n *\n * @callback Utilities~ForOwnCallback\n * @param {*} value - the value of the property\n * @param {string} key - the name of the property\n * @param {Object} object - the object to which the property belongs\n * @return {void}\n */\n\n /**\n * Contains contextual information for a single conversion process.\n *\n * @param {Europa} europa - the {@link Europa} instance responsible for this conversion\n * @param {Europa~Options} options - the options to be used\n * @public\n * @class\n * @extends Nevis\n */\n var Conversion = lite.extend(function(europa, options) {\n /**\n * The {@link Europa} instance responsible for this {@link Conversion}.\n *\n * @public\n * @type {Europa}\n * @memberof Conversion#\n */\n this.europa = europa;\n\n /**\n * The options for this {@link Conversion}.\n *\n * @public\n * @type {Europa~Options}\n * @memberof Conversion#\n */\n this.options = options;\n\n /**\n * Whether the buffer is at the start of the current line.\n *\n * @public\n * @type {boolean}\n * @memberof Conversion#\n */\n this.atLeft = true;\n\n /**\n * Whether any white space should be removed from the start of the next output.\n *\n * @public\n * @type {boolean}\n * @memberof Conversion#\n */\n this.atNoWhiteSpace = true;\n\n /**\n * Whether the buffer is at the start of a paragraph.\n *\n * @public\n * @type {boolean}\n * @memberof Conversion#\n */\n this.atParagraph = true;\n\n /**\n * The conversion output buffer to which the Markdown will be written.\n *\n * @public\n * @type {string}\n * @memberof Conversion#\n */\n this.buffer = '';\n\n /**\n * The context for this {@link Conversion}.\n *\n * @public\n * @type {Object.string
to be output.\n *\n * @param {string} string - the string to be appended\n * @return {Conversion} A reference to this {@link Conversion} for chaining purposes.\n * @public\n * @memberof Conversion#\n */\n append: function(string) {\n if (this.last != null) {\n this.buffer += this.last;\n }\n\n this.last = string;\n\n return this;\n },\n\n /**\n * Appends a paragraph to the output buffer.\n *\n * @return {Conversion} A reference to this {@link Conversion} for chaining purposes.\n * @public\n * @memberof Conversion#\n */\n appendParagraph: function() {\n if (this.atParagraph) {\n return this;\n }\n\n if (!this.atLeft) {\n this.append(this.left);\n\n this.atLeft = true;\n }\n\n this.append(this.left);\n\n this.atNoWhiteSpace = true;\n this.atParagraph = true;\n\n return this;\n },\n\n /**\n * Outputs the specified string
to the buffer.\n *\n * Optionally, string
can be \"cleaned\" before being output. Doing so will replace any certain special\n * characters as well as some white space.\n *\n * @param {string} string - the string to be output\n * @param {boolean} [clean=false] - true
to clean string
; otherwise false
\n * @return {Conversion} A reference to this {@link Conversion} for chaining purposes.\n * @public\n * @memberof Conversion#\n */\n output: function(string, clean) {\n if (!string) {\n return this;\n }\n\n string = string.replace(/\\r\\n/g, '\\n');\n\n if (clean) {\n string = string.replace(/\\n([ \\t]*\\n)+/g, '\\n')\n .replace(/\\n[ \\t]+/g, '\\n')\n .replace(/[ \\t]+/g, ' ');\n\n Utilities_1.forOwn(Conversion.replacements, function(value, key) {\n string = string.replace(Conversion.replacementsRegExp[key], value);\n });\n }\n\n if (!this.inPreformattedBlock) {\n if (this.atNoWhiteSpace) {\n string = string.replace(/^[ \\t\\n]+/, '');\n } else if (/^[ \\t]*\\n/.test(string)) {\n string = string.replace(/^[ \\t\\n]+/, '\\n');\n } else {\n string = string.replace(/^[ \\t]+/, ' ');\n }\n }\n\n if (!string) {\n return this;\n }\n\n this.atLeft = /\\n$/.test(string);\n this.atNoWhiteSpace = /[ \\t\\n]$/.test(string);\n this.atParagraph = /\\n{2}$/.test(string);\n\n return this.append(string.replace(/\\n/g, this.left));\n },\n\n /**\n * Replaces the start of the current line with the string
provided.\n *\n * @param {string} string - the string to replace the start of the current line\n * @return {Conversion} A reference to this {@link Conversion} for chaining purposes.\n * @public\n * @memberof Conversion#\n */\n replaceLeft: function(string) {\n if (!this.atLeft) {\n this.append(this.left.replace(/[ ]{2,4}$/, string));\n\n this.atLeft = true;\n this.atNoWhiteSpace = true;\n this.atParagraph = true;\n } else if (this.last) {\n this.last = this.last.replace(/[ ]{2,4}$/, string);\n }\n\n return this;\n }\n\n }, {\n\n /**\n * A map of special characters and their replacements.\n *\n * @public\n * @static\n * @type {Object.element
.\n *\n * @param {Element} element - the current element to be set\n * @return {void}\n * @public\n * @memberof Conversion#\n * @alias element\n */\n set: function(element) {\n this._element = element;\n this._tagName = element && element.tagName ? element.tagName.toLowerCase() : null;\n }\n },\n\n tagName: {\n /**\n * Returns the name of the tag for the current element for this {@link Conversion}.\n *\n * The tag name will always be in lower case, when available.\n *\n * @return {string} The current element's tag name.\n * @public\n * @memberof Conversion#\n * @alias tagName\n */\n get: function() {\n return this._tagName;\n }\n },\n\n window: {\n /**\n * Returns the current window for this {@link Conversion}.\n *\n * This may not be the same window as is associated with the {@link Europa} instance as this window may be nested\n * (e.g. a frame).\n *\n * @return {Window} The current window.\n * @public\n * @memberof Conversion#\n * @alias window\n */\n get: function() {\n return this._window;\n },\n\n /**\n * Sets the current window for this {@link Conversion} to window
.\n *\n * This may not be the same window as is associated with the {@link Europa} instance as this window may be nested\n * (e.g. a frame).\n *\n * @param {Window} window - the window to be set\n * @return {void}\n * @public\n * @memberof Conversion#\n * @alias window\n */\n set: function(window) {\n this._window = window;\n this._document = window ? window.document : null;\n }\n }\n\n });\n\n Utilities_1.forOwn(Conversion.replacements, function(value, key) {\n Conversion.replacementsRegExp[key] = new RegExp(key, 'g');\n });\n\n var Conversion_1 = Conversion;\n\n /**\n * Contains utility methods that are useful when dealing with the DOM.\n *\n * @public\n * @class\n * @extends Nevis\n */\n var DOMUtilities = lite.extend(null, {\n\n /**\n * Checks whether the specified element
is currently visible using the window
provided.\n *\n * This is not a very sophisticated check and could easily be mistaken, but it should catch a lot of the most simple\n * cases.\n *\n * @param {Element} element - the element whose visibility is to be checked\n * @param {Window} window - the window to be used\n * @return {boolean} true
if element
is visible; otherwise false
.\n * @public\n * @static\n * @memberof DOMUtilities\n */\n isVisible: function(element, window) {\n var style = window.getComputedStyle(element);\n\n return style.getPropertyValue('display') !== 'none' && style.getPropertyValue('visibility') !== 'hidden';\n }\n\n });\n\n var DOMUtilities_1 = DOMUtilities;\n\n /**\n * Defines an available option.\n *\n * If defaultValue
is a function, it will be called if/when needed and the return value will be used as the\n * default value. If the default value is to be a function itself, then defaultValue
must return that\n * function.\n *\n * @param {string} name - the name to be used\n * @param {*} [defaultValue] - the default value to be used\n * @public\n * @class\n * @extends Nevis\n */\n var Option = lite.extend(function(name, defaultValue) {\n /**\n * The name for this {@link Option}.\n *\n * @public\n * @type {string}\n * @memberof Option#\n */\n this.name = name;\n\n this._defaultValue = defaultValue;\n });\n\n Object.defineProperty(Option.prototype, 'defaultValue', {\n /**\n * Returns the default value for this {@link Option}.\n *\n * @return {*} The default value.\n * @public\n * @memberof Option#\n * @alias defaultValue\n */\n get: function() {\n var defaultValue = this._defaultValue;\n\n return typeof defaultValue === 'function' ? defaultValue.call(this) : defaultValue;\n }\n });\n\n var Option_1 = Option;\n\n /**\n * Manages multiple {@link Option} instances that are intended to be used by multiple implementations/instances.\n *\n * @param {Option[]} options - the options to be used\n * @public\n * @class\n * @extends Nevis\n */\n var OptionParser = lite.extend(function(options) {\n /**\n * The available options for this {@link OptionParser}.\n *\n * @public\n * @type {Option[]}\n * @memberof OptionParser#\n */\n this.options = options;\n }, {\n\n /**\n * Returns whether an option with the specified name
is available.\n *\n * @param {string} name - the name of the {@link Option} whose existence is to be checked\n * @return {boolean} true
if an {@link Option} exists with name
; otherwise\n * false
.\n * @public\n * @memberof OptionParser#\n */\n exists: function(name) {\n return this.options.some(function(option) {\n return option.name === name;\n });\n },\n\n /**\n * Parses the specified options
, extracting only properties that match valid options and applying default\n * values where required.\n *\n * @param {Object} [options] - the options to be parsed\n * @return {Object.context
can be used to receive any state for a single element conversion from {@link Plugin#before}\n * and {@link Plugin#convert}.\n *\n * @param {Conversion} conversion - the current {@link Conversion}\n * @param {Object.context
can be used to pass any state for a single element conversion to {@link Plugin#convert} and\n * then to {@link Plugin#after}.\n *\n * @param {Conversion} conversion - the current {@link Conversion}\n * @param {Object.conversion
which can be used to provide control over\n * the conversion and returns whether the children of the element should be converted.\n *\n * context
can be used to pass any state for a single element conversion from {@link Plugin#before} to\n * {@link Plugin#after}.\n *\n * @param {Conversion} conversion - the current {@link Conversion}\n * @param {Object.true
if the children of the current element should be converted; otherwise\n * false
.\n * @public\n * @memberof Plugin#\n */\n convert: function(conversion, context) {\n return true;\n },\n\n /**\n * Returns the names of tags with which this {@link Plugin} should be registered to handle.\n *\n * @return {string[]} The names of supported tags.\n * @public\n * @memberof Plugin#\n */\n getTagNames: function() {\n return [];\n }\n\n });\n\n var Plugin_1 = Plugin;\n\n /**\n * A basic manager for {@link Service} implementations that are mapped to simple names.\n *\n * @public\n * @class\n * @extends Nevis\n */\n var ServiceManager = lite.extend(function() {\n this._services = {};\n }, {\n\n /**\n * Returns the {@link Service} being managed with the specified name
.\n *\n * @param {string} name - the name of the {@link Service} to be returned\n * @return {Service} The {@link Service} is being managed with name
.\n * @throws {Error} If no {@link Service} is being managed with name
.\n * @public\n * @memberof ServiceManager#\n */\n getService: function(name) {\n var service = this._services[name];\n if (!service) {\n throw new Error('Service is not being managed with name: ' + name);\n }\n\n return service;\n },\n\n /**\n * Sets the {@link Service} implementation to be managed for the specified name
to the\n * service
provided.\n *\n * @param {string} name - the name of the {@link Service} to be managed with name
\n * @param {Service} service - the {@link Service} implementation to be managed\n * @return {void}\n * @throws {Error} If a {@link Service} is already being managed with the same name
.\n * @public\n * @memberof ServiceManager#\n */\n setService: function(name, service) {\n if (this._services[name]) {\n throw new Error('Service is already managed with name: ' + name);\n }\n\n if (service) {\n this._services[name] = service;\n }\n }\n\n });\n\n var ServiceManager_1 = ServiceManager;\n\n var plugins = {};\n var serviceManager = new ServiceManager_1();\n\n /**\n * Enables configuration of a HTML to Markdown converter that supports HTML strings and DOM elements.\n *\n * @param {Europa~Options} [options] - the options to be used\n * @public\n * @class\n * @extends Nevis\n */\n var Europa = lite.extend(function(options) {\n this._options = new OptionParser_1([\n new Option_1('absolute', false),\n new Option_1('baseUri', function() {\n return serviceManager.getService('window').getDefaultBaseUri();\n }),\n new Option_1('inline', false)\n ])\n .parse(options);\n this._window = null;\n }, {\n\n /**\n * Converts the specified html
into Markdown based on the options configured for this {@link Europa}\n * instance.\n *\n * html
can either be an HTML string or a DOM element whose HTML contents are to be converted into\n * Markdown.\n *\n * @param {Element|string} html - the HTML (or element whose inner HTML is) to be converted into Markdown\n * @return {string} The Markdown converted from html
.\n * @public\n * @memberof Europa#\n */\n convert: function(html) {\n if (!html) {\n return '';\n }\n\n var document = this.document;\n var root;\n\n if (typeof html === 'string') {\n root = document.createElement('div');\n root.innerHTML = html;\n } else {\n root = html;\n }\n\n var conversion = new Conversion_1(this, this._options);\n var wrapper;\n\n if (!document.contains(root)) {\n wrapper = document.createElement('div');\n wrapper.style.display = 'none';\n wrapper.appendChild(root);\n\n document.body.appendChild(wrapper);\n }\n\n try {\n Utilities_1.forOwn(plugins, function(plugin) {\n plugin.beforeAll(conversion);\n });\n\n this.convertElement(root, conversion);\n\n Utilities_1.forOwn(plugins, function(plugin) {\n plugin.afterAll(conversion);\n });\n } finally {\n if (wrapper) {\n document.body.removeChild(wrapper);\n\n wrapper.removeChild(root);\n }\n }\n\n return conversion.append('').buffer.trim();\n },\n\n /**\n * Converts the specified element
and it's children into Markdown using the conversion
\n * provided.\n *\n * Nothing happens if element
is null
or is invisible (simplified detection used).\n *\n * @param {Element} element - the element (along well as it's children) to be converted into Markdown\n * @param {Conversion} conversion - the current {@link Conversion}\n * @return {void}\n * @public\n * @memberof Europa#\n */\n convertElement: function(element, conversion) {\n if (!element) {\n return;\n }\n\n var convertChildren = false;\n var window = this.window;\n var context, i, plugin, value;\n\n if (element.nodeType === window.Node.ELEMENT_NODE) {\n if (!DOMUtilities_1.isVisible(element, window)) {\n return;\n }\n\n conversion.element = element;\n\n context = {};\n plugin = plugins[conversion.tagName];\n convertChildren = true;\n\n if (plugin) {\n plugin.before(conversion, context);\n convertChildren = plugin.convert(conversion, context);\n }\n\n if (convertChildren) {\n for (i = 0; i < element.childNodes.length; i++) {\n this.convertElement(element.childNodes[i], conversion);\n }\n }\n\n if (plugin) {\n plugin.after(conversion, context);\n }\n } else if (element.nodeType === window.Node.TEXT_NODE) {\n value = element.nodeValue || '';\n\n if (conversion.inPreformattedBlock) {\n conversion.output(value);\n } else if (conversion.inCodeBlock) {\n conversion.output(value.replace(/`/g, '\\\\`'));\n } else {\n conversion.output(value, true);\n }\n }\n },\n\n /**\n * Releases the window used by this {@link Europa} instance.\n *\n * This allows closeable {@link WindowService} implementations to close the window and free up resources. However,\n * this instance can and will simply retrieve another window from the {@link WindowService} the next time it is\n * required (i.e. {@link Europa#convert} is called).\n *\n * @return {Europa} A reference to this {@link Europa} for chaining purposes.\n * @public\n * @memberof Europa#\n */\n release: function() {\n if (this._window) {\n serviceManager.getService('window').closeWindow(this._window);\n this._window = null;\n }\n\n return this;\n }\n\n }, {\n\n /**\n * A convient reference to {@link Plugin} exposed on {@link Europa} for cases where Europa Core is bundled.\n *\n * @public\n * @static\n * @type {Function}\n * @memberof Europa\n */\n Plugin: Plugin_1,\n\n /**\n * Registers the specified plugin
to be used by all {@link Europa} instances.\n *\n * If plugin
declares support for a tag name which already has a {@link Plugin} registered for it,\n * plugin
will replace the previously registered plugin, but only for conflicting tag names.\n *\n * @param {Plugin} plugin - the {@link Plugin} to be registered\n * @return {void}\n * @public\n * @static\n * @memberof Europa\n */\n register: function(plugin) {\n plugin.getTagNames().forEach(function(tag) {\n plugins[tag] = plugin;\n });\n },\n\n /**\n * Configures the service
provided to be used by all {@link Europa} instances.\n *\n * @param {Service} service - the {@link Service} to be configured\n * @return {void}\n * @throws {Error} If a {@link Service} has already been configured with the same name.\n * @public\n * @static\n * @memberof Europa\n */\n use: function(service) {\n serviceManager.setService(service.getName(), service);\n }\n\n });\n\n Object.defineProperties(Europa.prototype, {\n\n document: {\n /**\n * Returns the document to be used for HTML to Markdown conversion by this {@link Europa} instance.\n *\n * @return {Document} The document.\n * @public\n * @memberof Europa#\n * @alias document\n */\n get: function() {\n return this.window.document;\n }\n },\n\n window: {\n /**\n * Returns the window to be used for HTML to Markdown conversion by this {@link Europa} instance.\n *\n * @return {Window} The window.\n * @public\n * @memberof Europa#\n * @alias window\n */\n get: function() {\n if (!this._window) {\n this._window = serviceManager.getService('window').getWindow(this._options.baseUri);\n }\n\n return this._window;\n }\n }\n\n });\n\n var Europa_1$2 = Europa;\n\n /**\n * The options used by {@link Europa}.\n *\n * @typedef {Object} Europa~Options\n * @property {boolean} [absolute=false] - Whether absolute URLS should be used for anchors/images.\n * @property {string} [baseUri] - The base URI for the window. This is ignored in environments where the base URI cannot\n * be changed.\n * @property {boolean} [inline=false] - Whether anchor/image URLs are to be inserted inline.\n */\n\n /**\n * A {@link Plugin} which extracts the URL from an anchor. Anchors without an href
are treated as plain\n * text.\n *\n * If the absolute
option is enabled, then the URL extracted from the anchor will be absolute. Otherwise,\n * the URL will be exactly as it is in the href
attribute.\n *\n * If the inline
option is enabled, then the URL (and any title
on the anchor) will be\n * inserted immediately after the anchor contents (e.g. [foo](/bar)
). Otherwise, all unique URL and title\n * combinations will be indexed (e.g. [foo][anchor0]
) and the references will be output at the very end.\n *\n * @public\n * @class\n * @extends Plugin\n */\n var AnchorPlugin = Plugin_1.extend({\n\n /**\n * @override\n */\n after: function(conversion, context) {\n if (context.value != null) {\n conversion.output(']' + context.value);\n }\n },\n\n /**\n * @override\n */\n afterAll: function(conversion) {\n var anchors = conversion.context.anchors;\n if (!anchors.length) {\n return;\n }\n\n conversion.append('\\n\\n');\n\n for (var i = 0; i < anchors.length; i++) {\n conversion.append('[anchor' + i + ']: ' + anchors[i] + '\\n');\n }\n },\n\n /**\n * @override\n */\n beforeAll: function(conversion) {\n conversion.context.anchorMap = {};\n conversion.context.anchors = [];\n },\n\n /**\n * @override\n */\n convert: function(conversion, context) {\n var element = conversion.element;\n var options = conversion.options;\n var href = options.absolute ? element.href : element.getAttribute('href');\n if (!href) {\n return true;\n }\n\n var anchorMap = conversion.context.anchorMap;\n var anchors = conversion.context.anchors;\n var title = element.getAttribute('title');\n var value = title ? href + ' \"' + title + '\"' : href;\n var index;\n\n if (options.inline) {\n context.value = '(' + value + ')';\n } else {\n index = anchorMap[value];\n if (index == null) {\n index = anchors.push(value) - 1;\n\n anchorMap[value] = index;\n }\n\n context.value = '[anchor' + index + ']';\n }\n\n conversion.output('[');\n\n conversion.atNoWhiteSpace = true;\n\n return true;\n },\n\n /**\n * @override\n */\n getTagNames: function() {\n return [ 'a' ];\n }\n\n });\n\n Europa_1$2.register(new AnchorPlugin());\n\n /**\n * A {@link Plugin} which outputs the contents in a block quote.\n *\n * @public\n * @class\n * @extends Plugin\n */\n var BlockQuotePlugin = Plugin_1.extend({\n\n /**\n * @override\n */\n after: function(conversion, context) {\n conversion.atLeft = false;\n conversion.atParagraph = false;\n conversion.left = context.previousLeft;\n\n conversion.appendParagraph();\n },\n\n /**\n * @override\n */\n before: function(conversion, context) {\n context.previousLeft = conversion.left;\n },\n\n /**\n * @override\n */\n convert: function(conversion, context) {\n var value = '> ';\n\n conversion.left += value;\n\n if (conversion.atParagraph) {\n conversion.append(value);\n } else {\n conversion.appendParagraph();\n }\n\n return true;\n },\n\n /**\n * @override\n */\n getTagNames: function() {\n return [\n 'blockquote',\n 'dd'\n ];\n }\n\n });\n\n Europa_1$2.register(new BlockQuotePlugin());\n\n /**\n * A {@link Plugin} which outputs an inline line break.\n *\n * @public\n * @class\n * @extends Plugin\n */\n var BreakPlugin = Plugin_1.extend({\n\n /**\n * @override\n */\n convert: function(conversion, context) {\n conversion.append(' ' + conversion.left);\n\n conversion.atLeft = true;\n conversion.atNoWhiteSpace = true;\n\n return false;\n },\n\n /**\n * @override\n */\n getTagNames: function() {\n return [ 'br' ];\n }\n\n });\n\n Europa_1$2.register(new BreakPlugin());\n\n /**\n * A {@link Plugin} which outputs the contents in a code block.\n *\n * @public\n * @class\n * @extends Plugin\n */\n var CodePlugin = Plugin_1.extend({\n\n /**\n * @override\n */\n after: function(conversion, context) {\n if (!context.skipped) {\n conversion.inCodeBlock = context.previousInCodeBlock;\n\n conversion.output('`');\n }\n },\n\n /**\n * @override\n */\n before: function(conversion, context) {\n context.previousInCodeBlock = conversion.inCodeBlock;\n },\n\n /**\n * @override\n */\n convert: function(conversion, context) {\n if (conversion.inPreformattedBlock) {\n context.skipped = true;\n } else {\n conversion.output('`');\n\n conversion.inCodeBlock = true;\n }\n\n return true;\n },\n\n /**\n * @override\n */\n getTagNames: function() {\n return [\n 'code',\n 'kbd',\n 'samp'\n ];\n }\n\n });\n\n Europa_1$2.register(new CodePlugin());\n\n /**\n * A {@link Plugin} which outputs a definition term as strong text.\n *\n * @public\n * @class\n * @extends Plugin\n */\n var DefinitionTermPlugin = Plugin_1.extend({\n\n /**\n * @override\n */\n after: function(conversion, context) {\n conversion.output('**');\n },\n\n /**\n * @override\n */\n convert: function(conversion, context) {\n conversion.appendParagraph();\n\n conversion.output('**');\n\n conversion.atNoWhiteSpace = true;\n\n return true;\n },\n\n /**\n * @override\n */\n getTagNames: function() {\n return [ 'dt' ];\n }\n\n });\n\n Europa_1$2.register(new DefinitionTermPlugin());\n\n /**\n * A {@link Plugin} which outputs a details section.\n *\n * If the details has an open
attribute then all of its children are converted. Otherwise, only the nested\n * summary
, if any, will be converted.\n *\n * @public\n * @class\n * @extends Plugin\n */\n var DetailsPlugin = Plugin_1.extend({\n\n /**\n * @override\n */\n convert: function(conversion, context) {\n var element = conversion.element;\n\n conversion.appendParagraph();\n\n if (element.hasAttribute('open')) {\n return true;\n }\n\n var summary = element.querySelector('summary');\n conversion.europa.convertElement(summary, conversion);\n\n return false;\n },\n\n /**\n * @override\n */\n getTagNames: function() {\n return [ 'details' ];\n }\n\n });\n\n Europa_1$2.register(new DetailsPlugin());\n\n /**\n * A {@link Plugin} which outputs as emphasised text.\n *\n * @public\n * @class\n * @extends Plugin\n */\n var EmphasisPlugin = Plugin_1.extend({\n\n /**\n * @override\n */\n after: function(conversion, context) {\n conversion.output('_');\n },\n\n /**\n * @override\n */\n convert: function(conversion, context) {\n conversion.output('_');\n\n conversion.atNoWhiteSpace = true;\n\n return true;\n },\n\n /**\n * @override\n */\n getTagNames: function() {\n return [\n 'cite',\n 'dfn',\n 'em',\n 'i',\n 'u',\n 'var'\n ];\n }\n\n });\n\n Europa_1$2.register(new EmphasisPlugin());\n\n /**\n * A {@link Plugin} which simply ensures that no children elements are converted.\n *\n * @public\n * @class\n * @extends Plugin\n */\n var EmptyPlugin = Plugin_1.extend({\n\n /**\n * @override\n */\n convert: function(conversion, context) {\n return false;\n },\n\n /**\n * @override\n */\n getTagNames: function() {\n return [\n 'applet',\n 'area',\n 'audio',\n 'button',\n 'canvas',\n 'datalist',\n 'embed',\n 'head',\n 'input',\n 'map',\n 'menu',\n 'meter',\n 'noframes',\n 'noscript',\n 'object',\n 'optgroup',\n 'option',\n 'param',\n 'progress',\n 'rp',\n 'rt',\n 'ruby',\n 'script',\n 'select',\n 'style',\n 'textarea',\n 'title',\n 'video'\n ];\n }\n\n });\n\n Europa_1$2.register(new EmptyPlugin());\n\n /**\n * A {@link Plugin} which outputs the contents of nested frame.\n *\n * @public\n * @class\n * @extends Plugin\n */\n var FramePlugin = Plugin_1.extend({\n\n /**\n * @override\n */\n after: function(conversion, context) {\n conversion.window = context.previousWindow;\n },\n\n /**\n * @override\n */\n before: function(conversion, context) {\n context.previousWindow = conversion.window;\n },\n\n /**\n * @override\n */\n convert: function(conversion, context) {\n var window = conversion.element.contentWindow;\n\n if (window) {\n conversion.window = window;\n\n conversion.europa.convertElement(window.document.body, conversion);\n }\n\n return false;\n },\n\n /**\n * @override\n */\n getTagNames: function() {\n return [ 'frame', 'iframe' ];\n }\n\n });\n\n Europa_1$2.register(new FramePlugin());\n\n /**\n * A {@link Plugin} which outputs a heading of various levels.\n *\n * @public\n * @class\n * @extends Plugin\n */\n var HeadingPlugin = Plugin_1.extend({\n\n /**\n * @override\n */\n convert: function(conversion, context) {\n var level = parseInt(conversion.tagName.match(/([1-6])$/)[1], 10);\n\n conversion.appendParagraph();\n\n var heading = '';\n for (var i = 0; i < level; i++) {\n heading += '#';\n }\n\n conversion.output(heading + ' ');\n\n return true;\n },\n\n /**\n * @override\n */\n getTagNames: function() {\n return [\n 'h1',\n 'h2',\n 'h3',\n 'h4',\n 'h5',\n 'h6'\n ];\n }\n\n });\n\n Europa_1$2.register(new HeadingPlugin());\n\n /**\n * A {@link Plugin} which outputs a horizontal rule.\n *\n * @public\n * @class\n * @extends Plugin\n */\n var HorizontalRulePlugin = Plugin_1.extend({\n\n /**\n * @override\n */\n convert: function(conversion, context) {\n conversion\n .appendParagraph()\n .output('---')\n .appendParagraph();\n\n return false;\n },\n\n /**\n * @override\n */\n getTagNames: function() {\n return [ 'hr' ];\n }\n\n });\n\n Europa_1$2.register(new HorizontalRulePlugin());\n\n /**\n * A {@link Plugin} which extracts the URL from an image.\n *\n * If the absolute
option is enabled, then the URL extracted from the image will be absolute. Otherwise,\n * the URL will be exactly as it is in the src
attribute.\n *\n * If the inline
option is enabled, then the URL will be inserted immediately after the alt
on\n * the image (e.g. 
). Otherwise, all unique URLs will be indexed\n * (e.g. ![foo][image0]
) and the references will be output at the very end.\n *\n * @public\n * @class\n * @extends Plugin\n */\n var ImagePlugin = Plugin_1.extend({\n\n /**\n * @override\n */\n afterAll: function(conversion) {\n var images = conversion.context.images;\n if (!images.length) {\n return;\n }\n\n conversion.append('\\n\\n');\n\n for (var i = 0; i < images.length; i++) {\n conversion.append('[image' + i + ']: ' + images[i] + '\\n');\n }\n },\n\n /**\n * @override\n */\n beforeAll: function(conversion) {\n conversion.context.imageMap = {};\n conversion.context.images = [];\n },\n\n /**\n * @override\n */\n convert: function(conversion, context) {\n var element = conversion.element;\n var options = conversion.options;\n var source = options.absolute ? element.src : element.getAttribute('src');\n if (!source) {\n return false;\n }\n\n var alternativeText = element.getAttribute('alt') || '';\n var imageMap = conversion.context.imageMap;\n var images = conversion.context.images;\n var title = element.getAttribute('title');\n var value = title ? source + ' \"' + title + '\"' : source;\n var index;\n\n if (options.inline) {\n value = '(' + value + ')';\n } else {\n index = imageMap[value];\n if (index == null) {\n index = images.push(value) - 1;\n\n imageMap[value] = index;\n }\n\n value = '[image' + index + ']';\n }\n\n conversion.output('![' + alternativeText + ']' + value);\n\n return false;\n },\n\n /**\n * @override\n */\n getTagNames: function() {\n return [ 'img' ];\n }\n\n });\n\n Europa_1$2.register(new ImagePlugin());\n\n /**\n * A {@link Plugin} which outputs a list item. The prefix for the list item will vary depending on what type of list the\n * item is contained within.\n *\n * @public\n * @class\n * @extends Plugin\n */\n var ListItemPlugin = Plugin_1.extend({\n\n /**\n * @override\n */\n convert: function(conversion, context) {\n var value = conversion.inOrderedList ? conversion.listIndex++ + '. ' : '* ';\n\n if (!conversion.atLeft) {\n conversion.append(conversion.left.replace(/[ ]{2,4}$/, '\\n'));\n\n conversion.atLeft = true;\n conversion.atNoWhiteSpace = true;\n conversion.atParagraph = true;\n } else if (conversion.last) {\n conversion.last = conversion.last.replace(/[ ]{2,4}$/, '\\n');\n }\n\n conversion.append(Utilities_1.leftPad(value, (conversion.listDepth - 1) * 2));\n\n return true;\n },\n\n /**\n * @override\n */\n getTagNames: function() {\n return [ 'li' ];\n }\n\n });\n\n Europa_1$2.register(new ListItemPlugin());\n\n /**\n * A {@link Plugin} which outputs an ordered list.\n *\n * @public\n * @class\n * @extends Plugin\n */\n var OrderedListPlugin = Plugin_1.extend({\n\n /**\n * @override\n */\n after: function(conversion, context) {\n conversion.inOrderedList = context.previousInOrderedList;\n conversion.listIndex = context.previousListIndex;\n conversion.listDepth--;\n },\n\n /**\n * @override\n */\n before: function(conversion, context) {\n context.previousInOrderedList = conversion.inOrderedList;\n context.previousListIndex = conversion.listIndex;\n },\n\n /**\n * @override\n */\n convert: function(conversion, context) {\n if (conversion.listDepth === 0) {\n conversion.appendParagraph();\n }\n\n conversion.inOrderedList = true;\n conversion.listIndex = 1;\n conversion.listDepth++;\n\n return true;\n },\n\n /**\n * @override\n */\n getTagNames: function() {\n return [ 'ol' ];\n }\n\n });\n\n Europa_1$2.register(new OrderedListPlugin());\n\n /**\n * A {@link Plugin} which outputs a paragraph.\n *\n * @public\n * @class\n * @extends Plugin\n */\n var ParagraphPlugin = Plugin_1.extend({\n\n /**\n * @override\n */\n convert: function(conversion, context) {\n conversion.appendParagraph();\n\n return true;\n },\n\n /**\n * @override\n */\n getTagNames: function() {\n return [\n 'address',\n 'article',\n 'aside',\n 'div',\n 'fieldset',\n 'footer',\n 'header',\n 'nav',\n 'p',\n 'section'\n ];\n }\n\n });\n\n Europa_1$2.register(new ParagraphPlugin());\n\n /**\n * A {@link Plugin} which outputs the contents in a preformatted block.\n *\n * @public\n * @class\n * @extends Plugin\n */\n var PreformattedPlugin = Plugin_1.extend({\n\n /**\n * @override\n */\n after: function(conversion, context) {\n conversion.atLeft = false;\n conversion.atParagraph = false;\n conversion.inPreformattedBlock = context.previousInPreformattedBlock;\n conversion.left = context.previousLeft;\n\n conversion.appendParagraph();\n },\n\n /**\n * @override\n */\n before: function(conversion, context) {\n context.previousInPreformattedBlock = conversion.inPreformattedBlock;\n context.previousLeft = conversion.left;\n },\n\n /**\n * @override\n */\n convert: function(conversion, context) {\n var value = ' ';\n\n conversion.left += value;\n\n if (conversion.atParagraph) {\n conversion.append(value);\n } else {\n conversion.appendParagraph();\n }\n\n return true;\n },\n\n /**\n * @override\n */\n getTagNames: function() {\n return [ 'pre' ];\n }\n\n });\n\n Europa_1$2.register(new PreformattedPlugin());\n\n /**\n * A {@link Plugin} which outputs as quoted text.\n *\n * @public\n * @class\n * @extends Plugin\n */\n var QuotePlugin = Plugin_1.extend({\n\n /**\n * @override\n */\n after: function(conversion, context) {\n conversion.output('\"');\n },\n\n /**\n * @override\n */\n convert: function(conversion, context) {\n conversion.output('\"');\n\n conversion.atNoWhiteSpace = true;\n\n return true;\n },\n\n /**\n * @override\n */\n getTagNames: function() {\n return [ 'q' ];\n }\n\n });\n\n Europa_1$2.register(new QuotePlugin());\n\n /**\n * A {@link Plugin} which outputs as strong text.\n *\n * @public\n * @class\n * @extends Plugin\n */\n var StrongPlugin = Plugin_1.extend({\n\n /**\n * @override\n */\n after: function(conversion, context) {\n conversion.output('**');\n },\n\n /**\n * @override\n */\n convert: function(conversion, context) {\n conversion.output('**');\n\n conversion.atNoWhiteSpace = true;\n\n return true;\n },\n\n /**\n * @override\n */\n getTagNames: function() {\n return [\n 'b',\n 'strong'\n ];\n }\n\n });\n\n Europa_1$2.register(new StrongPlugin());\n\n /**\n * A {@link Plugin} which outputs an unordered list.\n *\n * @public\n * @class\n * @extends Plugin\n */\n var UnorderedListPlugin = Plugin_1.extend({\n\n /**\n * @override\n */\n after: function(conversion, context) {\n conversion.inOrderedList = context.previousInOrderedList;\n conversion.listIndex = context.previousListIndex;\n conversion.listDepth--;\n },\n\n /**\n * @override\n */\n before: function(conversion, context) {\n context.previousInOrderedList = conversion.inOrderedList;\n context.previousListIndex = conversion.listIndex;\n },\n\n /**\n * @override\n */\n convert: function(conversion, context) {\n if (conversion.listDepth === 0) {\n conversion.appendParagraph();\n }\n\n conversion.inOrderedList = false;\n conversion.listIndex = 1;\n conversion.listDepth++;\n\n return true;\n },\n\n /**\n * @override\n */\n getTagNames: function() {\n return [ 'ul' ];\n }\n\n });\n\n Europa_1$2.register(new UnorderedListPlugin());\n\n var index = Europa_1$2;\n\n /**\n * Defines a service contract that must be met by all implementations.\n *\n * @public\n * @class\n * @extends Nevis\n */\n var Service = lite.extend({\n\n /**\n * Returns the name of this {@link Service}.\n *\n * @return {string} The service name.\n * @public\n * @abstract\n * @memberof Service#\n */\n getName: function() {}\n\n });\n\n var Service_1 = Service;\n\n /**\n * A service used to retrieve the window object for converting HTML to Markdown and, optionally, to close it upon\n * destruction of the {@link Europa} instance. This can be useful to free up resources as/when required in an artificial\n * browser environment.\n *\n * @public\n * @class\n * @extends Service\n */\n var WindowService = Service_1.extend({\n\n /**\n * Closes the specified window
but only if this {@link WindowService} is closeable.\n *\n * @param {Window} window - the window to be closed\n * @return {void}\n * @public\n * @memberof WindowService#\n */\n closeWindow: function(window) {\n if (this.isCloseable(window)) {\n window.close();\n }\n },\n\n /**\n * Returns the default base URI for windows provided by this {@link WindowService}.\n *\n * Implementations of {@link WindowService} must override this method with their own specific logic.\n *\n * @return {string} The default base URI.\n * @public\n * @abstract\n * @memberof WindowService#\n */\n getDefaultBaseUri: function() {},\n\n /**\n * @override\n */\n getName: function() {\n return 'window';\n },\n\n /**\n * Returns a window to be used for converting HTML to Markdown using the base URI provided.\n *\n * It's important to note that the base URI cannot be changed in some environments, in which case baseUri
\n * will be ignored.\n *\n * Implementations of {@link WindowService} must override this method with their own specific logic.\n *\n * @param {string} baseUri - the base URI to be used\n * @return {Window} The window.\n * @public\n * @abstract\n * @memberof WindowService#\n */\n getWindow: function(baseUri) {},\n\n /**\n * Returns whether the specified window
which was retrieved by this {@link WindowService} is closeable.\n *\n * The default implementation of this method will always return false
.\n *\n * @param {Window} window - the window to be checked\n * @return {boolean} true
if window
is closeable; otherwise false
.\n * @public\n * @memberof WindowService#\n */\n isCloseable: function(window) {\n return false;\n }\n\n });\n\n var WindowService_1 = WindowService;\n\n /**\n * An implementation of {@link WindowService} intended for use within a browser environment.\n *\n * @public\n * @class\n * @extends WindowService\n */\n var BrowserWindowService = WindowService_1.extend({\n\n /**\n * @override\n */\n getDefaultBaseUri: function() {\n return window.document.baseURI;\n },\n\n /**\n * @override\n */\n getWindow: function(baseUri) {\n return window;\n }\n\n });\n\n var BrowserWindowService_1 = BrowserWindowService;\n\n index.use(new BrowserWindowService_1());\n\n var Europa_1 = index;\n\n return Europa_1;\n\n})));\n\n//# sourceMappingURL=europa.js.map","/*global require,module*/\r\n'use strict';\r\nvar CodeMirror = require('codemirror');\r\nrequire('codemirror/addon/edit/continuelist.js');\r\nrequire('./codemirror/tablist');\r\nrequire('codemirror/addon/display/fullscreen.js');\r\nrequire('codemirror/mode/markdown/markdown.js');\r\nrequire('codemirror/addon/mode/overlay.js');\r\nrequire('codemirror/addon/display/placeholder.js');\r\nrequire('codemirror/addon/selection/mark-selection.js');\r\nrequire('codemirror/mode/gfm/gfm.js');\r\nrequire('codemirror/mode/xml/xml.js');\r\nvar CodeMirrorSpellChecker = require('codemirror-spell-checker');\r\nvar marked = require('marked');\r\n\r\n\r\n// Some variables\r\nvar isMac = /Mac/.test(navigator.platform);\r\n\r\n// Mapping of actions that can be bound to keyboard shortcuts or toolbar buttons\r\nvar bindings = {\r\n 'toggleBold': toggleBold,\r\n 'toggleItalic': toggleItalic,\r\n 'drawLink': drawLink,\r\n 'toggleHeadingSmaller': toggleHeadingSmaller,\r\n 'toggleHeadingBigger': toggleHeadingBigger,\r\n 'drawImage': drawImage,\r\n 'toggleBlockquote': toggleBlockquote,\r\n 'toggleOrderedList': toggleOrderedList,\r\n 'toggleUnorderedList': toggleUnorderedList,\r\n 'toggleCodeBlock': toggleCodeBlock,\r\n 'togglePreview': togglePreview,\r\n 'toggleStrikethrough': toggleStrikethrough,\r\n 'toggleHeading1': toggleHeading1,\r\n 'toggleHeading2': toggleHeading2,\r\n 'toggleHeading3': toggleHeading3,\r\n 'cleanBlock': cleanBlock,\r\n 'drawTable': drawTable,\r\n 'drawHorizontalRule': drawHorizontalRule,\r\n 'undo': undo,\r\n 'redo': redo,\r\n 'toggleSideBySide': toggleSideBySide,\r\n 'toggleFullScreen': toggleFullScreen\r\n};\r\n\r\nvar shortcuts = {\r\n 'toggleBold': 'Cmd-B',\r\n 'toggleItalic': 'Cmd-I',\r\n 'drawLink': 'Cmd-K',\r\n 'toggleHeadingSmaller': 'Cmd-H',\r\n 'toggleHeadingBigger': 'Shift-Cmd-H',\r\n 'cleanBlock': 'Cmd-E',\r\n 'drawImage': 'Cmd-Alt-I',\r\n 'toggleBlockquote': 'Cmd-\\'',\r\n 'toggleOrderedList': 'Cmd-Alt-L',\r\n 'toggleUnorderedList': 'Cmd-L',\r\n 'toggleCodeBlock': 'Cmd-Alt-C',\r\n 'togglePreview': 'Cmd-P',\r\n 'toggleSideBySide': 'F9',\r\n 'toggleFullScreen': 'F11'\r\n};\r\n\r\nvar getBindingName = function (f) {\r\n for (var key in bindings) {\r\n if (bindings[key] === f) {\r\n return key;\r\n }\r\n }\r\n return null;\r\n};\r\n\r\nvar isMobile = function () {\r\n var check = false;\r\n (function (a) {\r\n if (/(android|bb\\d+|meego).+mobile|avantgo|bada\\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\\.(browser|link)|vodafone|wap|windows ce|xda|xiino|android|ipad|playbook|silk/i.test(a) || /1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw-(n|u)|c55\\/|capi|ccwa|cdm-|cell|chtm|cldc|cmd-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc-s|devi|dica|dmob|do(c|p)o|ds(12|-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(-|_)|g1 u|g560|gene|gf-5|g-mo|go(\\.w|od)|gr(ad|un)|haie|hcit|hd-(m|p|t)|hei-|hi(pt|ta)|hp( i|ip)|hs-c|ht(c(-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i-(20|go|ma)|i230|iac( |-|\\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\\/)|klon|kpt |kwc-|kyo(c|k)|le(no|xi)|lg( g|\\/(k|l|u)|50|54|-[a-w])|libw|lynx|m1-w|m3ga|m50\\/|ma(te|ui|xo)|mc(01|21|ca)|m-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|-([1-8]|c))|phil|pire|pl(ay|uc)|pn-2|po(ck|rt|se)|prox|psio|pt-g|qa-a|qc(07|12|21|32|60|-[2-7]|i-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h-|oo|p-)|sdk\\/|se(c(-|0|1)|47|mc|nd|ri)|sgh-|shar|sie(-|m)|sk-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h-|v-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl-|tdg-|tel(i|m)|tim-|t-mo|to(pl|sh)|ts(70|m-|m3|m5)|tx-9|up(\\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas-|your|zeto|zte-/i.test(a.substr(0, 4))) check = true;\r\n })(navigator.userAgent || navigator.vendor || window.opera);\r\n return check;\r\n};\r\n\r\n\r\n/**\r\n * Fix shortcut. Mac use Command, others use Ctrl.\r\n */\r\nfunction fixShortcut(name) {\r\n if (isMac) {\r\n name = name.replace('Ctrl', 'Cmd');\r\n } else {\r\n name = name.replace('Cmd', 'Ctrl');\r\n }\r\n return name;\r\n}\r\n\r\n\r\n/**\r\n * Create icon element for toolbar.\r\n */\r\nfunction createIcon(options, enableTooltips, shortcuts) {\r\n options = options || {};\r\n var el = document.createElement('button');\r\n enableTooltips = (enableTooltips == undefined) ? true : enableTooltips;\r\n\r\n if (options.title && enableTooltips) {\r\n el.title = createTooltip(options.title, options.action, shortcuts);\r\n\r\n if (isMac) {\r\n el.title = el.title.replace('Ctrl', '⌘');\r\n el.title = el.title.replace('Alt', '⌥');\r\n }\r\n }\r\n\r\n if (options.noDisable) {\r\n el.classList.add('no-disable');\r\n }\r\n\r\n if (options.noMobile) {\r\n el.classList.add('no-mobile');\r\n }\r\n\r\n el.tabIndex = -1;\r\n\r\n // Create icon element and append as a child to the button\r\n var icon = document.createElement('i');\r\n icon.className = options.className;\r\n el.appendChild(icon);\r\n\r\n return el;\r\n}\r\n\r\nfunction createSep() {\r\n var el = document.createElement('i');\r\n el.className = 'separator';\r\n el.innerHTML = '|';\r\n return el;\r\n}\r\n\r\nfunction createTooltip(title, action, shortcuts) {\r\n var actionName;\r\n var tooltip = title;\r\n\r\n if (action) {\r\n actionName = getBindingName(action);\r\n if (shortcuts[actionName]) {\r\n tooltip += ' (' + fixShortcut(shortcuts[actionName]) + ')';\r\n }\r\n }\r\n\r\n return tooltip;\r\n}\r\n\r\n/**\r\n * The state of CodeMirror at the given position.\r\n */\r\nfunction getState(cm, pos) {\r\n pos = pos || cm.getCursor('start');\r\n var stat = cm.getTokenAt(pos);\r\n if (!stat.type) return {};\r\n\r\n var types = stat.type.split(' ');\r\n\r\n var ret = {},\r\n data, text;\r\n for (var i = 0; i < types.length; i++) {\r\n data = types[i];\r\n if (data === 'strong') {\r\n ret.bold = true;\r\n } else if (data === 'variable-2') {\r\n text = cm.getLine(pos.line);\r\n if (/^\\s*\\d+\\.\\s/.test(text)) {\r\n ret['ordered-list'] = true;\r\n } else {\r\n ret['unordered-list'] = true;\r\n }\r\n } else if (data === 'atom') {\r\n ret.quote = true;\r\n } else if (data === 'em') {\r\n ret.italic = true;\r\n } else if (data === 'quote') {\r\n ret.quote = true;\r\n } else if (data === 'strikethrough') {\r\n ret.strikethrough = true;\r\n } else if (data === 'comment') {\r\n ret.code = true;\r\n } else if (data === 'link') {\r\n ret.link = true;\r\n } else if (data === 'tag') {\r\n ret.image = true;\r\n } else if (data.match(/^header(-[1-6])?$/)) {\r\n ret[data.replace('header', 'heading')] = true;\r\n }\r\n }\r\n return ret;\r\n}\r\n\r\n\r\n// Saved overflow setting\r\nvar saved_overflow = '';\r\n\r\n/**\r\n * Toggle full screen of the editor.\r\n */\r\nfunction toggleFullScreen(editor) {\r\n // Set fullscreen\r\n var cm = editor.codemirror;\r\n cm.setOption('fullScreen', !cm.getOption('fullScreen'));\r\n\r\n\r\n // Prevent scrolling on body during fullscreen active\r\n if (cm.getOption('fullScreen')) {\r\n saved_overflow = document.body.style.overflow;\r\n document.body.style.overflow = 'hidden';\r\n } else {\r\n document.body.style.overflow = saved_overflow;\r\n }\r\n\r\n\r\n // Update toolbar class\r\n var wrap = cm.getWrapperElement();\r\n\r\n if (!/fullscreen/.test(wrap.previousSibling.className)) {\r\n wrap.previousSibling.className += ' fullscreen';\r\n } else {\r\n wrap.previousSibling.className = wrap.previousSibling.className.replace(/\\s*fullscreen\\b/, '');\r\n }\r\n\r\n\r\n // Update toolbar button\r\n if (editor.toolbarElements.fullscreen) {\r\n var toolbarButton = editor.toolbarElements.fullscreen;\r\n\r\n if (!/active/.test(toolbarButton.className)) {\r\n toolbarButton.className += ' active';\r\n } else {\r\n toolbarButton.className = toolbarButton.className.replace(/\\s*active\\s*/g, '');\r\n }\r\n }\r\n\r\n\r\n // Hide side by side if needed\r\n var sidebyside = cm.getWrapperElement().nextSibling;\r\n if (/editor-preview-active-side/.test(sidebyside.className))\r\n toggleSideBySide(editor);\r\n}\r\n\r\n\r\n/**\r\n * Action for toggling bold.\r\n */\r\nfunction toggleBold(editor) {\r\n _toggleBlock(editor, 'bold', editor.options.blockStyles.bold);\r\n}\r\n\r\n\r\n/**\r\n * Action for toggling italic.\r\n */\r\nfunction toggleItalic(editor) {\r\n _toggleBlock(editor, 'italic', editor.options.blockStyles.italic);\r\n}\r\n\r\n\r\n/**\r\n * Action for toggling strikethrough.\r\n */\r\nfunction toggleStrikethrough(editor) {\r\n _toggleBlock(editor, 'strikethrough', '~~');\r\n}\r\n\r\n/**\r\n * Action for toggling code block.\r\n */\r\nfunction toggleCodeBlock(editor) {\r\n var fenceCharsToInsert = editor.options.blockStyles.code;\r\n\r\n function fencing_line(line) {\r\n /* return true, if this is a ``` or ~~~ line */\r\n if (typeof line !== 'object') {\r\n throw 'fencing_line() takes a \\'line\\' object (not a line number, or line text). Got: ' + typeof line + ': ' + line;\r\n }\r\n return line.styles && line.styles[2] && line.styles[2].indexOf('formatting-code-block') !== -1;\r\n }\r\n\r\n function token_state(token) {\r\n // base goes an extra level deep when mode backdrops are used, e.g. spellchecker on\r\n return token.state.base.base || token.state.base;\r\n }\r\n\r\n function code_type(cm, line_num, line, firstTok, lastTok) {\r\n /*\r\n * Return \"single\", \"indented\", \"fenced\" or false\r\n *\r\n * cm and line_num are required. Others are optional for efficiency\r\n * To check in the middle of a line, pass in firstTok yourself.\r\n */\r\n line = line || cm.getLineHandle(line_num);\r\n firstTok = firstTok || cm.getTokenAt({\r\n line: line_num,\r\n ch: 1\r\n });\r\n lastTok = lastTok || (!!line.text && cm.getTokenAt({\r\n line: line_num,\r\n ch: line.text.length - 1\r\n }));\r\n var types = firstTok.type ? firstTok.type.split(' ') : [];\r\n if (lastTok && token_state(lastTok).indentedCode) {\r\n // have to check last char, since first chars of first line aren\"t marked as indented\r\n return 'indented';\r\n } else if (types.indexOf('comment') === -1) {\r\n // has to be after \"indented\" check, since first chars of first indented line aren\"t marked as such\r\n return false;\r\n } else if (token_state(firstTok).fencedChars || token_state(lastTok).fencedChars || fencing_line(line)) {\r\n return 'fenced';\r\n } else {\r\n return 'single';\r\n }\r\n }\r\n\r\n function insertFencingAtSelection(cm, cur_start, cur_end, fenceCharsToInsert) {\r\n var start_line_sel = cur_start.line + 1,\r\n end_line_sel = cur_end.line + 1,\r\n sel_multi = cur_start.line !== cur_end.line,\r\n repl_start = fenceCharsToInsert + '\\n',\r\n repl_end = '\\n' + fenceCharsToInsert;\r\n if (sel_multi) {\r\n end_line_sel++;\r\n }\r\n // handle last char including \\n or not\r\n if (sel_multi && cur_end.ch === 0) {\r\n repl_end = fenceCharsToInsert + '\\n';\r\n end_line_sel--;\r\n }\r\n _replaceSelection(cm, false, [repl_start, repl_end]);\r\n cm.setSelection({\r\n line: start_line_sel,\r\n ch: 0\r\n }, {\r\n line: end_line_sel,\r\n ch: 0\r\n });\r\n }\r\n\r\n var cm = editor.codemirror,\r\n cur_start = cm.getCursor('start'),\r\n cur_end = cm.getCursor('end'),\r\n tok = cm.getTokenAt({\r\n line: cur_start.line,\r\n ch: cur_start.ch || 1\r\n }), // avoid ch 0 which is a cursor pos but not token\r\n line = cm.getLineHandle(cur_start.line),\r\n is_code = code_type(cm, cur_start.line, line, tok);\r\n var block_start, block_end, lineCount;\r\n\r\n if (is_code === 'single') {\r\n // similar to some InscrybMDE _toggleBlock logic\r\n var start = line.text.slice(0, cur_start.ch).replace('`', ''),\r\n end = line.text.slice(cur_start.ch).replace('`', '');\r\n cm.replaceRange(start + end, {\r\n line: cur_start.line,\r\n ch: 0\r\n }, {\r\n line: cur_start.line,\r\n ch: 99999999999999\r\n });\r\n cur_start.ch--;\r\n if (cur_start !== cur_end) {\r\n cur_end.ch--;\r\n }\r\n cm.setSelection(cur_start, cur_end);\r\n cm.focus();\r\n } else if (is_code === 'fenced') {\r\n if (cur_start.line !== cur_end.line || cur_start.ch !== cur_end.ch) {\r\n // use selection\r\n\r\n // find the fenced line so we know what type it is (tilde, backticks, number of them)\r\n for (block_start = cur_start.line; block_start >= 0; block_start--) {\r\n line = cm.getLineHandle(block_start);\r\n if (fencing_line(line)) {\r\n break;\r\n }\r\n }\r\n var fencedTok = cm.getTokenAt({\r\n line: block_start,\r\n ch: 1\r\n });\r\n var fence_chars = token_state(fencedTok).fencedChars;\r\n var start_text, start_line;\r\n var end_text, end_line;\r\n // check for selection going up against fenced lines, in which case we don't want to add more fencing\r\n if (fencing_line(cm.getLineHandle(cur_start.line))) {\r\n start_text = '';\r\n start_line = cur_start.line;\r\n } else if (fencing_line(cm.getLineHandle(cur_start.line - 1))) {\r\n start_text = '';\r\n start_line = cur_start.line - 1;\r\n } else {\r\n start_text = fence_chars + '\\n';\r\n start_line = cur_start.line;\r\n }\r\n if (fencing_line(cm.getLineHandle(cur_end.line))) {\r\n end_text = '';\r\n end_line = cur_end.line;\r\n if (cur_end.ch === 0) {\r\n end_line += 1;\r\n }\r\n } else if (cur_end.ch !== 0 && fencing_line(cm.getLineHandle(cur_end.line + 1))) {\r\n end_text = '';\r\n end_line = cur_end.line + 1;\r\n } else {\r\n end_text = fence_chars + '\\n';\r\n end_line = cur_end.line + 1;\r\n }\r\n if (cur_end.ch === 0) {\r\n // full last line selected, putting cursor at beginning of next\r\n end_line -= 1;\r\n }\r\n cm.operation(function () {\r\n // end line first, so that line numbers don't change\r\n cm.replaceRange(end_text, {\r\n line: end_line,\r\n ch: 0\r\n }, {\r\n line: end_line + (end_text ? 0 : 1),\r\n ch: 0\r\n });\r\n cm.replaceRange(start_text, {\r\n line: start_line,\r\n ch: 0\r\n }, {\r\n line: start_line + (start_text ? 0 : 1),\r\n ch: 0\r\n });\r\n });\r\n cm.setSelection({\r\n line: start_line + (start_text ? 1 : 0),\r\n ch: 0\r\n }, {\r\n line: end_line + (start_text ? 1 : -1),\r\n ch: 0\r\n });\r\n cm.focus();\r\n } else {\r\n // no selection, search for ends of this fenced block\r\n var search_from = cur_start.line;\r\n if (fencing_line(cm.getLineHandle(cur_start.line))) { // gets a little tricky if cursor is right on a fenced line\r\n if (code_type(cm, cur_start.line + 1) === 'fenced') {\r\n block_start = cur_start.line;\r\n search_from = cur_start.line + 1; // for searching for \"end\"\r\n } else {\r\n block_end = cur_start.line;\r\n search_from = cur_start.line - 1; // for searching for \"start\"\r\n }\r\n }\r\n if (block_start === undefined) {\r\n for (block_start = search_from; block_start >= 0; block_start--) {\r\n line = cm.getLineHandle(block_start);\r\n if (fencing_line(line)) {\r\n break;\r\n }\r\n }\r\n }\r\n if (block_end === undefined) {\r\n lineCount = cm.lineCount();\r\n for (block_end = search_from; block_end < lineCount; block_end++) {\r\n line = cm.getLineHandle(block_end);\r\n if (fencing_line(line)) {\r\n break;\r\n }\r\n }\r\n }\r\n cm.operation(function () {\r\n cm.replaceRange('', {\r\n line: block_start,\r\n ch: 0\r\n }, {\r\n line: block_start + 1,\r\n ch: 0\r\n });\r\n cm.replaceRange('', {\r\n line: block_end - 1,\r\n ch: 0\r\n }, {\r\n line: block_end,\r\n ch: 0\r\n });\r\n });\r\n cm.focus();\r\n }\r\n } else if (is_code === 'indented') {\r\n if (cur_start.line !== cur_end.line || cur_start.ch !== cur_end.ch) {\r\n // use selection\r\n block_start = cur_start.line;\r\n block_end = cur_end.line;\r\n if (cur_end.ch === 0) {\r\n block_end--;\r\n }\r\n } else {\r\n // no selection, search for ends of this indented block\r\n for (block_start = cur_start.line; block_start >= 0; block_start--) {\r\n line = cm.getLineHandle(block_start);\r\n if (line.text.match(/^\\s*$/)) {\r\n // empty or all whitespace - keep going\r\n continue;\r\n } else {\r\n if (code_type(cm, block_start, line) !== 'indented') {\r\n block_start += 1;\r\n break;\r\n }\r\n }\r\n }\r\n lineCount = cm.lineCount();\r\n for (block_end = cur_start.line; block_end < lineCount; block_end++) {\r\n line = cm.getLineHandle(block_end);\r\n if (line.text.match(/^\\s*$/)) {\r\n // empty or all whitespace - keep going\r\n continue;\r\n } else {\r\n if (code_type(cm, block_end, line) !== 'indented') {\r\n block_end -= 1;\r\n break;\r\n }\r\n }\r\n }\r\n }\r\n // if we are going to un-indent based on a selected set of lines, and the next line is indented too, we need to\r\n // insert a blank line so that the next line(s) continue to be indented code\r\n var next_line = cm.getLineHandle(block_end + 1),\r\n next_line_last_tok = next_line && cm.getTokenAt({\r\n line: block_end + 1,\r\n ch: next_line.text.length - 1\r\n }),\r\n next_line_indented = next_line_last_tok && token_state(next_line_last_tok).indentedCode;\r\n if (next_line_indented) {\r\n cm.replaceRange('\\n', {\r\n line: block_end + 1,\r\n ch: 0\r\n });\r\n }\r\n\r\n for (var i = block_start; i <= block_end; i++) {\r\n cm.indentLine(i, 'subtract'); // TODO: this doesn't get tracked in the history, so can't be undone :(\r\n }\r\n cm.focus();\r\n } else {\r\n // insert code formatting\r\n var no_sel_and_starting_of_line = (cur_start.line === cur_end.line && cur_start.ch === cur_end.ch && cur_start.ch === 0);\r\n var sel_multi = cur_start.line !== cur_end.line;\r\n if (no_sel_and_starting_of_line || sel_multi) {\r\n insertFencingAtSelection(cm, cur_start, cur_end, fenceCharsToInsert);\r\n } else {\r\n _replaceSelection(cm, false, ['`', '`']);\r\n }\r\n }\r\n}\r\n\r\n/**\r\n * Action for toggling blockquote.\r\n */\r\nfunction toggleBlockquote(editor) {\r\n var cm = editor.codemirror;\r\n _toggleLine(cm, 'quote');\r\n}\r\n\r\n/**\r\n * Action for toggling heading size: normal -> h1 -> h2 -> h3 -> h4 -> h5 -> h6 -> normal\r\n */\r\nfunction toggleHeadingSmaller(editor) {\r\n var cm = editor.codemirror;\r\n _toggleHeading(cm, 'smaller');\r\n}\r\n\r\n/**\r\n * Action for toggling heading size: normal -> h6 -> h5 -> h4 -> h3 -> h2 -> h1 -> normal\r\n */\r\nfunction toggleHeadingBigger(editor) {\r\n var cm = editor.codemirror;\r\n _toggleHeading(cm, 'bigger');\r\n}\r\n\r\n/**\r\n * Action for toggling heading size 1\r\n */\r\nfunction toggleHeading1(editor) {\r\n var cm = editor.codemirror;\r\n _toggleHeading(cm, undefined, 1);\r\n}\r\n\r\n/**\r\n * Action for toggling heading size 2\r\n */\r\nfunction toggleHeading2(editor) {\r\n var cm = editor.codemirror;\r\n _toggleHeading(cm, undefined, 2);\r\n}\r\n\r\n/**\r\n * Action for toggling heading size 3\r\n */\r\nfunction toggleHeading3(editor) {\r\n var cm = editor.codemirror;\r\n _toggleHeading(cm, undefined, 3);\r\n}\r\n\r\n\r\n/**\r\n * Action for toggling ul.\r\n */\r\nfunction toggleUnorderedList(editor) {\r\n var cm = editor.codemirror;\r\n _toggleLine(cm, 'unordered-list');\r\n}\r\n\r\n\r\n/**\r\n * Action for toggling ol.\r\n */\r\nfunction toggleOrderedList(editor) {\r\n var cm = editor.codemirror;\r\n _toggleLine(cm, 'ordered-list');\r\n}\r\n\r\n/**\r\n * Action for clean block (remove headline, list, blockquote code, markers)\r\n */\r\nfunction cleanBlock(editor) {\r\n var cm = editor.codemirror;\r\n _cleanBlock(cm);\r\n}\r\n\r\n/**\r\n * Action for drawing a link.\r\n */\r\nfunction drawLink(editor) {\r\n var cm = editor.codemirror;\r\n var stat = getState(cm);\r\n var options = editor.options;\r\n var url = 'https://';\r\n if (options.promptURLs) {\r\n url = prompt(options.promptTexts.link);\r\n if (!url) {\r\n return false;\r\n }\r\n }\r\n _replaceSelection(cm, stat.link, options.insertTexts.link, url);\r\n}\r\n\r\n/**\r\n * Action for drawing an img.\r\n */\r\nfunction drawImage(editor) {\r\n var cm = editor.codemirror;\r\n var stat = getState(cm);\r\n var options = editor.options;\r\n var url = 'https://';\r\n if (options.promptURLs) {\r\n url = prompt(options.promptTexts.image);\r\n if (!url) {\r\n return false;\r\n }\r\n }\r\n _replaceSelection(cm, stat.image, options.insertTexts.image, url);\r\n}\r\n\r\n/**\r\n * Action for drawing a table.\r\n */\r\nfunction drawTable(editor) {\r\n var cm = editor.codemirror;\r\n var stat = getState(cm);\r\n var options = editor.options;\r\n _replaceSelection(cm, stat.table, options.insertTexts.table);\r\n}\r\n\r\n/**\r\n * Action for drawing a horizontal rule.\r\n */\r\nfunction drawHorizontalRule(editor) {\r\n var cm = editor.codemirror;\r\n var stat = getState(cm);\r\n var options = editor.options;\r\n _replaceSelection(cm, stat.image, options.insertTexts.horizontalRule);\r\n}\r\n\r\n\r\n/**\r\n * Undo action.\r\n */\r\nfunction undo(editor) {\r\n var cm = editor.codemirror;\r\n cm.undo();\r\n cm.focus();\r\n}\r\n\r\n\r\n/**\r\n * Redo action.\r\n */\r\nfunction redo(editor) {\r\n var cm = editor.codemirror;\r\n cm.redo();\r\n cm.focus();\r\n}\r\n\r\n\r\n/**\r\n * Toggle side by side preview\r\n */\r\nfunction toggleSideBySide(editor) {\r\n var cm = editor.codemirror;\r\n var wrapper = cm.getWrapperElement();\r\n var preview = wrapper.nextSibling;\r\n var toolbarButton = editor.toolbarElements['side-by-side'];\r\n var useSideBySideListener = false;\r\n if (/editor-preview-active-side/.test(preview.className)) {\r\n preview.className = preview.className.replace(\r\n /\\s*editor-preview-active-side\\s*/g, ''\r\n );\r\n toolbarButton.className = toolbarButton.className.replace(/\\s*active\\s*/g, '');\r\n wrapper.className = wrapper.className.replace(/\\s*CodeMirror-sided\\s*/g, ' ');\r\n } else {\r\n // When the preview button is clicked for the first time,\r\n // give some time for the transition from editor.css to fire and the view to slide from right to left,\r\n // instead of just appearing.\r\n setTimeout(function () {\r\n if (!cm.getOption('fullScreen'))\r\n toggleFullScreen(editor);\r\n preview.className += ' editor-preview-active-side';\r\n }, 1);\r\n toolbarButton.className += ' active';\r\n wrapper.className += ' CodeMirror-sided';\r\n useSideBySideListener = true;\r\n }\r\n\r\n // Hide normal preview if active\r\n var previewNormal = wrapper.lastChild;\r\n if (/editor-preview-active/.test(previewNormal.className)) {\r\n previewNormal.className = previewNormal.className.replace(\r\n /\\s*editor-preview-active\\s*/g, ''\r\n );\r\n var toolbar = editor.toolbarElements.preview;\r\n var toolbar_div = wrapper.previousSibling;\r\n toolbar.className = toolbar.className.replace(/\\s*active\\s*/g, '');\r\n toolbar_div.className = toolbar_div.className.replace(/\\s*disabled-for-preview*/g, '');\r\n }\r\n\r\n var sideBySideRenderingFunction = function () {\r\n preview.innerHTML = editor.options.previewRender(editor.value(), preview);\r\n };\r\n\r\n if (!cm.sideBySideRenderingFunction) {\r\n cm.sideBySideRenderingFunction = sideBySideRenderingFunction;\r\n }\r\n\r\n if (useSideBySideListener) {\r\n preview.innerHTML = editor.options.previewRender(editor.value(), preview);\r\n cm.on('update', cm.sideBySideRenderingFunction);\r\n } else {\r\n cm.off('update', cm.sideBySideRenderingFunction);\r\n }\r\n\r\n // Refresh to fix selection being off (#309)\r\n cm.refresh();\r\n}\r\n\r\n\r\n/**\r\n * Preview action.\r\n */\r\nfunction togglePreview(editor) {\r\n var cm = editor.codemirror;\r\n var wrapper = cm.getWrapperElement();\r\n var toolbar_div = wrapper.previousSibling;\r\n var toolbar = editor.options.toolbar ? editor.toolbarElements.preview : false;\r\n var preview = wrapper.lastChild;\r\n if (!preview || !/editor-preview/.test(preview.className)) {\r\n preview = document.createElement('div');\r\n preview.className = 'editor-preview';\r\n wrapper.appendChild(preview);\r\n }\r\n if (/editor-preview-active/.test(preview.className)) {\r\n preview.className = preview.className.replace(\r\n /\\s*editor-preview-active\\s*/g, ''\r\n );\r\n if (toolbar) {\r\n toolbar.className = toolbar.className.replace(/\\s*active\\s*/g, '');\r\n toolbar_div.className = toolbar_div.className.replace(/\\s*disabled-for-preview*/g, '');\r\n }\r\n } else {\r\n // When the preview button is clicked for the first time,\r\n // give some time for the transition from editor.css to fire and the view to slide from right to left,\r\n // instead of just appearing.\r\n setTimeout(function () {\r\n preview.className += ' editor-preview-active';\r\n }, 1);\r\n if (toolbar) {\r\n toolbar.className += ' active';\r\n toolbar_div.className += ' disabled-for-preview';\r\n }\r\n }\r\n preview.innerHTML = editor.options.previewRender(editor.value(), preview);\r\n\r\n // Turn off side by side if needed\r\n var sidebyside = cm.getWrapperElement().nextSibling;\r\n if (/editor-preview-active-side/.test(sidebyside.className))\r\n toggleSideBySide(editor);\r\n}\r\n\r\nfunction _replaceSelection(cm, active, startEnd, url) {\r\n if (/editor-preview-active/.test(cm.getWrapperElement().lastChild.className))\r\n return;\r\n\r\n var text;\r\n var start = startEnd[0];\r\n var end = startEnd[1];\r\n var startPoint = {},\r\n endPoint = {};\r\n Object.assign(startPoint, cm.getCursor('start'));\r\n Object.assign(endPoint, cm.getCursor('end'));\r\n if (url) {\r\n end = end.replace('#url#', url);\r\n }\r\n if (active) {\r\n text = cm.getLine(startPoint.line);\r\n start = text.slice(0, startPoint.ch);\r\n end = text.slice(startPoint.ch);\r\n cm.replaceRange(start + end, {\r\n line: startPoint.line,\r\n ch: 0\r\n });\r\n } else {\r\n text = cm.getSelection();\r\n cm.replaceSelection(start + text + end);\r\n\r\n startPoint.ch += start.length;\r\n if (startPoint !== endPoint) {\r\n endPoint.ch += start.length;\r\n }\r\n }\r\n cm.setSelection(startPoint, endPoint);\r\n cm.focus();\r\n}\r\n\r\n\r\nfunction _toggleHeading(cm, direction, size) {\r\n if (/editor-preview-active/.test(cm.getWrapperElement().lastChild.className))\r\n return;\r\n\r\n var startPoint = cm.getCursor('start');\r\n var endPoint = cm.getCursor('end');\r\n for (var i = startPoint.line; i <= endPoint.line; i++) {\r\n (function (i) {\r\n var text = cm.getLine(i);\r\n var currHeadingLevel = text.search(/[^#]/);\r\n\r\n if (direction !== undefined) {\r\n if (currHeadingLevel <= 0) {\r\n if (direction == 'bigger') {\r\n text = '###### ' + text;\r\n } else {\r\n text = '# ' + text;\r\n }\r\n } else if (currHeadingLevel == 6 && direction == 'smaller') {\r\n text = text.substr(7);\r\n } else if (currHeadingLevel == 1 && direction == 'bigger') {\r\n text = text.substr(2);\r\n } else {\r\n if (direction == 'bigger') {\r\n text = text.substr(1);\r\n } else {\r\n text = '#' + text;\r\n }\r\n }\r\n } else {\r\n if (size == 1) {\r\n if (currHeadingLevel <= 0) {\r\n text = '# ' + text;\r\n } else if (currHeadingLevel == size) {\r\n text = text.substr(currHeadingLevel + 1);\r\n } else {\r\n text = '# ' + text.substr(currHeadingLevel + 1);\r\n }\r\n } else if (size == 2) {\r\n if (currHeadingLevel <= 0) {\r\n text = '## ' + text;\r\n } else if (currHeadingLevel == size) {\r\n text = text.substr(currHeadingLevel + 1);\r\n } else {\r\n text = '## ' + text.substr(currHeadingLevel + 1);\r\n }\r\n } else {\r\n if (currHeadingLevel <= 0) {\r\n text = '### ' + text;\r\n } else if (currHeadingLevel == size) {\r\n text = text.substr(currHeadingLevel + 1);\r\n } else {\r\n text = '### ' + text.substr(currHeadingLevel + 1);\r\n }\r\n }\r\n }\r\n\r\n cm.replaceRange(text, {\r\n line: i,\r\n ch: 0\r\n }, {\r\n line: i,\r\n ch: 99999999999999\r\n });\r\n })(i);\r\n }\r\n cm.focus();\r\n}\r\n\r\n\r\nfunction _toggleLine(cm, name) {\r\n if (/editor-preview-active/.test(cm.getWrapperElement().lastChild.className))\r\n return;\r\n\r\n var listRegexp = /^(\\s*)(\\*|-|\\+|\\d*\\.)(\\s+)/;\r\n var whitespacesRegexp = /^\\s*/;\r\n\r\n var stat = getState(cm);\r\n var startPoint = cm.getCursor('start');\r\n var endPoint = cm.getCursor('end');\r\n var repl = {\r\n 'quote': /^(\\s*)>\\s+/,\r\n 'unordered-list': listRegexp,\r\n 'ordered-list': listRegexp\r\n };\r\n\r\n var _getChar = function (name, i) {\r\n var map = {\r\n 'quote': '>',\r\n 'unordered-list': '*',\r\n 'ordered-list': '%%i.'\r\n };\r\n\r\n return map[name].replace('%%i', i);\r\n };\r\n\r\n var _checkChar = function (name, char) {\r\n var map = {\r\n 'quote': '>',\r\n 'unordered-list': '*',\r\n 'ordered-list': 'd+.'\r\n };\r\n var rt = new RegExp(map[name]);\r\n\r\n return char && rt.test(char);\r\n };\r\n\r\n var line = 1;\r\n for (var i = startPoint.line; i <= endPoint.line; i++) {\r\n (function (i) {\r\n var text = cm.getLine(i);\r\n if (stat[name]) {\r\n text = text.replace(repl[name], '$1');\r\n } else {\r\n var arr = listRegexp.exec(text);\r\n var char = _getChar(name, line);\r\n if (arr !== null) {\r\n if (_checkChar(name, arr[2])) {\r\n char = '';\r\n }\r\n text = arr[1] + char + arr[3] + text.replace(whitespacesRegexp, '').replace(repl[name], '$1');\r\n } else {\r\n text = char + ' ' + text;\r\n }\r\n line += 1;\r\n }\r\n cm.replaceRange(text, {\r\n line: i,\r\n ch: 0\r\n }, {\r\n line: i,\r\n ch: 99999999999999\r\n });\r\n })(i);\r\n }\r\n cm.focus();\r\n}\r\n\r\nfunction _toggleBlock(editor, type, start_chars, end_chars) {\r\n if (/editor-preview-active/.test(editor.codemirror.getWrapperElement().lastChild.className))\r\n return;\r\n\r\n end_chars = (typeof end_chars === 'undefined') ? start_chars : end_chars;\r\n var cm = editor.codemirror;\r\n var stat = getState(cm);\r\n\r\n var text;\r\n var start = start_chars;\r\n var end = end_chars;\r\n\r\n var startPoint = cm.getCursor('start');\r\n var endPoint = cm.getCursor('end');\r\n\r\n if (stat[type]) {\r\n text = cm.getLine(startPoint.line);\r\n start = text.slice(0, startPoint.ch);\r\n end = text.slice(startPoint.ch);\r\n if (type == 'bold') {\r\n start = start.replace(/(\\*\\*|__)(?![\\s\\S]*(\\*\\*|__))/, '');\r\n end = end.replace(/(\\*\\*|__)/, '');\r\n } else if (type == 'italic') {\r\n start = start.replace(/(\\*|_)(?![\\s\\S]*(\\*|_))/, '');\r\n end = end.replace(/(\\*|_)/, '');\r\n } else if (type == 'strikethrough') {\r\n start = start.replace(/(\\*\\*|~~)(?![\\s\\S]*(\\*\\*|~~))/, '');\r\n end = end.replace(/(\\*\\*|~~)/, '');\r\n }\r\n cm.replaceRange(start + end, {\r\n line: startPoint.line,\r\n ch: 0\r\n }, {\r\n line: startPoint.line,\r\n ch: 99999999999999\r\n });\r\n\r\n if (type == 'bold' || type == 'strikethrough') {\r\n startPoint.ch -= 2;\r\n if (startPoint !== endPoint) {\r\n endPoint.ch -= 2;\r\n }\r\n } else if (type == 'italic') {\r\n startPoint.ch -= 1;\r\n if (startPoint !== endPoint) {\r\n endPoint.ch -= 1;\r\n }\r\n }\r\n } else {\r\n text = cm.getSelection();\r\n if (type == 'bold') {\r\n text = text.split('**').join('');\r\n text = text.split('__').join('');\r\n } else if (type == 'italic') {\r\n text = text.split('*').join('');\r\n text = text.split('_').join('');\r\n } else if (type == 'strikethrough') {\r\n text = text.split('~~').join('');\r\n }\r\n cm.replaceSelection(start + text + end);\r\n\r\n startPoint.ch += start_chars.length;\r\n endPoint.ch = startPoint.ch + text.length;\r\n }\r\n\r\n cm.setSelection(startPoint, endPoint);\r\n cm.focus();\r\n}\r\n\r\nfunction _cleanBlock(cm) {\r\n if (/editor-preview-active/.test(cm.getWrapperElement().lastChild.className))\r\n return;\r\n\r\n var startPoint = cm.getCursor('start');\r\n var endPoint = cm.getCursor('end');\r\n var text;\r\n\r\n for (var line = startPoint.line; line <= endPoint.line; line++) {\r\n text = cm.getLine(line);\r\n text = text.replace(/^[ ]*([# ]+|\\*|-|[> ]+|[0-9]+(.|\\)))[ ]*/, '');\r\n\r\n cm.replaceRange(text, {\r\n line: line,\r\n ch: 0\r\n }, {\r\n line: line,\r\n ch: 99999999999999\r\n });\r\n }\r\n}\r\n\r\n// Merge the properties of one object into another.\r\nfunction _mergeProperties(target, source) {\r\n for (var property in source) {\r\n if (source.hasOwnProperty(property)) {\r\n if (source[property] instanceof Array) {\r\n target[property] = source[property].concat(target[property] instanceof Array ? target[property] : []);\r\n } else if (\r\n source[property] !== null &&\r\n typeof source[property] === 'object' &&\r\n source[property].constructor === Object\r\n ) {\r\n target[property] = _mergeProperties(target[property] || {}, source[property]);\r\n } else {\r\n target[property] = source[property];\r\n }\r\n }\r\n }\r\n\r\n return target;\r\n}\r\n\r\n// Merge an arbitrary number of objects into one.\r\nfunction extend(target) {\r\n for (var i = 1; i < arguments.length; i++) {\r\n target = _mergeProperties(target, arguments[i]);\r\n }\r\n\r\n return target;\r\n}\r\n\r\n/* The right word count in respect for CJK. */\r\nfunction wordCount(data) {\r\n var pattern = /[a-zA-Z0-9_\\u0392-\\u03c9\\u0410-\\u04F9]+|[\\u4E00-\\u9FFF\\u3400-\\u4dbf\\uf900-\\ufaff\\u3040-\\u309f\\uac00-\\ud7af]+/g;\r\n var m = data.match(pattern);\r\n var count = 0;\r\n if (m === null) return count;\r\n for (var i = 0; i < m.length; i++) {\r\n if (m[i].charCodeAt(0) >= 0x4E00) {\r\n count += m[i].length;\r\n } else {\r\n count += 1;\r\n }\r\n }\r\n return count;\r\n}\r\n\r\nvar toolbarBuiltInButtons = {\r\n 'bold': {\r\n name: 'bold',\r\n action: toggleBold,\r\n className: 'fa fa-bold',\r\n title: 'Bold',\r\n default: true\r\n },\r\n 'italic': {\r\n name: 'italic',\r\n action: toggleItalic,\r\n className: 'fa fa-italic',\r\n title: 'Italic',\r\n default: true\r\n },\r\n 'strikethrough': {\r\n name: 'strikethrough',\r\n action: toggleStrikethrough,\r\n className: 'fa fa-strikethrough',\r\n title: 'Strikethrough'\r\n },\r\n 'heading': {\r\n name: 'heading',\r\n action: toggleHeadingSmaller,\r\n className: 'fa fa-header fa-heading',\r\n title: 'Heading',\r\n default: true\r\n },\r\n 'heading-smaller': {\r\n name: 'heading-smaller',\r\n action: toggleHeadingSmaller,\r\n className: 'fa fa-header fa-header-x fa-header-smaller',\r\n title: 'Smaller Heading'\r\n },\r\n 'heading-bigger': {\r\n name: 'heading-bigger',\r\n action: toggleHeadingBigger,\r\n className: 'fa fa-header fa-header-x fa-header-bigger',\r\n title: 'Bigger Heading'\r\n },\r\n 'heading-1': {\r\n name: 'heading-1',\r\n action: toggleHeading1,\r\n className: 'fa fa-header fa-header-x fa-header-1 fa-h1',\r\n title: 'Big Heading'\r\n },\r\n 'heading-2': {\r\n name: 'heading-2',\r\n action: toggleHeading2,\r\n className: 'fa fa-header fa-header-x fa-header-2 fa-h2',\r\n title: 'Medium Heading'\r\n },\r\n 'heading-3': {\r\n name: 'heading-3',\r\n action: toggleHeading3,\r\n className: 'fa fa-header fa-header-x fa-header-3 fa-h3',\r\n title: 'Small Heading'\r\n },\r\n 'separator-1': {\r\n name: 'separator-1'\r\n },\r\n 'code': {\r\n name: 'code',\r\n action: toggleCodeBlock,\r\n className: 'fa fa-code',\r\n title: 'Code'\r\n },\r\n 'quote': {\r\n name: 'quote',\r\n action: toggleBlockquote,\r\n className: 'fa fa-quote-left',\r\n title: 'Quote',\r\n default: true\r\n },\r\n 'unordered-list': {\r\n name: 'unordered-list',\r\n action: toggleUnorderedList,\r\n className: 'fa fa-list-ul',\r\n title: 'Generic List',\r\n default: true\r\n },\r\n 'ordered-list': {\r\n name: 'ordered-list',\r\n action: toggleOrderedList,\r\n className: 'fa fa-list-ol',\r\n title: 'Numbered List',\r\n default: true\r\n },\r\n 'clean-block': {\r\n name: 'clean-block',\r\n action: cleanBlock,\r\n className: 'fa fa-eraser fa-clean-block',\r\n title: 'Clean block'\r\n },\r\n 'separator-2': {\r\n name: 'separator-2'\r\n },\r\n 'link': {\r\n name: 'link',\r\n action: drawLink,\r\n className: 'fa fa-link',\r\n title: 'Create Link',\r\n default: true\r\n },\r\n 'image': {\r\n name: 'image',\r\n action: drawImage,\r\n className: 'fa fa-image',\r\n title: 'Insert Image',\r\n default: true\r\n },\r\n 'table': {\r\n name: 'table',\r\n action: drawTable,\r\n className: 'fa fa-table',\r\n title: 'Insert Table'\r\n },\r\n 'horizontal-rule': {\r\n name: 'horizontal-rule',\r\n action: drawHorizontalRule,\r\n className: 'fa fa-minus',\r\n title: 'Insert Horizontal Line'\r\n },\r\n 'separator-3': {\r\n name: 'separator-3'\r\n },\r\n 'preview': {\r\n name: 'preview',\r\n action: togglePreview,\r\n className: 'fa fa-eye',\r\n noDisable: true,\r\n title: 'Toggle Preview',\r\n default: true\r\n },\r\n 'side-by-side': {\r\n name: 'side-by-side',\r\n action: toggleSideBySide,\r\n className: 'fa fa-columns',\r\n noDisable: true,\r\n noMobile: true,\r\n title: 'Toggle Side by Side',\r\n default: true\r\n },\r\n 'fullscreen': {\r\n name: 'fullscreen',\r\n action: toggleFullScreen,\r\n className: 'fa fa-arrows-alt',\r\n noDisable: true,\r\n noMobile: true,\r\n title: 'Toggle Fullscreen',\r\n default: true\r\n },\r\n 'separator-4': {\r\n name: 'separator-4'\r\n },\r\n 'guide': {\r\n name: 'guide',\r\n action: 'https://www.inscryb.com/InscrybMDE/markdown-guide/',\r\n className: 'fa fa-question-circle',\r\n noDisable: true,\r\n title: 'Markdown Guide',\r\n default: true\r\n },\r\n 'separator-5': {\r\n name: 'separator-5'\r\n },\r\n 'undo': {\r\n name: 'undo',\r\n action: undo,\r\n className: 'fa fa-undo',\r\n noDisable: true,\r\n title: 'Undo'\r\n },\r\n 'redo': {\r\n name: 'redo',\r\n action: redo,\r\n className: 'fa fa-repeat',\r\n noDisable: true,\r\n title: 'Redo'\r\n }\r\n};\r\n\r\nvar insertTexts = {\r\n link: ['[', '](#url#)'],\r\n image: [''],\r\n table: ['', '\\n\\n| Column 1 | Column 2 | Column 3 |\\n| -------- | -------- | -------- |\\n| Text | Text | Text |\\n\\n'],\r\n horizontalRule: ['', '\\n\\n-----\\n\\n']\r\n};\r\n\r\nvar promptTexts = {\r\n link: 'URL for the link:',\r\n image: 'URL of the image:'\r\n};\r\n\r\nvar blockStyles = {\r\n 'bold': '**',\r\n 'code': '```',\r\n 'italic': '*'\r\n};\r\n\r\n/**\r\n * Interface of InscrybMDE.\r\n */\r\nfunction InscrybMDE(options) {\r\n // Handle options parameter\r\n options = options || {};\r\n\r\n\r\n // Used later to refer to it\"s parent\r\n options.parent = this;\r\n\r\n\r\n // Check if Font Awesome needs to be auto downloaded\r\n var autoDownloadFA = true;\r\n\r\n if (options.autoDownloadFontAwesome === false) {\r\n autoDownloadFA = false;\r\n }\r\n\r\n if (options.autoDownloadFontAwesome !== true) {\r\n var styleSheets = document.styleSheets;\r\n for (var i = 0; i < styleSheets.length; i++) {\r\n if (!styleSheets[i].href)\r\n continue;\r\n\r\n if (styleSheets[i].href.indexOf('//maxcdn.bootstrapcdn.com/font-awesome/') > -1) {\r\n autoDownloadFA = false;\r\n }\r\n }\r\n }\r\n\r\n if (autoDownloadFA) {\r\n var link = document.createElement('link');\r\n link.rel = 'stylesheet';\r\n link.href = 'https://maxcdn.bootstrapcdn.com/font-awesome/latest/css/font-awesome.min.css';\r\n document.getElementsByTagName('head')[0].appendChild(link);\r\n }\r\n\r\n\r\n // Find the textarea to use\r\n if (options.element) {\r\n this.element = options.element;\r\n } else if (options.element === null) {\r\n // This means that the element option was specified, but no element was found\r\n console.log('InscrybMDE: Error. No element was found.');\r\n return;\r\n }\r\n\r\n\r\n // Handle toolbar\r\n if (options.toolbar === undefined) {\r\n // Initialize\r\n options.toolbar = [];\r\n\r\n\r\n // Loop over the built in buttons, to get the preferred order\r\n for (var key in toolbarBuiltInButtons) {\r\n if (toolbarBuiltInButtons.hasOwnProperty(key)) {\r\n if (key.indexOf('separator-') != -1) {\r\n options.toolbar.push('|');\r\n }\r\n\r\n if (toolbarBuiltInButtons[key].default === true || (options.showIcons && options.showIcons.constructor === Array && options.showIcons.indexOf(key) != -1)) {\r\n options.toolbar.push(key);\r\n }\r\n }\r\n }\r\n }\r\n\r\n\r\n // Handle status bar\r\n if (!options.hasOwnProperty('status')) {\r\n options.status = ['autosave', 'lines', 'words', 'cursor'];\r\n }\r\n\r\n\r\n // Add default preview rendering function\r\n if (!options.previewRender) {\r\n options.previewRender = function (plainText) {\r\n // Note: \"this\" refers to the options object\r\n return this.parent.markdown(plainText);\r\n };\r\n }\r\n\r\n\r\n // Set default options for parsing config\r\n options.parsingConfig = extend({\r\n highlightFormatting: true // needed for toggleCodeBlock to detect types of code\r\n }, options.parsingConfig || {});\r\n\r\n\r\n // Merging the insertTexts, with the given options\r\n options.insertTexts = extend({}, insertTexts, options.insertTexts || {});\r\n\r\n\r\n // Merging the promptTexts, with the given options\r\n options.promptTexts = extend({}, promptTexts, options.promptTexts || {});\r\n\r\n\r\n // Merging the blockStyles, with the given options\r\n options.blockStyles = extend({}, blockStyles, options.blockStyles || {});\r\n\r\n\r\n // Merging the shortcuts, with the given options\r\n options.shortcuts = extend({}, shortcuts, options.shortcuts || {});\r\n\r\n options.minHeight = options.minHeight || '300px';\r\n\r\n\r\n // Change unique_id to uniqueId for backwards compatibility\r\n if (options.autosave != undefined && options.autosave.unique_id != undefined && options.autosave.unique_id != '')\r\n options.autosave.uniqueId = options.autosave.unique_id;\r\n\r\n\r\n // Update this options\r\n this.options = options;\r\n\r\n\r\n // Auto render\r\n this.render();\r\n\r\n\r\n // The codemirror component is only available after rendering\r\n // so, the setter for the initialValue can only run after\r\n // the element has been rendered\r\n if (options.initialValue && (!this.options.autosave || this.options.autosave.foundSavedValue !== true)) {\r\n this.value(options.initialValue);\r\n }\r\n}\r\n\r\n/**\r\n * Default markdown render.\r\n */\r\nInscrybMDE.prototype.markdown = function (text) {\r\n if (marked) {\r\n // Initialize\r\n var markedOptions;\r\n if (this.options && this.options.renderingConfig && this.options.renderingConfig.markedOptions) {\r\n markedOptions = this.options.renderingConfig.markedOptions;\r\n } else {\r\n markedOptions = {};\r\n }\r\n\r\n // Update options\r\n if (this.options && this.options.renderingConfig && this.options.renderingConfig.singleLineBreaks === false) {\r\n markedOptions.breaks = false;\r\n } else {\r\n markedOptions.breaks = true;\r\n }\r\n\r\n if (this.options && this.options.renderingConfig && this.options.renderingConfig.codeSyntaxHighlighting === true) {\r\n\r\n /* Get HLJS from config or window */\r\n var hljs = this.options.renderingConfig.hljs || window.hljs;\r\n\r\n /* Check if HLJS loaded */\r\n if (hljs) {\r\n markedOptions.highlight = function (code) {\r\n return hljs.highlightAuto(code).value;\r\n };\r\n }\r\n }\r\n\r\n\r\n // Set options\r\n marked.setOptions(markedOptions);\r\n\r\n\r\n // Return\r\n return marked(text);\r\n }\r\n};\r\n\r\n/**\r\n * Render editor to the given element.\r\n */\r\nInscrybMDE.prototype.render = function (el) {\r\n if (!el) {\r\n el = this.element || document.getElementsByTagName('textarea')[0];\r\n }\r\n\r\n if (this._rendered && this._rendered === el) {\r\n // Already rendered.\r\n return;\r\n }\r\n\r\n this.element = el;\r\n var options = this.options;\r\n\r\n var self = this;\r\n var keyMaps = {};\r\n\r\n for (var key in options.shortcuts) {\r\n // null stands for \"do not bind this command\"\r\n if (options.shortcuts[key] !== null && bindings[key] !== null) {\r\n (function (key) {\r\n keyMaps[fixShortcut(options.shortcuts[key])] = function () {\r\n bindings[key](self);\r\n };\r\n })(key);\r\n }\r\n }\r\n\r\n keyMaps['Enter'] = 'newlineAndIndentContinueMarkdownList';\r\n keyMaps['Tab'] = 'tabAndIndentMarkdownList';\r\n keyMaps['Shift-Tab'] = 'shiftTabAndUnindentMarkdownList';\r\n keyMaps['Esc'] = function (cm) {\r\n if (cm.getOption('fullScreen')) toggleFullScreen(self);\r\n };\r\n\r\n document.addEventListener('keydown', function (e) {\r\n e = e || window.event;\r\n\r\n if (e.keyCode == 27) {\r\n if (self.codemirror.getOption('fullScreen')) toggleFullScreen(self);\r\n }\r\n }, false);\r\n\r\n var mode, backdrop;\r\n if (options.spellChecker !== false) {\r\n mode = 'spell-checker';\r\n backdrop = options.parsingConfig;\r\n backdrop.name = 'gfm';\r\n backdrop.gitHubSpice = false;\r\n\r\n CodeMirrorSpellChecker({\r\n codeMirrorInstance: CodeMirror\r\n });\r\n } else {\r\n mode = options.parsingConfig;\r\n mode.name = 'gfm';\r\n mode.gitHubSpice = false;\r\n }\r\n\r\n this.codemirror = CodeMirror.fromTextArea(el, {\r\n mode: mode,\r\n backdrop: backdrop,\r\n theme: 'paper',\r\n tabSize: (options.tabSize != undefined) ? options.tabSize : 2,\r\n indentUnit: (options.tabSize != undefined) ? options.tabSize : 2,\r\n indentWithTabs: (options.indentWithTabs === false) ? false : true,\r\n lineNumbers: false,\r\n autofocus: (options.autofocus === true) ? true : false,\r\n extraKeys: keyMaps,\r\n lineWrapping: (options.lineWrapping === false) ? false : true,\r\n allowDropFileTypes: ['text/plain'],\r\n placeholder: options.placeholder || el.getAttribute('placeholder') || '',\r\n styleSelectedText: (options.styleSelectedText != undefined) ? options.styleSelectedText : !isMobile(),\r\n inputStyle: (options.inputStyle != undefined) ? options.inputStyle : isMobile() ? 'contenteditable' : 'textarea',\r\n });\r\n\r\n this.codemirror.getScrollerElement().style.minHeight = options.minHeight;\r\n\r\n if (options.forceSync === true) {\r\n var cm = this.codemirror;\r\n cm.on('change', function () {\r\n cm.save();\r\n });\r\n }\r\n\r\n this.gui = {};\r\n\r\n if (options.toolbar !== false) {\r\n this.gui.toolbar = this.createToolbar();\r\n }\r\n if (options.status !== false) {\r\n this.gui.statusbar = this.createStatusbar();\r\n }\r\n if (options.autosave != undefined && options.autosave.enabled === true) {\r\n this.autosave();\r\n }\r\n\r\n this.gui.sideBySide = this.createSideBySide();\r\n\r\n this._rendered = this.element;\r\n\r\n\r\n // Fixes CodeMirror bug (#344)\r\n var temp_cm = this.codemirror;\r\n setTimeout(function () {\r\n temp_cm.refresh();\r\n }.bind(temp_cm), 0);\r\n};\r\n\r\n// Safari, in Private Browsing Mode, looks like it supports localStorage but all calls to setItem throw QuotaExceededError. We're going to detect this and set a variable accordingly.\r\nfunction isLocalStorageAvailable() {\r\n if (typeof localStorage === 'object') {\r\n try {\r\n localStorage.setItem('smde_localStorage', 1);\r\n localStorage.removeItem('smde_localStorage');\r\n } catch (e) {\r\n return false;\r\n }\r\n } else {\r\n return false;\r\n }\r\n\r\n return true;\r\n}\r\n\r\nInscrybMDE.prototype.autosave = function () {\r\n if (isLocalStorageAvailable()) {\r\n var inscrybmde = this;\r\n\r\n if (this.options.autosave.uniqueId == undefined || this.options.autosave.uniqueId == '') {\r\n console.log('InscrybMDE: You must set a uniqueId to use the autosave feature');\r\n return;\r\n }\r\n\r\n if (inscrybmde.element.form != null && inscrybmde.element.form != undefined) {\r\n inscrybmde.element.form.addEventListener('submit', function () {\r\n localStorage.removeItem('smde_' + inscrybmde.options.autosave.uniqueId);\r\n });\r\n }\r\n\r\n if (this.options.autosave.loaded !== true) {\r\n if (typeof localStorage.getItem('smde_' + this.options.autosave.uniqueId) == 'string' && localStorage.getItem('smde_' + this.options.autosave.uniqueId) != '') {\r\n this.codemirror.setValue(localStorage.getItem('smde_' + this.options.autosave.uniqueId));\r\n this.options.autosave.foundSavedValue = true;\r\n }\r\n\r\n this.options.autosave.loaded = true;\r\n }\r\n\r\n localStorage.setItem('smde_' + this.options.autosave.uniqueId, inscrybmde.value());\r\n\r\n if (typeof this.options.autosave.callback === 'function')\r\n this.options.autosave.callback.call(this);\r\n\r\n var el = document.getElementById('autosaved');\r\n if (el != null && el != undefined && el != '') {\r\n var d = new Date();\r\n var hh = d.getHours();\r\n var m = d.getMinutes();\r\n var dd = 'am';\r\n var h = hh;\r\n if (h >= 12) {\r\n h = hh - 12;\r\n dd = 'pm';\r\n }\r\n if (h == 0) {\r\n h = 12;\r\n }\r\n m = m < 10 ? '0' + m : m;\r\n\r\n el.innerHTML = 'Autosaved: ' + h + ':' + m + ' ' + dd;\r\n }\r\n\r\n this.autosaveTimeoutId = setTimeout(function () {\r\n inscrybmde.autosave();\r\n }, this.options.autosave.delay || 10000);\r\n } else {\r\n console.log('InscrybMDE: localStorage not available, cannot autosave');\r\n }\r\n};\r\n\r\nInscrybMDE.prototype.clearAutosavedValue = function () {\r\n if (isLocalStorageAvailable()) {\r\n if (this.options.autosave == undefined || this.options.autosave.uniqueId == undefined || this.options.autosave.uniqueId == '') {\r\n console.log('InscrybMDE: You must set a uniqueId to clear the autosave value');\r\n return;\r\n }\r\n\r\n localStorage.removeItem('smde_' + this.options.autosave.uniqueId);\r\n } else {\r\n console.log('InscrybMDE: localStorage not available, cannot autosave');\r\n }\r\n};\r\n\r\nInscrybMDE.prototype.createSideBySide = function () {\r\n var cm = this.codemirror;\r\n var wrapper = cm.getWrapperElement();\r\n var preview = wrapper.nextSibling;\r\n\r\n if (!preview || !/editor-preview-side/.test(preview.className)) {\r\n preview = document.createElement('div');\r\n preview.className = 'editor-preview-side';\r\n wrapper.parentNode.insertBefore(preview, wrapper.nextSibling);\r\n }\r\n\r\n if (this.options.syncSideBySidePreviewScroll === false) return preview;\r\n // Syncs scroll editor -> preview\r\n var cScroll = false;\r\n var pScroll = false;\r\n cm.on('scroll', function (v) {\r\n if (cScroll) {\r\n cScroll = false;\r\n return;\r\n }\r\n pScroll = true;\r\n var height = v.getScrollInfo().height - v.getScrollInfo().clientHeight;\r\n var ratio = parseFloat(v.getScrollInfo().top) / height;\r\n var move = (preview.scrollHeight - preview.clientHeight) * ratio;\r\n preview.scrollTop = move;\r\n });\r\n\r\n // Syncs scroll preview -> editor\r\n preview.onscroll = function () {\r\n if (pScroll) {\r\n pScroll = false;\r\n return;\r\n }\r\n cScroll = true;\r\n var height = preview.scrollHeight - preview.clientHeight;\r\n var ratio = parseFloat(preview.scrollTop) / height;\r\n var move = (cm.getScrollInfo().height - cm.getScrollInfo().clientHeight) * ratio;\r\n cm.scrollTo(0, move);\r\n };\r\n return preview;\r\n};\r\n\r\nInscrybMDE.prototype.createToolbar = function (items) {\r\n items = items || this.options.toolbar;\r\n\r\n if (!items || items.length === 0) {\r\n return;\r\n }\r\n var i;\r\n for (i = 0; i < items.length; i++) {\r\n if (toolbarBuiltInButtons[items[i]] != undefined) {\r\n items[i] = toolbarBuiltInButtons[items[i]];\r\n }\r\n }\r\n\r\n var bar = document.createElement('div');\r\n bar.className = 'editor-toolbar';\r\n\r\n var self = this;\r\n\r\n var toolbarData = {};\r\n self.toolbar = items;\r\n\r\n for (i = 0; i < items.length; i++) {\r\n if (items[i].name == 'guide' && self.options.toolbarGuideIcon === false)\r\n continue;\r\n\r\n if (self.options.hideIcons && self.options.hideIcons.indexOf(items[i].name) != -1)\r\n continue;\r\n\r\n // Fullscreen does not work well on mobile devices (even tablets)\r\n // In the future, hopefully this can be resolved\r\n if ((items[i].name == 'fullscreen' || items[i].name == 'side-by-side') && isMobile())\r\n continue;\r\n\r\n\r\n // Don't include trailing separators\r\n if (items[i] === '|') {\r\n var nonSeparatorIconsFollow = false;\r\n\r\n for (var x = (i + 1); x < items.length; x++) {\r\n if (items[x] !== '|' && (!self.options.hideIcons || self.options.hideIcons.indexOf(items[x].name) == -1)) {\r\n nonSeparatorIconsFollow = true;\r\n }\r\n }\r\n\r\n if (!nonSeparatorIconsFollow)\r\n continue;\r\n }\r\n\r\n\r\n // Create the icon and append to the toolbar\r\n (function (item) {\r\n var el;\r\n if (item === '|') {\r\n el = createSep();\r\n } else {\r\n el = createIcon(item, self.options.toolbarTips, self.options.shortcuts);\r\n }\r\n\r\n // bind events, special for info\r\n if (item.action) {\r\n if (typeof item.action === 'function') {\r\n el.onclick = function (e) {\r\n e.preventDefault();\r\n item.action(self);\r\n };\r\n } else if (typeof item.action === 'string') {\r\n el.onclick = function (e) {\r\n e.preventDefault();\r\n window.open(item.action, '_blank');\r\n };\r\n }\r\n }\r\n\r\n toolbarData[item.name || item] = el;\r\n bar.appendChild(el);\r\n })(items[i]);\r\n }\r\n\r\n self.toolbarElements = toolbarData;\r\n\r\n var cm = this.codemirror;\r\n cm.on('cursorActivity', function () {\r\n var stat = getState(cm);\r\n\r\n for (var key in toolbarData) {\r\n (function (key) {\r\n var el = toolbarData[key];\r\n if (stat[key]) {\r\n el.className += ' active';\r\n } else if (key != 'fullscreen' && key != 'side-by-side') {\r\n el.className = el.className.replace(/\\s*active\\s*/g, '');\r\n }\r\n })(key);\r\n }\r\n });\r\n\r\n var cmWrapper = cm.getWrapperElement();\r\n cmWrapper.parentNode.insertBefore(bar, cmWrapper);\r\n return bar;\r\n};\r\n\r\nInscrybMDE.prototype.createStatusbar = function (status) {\r\n // Initialize\r\n status = status || this.options.status;\r\n var options = this.options;\r\n var cm = this.codemirror;\r\n\r\n\r\n // Make sure the status variable is valid\r\n if (!status || status.length === 0)\r\n return;\r\n\r\n\r\n // Set up the built-in items\r\n var items = [];\r\n var i, onUpdate, defaultValue;\r\n\r\n for (i = 0; i < status.length; i++) {\r\n // Reset some values\r\n onUpdate = undefined;\r\n defaultValue = undefined;\r\n\r\n\r\n // Handle if custom or not\r\n if (typeof status[i] === 'object') {\r\n items.push({\r\n className: status[i].className,\r\n defaultValue: status[i].defaultValue,\r\n onUpdate: status[i].onUpdate\r\n });\r\n } else {\r\n var name = status[i];\r\n\r\n if (name === 'words') {\r\n defaultValue = function (el) {\r\n el.innerHTML = wordCount(cm.getValue());\r\n };\r\n onUpdate = function (el) {\r\n el.innerHTML = wordCount(cm.getValue());\r\n };\r\n } else if (name === 'lines') {\r\n defaultValue = function (el) {\r\n el.innerHTML = cm.lineCount();\r\n };\r\n onUpdate = function (el) {\r\n el.innerHTML = cm.lineCount();\r\n };\r\n } else if (name === 'cursor') {\r\n defaultValue = function (el) {\r\n el.innerHTML = '0:0';\r\n };\r\n onUpdate = function (el) {\r\n var pos = cm.getCursor();\r\n el.innerHTML = pos.line + ':' + pos.ch;\r\n };\r\n } else if (name === 'autosave') {\r\n defaultValue = function (el) {\r\n if (options.autosave != undefined && options.autosave.enabled === true) {\r\n el.setAttribute('id', 'autosaved');\r\n }\r\n };\r\n }\r\n\r\n items.push({\r\n className: name,\r\n defaultValue: defaultValue,\r\n onUpdate: onUpdate\r\n });\r\n }\r\n }\r\n\r\n\r\n // Create element for the status bar\r\n var bar = document.createElement('div');\r\n bar.className = 'editor-statusbar';\r\n\r\n\r\n // Create a new span for each item\r\n for (i = 0; i < items.length; i++) {\r\n // Store in temporary variable\r\n var item = items[i];\r\n\r\n\r\n // Create span element\r\n var el = document.createElement('span');\r\n el.className = item.className;\r\n\r\n\r\n // Ensure the defaultValue is a function\r\n if (typeof item.defaultValue === 'function') {\r\n item.defaultValue(el);\r\n }\r\n\r\n\r\n // Ensure the onUpdate is a function\r\n if (typeof item.onUpdate === 'function') {\r\n // Create a closure around the span of the current action, then execute the onUpdate handler\r\n this.codemirror.on('update', (function (el, item) {\r\n return function () {\r\n item.onUpdate(el);\r\n };\r\n }(el, item)));\r\n }\r\n\r\n\r\n // Append the item to the status bar\r\n bar.appendChild(el);\r\n }\r\n\r\n\r\n // Insert the status bar into the DOM\r\n var cmWrapper = this.codemirror.getWrapperElement();\r\n cmWrapper.parentNode.insertBefore(bar, cmWrapper.nextSibling);\r\n return bar;\r\n};\r\n\r\n/**\r\n * Get or set the text content.\r\n */\r\nInscrybMDE.prototype.value = function (val) {\r\n var cm = this.codemirror;\r\n if (val === undefined) {\r\n return cm.getValue();\r\n } else {\r\n cm.getDoc().setValue(val);\r\n if (this.isPreviewActive()) {\r\n var wrapper = cm.getWrapperElement();\r\n var preview = wrapper.lastChild;\r\n preview.innerHTML = this.options.previewRender(val, preview);\r\n }\r\n return this;\r\n }\r\n};\r\n\r\n\r\n/**\r\n * Bind static methods for exports.\r\n */\r\nInscrybMDE.toggleBold = toggleBold;\r\nInscrybMDE.toggleItalic = toggleItalic;\r\nInscrybMDE.toggleStrikethrough = toggleStrikethrough;\r\nInscrybMDE.toggleBlockquote = toggleBlockquote;\r\nInscrybMDE.toggleHeadingSmaller = toggleHeadingSmaller;\r\nInscrybMDE.toggleHeadingBigger = toggleHeadingBigger;\r\nInscrybMDE.toggleHeading1 = toggleHeading1;\r\nInscrybMDE.toggleHeading2 = toggleHeading2;\r\nInscrybMDE.toggleHeading3 = toggleHeading3;\r\nInscrybMDE.toggleCodeBlock = toggleCodeBlock;\r\nInscrybMDE.toggleUnorderedList = toggleUnorderedList;\r\nInscrybMDE.toggleOrderedList = toggleOrderedList;\r\nInscrybMDE.cleanBlock = cleanBlock;\r\nInscrybMDE.drawLink = drawLink;\r\nInscrybMDE.drawImage = drawImage;\r\nInscrybMDE.drawTable = drawTable;\r\nInscrybMDE.drawHorizontalRule = drawHorizontalRule;\r\nInscrybMDE.undo = undo;\r\nInscrybMDE.redo = redo;\r\nInscrybMDE.togglePreview = togglePreview;\r\nInscrybMDE.toggleSideBySide = toggleSideBySide;\r\nInscrybMDE.toggleFullScreen = toggleFullScreen;\r\n\r\n/**\r\n * Bind instance methods for exports.\r\n */\r\nInscrybMDE.prototype.toggleBold = function () {\r\n toggleBold(this);\r\n};\r\nInscrybMDE.prototype.toggleItalic = function () {\r\n toggleItalic(this);\r\n};\r\nInscrybMDE.prototype.toggleStrikethrough = function () {\r\n toggleStrikethrough(this);\r\n};\r\nInscrybMDE.prototype.toggleBlockquote = function () {\r\n toggleBlockquote(this);\r\n};\r\nInscrybMDE.prototype.toggleHeadingSmaller = function () {\r\n toggleHeadingSmaller(this);\r\n};\r\nInscrybMDE.prototype.toggleHeadingBigger = function () {\r\n toggleHeadingBigger(this);\r\n};\r\nInscrybMDE.prototype.toggleHeading1 = function () {\r\n toggleHeading1(this);\r\n};\r\nInscrybMDE.prototype.toggleHeading2 = function () {\r\n toggleHeading2(this);\r\n};\r\nInscrybMDE.prototype.toggleHeading3 = function () {\r\n toggleHeading3(this);\r\n};\r\nInscrybMDE.prototype.toggleCodeBlock = function () {\r\n toggleCodeBlock(this);\r\n};\r\nInscrybMDE.prototype.toggleUnorderedList = function () {\r\n toggleUnorderedList(this);\r\n};\r\nInscrybMDE.prototype.toggleOrderedList = function () {\r\n toggleOrderedList(this);\r\n};\r\nInscrybMDE.prototype.cleanBlock = function () {\r\n cleanBlock(this);\r\n};\r\nInscrybMDE.prototype.drawLink = function () {\r\n drawLink(this);\r\n};\r\nInscrybMDE.prototype.drawImage = function () {\r\n drawImage(this);\r\n};\r\nInscrybMDE.prototype.drawTable = function () {\r\n drawTable(this);\r\n};\r\nInscrybMDE.prototype.drawHorizontalRule = function () {\r\n drawHorizontalRule(this);\r\n};\r\nInscrybMDE.prototype.undo = function () {\r\n undo(this);\r\n};\r\nInscrybMDE.prototype.redo = function () {\r\n redo(this);\r\n};\r\nInscrybMDE.prototype.togglePreview = function () {\r\n togglePreview(this);\r\n};\r\nInscrybMDE.prototype.toggleSideBySide = function () {\r\n toggleSideBySide(this);\r\n};\r\nInscrybMDE.prototype.toggleFullScreen = function () {\r\n toggleFullScreen(this);\r\n};\r\n\r\nInscrybMDE.prototype.isPreviewActive = function () {\r\n var cm = this.codemirror;\r\n var wrapper = cm.getWrapperElement();\r\n var preview = wrapper.lastChild;\r\n\r\n return /editor-preview-active/.test(preview.className);\r\n};\r\n\r\nInscrybMDE.prototype.isSideBySideActive = function () {\r\n var cm = this.codemirror;\r\n var wrapper = cm.getWrapperElement();\r\n var preview = wrapper.nextSibling;\r\n\r\n return /editor-preview-active-side/.test(preview.className);\r\n};\r\n\r\nInscrybMDE.prototype.isFullscreenActive = function () {\r\n var cm = this.codemirror;\r\n\r\n return cm.getOption('fullScreen');\r\n};\r\n\r\nInscrybMDE.prototype.getState = function () {\r\n var cm = this.codemirror;\r\n\r\n return getState(cm);\r\n};\r\n\r\nInscrybMDE.prototype.toTextArea = function () {\r\n var cm = this.codemirror;\r\n var wrapper = cm.getWrapperElement();\r\n\r\n if (wrapper.parentNode) {\r\n if (this.gui.toolbar) {\r\n wrapper.parentNode.removeChild(this.gui.toolbar);\r\n }\r\n if (this.gui.statusbar) {\r\n wrapper.parentNode.removeChild(this.gui.statusbar);\r\n }\r\n if (this.gui.sideBySide) {\r\n wrapper.parentNode.removeChild(this.gui.sideBySide);\r\n }\r\n }\r\n\r\n cm.toTextArea();\r\n\r\n if (this.autosaveTimeoutId) {\r\n clearTimeout(this.autosaveTimeoutId);\r\n this.autosaveTimeoutId = undefined;\r\n this.clearAutosavedValue();\r\n }\r\n};\r\n\r\nmodule.exports = InscrybMDE;\r\n","/**\n * marked - a markdown parser\n * Copyright (c) 2011-2018, Christopher Jeffrey. (MIT Licensed)\n * https://github.com/markedjs/marked\n */\n\n;(function(root) {\n'use strict';\n\n/**\n * Block-Level Grammar\n */\n\nvar block = {\n newline: /^\\n+/,\n code: /^( {4}[^\\n]+\\n*)+/,\n fences: noop,\n hr: /^ {0,3}((?:- *){3,}|(?:_ *){3,}|(?:\\* *){3,})(?:\\n+|$)/,\n heading: /^ *(#{1,6}) *([^\\n]+?) *(?:#+ *)?(?:\\n+|$)/,\n nptable: noop,\n blockquote: /^( {0,3}> ?(paragraph|[^\\n]*)(?:\\n|$))+/,\n list: /^( *)(bull) [\\s\\S]+?(?:hr|def|\\n{2,}(?! )(?!\\1bull )\\n*|\\s*$)/,\n html: '^ {0,3}(?:' // optional indentation\n + '<(script|pre|style)[\\\\s>][\\\\s\\\\S]*?(?:\\\\1>[^\\\\n]*\\\\n+|$)' // (1)\n + '|comment[^\\\\n]*(\\\\n+|$)' // (2)\n + '|<\\\\?[\\\\s\\\\S]*?\\\\?>\\\\n*' // (3)\n + '|\\\\n*' // (4)\n + '|\\\\n*' // (5)\n + '|?(tag)(?: +|\\\\n|/?>)[\\\\s\\\\S]*?(?:\\\\n{2,}|$)' // (6)\n + '|<(?!script|pre|style)([a-z][\\\\w-]*)(?:attribute)*? */?>(?=\\\\h*\\\\n)[\\\\s\\\\S]*?(?:\\\\n{2,}|$)' // (7) open tag\n + '|(?!script|pre|style)[a-z][\\\\w-]*\\\\s*>(?=\\\\h*\\\\n)[\\\\s\\\\S]*?(?:\\\\n{2,}|$)' // (7) closing tag\n + ')',\n def: /^ {0,3}\\[(label)\\]: *\\n? *([^\\s>]+)>?(?:(?: +\\n? *| *\\n *)(title))? *(?:\\n+|$)/,\n table: noop,\n lheading: /^([^\\n]+)\\n *(=|-){2,} *(?:\\n+|$)/,\n paragraph: /^([^\\n]+(?:\\n(?!hr|heading|lheading| {0,3}>|<\\/?(?:tag)(?: +|\\n|\\/?>)|<(?:script|pre|style|!--))[^\\n]+)*)/,\n text: /^[^\\n]+/\n};\n\nblock._label = /(?!\\s*\\])(?:\\\\[\\[\\]]|[^\\[\\]])+/;\nblock._title = /(?:\"(?:\\\\\"?|[^\"\\\\])*\"|'[^'\\n]*(?:\\n[^'\\n]+)*\\n?'|\\([^()]*\\))/;\nblock.def = edit(block.def)\n .replace('label', block._label)\n .replace('title', block._title)\n .getRegex();\n\nblock.bullet = /(?:[*+-]|\\d+\\.)/;\nblock.item = /^( *)(bull) [^\\n]*(?:\\n(?!\\1bull )[^\\n]*)*/;\nblock.item = edit(block.item, 'gm')\n .replace(/bull/g, block.bullet)\n .getRegex();\n\nblock.list = edit(block.list)\n .replace(/bull/g, block.bullet)\n .replace('hr', '\\\\n+(?=\\\\1?(?:(?:- *){3,}|(?:_ *){3,}|(?:\\\\* *){3,})(?:\\\\n+|$))')\n .replace('def', '\\\\n+(?=' + block.def.source + ')')\n .getRegex();\n\nblock._tag = 'address|article|aside|base|basefont|blockquote|body|caption'\n + '|center|col|colgroup|dd|details|dialog|dir|div|dl|dt|fieldset|figcaption'\n + '|figure|footer|form|frame|frameset|h[1-6]|head|header|hr|html|iframe'\n + '|legend|li|link|main|menu|menuitem|meta|nav|noframes|ol|optgroup|option'\n + '|p|param|section|source|summary|table|tbody|td|tfoot|th|thead|title|tr'\n + '|track|ul';\nblock._comment = //;\nblock.html = edit(block.html, 'i')\n .replace('comment', block._comment)\n .replace('tag', block._tag)\n .replace('attribute', / +[a-zA-Z:_][\\w.:-]*(?: *= *\"[^\"\\n]*\"| *= *'[^'\\n]*'| *= *[^\\s\"'=<>`]+)?/)\n .getRegex();\n\nblock.paragraph = edit(block.paragraph)\n .replace('hr', block.hr)\n .replace('heading', block.heading)\n .replace('lheading', block.lheading)\n .replace('tag', block._tag) // pars can be interrupted by type (6) html blocks\n .getRegex();\n\nblock.blockquote = edit(block.blockquote)\n .replace('paragraph', block.paragraph)\n .getRegex();\n\n/**\n * Normal Block Grammar\n */\n\nblock.normal = merge({}, block);\n\n/**\n * GFM Block Grammar\n */\n\nblock.gfm = merge({}, block.normal, {\n fences: /^ *(`{3,}|~{3,})[ \\.]*(\\S+)? *\\n([\\s\\S]*?)\\n? *\\1 *(?:\\n+|$)/,\n paragraph: /^/,\n heading: /^ *(#{1,6}) +([^\\n]+?) *#* *(?:\\n+|$)/\n});\n\nblock.gfm.paragraph = edit(block.paragraph)\n .replace('(?!', '(?!'\n + block.gfm.fences.source.replace('\\\\1', '\\\\2') + '|'\n + block.list.source.replace('\\\\1', '\\\\3') + '|')\n .getRegex();\n\n/**\n * GFM + Tables Block Grammar\n */\n\nblock.tables = merge({}, block.gfm, {\n nptable: /^ *([^|\\n ].*\\|.*)\\n *([-:]+ *\\|[-| :]*)(?:\\n((?:.*[^>\\n ].*(?:\\n|$))*)\\n*|$)/,\n table: /^ *\\|(.+)\\n *\\|?( *[-:]+[-| :]*)(?:\\n((?: *[^>\\n ].*(?:\\n|$))*)\\n*|$)/\n});\n\n/**\n * Pedantic grammar\n */\n\nblock.pedantic = merge({}, block.normal, {\n html: edit(\n '^ *(?:comment *(?:\\\\n|\\\\s*$)'\n + '|<(tag)[\\\\s\\\\S]+?\\\\1> *(?:\\\\n{2,}|\\\\s*$)' // closed tag\n + '|'\n + (escaped ? code : escape(code, true))\n + '
';\n }\n\n return ''\n + (escaped ? code : escape(code, true))\n + '
\\n';\n};\n\nRenderer.prototype.blockquote = function(quote) {\n return '\\n' + quote + '\\n';\n};\n\nRenderer.prototype.html = function(html) {\n return html;\n};\n\nRenderer.prototype.heading = function(text, level, raw) {\n if (this.options.headerIds) {\n return '
' + text + '
\\n';\n};\n\nRenderer.prototype.table = function(header, body) {\n if (body) body = '' + body + '';\n\n return '' + text + '
';\n};\n\nRenderer.prototype.br = function() {\n return this.options.xhtml ? 'An error occurred:
'\n + escape(e.message + '', true)\n + '';\n }\n throw e;\n }\n}\n\n/**\n * Options\n */\n\nmarked.options =\nmarked.setOptions = function(opt) {\n merge(marked.defaults, opt);\n return marked;\n};\n\nmarked.getDefaults = function () {\n return {\n baseUrl: null,\n breaks: false,\n gfm: true,\n headerIds: true,\n headerPrefix: '',\n highlight: null,\n langPrefix: 'language-',\n mangle: true,\n pedantic: false,\n renderer: new Renderer(),\n sanitize: false,\n sanitizer: null,\n silent: false,\n smartLists: false,\n smartypants: false,\n tables: true,\n xhtml: false\n };\n};\n\nmarked.defaults = marked.getDefaults();\n\n/**\n * Expose\n */\n\nmarked.Parser = Parser;\nmarked.parser = Parser.parse;\n\nmarked.Renderer = Renderer;\nmarked.TextRenderer = TextRenderer;\n\nmarked.Lexer = Lexer;\nmarked.lexer = Lexer.lex;\n\nmarked.InlineLexer = InlineLexer;\nmarked.inlineLexer = InlineLexer.output;\n\nmarked.parse = marked;\n\nif (typeof module !== 'undefined' && typeof exports === 'object') {\n module.exports = marked;\n} else if (typeof define === 'function' && define.amd) {\n define(function() { return marked; });\n} else {\n root.marked = marked;\n}\n})(this || (typeof window !== 'undefined' ? window : global));\n","var deepFreezeEs6 = {exports: {}};\n\nfunction deepFreeze(obj) {\n if (obj instanceof Map) {\n obj.clear = obj.delete = obj.set = function () {\n throw new Error('map is read-only');\n };\n } else if (obj instanceof Set) {\n obj.add = obj.clear = obj.delete = function () {\n throw new Error('set is read-only');\n };\n }\n\n // Freeze self\n Object.freeze(obj);\n\n Object.getOwnPropertyNames(obj).forEach(function (name) {\n var prop = obj[name];\n\n // Freeze prop if it is an object\n if (typeof prop == 'object' && !Object.isFrozen(prop)) {\n deepFreeze(prop);\n }\n });\n\n return obj;\n}\n\ndeepFreezeEs6.exports = deepFreeze;\ndeepFreezeEs6.exports.default = deepFreeze;\n\n/** @typedef {import('highlight.js').CallbackResponse} CallbackResponse */\n/** @typedef {import('highlight.js').CompiledMode} CompiledMode */\n/** @implements CallbackResponse */\n\nclass Response {\n /**\n * @param {CompiledMode} mode\n */\n constructor(mode) {\n // eslint-disable-next-line no-undefined\n if (mode.data === undefined) mode.data = {};\n\n this.data = mode.data;\n this.isMatchIgnored = false;\n }\n\n ignoreMatch() {\n this.isMatchIgnored = true;\n }\n}\n\n/**\n * @param {string} value\n * @returns {string}\n */\nfunction escapeHTML(value) {\n return value\n .replace(/&/g, '&')\n .replace(//g, '>')\n .replace(/\"/g, '"')\n .replace(/'/g, ''');\n}\n\n/**\n * performs a shallow merge of multiple objects into one\n *\n * @template T\n * @param {T} original\n * @param {Record