medium-editor
Advanced tools
Comparing version 1.7.5 to 1.8.0
{ | ||
"name": "medium-editor", | ||
"version": "1.7.5", | ||
"version": "1.8.0", | ||
"homepage": "http://daviferreira.github.io/medium-editor/", | ||
@@ -5,0 +5,0 @@ "authors": [ |
@@ -0,1 +1,11 @@ | ||
1.8.0 / 2014-04-12 | ||
================== | ||
* Removes anchor preview listeners on deactivate | ||
* Implements clean paste | ||
* Adds an option to validate links | ||
* Adds a basic extensions support | ||
* Misc minor fixes | ||
1.7.5 / 2014-03-30 | ||
@@ -2,0 +12,0 @@ ================== |
@@ -88,2 +88,7 @@ function MediumEditor(elements, options) { | ||
// https://github.com/jashkenas/underscore | ||
function isElement(obj) { | ||
return !!(obj && obj.nodeType === 1); | ||
} | ||
MediumEditor.prototype = { | ||
@@ -93,4 +98,7 @@ defaults: { | ||
anchorInputPlaceholder: 'Paste or type a link', | ||
anchorPreviewHideDelay: 500, | ||
buttons: ['bold', 'italic', 'underline', 'anchor', 'header1', 'header2', 'quote'], | ||
buttonLabels: false, | ||
checkLinkFormat: false, | ||
cleanPastedHTML: false, | ||
delay: 0, | ||
@@ -107,3 +115,3 @@ diffLeft: 0, | ||
targetBlank: false, | ||
anchorPreviewHideDelay: 500 | ||
extensions: {} | ||
}, | ||
@@ -147,3 +155,3 @@ | ||
this.elements[i].setAttribute('data-medium-element', true); | ||
this.bindParagraphCreation(i).bindReturn(i).bindTab(i).bindAnchorPreview(i); | ||
this.bindParagraphCreation(i).bindReturn(i).bindTab(i); | ||
if (!this.options.disableToolbar && !this.elements[i].getAttribute('data-disable-toolbar')) { | ||
@@ -157,3 +165,4 @@ addToolbar = true; | ||
.bindButtons() | ||
.bindAnchorForm(); | ||
.bindAnchorForm() | ||
.bindAnchorPreview(); | ||
} | ||
@@ -176,2 +185,28 @@ return this; | ||
/** | ||
* Helper function to call a method with a number of parameters on all registered extensions. | ||
* The function assures that the function exists before calling. | ||
* | ||
* @param {string} funcName name of the function to call | ||
* @param [args] arguments passed into funcName | ||
*/ | ||
callExtensions: function (funcName) { | ||
if (arguments.length < 1) { | ||
return; | ||
} | ||
var args = Array.prototype.slice.call(arguments, 1), | ||
ext, | ||
name; | ||
for (name in this.options.extensions) { | ||
if (this.options.extensions.hasOwnProperty(name)) { | ||
ext = this.options.extensions[name]; | ||
if (ext[funcName] !== undefined) { | ||
ext[funcName].apply(ext, args); | ||
} | ||
} | ||
} | ||
}, | ||
bindParagraphCreation: function (index) { | ||
@@ -253,18 +288,18 @@ var self = this; | ||
buttonTemplates = { | ||
'bold': '<li><button class="medium-editor-action medium-editor-action-bold" data-action="bold" data-element="b">' + buttonLabels.bold + '</button></li>', | ||
'italic': '<li><button class="medium-editor-action medium-editor-action-italic" data-action="italic" data-element="i">' + buttonLabels.italic + '</button></li>', | ||
'underline': '<li><button class="medium-editor-action medium-editor-action-underline" data-action="underline" data-element="u">' + buttonLabels.underline + '</button></li>', | ||
'strikethrough': '<li><button class="medium-editor-action medium-editor-action-strikethrough" data-action="strikethrough" data-element="strike"><strike>A</strike></button></li>', | ||
'superscript': '<li><button class="medium-editor-action medium-editor-action-superscript" data-action="superscript" data-element="sup">' + buttonLabels.superscript + '</button></li>', | ||
'subscript': '<li><button class="medium-editor-action medium-editor-action-subscript" data-action="subscript" data-element="sub">' + buttonLabels.subscript + '</button></li>', | ||
'anchor': '<li><button class="medium-editor-action medium-editor-action-anchor" data-action="anchor" data-element="a">' + buttonLabels.anchor + '</button></li>', | ||
'image': '<li><button class="medium-editor-action medium-editor-action-image" data-action="image" data-element="img">' + buttonLabels.image + '</button></li>', | ||
'header1': '<li><button class="medium-editor-action medium-editor-action-header1" data-action="append-' + this.options.firstHeader + '" data-element="' + this.options.firstHeader + '">' + buttonLabels.header1 + '</button></li>', | ||
'header2': '<li><button class="medium-editor-action medium-editor-action-header2" data-action="append-' + this.options.secondHeader + '" data-element="' + this.options.secondHeader + '">' + buttonLabels.header2 + '</button></li>', | ||
'quote': '<li><button class="medium-editor-action medium-editor-action-quote" data-action="append-blockquote" data-element="blockquote">' + buttonLabels.quote + '</button></li>', | ||
'orderedlist': '<li><button class="medium-editor-action medium-editor-action-orderedlist" data-action="insertorderedlist" data-element="ol">' + buttonLabels.orderedlist + '</button></li>', | ||
'unorderedlist': '<li><button class="medium-editor-action medium-editor-action-unorderedlist" data-action="insertunorderedlist" data-element="ul">' + buttonLabels.unorderedlist + '</button></li>', | ||
'pre': '<li><button class="medium-editor-action medium-editor-action-pre" data-action="append-pre" data-element="pre">' + buttonLabels.pre + '</button></li>', | ||
'indent': '<li><button class="medium-editor-action medium-editor-action-indent" data-action="indent" data-element="ul">' + buttonLabels.indent + '</button></li>', | ||
'outdent': '<li><button class="medium-editor-action medium-editor-action-outdent" data-action="outdent" data-element="ul">' + buttonLabels.outdent + '</button></li>' | ||
'bold': '<button class="medium-editor-action medium-editor-action-bold" data-action="bold" data-element="b">' + buttonLabels.bold + '</button>', | ||
'italic': '<button class="medium-editor-action medium-editor-action-italic" data-action="italic" data-element="i">' + buttonLabels.italic + '</button>', | ||
'underline': '<button class="medium-editor-action medium-editor-action-underline" data-action="underline" data-element="u">' + buttonLabels.underline + '</button>', | ||
'strikethrough': '<button class="medium-editor-action medium-editor-action-strikethrough" data-action="strikethrough" data-element="strike"><strike>A</strike></button>', | ||
'superscript': '<button class="medium-editor-action medium-editor-action-superscript" data-action="superscript" data-element="sup">' + buttonLabels.superscript + '</button>', | ||
'subscript': '<button class="medium-editor-action medium-editor-action-subscript" data-action="subscript" data-element="sub">' + buttonLabels.subscript + '</button>', | ||
'anchor': '<button class="medium-editor-action medium-editor-action-anchor" data-action="anchor" data-element="a">' + buttonLabels.anchor + '</button>', | ||
'image': '<button class="medium-editor-action medium-editor-action-image" data-action="image" data-element="img">' + buttonLabels.image + '</button>', | ||
'header1': '<button class="medium-editor-action medium-editor-action-header1" data-action="append-' + this.options.firstHeader + '" data-element="' + this.options.firstHeader + '">' + buttonLabels.header1 + '</button>', | ||
'header2': '<button class="medium-editor-action medium-editor-action-header2" data-action="append-' + this.options.secondHeader + '" data-element="' + this.options.secondHeader + '">' + buttonLabels.header2 + '</button>', | ||
'quote': '<button class="medium-editor-action medium-editor-action-quote" data-action="append-blockquote" data-element="blockquote">' + buttonLabels.quote + '</button>', | ||
'orderedlist': '<button class="medium-editor-action medium-editor-action-orderedlist" data-action="insertorderedlist" data-element="ol">' + buttonLabels.orderedlist + '</button>', | ||
'unorderedlist': '<button class="medium-editor-action medium-editor-action-unorderedlist" data-action="insertunorderedlist" data-element="ul">' + buttonLabels.unorderedlist + '</button>', | ||
'pre': '<button class="medium-editor-action medium-editor-action-pre" data-action="append-pre" data-element="pre">' + buttonLabels.pre + '</button>', | ||
'indent': '<button class="medium-editor-action medium-editor-action-indent" data-action="indent" data-element="ul">' + buttonLabels.indent + '</button>', | ||
'outdent': '<button class="medium-editor-action medium-editor-action-outdent" data-action="outdent" data-element="ul">' + buttonLabels.outdent + '</button>' | ||
}; | ||
@@ -324,23 +359,2 @@ return buttonTemplates[btnType] || false; | ||
//TODO: actionTemplate | ||
toolbarTemplate: function () { | ||
var btns = this.options.buttons, | ||
html = '<ul id="medium-editor-toolbar-actions" class="medium-editor-toolbar-actions clearfix">', | ||
i, | ||
tpl; | ||
for (i = 0; i < btns.length; i += 1) { | ||
tpl = this.buttonTemplate(btns[i]); | ||
if (tpl) { | ||
html += tpl; | ||
} | ||
} | ||
html += '</ul>' + | ||
'<div class="medium-editor-toolbar-form-anchor" id="medium-editor-toolbar-form-anchor">' + | ||
' <input type="text" value="" placeholder="' + this.options.anchorInputPlaceholder + '">' + | ||
' <a href="#">×</a>' + | ||
'</div>'; | ||
return html; | ||
}, | ||
initToolbar: function () { | ||
@@ -364,3 +378,4 @@ if (this.toolbar) { | ||
toolbar.className = 'medium-editor-toolbar'; | ||
toolbar.innerHTML = this.toolbarTemplate(); | ||
toolbar.appendChild(this.toolbarButtons()); | ||
toolbar.appendChild(this.toolbarFormAnchor()); | ||
document.body.appendChild(toolbar); | ||
@@ -370,2 +385,55 @@ return toolbar; | ||
//TODO: actionTemplate | ||
toolbarButtons: function () { | ||
var btns = this.options.buttons, | ||
ul = document.createElement('ul'), | ||
li, | ||
i, | ||
btn, | ||
ext; | ||
ul.id = 'medium-editor-toolbar-actions'; | ||
ul.className = 'medium-editor-toolbar-actions clearfix'; | ||
for (i = 0; i < btns.length; i += 1) { | ||
if (this.options.extensions.hasOwnProperty(btns[i])) { | ||
ext = this.options.extensions[btns[i]]; | ||
btn = ext.getButton !== undefined ? ext.getButton() : null; | ||
} else { | ||
btn = this.buttonTemplate(btns[i]); | ||
} | ||
if (btn) { | ||
li = document.createElement('li'); | ||
if (isElement(btn)) { | ||
li.appendChild(btn); | ||
} else { | ||
li.innerHTML = btn; | ||
} | ||
ul.appendChild(li); | ||
} | ||
} | ||
return ul; | ||
}, | ||
toolbarFormAnchor: function () { | ||
var anchor = document.createElement('div'), | ||
input = document.createElement('input'), | ||
a = document.createElement('a'); | ||
a.setAttribute('href', '#'); | ||
a.innerHTML = '×'; | ||
input.setAttribute('type', 'text'); | ||
input.setAttribute('placeholder', this.options.anchorInputPlaceholder); | ||
anchor.className = 'medium-editor-toolbar-form-anchor'; | ||
anchor.id = 'medium-editor-toolbar-form-anchor'; | ||
anchor.appendChild(input); | ||
anchor.appendChild(a); | ||
return anchor; | ||
}, | ||
bindSelect: function () { | ||
@@ -451,5 +519,3 @@ var self = this, | ||
var selection = window.getSelection(), | ||
range = selection.getRangeAt(0), | ||
current = range.commonAncestorContainer, | ||
parent = current.parentNode, | ||
range, current, parent, | ||
result, | ||
@@ -469,2 +535,6 @@ getMediumElement = function (e) { | ||
try { | ||
range = selection.getRangeAt(0); | ||
current = range.commonAncestorContainer; | ||
parent = current.parentNode; | ||
if (current.getAttribute('data-medium-element')) { | ||
@@ -523,3 +593,4 @@ result = current; | ||
checkActiveButtons: function () { | ||
var parentNode = this.selection.anchorNode; | ||
var elements = Array.prototype.slice.call(this.elements), | ||
parentNode = this.selection.anchorNode; | ||
if (!parentNode.tagName) { | ||
@@ -530,2 +601,8 @@ parentNode = this.selection.anchorNode.parentNode; | ||
this.activateButton(parentNode.tagName.toLowerCase()); | ||
this.callExtensions('checkState', parentNode); | ||
// we can abort the search upwards if we leave the contentEditable element | ||
if (elements.indexOf(parentNode) !== -1) { | ||
break; | ||
} | ||
parentNode = parentNode.parentNode; | ||
@@ -557,3 +634,5 @@ } | ||
} | ||
self.execAction(this.getAttribute('data-action'), e); | ||
if (this.hasAttribute('data-action')) { | ||
self.execAction(this.getAttribute('data-action'), e); | ||
} | ||
}; | ||
@@ -568,4 +647,6 @@ for (i = 0; i < buttons.length; i += 1) { | ||
setFirstAndLastItems: function (buttons) { | ||
buttons[0].className += ' medium-editor-button-first'; | ||
buttons[buttons.length - 1].className += ' medium-editor-button-last'; | ||
if (buttons.length > 0) { | ||
buttons[0].className += ' medium-editor-button-first'; | ||
buttons[buttons.length - 1].className += ' medium-editor-button-last'; | ||
} | ||
return this; | ||
@@ -657,3 +738,5 @@ }, | ||
this.keepToolbarAlive = false; | ||
this.toolbar.classList.remove('medium-editor-toolbar-active'); | ||
if (this.toolbar !== undefined) { | ||
this.toolbar.classList.remove('medium-editor-toolbar-active'); | ||
} | ||
}, | ||
@@ -669,3 +752,3 @@ | ||
timer = setTimeout(function () { | ||
if (!self.toolbar.classList.contains('medium-editor-toolbar-active')) { | ||
if (self.toolbar && !self.toolbar.classList.contains('medium-editor-toolbar-active')) { | ||
self.toolbar.classList.add('medium-editor-toolbar-active'); | ||
@@ -739,3 +822,3 @@ } | ||
timer = setTimeout(function () { | ||
if (!self.anchorPreview.classList.contains('medium-editor-anchor-preview-active')) { | ||
if (self.anchorPreview && !self.anchorPreview.classList.contains('medium-editor-anchor-preview-active')) { | ||
self.anchorPreview.classList.add('medium-editor-anchor-preview-active'); | ||
@@ -833,3 +916,5 @@ } | ||
setTimeout(function () { | ||
self.showAnchorForm(self.activeAnchor.href); | ||
if (self.activeAnchor) { | ||
self.showAnchorForm(self.activeAnchor.href); | ||
} | ||
self.keepToolbarAlive = false; | ||
@@ -855,3 +940,3 @@ }, 100 + self.options.delay); | ||
// Detect empty href attributes | ||
// The browser will make href="" or href="#top" | ||
// The browser will make href="" or href="#top" | ||
// into absolute urls when accessed as e.targed.href, so check the html | ||
@@ -882,9 +967,20 @@ if (!/href=["']\S+["']/.test(e.target.outerHTML) || /href=["']#\S+["']/.test(e.target.outerHTML)) { | ||
bindAnchorPreview: function (index) { | ||
var self = this; | ||
this.elements[index].addEventListener('mouseover', function (e) { | ||
var i, self = this; | ||
this.editorAnchorObserverWrapper = function (e) { | ||
self.editorAnchorObserver(e); | ||
}); | ||
}; | ||
for (i = 0; i < this.elements.length; i += 1) { | ||
this.elements[i].addEventListener('mouseover', this.editorAnchorObserverWrapper); | ||
} | ||
return this; | ||
}, | ||
checkLinkFormat: function (value) { | ||
var re = /^https?:\/\//; | ||
if (value.match(re)) { | ||
return value; | ||
} | ||
return "http://" + value; | ||
}, | ||
setTargetBlank: function () { | ||
@@ -905,2 +1001,5 @@ var el = getSelectionStart(), | ||
restoreSelection(this.savedSelection); | ||
if (this.options.checkLinkFormat) { | ||
input.value = this.checkLinkFormat(input.value); | ||
} | ||
document.execCommand('createLink', false, input.value); | ||
@@ -956,2 +1055,3 @@ if (this.options.targetBlank) { | ||
for (i = 0; i < this.elements.length; i += 1) { | ||
this.elements[i].removeEventListener('mouseover', this.editorAnchorObserverWrapper); | ||
this.elements[i].removeEventListener('keyup', this.checkSelectionWrapper); | ||
@@ -966,2 +1066,8 @@ this.elements[i].removeEventListener('blur', this.checkSelectionWrapper); | ||
htmlEntities: function (str) { | ||
// converts special characters (like <) into their escaped/encoded values (like <). | ||
// This allows you to show to display the string without the browser reading it as HTML. | ||
return String(str).replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"'); | ||
}, | ||
bindPaste: function () { | ||
@@ -975,3 +1081,3 @@ var i, self = this; | ||
this.classList.remove('medium-editor-placeholder'); | ||
if (!self.options.forcePlainText) { | ||
if (!self.options.forcePlainText && !self.options.cleanPastedHTML) { | ||
return this; | ||
@@ -982,2 +1088,6 @@ } | ||
e.preventDefault(); | ||
if (self.options.cleanPastedHTML && e.clipboardData.getData('text/html')) { | ||
return self.cleanPaste(e.clipboardData.getData('text/html')); | ||
} | ||
if (!self.options.disableReturn) { | ||
@@ -987,3 +1097,7 @@ paragraphs = e.clipboardData.getData('text/plain').split(/[\r\n]/g); | ||
if (paragraphs[p] !== '') { | ||
html += '<p>' + paragraphs[p] + '</p>'; | ||
if (navigator.userAgent.match(/firefox/i) && p === 0) { | ||
html += self.htmlEntities(paragraphs[p]); | ||
} else { | ||
html += '<p>' + self.htmlEntities(paragraphs[p]) + '</p>'; | ||
} | ||
} | ||
@@ -1022,2 +1136,189 @@ } | ||
return this; | ||
}, | ||
cleanPaste: function (text) { | ||
/*jslint regexp: true*/ | ||
/* | ||
jslint does not allow character negation, because the negation | ||
will not match any unicode characters. In the regexes in this | ||
block, negation is used specifically to match the end of an html | ||
tag, and in fact unicode characters *should* be allowed. | ||
*/ | ||
var i, elList, workEl, | ||
el = this.getSelectionElement(), | ||
multiline = /<p|<br|<div/.test(text), | ||
replacements = [ | ||
// replace two bogus tags that begin pastes from google docs | ||
[new RegExp(/<[^>]*docs-internal-guid[^>]*>/gi), ""], | ||
[new RegExp(/<\/b>(<br[^>]*>)?$/gi), ""], | ||
// un-html spaces and newlines inserted by OS X | ||
[new RegExp(/<span class="Apple-converted-space">\s+<\/span>/g), ' '], | ||
[new RegExp(/<br class="Apple-interchange-newline">/g), '<br>'], | ||
// replace google docs italics+bold with a span to be replaced once the html is inserted | ||
[new RegExp(/<span[^>]*(font-style:italic;font-weight:bold|font-weight:bold;font-style:italic)[^>]*>/gi), '<span class="replace-with italic bold">'], | ||
// replace google docs italics with a span to be replaced once the html is inserted | ||
[new RegExp(/<span[^>]*font-style:italic[^>]*>/gi), '<span class="replace-with italic">'], | ||
//[replace google docs bolds with a span to be replaced once the html is inserted | ||
[new RegExp(/<span[^>]*font-weight:bold[^>]*>/gi), '<span class="replace-with bold">'], | ||
// replace manually entered b/i/a tags with real ones | ||
[new RegExp(/<(\/?)(i|b|a)>/gi), '<$1$2>'], | ||
// replace manually a tags with real ones, converting smart-quotes from google docs | ||
[new RegExp(/<a\s+href=("|”|“|“|”)([^&]+)("|”|“|“|”)>/gi), '<a href="$2">'] | ||
]; | ||
/*jslint regexp: false*/ | ||
for (i = 0; i < replacements.length; i += 1) { | ||
text = text.replace(replacements[i][0], replacements[i][1]); | ||
} | ||
if (multiline) { | ||
// double br's aren't converted to p tags, but we want paragraphs. | ||
elList = text.split('<br><br>'); | ||
this.pasteHTML('<p>' + elList.join('</p><p>') + '</p>'); | ||
document.execCommand('insertText', false, "\n"); | ||
// block element cleanup | ||
elList = el.querySelectorAll('p,div,br'); | ||
for (i = 0; i < elList.length; i += 1) { | ||
workEl = elList[i]; | ||
switch (workEl.tagName.toLowerCase()) { | ||
case 'p': | ||
case 'div': | ||
this.filterCommonBlocks(workEl); | ||
break; | ||
case 'br': | ||
this.filterLineBreak(workEl); | ||
break; | ||
} | ||
} | ||
} else { | ||
this.pasteHTML(text); | ||
} | ||
}, | ||
pasteHTML: function (html) { | ||
var elList, workEl, i, fragmentBody, pasteBlock = document.createDocumentFragment(); | ||
pasteBlock.appendChild(document.createElement('body')); | ||
fragmentBody = pasteBlock.querySelector('body'); | ||
fragmentBody.innerHTML = html; | ||
this.cleanupSpans(fragmentBody); | ||
elList = fragmentBody.querySelectorAll('*'); | ||
for (i = 0; i < elList.length; i += 1) { | ||
workEl = elList[i]; | ||
// delete ugly attributes | ||
workEl.removeAttribute('class'); | ||
workEl.removeAttribute('style'); | ||
workEl.removeAttribute('dir'); | ||
if (workEl.tagName.toLowerCase() === 'meta') { | ||
workEl.parentNode.removeChild(workEl); | ||
} | ||
} | ||
document.execCommand('insertHTML', false, fragmentBody.innerHTML.replace(/ /g, ' ')); | ||
}, | ||
isCommonBlock: function (el) { | ||
return (el && (el.tagName.toLowerCase() === 'p' || el.tagName.toLowerCase() === 'div')); | ||
}, | ||
filterCommonBlocks: function (el) { | ||
if (/^\s*$/.test(el.innerText)) { | ||
el.parentNode.removeChild(el); | ||
} | ||
}, | ||
filterLineBreak: function (el) { | ||
if (this.isCommonBlock(el.previousElementSibling)) { | ||
// remove stray br's following common block elements | ||
el.parentNode.removeChild(el); | ||
} else if (this.isCommonBlock(el.parentNode) && (el.parentNode.firstChild === el || el.parentNode.lastChild === el)) { | ||
// remove br's just inside open or close tags of a div/p | ||
el.parentNode.removeChild(el); | ||
} else if (el.parentNode.childElementCount === 1) { | ||
// and br's that are the only child of a div/p | ||
this.removeWithParent(el); | ||
} | ||
}, | ||
// remove an element, including its parent, if it is the only element within its parent | ||
removeWithParent: function (el) { | ||
if (el && el.parentNode) { | ||
if (el.parentNode.parentNode && el.parentNode.childElementCount === 1) { | ||
el.parentNode.parentNode.removeChild(el.parentNode); | ||
} else { | ||
el.parentNode.removeChild(el.parentNode); | ||
} | ||
} | ||
}, | ||
cleanupSpans: function (container_el) { | ||
var i, | ||
el, | ||
new_el, | ||
spans = container_el.querySelectorAll('.replace-with'); | ||
for (i = 0; i < spans.length; i += 1) { | ||
el = spans[i]; | ||
new_el = document.createElement(el.classList.contains('bold') ? 'b' : 'i'); | ||
if (el.classList.contains('bold') && el.classList.contains('italic')) { | ||
// add an i tag as well if this has both italics and bold | ||
new_el.innerHTML = '<i>' + el.innerHTML + '</i>'; | ||
} else { | ||
new_el.innerHTML = el.innerHTML; | ||
} | ||
el.parentNode.replaceChild(new_el, el); | ||
} | ||
spans = container_el.querySelectorAll('span'); | ||
for (i = 0; i < spans.length; i += 1) { | ||
el = spans[i]; | ||
// remove empty spans, replace others with their contents | ||
if (/^\s*$/.test()) { | ||
el.parentNode.removeChild(el); | ||
} else { | ||
el.parentNode.replaceChild(document.createTextNode(el.innerText), el); | ||
} | ||
} | ||
} | ||
@@ -1024,0 +1325,0 @@ |
@@ -1,1 +0,1 @@ | ||
function MediumEditor(a,b){"use strict";return this.init(a,b)}"object"==typeof module&&(module.exports=MediumEditor),function(a,b){"use strict";function c(a,b){var c;if(void 0===a)return b;for(c in b)b.hasOwnProperty(c)&&a.hasOwnProperty(c)===!1&&(a[c]=b[c]);return a}function d(){var b,c,d,e=a.getSelection();if(e.getRangeAt&&e.rangeCount){for(d=[],b=0,c=e.rangeCount;c>b;b+=1)d.push(e.getRangeAt(b));return d}return null}function e(b){var c,d,e=a.getSelection();if(b)for(e.removeAllRanges(),c=0,d=b.length;d>c;c+=1)e.addRange(b[c])}function f(){var a=b.getSelection().anchorNode,c=a&&3===a.nodeType?a.parentNode:a;return c}function g(){var c,d,e,f,g="";if(void 0!==a.getSelection){if(d=a.getSelection(),d.rangeCount){for(f=b.createElement("div"),c=0,e=d.rangeCount;e>c;c+=1)f.appendChild(d.getRangeAt(c).cloneContents());g=f.innerHTML}}else void 0!==b.selection&&"Text"===b.selection.type&&(g=b.selection.createRange().htmlText);return g}MediumEditor.prototype={defaults:{allowMultiParagraphSelection:!0,anchorInputPlaceholder:"Paste or type a link",buttons:["bold","italic","underline","anchor","header1","header2","quote"],buttonLabels:!1,delay:0,diffLeft:0,diffTop:-10,disableReturn:!1,disableDoubleReturn:!1,disableToolbar:!1,firstHeader:"h3",forcePlainText:!0,placeholder:"Type your text",secondHeader:"h4",targetBlank:!1,anchorPreviewHideDelay:500},isIE:"Microsoft Internet Explorer"===navigator.appName||"Netscape"===navigator.appName&&null!==new RegExp("Trident/.*rv:([0-9]{1,}[.0-9]{0,})").exec(navigator.userAgent),init:function(a,d){return this.elements="string"==typeof a?b.querySelectorAll(a):a,1===this.elements.nodeType&&(this.elements=[this.elements]),0!==this.elements.length?(this.parentElements=["p","h1","h2","h3","h4","h5","h6","blockquote","pre"],this.id=b.querySelectorAll(".medium-editor-toolbar").length+1,this.options=c(d,this.defaults),this.setup()):void 0},setup:function(){this.isActive=!0,this.initElements().bindSelect().bindPaste().setPlaceholders().bindWindowActions()},initElements:function(){var a,b=!1;for(a=0;a<this.elements.length;a+=1)this.elements[a].setAttribute("contentEditable",!0),this.elements[a].getAttribute("data-placeholder")||this.elements[a].setAttribute("data-placeholder",this.options.placeholder),this.elements[a].setAttribute("data-medium-element",!0),this.bindParagraphCreation(a).bindReturn(a).bindTab(a).bindAnchorPreview(a),this.options.disableToolbar||this.elements[a].getAttribute("data-disable-toolbar")||(b=!0);return b&&this.initToolbar().bindButtons().bindAnchorForm(),this},serialize:function(){var a,b,c={};for(a=0;a<this.elements.length;a+=1)b=""!==this.elements[a].id?this.elements[a].id:"element-"+a,c[b]={value:this.elements[a].innerHTML.trim()};return c},bindParagraphCreation:function(a){var c=this;return this.elements[a].addEventListener("keyup",function(a){var d,e=f();e&&e.getAttribute("data-medium-element")&&0===e.children.length&&!c.options.disableReturn&&!e.getAttribute("data-disable-return")&&b.execCommand("formatBlock",!1,"p"),13===a.which&&(e=f(),d=e.tagName.toLowerCase(),c.options.disableReturn||this.getAttribute("data-disable-return")||"li"===d||c.isListItemChild(e)||(a.shiftKey||b.execCommand("formatBlock",!1,"p"),"a"===d&&b.execCommand("unlink",!1,null)))}),this},isListItemChild:function(a){for(var b=a.parentNode,c=b.tagName.toLowerCase();-1===this.parentElements.indexOf(c)&&"div"!==c;){if("li"===c)return!0;if(b=b.parentNode,!b||!b.tagName)return!1;c=b.tagName.toLowerCase()}return!1},bindReturn:function(a){var b=this;return this.elements[a].addEventListener("keypress",function(a){if(13===a.which)if(b.options.disableReturn||this.getAttribute("data-disable-return"))a.preventDefault();else if(b.options.disableDoubleReturn||this.getAttribute("data-disable-double-return")){var c=f();c&&"\n"===c.innerText&&a.preventDefault()}}),this},bindTab:function(a){return this.elements[a].addEventListener("keydown",function(a){if(9===a.which){var c=f().tagName.toLowerCase();"pre"===c&&(a.preventDefault(),b.execCommand("insertHtml",null," "))}}),this},buttonTemplate:function(a){var b=this.getButtonLabels(this.options.buttonLabels),c={bold:'<li><button class="medium-editor-action medium-editor-action-bold" data-action="bold" data-element="b">'+b.bold+"</button></li>",italic:'<li><button class="medium-editor-action medium-editor-action-italic" data-action="italic" data-element="i">'+b.italic+"</button></li>",underline:'<li><button class="medium-editor-action medium-editor-action-underline" data-action="underline" data-element="u">'+b.underline+"</button></li>",strikethrough:'<li><button class="medium-editor-action medium-editor-action-strikethrough" data-action="strikethrough" data-element="strike"><strike>A</strike></button></li>',superscript:'<li><button class="medium-editor-action medium-editor-action-superscript" data-action="superscript" data-element="sup">'+b.superscript+"</button></li>",subscript:'<li><button class="medium-editor-action medium-editor-action-subscript" data-action="subscript" data-element="sub">'+b.subscript+"</button></li>",anchor:'<li><button class="medium-editor-action medium-editor-action-anchor" data-action="anchor" data-element="a">'+b.anchor+"</button></li>",image:'<li><button class="medium-editor-action medium-editor-action-image" data-action="image" data-element="img">'+b.image+"</button></li>",header1:'<li><button class="medium-editor-action medium-editor-action-header1" data-action="append-'+this.options.firstHeader+'" data-element="'+this.options.firstHeader+'">'+b.header1+"</button></li>",header2:'<li><button class="medium-editor-action medium-editor-action-header2" data-action="append-'+this.options.secondHeader+'" data-element="'+this.options.secondHeader+'">'+b.header2+"</button></li>",quote:'<li><button class="medium-editor-action medium-editor-action-quote" data-action="append-blockquote" data-element="blockquote">'+b.quote+"</button></li>",orderedlist:'<li><button class="medium-editor-action medium-editor-action-orderedlist" data-action="insertorderedlist" data-element="ol">'+b.orderedlist+"</button></li>",unorderedlist:'<li><button class="medium-editor-action medium-editor-action-unorderedlist" data-action="insertunorderedlist" data-element="ul">'+b.unorderedlist+"</button></li>",pre:'<li><button class="medium-editor-action medium-editor-action-pre" data-action="append-pre" data-element="pre">'+b.pre+"</button></li>",indent:'<li><button class="medium-editor-action medium-editor-action-indent" data-action="indent" data-element="ul">'+b.indent+"</button></li>",outdent:'<li><button class="medium-editor-action medium-editor-action-outdent" data-action="outdent" data-element="ul">'+b.outdent+"</button></li>"};return c[a]||!1},getButtonLabels:function(a){var b,c,d={bold:"<b>B</b>",italic:"<b><i>I</i></b>",underline:"<b><u>U</u></b>",superscript:"<b>x<sup>1</sup></b>",subscript:"<b>x<sub>1</sub></b>",anchor:"<b>#</b>",image:"<b>image</b>",header1:"<b>H1</b>",header2:"<b>H2</b>",quote:"<b>“</b>",orderedlist:"<b>1.</b>",unorderedlist:"<b>•</b>",pre:"<b>0101</b>",indent:"<b>→</b>",outdent:"<b>←</b>"};if("fontawesome"===a?b={bold:'<i class="fa fa-bold"></i>',italic:'<i class="fa fa-italic"></i>',underline:'<i class="fa fa-underline"></i>',superscript:'<i class="fa fa-superscript"></i>',subscript:'<i class="fa fa-subscript"></i>',anchor:'<i class="fa fa-link"></i>',image:'<i class="fa fa-picture-o"></i>',quote:'<i class="fa fa-quote-right"></i>',orderedlist:'<i class="fa fa-list-ol"></i>',unorderedlist:'<i class="fa fa-list-ul"></i>',pre:'<i class="fa fa-code fa-lg"></i>',indent:'<i class="fa fa-indent"></i>',outdent:'<i class="fa fa-outdent"></i>'}:"object"==typeof a&&(b=a),"object"==typeof b)for(c in b)b.hasOwnProperty(c)&&(d[c]=b[c]);return d},toolbarTemplate:function(){var a,b,c=this.options.buttons,d='<ul id="medium-editor-toolbar-actions" class="medium-editor-toolbar-actions clearfix">';for(a=0;a<c.length;a+=1)b=this.buttonTemplate(c[a]),b&&(d+=b);return d+='</ul><div class="medium-editor-toolbar-form-anchor" id="medium-editor-toolbar-form-anchor"> <input type="text" value="" placeholder="'+this.options.anchorInputPlaceholder+'"> <a href="#">×</a></div>'},initToolbar:function(){return this.toolbar?this:(this.toolbar=this.createToolbar(),this.keepToolbarAlive=!1,this.anchorForm=this.toolbar.querySelector(".medium-editor-toolbar-form-anchor"),this.anchorInput=this.anchorForm.querySelector("input"),this.toolbarActions=this.toolbar.querySelector(".medium-editor-toolbar-actions"),this.anchorPreview=this.createAnchorPreview(),this)},createToolbar:function(){var a=b.createElement("div");return a.id="medium-editor-toolbar-"+this.id,a.className="medium-editor-toolbar",a.innerHTML=this.toolbarTemplate(),b.body.appendChild(a),a},bindSelect:function(){var a,c=this,d="";for(this.checkSelectionWrapper=function(a){return a&&c.clickingIntoArchorForm(a)?!1:(clearTimeout(d),void(d=setTimeout(function(){c.checkSelection()},c.options.delay)))},b.documentElement.addEventListener("mouseup",this.checkSelectionWrapper),a=0;a<this.elements.length;a+=1)this.elements[a].addEventListener("keyup",this.checkSelectionWrapper),this.elements[a].addEventListener("blur",this.checkSelectionWrapper);return this},checkSelection:function(){var b,c;return this.keepToolbarAlive===!0||this.options.disableToolbar||(b=a.getSelection(),""===b.toString().trim()||this.options.allowMultiParagraphSelection===!1&&this.hasMultiParagraphs()?this.hideToolbarActions():(c=this.getSelectionElement(),!c||c.getAttribute("data-disable-toolbar")?this.hideToolbarActions():this.checkSelectionElement(b,c))),this},clickingIntoArchorForm:function(a){var b=this;return a.type&&"blur"===a.type.toLowerCase()&&a.relatedTarget&&a.relatedTarget===b.anchorInput?!0:!1},hasMultiParagraphs:function(){var a=g().replace(/<[\S]+><\/[\S]+>/gim,""),b=a.match(/<(p|h[0-6]|blockquote)>([\s\S]*?)<\/(p|h[0-6]|blockquote)>/g);return b?b.length:0},checkSelectionElement:function(a,b){var c;for(this.selection=a,this.selectionRange=this.selection.getRangeAt(0),c=0;c<this.elements.length;c+=1)if(this.elements[c]===b)return void this.setToolbarButtonStates().setToolbarPosition().showToolbarActions();this.hideToolbarActions()},getSelectionElement:function(){var b,c=a.getSelection(),d=c.getRangeAt(0),e=d.commonAncestorContainer,f=e.parentNode,g=function(a){var b=a;try{for(;!b.getAttribute("data-medium-element");)b=b.parentNode}catch(c){return!1}return b};try{b=e.getAttribute("data-medium-element")?e:g(f)}catch(h){b=g(f)}return b},setToolbarPosition:function(){var b=50,c=a.getSelection(),d=c.getRangeAt(0),e=d.getBoundingClientRect(),f=this.options.diffLeft-this.toolbar.offsetWidth/2,g=(e.left+e.right)/2,h=this.toolbar.offsetWidth/2;return e.top<b?(this.toolbar.classList.add("medium-toolbar-arrow-over"),this.toolbar.classList.remove("medium-toolbar-arrow-under"),this.toolbar.style.top=b+e.bottom-this.options.diffTop+a.pageYOffset-this.toolbar.offsetHeight+"px"):(this.toolbar.classList.add("medium-toolbar-arrow-under"),this.toolbar.classList.remove("medium-toolbar-arrow-over"),this.toolbar.style.top=e.top+this.options.diffTop+a.pageYOffset-this.toolbar.offsetHeight+"px"),this.toolbar.style.left=h>g?f+h+"px":a.innerWidth-g<h?a.innerWidth+f-h+"px":f+g+"px",this.hideAnchorPreview(),this},setToolbarButtonStates:function(){var a,b=this.toolbarActions.querySelectorAll("button");for(a=0;a<b.length;a+=1)b[a].classList.remove("medium-editor-button-active");return this.checkActiveButtons(),this},checkActiveButtons:function(){var a=this.selection.anchorNode;for(a.tagName||(a=this.selection.anchorNode.parentNode);void 0!==a.tagName&&-1===this.parentElements.indexOf(a.tagName.toLowerCase);)this.activateButton(a.tagName.toLowerCase()),a=a.parentNode},activateButton:function(a){var b=this.toolbar.querySelector('[data-element="'+a+'"]');null!==b&&-1===b.className.indexOf("medium-editor-button-active")&&(b.className+=" medium-editor-button-active")},bindButtons:function(){var a,b=this.toolbar.querySelectorAll("button"),c=this,d=function(a){a.preventDefault(),a.stopPropagation(),void 0===c.selection&&c.checkSelection(),this.className.indexOf("medium-editor-button-active")>-1?this.classList.remove("medium-editor-button-active"):this.className+=" medium-editor-button-active",c.execAction(this.getAttribute("data-action"),a)};for(a=0;a<b.length;a+=1)b[a].addEventListener("click",d);return this.setFirstAndLastItems(b),this},setFirstAndLastItems:function(a){return a[0].className+=" medium-editor-button-first",a[a.length-1].className+=" medium-editor-button-last",this},execAction:function(c,d){c.indexOf("append-")>-1?(this.execFormatBlock(c.replace("append-","")),this.setToolbarPosition(),this.setToolbarButtonStates()):"anchor"===c?this.triggerAnchorAction(d):"image"===c?b.execCommand("insertImage",!1,a.getSelection()):(b.execCommand(c,!1,null),this.setToolbarPosition())},triggerAnchorAction:function(){return"a"===this.selection.anchorNode.parentNode.tagName.toLowerCase()?b.execCommand("unlink",!1,null):"block"===this.anchorForm.style.display?this.showToolbarActions():this.showAnchorForm(),this},execFormatBlock:function(a){var c=this.getSelectionData(this.selection.anchorNode);if("blockquote"===a&&c.el&&"blockquote"===c.el.parentNode.tagName.toLowerCase())return b.execCommand("outdent",!1,null);if(c.tagName===a&&(a="p"),this.isIE){if("blockquote"===a)return b.execCommand("indent",!1,a);a="<"+a+">"}return b.execCommand("formatBlock",!1,a)},getSelectionData:function(a){var b;for(a&&a.tagName&&(b=a.tagName.toLowerCase());a&&-1===this.parentElements.indexOf(b);)a=a.parentNode,a&&a.tagName&&(b=a.tagName.toLowerCase());return{el:a,tagName:b}},getFirstChild:function(a){for(var b=a.firstChild;null!==b&&1!==b.nodeType;)b=b.nextSibling;return b},hideToolbarActions:function(){this.keepToolbarAlive=!1,this.toolbar.classList.remove("medium-editor-toolbar-active")},showToolbarActions:function(){var a,b=this;this.anchorForm.style.display="none",this.toolbarActions.style.display="block",this.keepToolbarAlive=!1,clearTimeout(a),a=setTimeout(function(){b.toolbar.classList.contains("medium-editor-toolbar-active")||b.toolbar.classList.add("medium-editor-toolbar-active")},100)},showAnchorForm:function(a){this.toolbarActions.style.display="none",this.savedSelection=d(),this.anchorForm.style.display="block",this.keepToolbarAlive=!0,this.anchorInput.focus(),this.anchorInput.value=a||""},bindAnchorForm:function(){var a=this.anchorForm.querySelector("a"),b=this;return this.anchorForm.addEventListener("click",function(a){a.stopPropagation()}),this.anchorInput.addEventListener("keyup",function(a){13===a.keyCode&&(a.preventDefault(),b.createLink(this))}),this.anchorInput.addEventListener("click",function(a){a.stopPropagation(),b.keepToolbarAlive=!0}),this.anchorInput.addEventListener("blur",function(){b.keepToolbarAlive=!1,b.checkSelection()}),a.addEventListener("click",function(a){a.preventDefault(),b.showToolbarActions(),e(b.savedSelection)}),this},hideAnchorPreview:function(){this.anchorPreview.classList.remove("medium-editor-anchor-preview-active")},showAnchorPreview:function(b){if(this.anchorPreview.classList.contains("medium-editor-anchor-preview-active"))return!0;var c,d,e,f=this,g=40,h=b.getBoundingClientRect(),i=(h.left+h.right)/2;return f.anchorPreview.querySelector("i").innerHTML=b.href,c=f.anchorPreview.offsetWidth/2,d=f.options.diffLeft-c,clearTimeout(e),e=setTimeout(function(){f.anchorPreview.classList.contains("medium-editor-anchor-preview-active")||f.anchorPreview.classList.add("medium-editor-anchor-preview-active")},100),f.observeAnchorPreview(b),f.anchorPreview.classList.add("medium-toolbar-arrow-over"),f.anchorPreview.classList.remove("medium-toolbar-arrow-under"),f.anchorPreview.style.top=Math.round(g+h.bottom-f.options.diffTop+a.pageYOffset-f.anchorPreview.offsetHeight)+"px",f.anchorPreview.style.left=c>i?d+c+"px":a.innerWidth-i<c?a.innerWidth+d-c+"px":d+i+"px",this},observeAnchorPreview:function(a){var b=this,c=(new Date).getTime(),d=!0,e=function(){c=(new Date).getTime(),d=!0},f=function(a){a.relatedTarget&&/anchor-preview/.test(a.relatedTarget.className)||(d=!1)},g=setInterval(function(){if(d)return!0;var h=(new Date).getTime()-c;h>b.options.anchorPreviewHideDelay&&(b.hideAnchorPreview(),clearInterval(g),b.anchorPreview.removeEventListener("mouseover",e),b.anchorPreview.removeEventListener("mouseout",f),a.removeEventListener("mouseover",e),a.removeEventListener("mouseout",f))},200);b.anchorPreview.addEventListener("mouseover",e),b.anchorPreview.addEventListener("mouseout",f),a.addEventListener("mouseover",e),a.addEventListener("mouseout",f)},createAnchorPreview:function(){var a=this,c=b.createElement("div");return c.id="medium-editor-anchor-preview-"+this.id,c.className="medium-editor-anchor-preview",c.innerHTML=this.anchorPreviewTemplate(),b.body.appendChild(c),c.addEventListener("click",function(){a.anchorPreviewClickHandler()}),c},anchorPreviewTemplate:function(){return'<div class="medium-editor-toolbar-anchor-preview" id="medium-editor-toolbar-anchor-preview"> <i class="medium-editor-toolbar-anchor-preview-inner"></i></div>'},anchorPreviewClickHandler:function(){if(this.activeAnchor){var c=this,d=b.createRange(),e=a.getSelection();d.selectNodeContents(c.activeAnchor),e.removeAllRanges(),e.addRange(d),setTimeout(function(){c.showAnchorForm(c.activeAnchor.href),c.keepToolbarAlive=!1},100+c.options.delay)}this.hideAnchorPreview()},editorAnchorObserver:function(a){var b=this,c=!0,d=function(){c=!1,b.activeAnchor.removeEventListener("mouseout",d)};if(a.target&&"a"===a.target.tagName.toLowerCase()){if(!/href=["']\S+["']/.test(a.target.outerHTML)||/href=["']#\S+["']/.test(a.target.outerHTML))return!0;if(this.toolbar.classList.contains("medium-editor-toolbar-active"))return!0;this.activeAnchor=a.target,this.activeAnchor.addEventListener("mouseout",d),setTimeout(function(){c&&b.showAnchorPreview(a.target)},b.options.delay)}},bindAnchorPreview:function(a){var b=this;return this.elements[a].addEventListener("mouseover",function(a){b.editorAnchorObserver(a)}),this},setTargetBlank:function(){var a,b=f();if("a"===b.tagName.toLowerCase())b.target="_blank";else for(b=b.getElementsByTagName("a"),a=0;a<b.length;a+=1)b[a].target="_blank"},createLink:function(a){e(this.savedSelection),b.execCommand("createLink",!1,a.value),this.options.targetBlank&&this.setTargetBlank(),this.showToolbarActions(),a.value=""},bindWindowActions:function(){var b,c=this;return this.windowResizeHandler=function(){clearTimeout(b),b=setTimeout(function(){c.toolbar&&c.toolbar.classList.contains("medium-editor-toolbar-active")&&c.setToolbarPosition()},100)},a.addEventListener("resize",this.windowResizeHandler),this},activate:function(){this.isActive||this.setup()},deactivate:function(){var c;if(this.isActive)for(this.isActive=!1,void 0!==this.toolbar&&(b.body.removeChild(this.anchorPreview),b.body.removeChild(this.toolbar),delete this.toolbar,delete this.anchorPreview),b.documentElement.removeEventListener("mouseup",this.checkSelectionWrapper),a.removeEventListener("resize",this.windowResizeHandler),c=0;c<this.elements.length;c+=1)this.elements[c].removeEventListener("keyup",this.checkSelectionWrapper),this.elements[c].removeEventListener("blur",this.checkSelectionWrapper),this.elements[c].removeEventListener("paste",this.pasteWrapper),this.elements[c].removeAttribute("contentEditable"),this.elements[c].removeAttribute("data-medium-element")},bindPaste:function(){var a,c=this;for(this.pasteWrapper=function(a){var d,e,f="";if(this.classList.remove("medium-editor-placeholder"),!c.options.forcePlainText)return this;if(a.clipboardData&&a.clipboardData.getData&&!a.defaultPrevented)if(a.preventDefault(),c.options.disableReturn)b.execCommand("insertHTML",!1,a.clipboardData.getData("text/plain"));else{for(d=a.clipboardData.getData("text/plain").split(/[\r\n]/g),e=0;e<d.length;e+=1)""!==d[e]&&(f+="<p>"+d[e]+"</p>");b.execCommand("insertHTML",!1,f)}},a=0;a<this.elements.length;a+=1)this.elements[a].addEventListener("paste",this.pasteWrapper);return this},setPlaceholders:function(){var a,b=function(a){""===a.textContent.replace(/^\s+|\s+$/g,"")&&a.classList.add("medium-editor-placeholder")},c=function(a){this.classList.remove("medium-editor-placeholder"),"keypress"!==a.type&&b(this)};for(a=0;a<this.elements.length;a+=1)b(this.elements[a]),this.elements[a].addEventListener("blur",c),this.elements[a].addEventListener("keypress",c);return this}}}(window,document); | ||
function MediumEditor(a,b){"use strict";return this.init(a,b)}"object"==typeof module&&(module.exports=MediumEditor),function(a,b){"use strict";function c(a,b){var c;if(void 0===a)return b;for(c in b)b.hasOwnProperty(c)&&a.hasOwnProperty(c)===!1&&(a[c]=b[c]);return a}function d(){var b,c,d,e=a.getSelection();if(e.getRangeAt&&e.rangeCount){for(d=[],b=0,c=e.rangeCount;c>b;b+=1)d.push(e.getRangeAt(b));return d}return null}function e(b){var c,d,e=a.getSelection();if(b)for(e.removeAllRanges(),c=0,d=b.length;d>c;c+=1)e.addRange(b[c])}function f(){var a=b.getSelection().anchorNode,c=a&&3===a.nodeType?a.parentNode:a;return c}function g(){var c,d,e,f,g="";if(void 0!==a.getSelection){if(d=a.getSelection(),d.rangeCount){for(f=b.createElement("div"),c=0,e=d.rangeCount;e>c;c+=1)f.appendChild(d.getRangeAt(c).cloneContents());g=f.innerHTML}}else void 0!==b.selection&&"Text"===b.selection.type&&(g=b.selection.createRange().htmlText);return g}function h(a){return!(!a||1!==a.nodeType)}MediumEditor.prototype={defaults:{allowMultiParagraphSelection:!0,anchorInputPlaceholder:"Paste or type a link",anchorPreviewHideDelay:500,buttons:["bold","italic","underline","anchor","header1","header2","quote"],buttonLabels:!1,checkLinkFormat:!1,cleanPastedHTML:!1,delay:0,diffLeft:0,diffTop:-10,disableReturn:!1,disableDoubleReturn:!1,disableToolbar:!1,firstHeader:"h3",forcePlainText:!0,placeholder:"Type your text",secondHeader:"h4",targetBlank:!1,extensions:{}},isIE:"Microsoft Internet Explorer"===navigator.appName||"Netscape"===navigator.appName&&null!==new RegExp("Trident/.*rv:([0-9]{1,}[.0-9]{0,})").exec(navigator.userAgent),init:function(a,d){return this.elements="string"==typeof a?b.querySelectorAll(a):a,1===this.elements.nodeType&&(this.elements=[this.elements]),0!==this.elements.length?(this.parentElements=["p","h1","h2","h3","h4","h5","h6","blockquote","pre"],this.id=b.querySelectorAll(".medium-editor-toolbar").length+1,this.options=c(d,this.defaults),this.setup()):void 0},setup:function(){this.isActive=!0,this.initElements().bindSelect().bindPaste().setPlaceholders().bindWindowActions()},initElements:function(){var a,b=!1;for(a=0;a<this.elements.length;a+=1)this.elements[a].setAttribute("contentEditable",!0),this.elements[a].getAttribute("data-placeholder")||this.elements[a].setAttribute("data-placeholder",this.options.placeholder),this.elements[a].setAttribute("data-medium-element",!0),this.bindParagraphCreation(a).bindReturn(a).bindTab(a),this.options.disableToolbar||this.elements[a].getAttribute("data-disable-toolbar")||(b=!0);return b&&this.initToolbar().bindButtons().bindAnchorForm().bindAnchorPreview(),this},serialize:function(){var a,b,c={};for(a=0;a<this.elements.length;a+=1)b=""!==this.elements[a].id?this.elements[a].id:"element-"+a,c[b]={value:this.elements[a].innerHTML.trim()};return c},callExtensions:function(a){if(!(arguments.length<1)){var b,c,d=Array.prototype.slice.call(arguments,1);for(c in this.options.extensions)this.options.extensions.hasOwnProperty(c)&&(b=this.options.extensions[c],void 0!==b[a]&&b[a].apply(b,d))}},bindParagraphCreation:function(a){var c=this;return this.elements[a].addEventListener("keyup",function(a){var d,e=f();e&&e.getAttribute("data-medium-element")&&0===e.children.length&&!c.options.disableReturn&&!e.getAttribute("data-disable-return")&&b.execCommand("formatBlock",!1,"p"),13===a.which&&(e=f(),d=e.tagName.toLowerCase(),c.options.disableReturn||this.getAttribute("data-disable-return")||"li"===d||c.isListItemChild(e)||(a.shiftKey||b.execCommand("formatBlock",!1,"p"),"a"===d&&b.execCommand("unlink",!1,null)))}),this},isListItemChild:function(a){for(var b=a.parentNode,c=b.tagName.toLowerCase();-1===this.parentElements.indexOf(c)&&"div"!==c;){if("li"===c)return!0;if(b=b.parentNode,!b||!b.tagName)return!1;c=b.tagName.toLowerCase()}return!1},bindReturn:function(a){var b=this;return this.elements[a].addEventListener("keypress",function(a){if(13===a.which)if(b.options.disableReturn||this.getAttribute("data-disable-return"))a.preventDefault();else if(b.options.disableDoubleReturn||this.getAttribute("data-disable-double-return")){var c=f();c&&"\n"===c.innerText&&a.preventDefault()}}),this},bindTab:function(a){return this.elements[a].addEventListener("keydown",function(a){if(9===a.which){var c=f().tagName.toLowerCase();"pre"===c&&(a.preventDefault(),b.execCommand("insertHtml",null," "))}}),this},buttonTemplate:function(a){var b=this.getButtonLabels(this.options.buttonLabels),c={bold:'<button class="medium-editor-action medium-editor-action-bold" data-action="bold" data-element="b">'+b.bold+"</button>",italic:'<button class="medium-editor-action medium-editor-action-italic" data-action="italic" data-element="i">'+b.italic+"</button>",underline:'<button class="medium-editor-action medium-editor-action-underline" data-action="underline" data-element="u">'+b.underline+"</button>",strikethrough:'<button class="medium-editor-action medium-editor-action-strikethrough" data-action="strikethrough" data-element="strike"><strike>A</strike></button>',superscript:'<button class="medium-editor-action medium-editor-action-superscript" data-action="superscript" data-element="sup">'+b.superscript+"</button>",subscript:'<button class="medium-editor-action medium-editor-action-subscript" data-action="subscript" data-element="sub">'+b.subscript+"</button>",anchor:'<button class="medium-editor-action medium-editor-action-anchor" data-action="anchor" data-element="a">'+b.anchor+"</button>",image:'<button class="medium-editor-action medium-editor-action-image" data-action="image" data-element="img">'+b.image+"</button>",header1:'<button class="medium-editor-action medium-editor-action-header1" data-action="append-'+this.options.firstHeader+'" data-element="'+this.options.firstHeader+'">'+b.header1+"</button>",header2:'<button class="medium-editor-action medium-editor-action-header2" data-action="append-'+this.options.secondHeader+'" data-element="'+this.options.secondHeader+'">'+b.header2+"</button>",quote:'<button class="medium-editor-action medium-editor-action-quote" data-action="append-blockquote" data-element="blockquote">'+b.quote+"</button>",orderedlist:'<button class="medium-editor-action medium-editor-action-orderedlist" data-action="insertorderedlist" data-element="ol">'+b.orderedlist+"</button>",unorderedlist:'<button class="medium-editor-action medium-editor-action-unorderedlist" data-action="insertunorderedlist" data-element="ul">'+b.unorderedlist+"</button>",pre:'<button class="medium-editor-action medium-editor-action-pre" data-action="append-pre" data-element="pre">'+b.pre+"</button>",indent:'<button class="medium-editor-action medium-editor-action-indent" data-action="indent" data-element="ul">'+b.indent+"</button>",outdent:'<button class="medium-editor-action medium-editor-action-outdent" data-action="outdent" data-element="ul">'+b.outdent+"</button>"};return c[a]||!1},getButtonLabels:function(a){var b,c,d={bold:"<b>B</b>",italic:"<b><i>I</i></b>",underline:"<b><u>U</u></b>",superscript:"<b>x<sup>1</sup></b>",subscript:"<b>x<sub>1</sub></b>",anchor:"<b>#</b>",image:"<b>image</b>",header1:"<b>H1</b>",header2:"<b>H2</b>",quote:"<b>“</b>",orderedlist:"<b>1.</b>",unorderedlist:"<b>•</b>",pre:"<b>0101</b>",indent:"<b>→</b>",outdent:"<b>←</b>"};if("fontawesome"===a?b={bold:'<i class="fa fa-bold"></i>',italic:'<i class="fa fa-italic"></i>',underline:'<i class="fa fa-underline"></i>',superscript:'<i class="fa fa-superscript"></i>',subscript:'<i class="fa fa-subscript"></i>',anchor:'<i class="fa fa-link"></i>',image:'<i class="fa fa-picture-o"></i>',quote:'<i class="fa fa-quote-right"></i>',orderedlist:'<i class="fa fa-list-ol"></i>',unorderedlist:'<i class="fa fa-list-ul"></i>',pre:'<i class="fa fa-code fa-lg"></i>',indent:'<i class="fa fa-indent"></i>',outdent:'<i class="fa fa-outdent"></i>'}:"object"==typeof a&&(b=a),"object"==typeof b)for(c in b)b.hasOwnProperty(c)&&(d[c]=b[c]);return d},initToolbar:function(){return this.toolbar?this:(this.toolbar=this.createToolbar(),this.keepToolbarAlive=!1,this.anchorForm=this.toolbar.querySelector(".medium-editor-toolbar-form-anchor"),this.anchorInput=this.anchorForm.querySelector("input"),this.toolbarActions=this.toolbar.querySelector(".medium-editor-toolbar-actions"),this.anchorPreview=this.createAnchorPreview(),this)},createToolbar:function(){var a=b.createElement("div");return a.id="medium-editor-toolbar-"+this.id,a.className="medium-editor-toolbar",a.appendChild(this.toolbarButtons()),a.appendChild(this.toolbarFormAnchor()),b.body.appendChild(a),a},toolbarButtons:function(){var a,c,d,e,f=this.options.buttons,g=b.createElement("ul");for(g.id="medium-editor-toolbar-actions",g.className="medium-editor-toolbar-actions clearfix",c=0;c<f.length;c+=1)this.options.extensions.hasOwnProperty(f[c])?(e=this.options.extensions[f[c]],d=void 0!==e.getButton?e.getButton():null):d=this.buttonTemplate(f[c]),d&&(a=b.createElement("li"),h(d)?a.appendChild(d):a.innerHTML=d,g.appendChild(a));return g},toolbarFormAnchor:function(){var a=b.createElement("div"),c=b.createElement("input"),d=b.createElement("a");return d.setAttribute("href","#"),d.innerHTML="×",c.setAttribute("type","text"),c.setAttribute("placeholder",this.options.anchorInputPlaceholder),a.className="medium-editor-toolbar-form-anchor",a.id="medium-editor-toolbar-form-anchor",a.appendChild(c),a.appendChild(d),a},bindSelect:function(){var a,c=this,d="";for(this.checkSelectionWrapper=function(a){return a&&c.clickingIntoArchorForm(a)?!1:(clearTimeout(d),void(d=setTimeout(function(){c.checkSelection()},c.options.delay)))},b.documentElement.addEventListener("mouseup",this.checkSelectionWrapper),a=0;a<this.elements.length;a+=1)this.elements[a].addEventListener("keyup",this.checkSelectionWrapper),this.elements[a].addEventListener("blur",this.checkSelectionWrapper);return this},checkSelection:function(){var b,c;return this.keepToolbarAlive===!0||this.options.disableToolbar||(b=a.getSelection(),""===b.toString().trim()||this.options.allowMultiParagraphSelection===!1&&this.hasMultiParagraphs()?this.hideToolbarActions():(c=this.getSelectionElement(),!c||c.getAttribute("data-disable-toolbar")?this.hideToolbarActions():this.checkSelectionElement(b,c))),this},clickingIntoArchorForm:function(a){var b=this;return a.type&&"blur"===a.type.toLowerCase()&&a.relatedTarget&&a.relatedTarget===b.anchorInput?!0:!1},hasMultiParagraphs:function(){var a=g().replace(/<[\S]+><\/[\S]+>/gim,""),b=a.match(/<(p|h[0-6]|blockquote)>([\s\S]*?)<\/(p|h[0-6]|blockquote)>/g);return b?b.length:0},checkSelectionElement:function(a,b){var c;for(this.selection=a,this.selectionRange=this.selection.getRangeAt(0),c=0;c<this.elements.length;c+=1)if(this.elements[c]===b)return void this.setToolbarButtonStates().setToolbarPosition().showToolbarActions();this.hideToolbarActions()},getSelectionElement:function(){var b,c,d,e,f=a.getSelection(),g=function(a){var b=a;try{for(;!b.getAttribute("data-medium-element");)b=b.parentNode}catch(c){return!1}return b};try{b=f.getRangeAt(0),c=b.commonAncestorContainer,d=c.parentNode,e=c.getAttribute("data-medium-element")?c:g(d)}catch(h){e=g(d)}return e},setToolbarPosition:function(){var b=50,c=a.getSelection(),d=c.getRangeAt(0),e=d.getBoundingClientRect(),f=this.options.diffLeft-this.toolbar.offsetWidth/2,g=(e.left+e.right)/2,h=this.toolbar.offsetWidth/2;return e.top<b?(this.toolbar.classList.add("medium-toolbar-arrow-over"),this.toolbar.classList.remove("medium-toolbar-arrow-under"),this.toolbar.style.top=b+e.bottom-this.options.diffTop+a.pageYOffset-this.toolbar.offsetHeight+"px"):(this.toolbar.classList.add("medium-toolbar-arrow-under"),this.toolbar.classList.remove("medium-toolbar-arrow-over"),this.toolbar.style.top=e.top+this.options.diffTop+a.pageYOffset-this.toolbar.offsetHeight+"px"),this.toolbar.style.left=h>g?f+h+"px":a.innerWidth-g<h?a.innerWidth+f-h+"px":f+g+"px",this.hideAnchorPreview(),this},setToolbarButtonStates:function(){var a,b=this.toolbarActions.querySelectorAll("button");for(a=0;a<b.length;a+=1)b[a].classList.remove("medium-editor-button-active");return this.checkActiveButtons(),this},checkActiveButtons:function(){var a=Array.prototype.slice.call(this.elements),b=this.selection.anchorNode;for(b.tagName||(b=this.selection.anchorNode.parentNode);void 0!==b.tagName&&-1===this.parentElements.indexOf(b.tagName.toLowerCase)&&(this.activateButton(b.tagName.toLowerCase()),this.callExtensions("checkState",b),-1===a.indexOf(b));)b=b.parentNode},activateButton:function(a){var b=this.toolbar.querySelector('[data-element="'+a+'"]');null!==b&&-1===b.className.indexOf("medium-editor-button-active")&&(b.className+=" medium-editor-button-active")},bindButtons:function(){var a,b=this.toolbar.querySelectorAll("button"),c=this,d=function(a){a.preventDefault(),a.stopPropagation(),void 0===c.selection&&c.checkSelection(),this.className.indexOf("medium-editor-button-active")>-1?this.classList.remove("medium-editor-button-active"):this.className+=" medium-editor-button-active",this.hasAttribute("data-action")&&c.execAction(this.getAttribute("data-action"),a)};for(a=0;a<b.length;a+=1)b[a].addEventListener("click",d);return this.setFirstAndLastItems(b),this},setFirstAndLastItems:function(a){return a.length>0&&(a[0].className+=" medium-editor-button-first",a[a.length-1].className+=" medium-editor-button-last"),this},execAction:function(c,d){c.indexOf("append-")>-1?(this.execFormatBlock(c.replace("append-","")),this.setToolbarPosition(),this.setToolbarButtonStates()):"anchor"===c?this.triggerAnchorAction(d):"image"===c?b.execCommand("insertImage",!1,a.getSelection()):(b.execCommand(c,!1,null),this.setToolbarPosition())},triggerAnchorAction:function(){return"a"===this.selection.anchorNode.parentNode.tagName.toLowerCase()?b.execCommand("unlink",!1,null):"block"===this.anchorForm.style.display?this.showToolbarActions():this.showAnchorForm(),this},execFormatBlock:function(a){var c=this.getSelectionData(this.selection.anchorNode);if("blockquote"===a&&c.el&&"blockquote"===c.el.parentNode.tagName.toLowerCase())return b.execCommand("outdent",!1,null);if(c.tagName===a&&(a="p"),this.isIE){if("blockquote"===a)return b.execCommand("indent",!1,a);a="<"+a+">"}return b.execCommand("formatBlock",!1,a)},getSelectionData:function(a){var b;for(a&&a.tagName&&(b=a.tagName.toLowerCase());a&&-1===this.parentElements.indexOf(b);)a=a.parentNode,a&&a.tagName&&(b=a.tagName.toLowerCase());return{el:a,tagName:b}},getFirstChild:function(a){for(var b=a.firstChild;null!==b&&1!==b.nodeType;)b=b.nextSibling;return b},hideToolbarActions:function(){this.keepToolbarAlive=!1,void 0!==this.toolbar&&this.toolbar.classList.remove("medium-editor-toolbar-active")},showToolbarActions:function(){var a,b=this;this.anchorForm.style.display="none",this.toolbarActions.style.display="block",this.keepToolbarAlive=!1,clearTimeout(a),a=setTimeout(function(){b.toolbar&&!b.toolbar.classList.contains("medium-editor-toolbar-active")&&b.toolbar.classList.add("medium-editor-toolbar-active")},100)},showAnchorForm:function(a){this.toolbarActions.style.display="none",this.savedSelection=d(),this.anchorForm.style.display="block",this.keepToolbarAlive=!0,this.anchorInput.focus(),this.anchorInput.value=a||""},bindAnchorForm:function(){var a=this.anchorForm.querySelector("a"),b=this;return this.anchorForm.addEventListener("click",function(a){a.stopPropagation()}),this.anchorInput.addEventListener("keyup",function(a){13===a.keyCode&&(a.preventDefault(),b.createLink(this))}),this.anchorInput.addEventListener("click",function(a){a.stopPropagation(),b.keepToolbarAlive=!0}),this.anchorInput.addEventListener("blur",function(){b.keepToolbarAlive=!1,b.checkSelection()}),a.addEventListener("click",function(a){a.preventDefault(),b.showToolbarActions(),e(b.savedSelection)}),this},hideAnchorPreview:function(){this.anchorPreview.classList.remove("medium-editor-anchor-preview-active")},showAnchorPreview:function(b){if(this.anchorPreview.classList.contains("medium-editor-anchor-preview-active"))return!0;var c,d,e,f=this,g=40,h=b.getBoundingClientRect(),i=(h.left+h.right)/2;return f.anchorPreview.querySelector("i").innerHTML=b.href,c=f.anchorPreview.offsetWidth/2,d=f.options.diffLeft-c,clearTimeout(e),e=setTimeout(function(){f.anchorPreview&&!f.anchorPreview.classList.contains("medium-editor-anchor-preview-active")&&f.anchorPreview.classList.add("medium-editor-anchor-preview-active")},100),f.observeAnchorPreview(b),f.anchorPreview.classList.add("medium-toolbar-arrow-over"),f.anchorPreview.classList.remove("medium-toolbar-arrow-under"),f.anchorPreview.style.top=Math.round(g+h.bottom-f.options.diffTop+a.pageYOffset-f.anchorPreview.offsetHeight)+"px",f.anchorPreview.style.left=c>i?d+c+"px":a.innerWidth-i<c?a.innerWidth+d-c+"px":d+i+"px",this},observeAnchorPreview:function(a){var b=this,c=(new Date).getTime(),d=!0,e=function(){c=(new Date).getTime(),d=!0},f=function(a){a.relatedTarget&&/anchor-preview/.test(a.relatedTarget.className)||(d=!1)},g=setInterval(function(){if(d)return!0;var h=(new Date).getTime()-c;h>b.options.anchorPreviewHideDelay&&(b.hideAnchorPreview(),clearInterval(g),b.anchorPreview.removeEventListener("mouseover",e),b.anchorPreview.removeEventListener("mouseout",f),a.removeEventListener("mouseover",e),a.removeEventListener("mouseout",f))},200);b.anchorPreview.addEventListener("mouseover",e),b.anchorPreview.addEventListener("mouseout",f),a.addEventListener("mouseover",e),a.addEventListener("mouseout",f)},createAnchorPreview:function(){var a=this,c=b.createElement("div");return c.id="medium-editor-anchor-preview-"+this.id,c.className="medium-editor-anchor-preview",c.innerHTML=this.anchorPreviewTemplate(),b.body.appendChild(c),c.addEventListener("click",function(){a.anchorPreviewClickHandler()}),c},anchorPreviewTemplate:function(){return'<div class="medium-editor-toolbar-anchor-preview" id="medium-editor-toolbar-anchor-preview"> <i class="medium-editor-toolbar-anchor-preview-inner"></i></div>'},anchorPreviewClickHandler:function(){if(this.activeAnchor){var c=this,d=b.createRange(),e=a.getSelection();d.selectNodeContents(c.activeAnchor),e.removeAllRanges(),e.addRange(d),setTimeout(function(){c.activeAnchor&&c.showAnchorForm(c.activeAnchor.href),c.keepToolbarAlive=!1},100+c.options.delay)}this.hideAnchorPreview()},editorAnchorObserver:function(a){var b=this,c=!0,d=function(){c=!1,b.activeAnchor.removeEventListener("mouseout",d)};if(a.target&&"a"===a.target.tagName.toLowerCase()){if(!/href=["']\S+["']/.test(a.target.outerHTML)||/href=["']#\S+["']/.test(a.target.outerHTML))return!0;if(this.toolbar.classList.contains("medium-editor-toolbar-active"))return!0;this.activeAnchor=a.target,this.activeAnchor.addEventListener("mouseout",d),setTimeout(function(){c&&b.showAnchorPreview(a.target)},b.options.delay)}},bindAnchorPreview:function(){var a,b=this;for(this.editorAnchorObserverWrapper=function(a){b.editorAnchorObserver(a)},a=0;a<this.elements.length;a+=1)this.elements[a].addEventListener("mouseover",this.editorAnchorObserverWrapper);return this},checkLinkFormat:function(a){var b=/^https?:\/\//;return a.match(b)?a:"http://"+a},setTargetBlank:function(){var a,b=f();if("a"===b.tagName.toLowerCase())b.target="_blank";else for(b=b.getElementsByTagName("a"),a=0;a<b.length;a+=1)b[a].target="_blank"},createLink:function(a){e(this.savedSelection),this.options.checkLinkFormat&&(a.value=this.checkLinkFormat(a.value)),b.execCommand("createLink",!1,a.value),this.options.targetBlank&&this.setTargetBlank(),this.showToolbarActions(),a.value=""},bindWindowActions:function(){var b,c=this;return this.windowResizeHandler=function(){clearTimeout(b),b=setTimeout(function(){c.toolbar&&c.toolbar.classList.contains("medium-editor-toolbar-active")&&c.setToolbarPosition()},100)},a.addEventListener("resize",this.windowResizeHandler),this},activate:function(){this.isActive||this.setup()},deactivate:function(){var c;if(this.isActive)for(this.isActive=!1,void 0!==this.toolbar&&(b.body.removeChild(this.anchorPreview),b.body.removeChild(this.toolbar),delete this.toolbar,delete this.anchorPreview),b.documentElement.removeEventListener("mouseup",this.checkSelectionWrapper),a.removeEventListener("resize",this.windowResizeHandler),c=0;c<this.elements.length;c+=1)this.elements[c].removeEventListener("mouseover",this.editorAnchorObserverWrapper),this.elements[c].removeEventListener("keyup",this.checkSelectionWrapper),this.elements[c].removeEventListener("blur",this.checkSelectionWrapper),this.elements[c].removeEventListener("paste",this.pasteWrapper),this.elements[c].removeAttribute("contentEditable"),this.elements[c].removeAttribute("data-medium-element")},htmlEntities:function(a){return String(a).replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""")},bindPaste:function(){var a,c=this;for(this.pasteWrapper=function(a){var d,e,f="";if(this.classList.remove("medium-editor-placeholder"),!c.options.forcePlainText&&!c.options.cleanPastedHTML)return this;if(a.clipboardData&&a.clipboardData.getData&&!a.defaultPrevented){if(a.preventDefault(),c.options.cleanPastedHTML&&a.clipboardData.getData("text/html"))return c.cleanPaste(a.clipboardData.getData("text/html"));if(c.options.disableReturn)b.execCommand("insertHTML",!1,a.clipboardData.getData("text/plain"));else{for(d=a.clipboardData.getData("text/plain").split(/[\r\n]/g),e=0;e<d.length;e+=1)""!==d[e]&&(f+=navigator.userAgent.match(/firefox/i)&&0===e?c.htmlEntities(d[e]):"<p>"+c.htmlEntities(d[e])+"</p>");b.execCommand("insertHTML",!1,f)}}},a=0;a<this.elements.length;a+=1)this.elements[a].addEventListener("paste",this.pasteWrapper);return this},setPlaceholders:function(){var a,b=function(a){""===a.textContent.replace(/^\s+|\s+$/g,"")&&a.classList.add("medium-editor-placeholder")},c=function(a){this.classList.remove("medium-editor-placeholder"),"keypress"!==a.type&&b(this)};for(a=0;a<this.elements.length;a+=1)b(this.elements[a]),this.elements[a].addEventListener("blur",c),this.elements[a].addEventListener("keypress",c);return this},cleanPaste:function(a){var c,d,e,f=this.getSelectionElement(),g=/<p|<br|<div/.test(a),h=[[new RegExp(/<[^>]*docs-internal-guid[^>]*>/gi),""],[new RegExp(/<\/b>(<br[^>]*>)?$/gi),""],[new RegExp(/<span class="Apple-converted-space">\s+<\/span>/g)," "],[new RegExp(/<br class="Apple-interchange-newline">/g),"<br>"],[new RegExp(/<span[^>]*(font-style:italic;font-weight:bold|font-weight:bold;font-style:italic)[^>]*>/gi),'<span class="replace-with italic bold">'],[new RegExp(/<span[^>]*font-style:italic[^>]*>/gi),'<span class="replace-with italic">'],[new RegExp(/<span[^>]*font-weight:bold[^>]*>/gi),'<span class="replace-with bold">'],[new RegExp(/<(\/?)(i|b|a)>/gi),"<$1$2>"],[new RegExp(/<a\s+href=("|”|“|“|”)([^&]+)("|”|“|“|”)>/gi),'<a href="$2">']];for(c=0;c<h.length;c+=1)a=a.replace(h[c][0],h[c][1]);if(g)for(d=a.split("<br><br>"),this.pasteHTML("<p>"+d.join("</p><p>")+"</p>"),b.execCommand("insertText",!1,"\n"),d=f.querySelectorAll("p,div,br"),c=0;c<d.length;c+=1)switch(e=d[c],e.tagName.toLowerCase()){case"p":case"div":this.filterCommonBlocks(e);break;case"br":this.filterLineBreak(e)}else this.pasteHTML(a)},pasteHTML:function(a){var c,d,e,f,g=b.createDocumentFragment();for(g.appendChild(b.createElement("body")),f=g.querySelector("body"),f.innerHTML=a,this.cleanupSpans(f),c=f.querySelectorAll("*"),e=0;e<c.length;e+=1)d=c[e],d.removeAttribute("class"),d.removeAttribute("style"),d.removeAttribute("dir"),"meta"===d.tagName.toLowerCase()&&d.parentNode.removeChild(d);b.execCommand("insertHTML",!1,f.innerHTML.replace(/ /g," "))},isCommonBlock:function(a){return a&&("p"===a.tagName.toLowerCase()||"div"===a.tagName.toLowerCase())},filterCommonBlocks:function(a){/^\s*$/.test(a.innerText)&&a.parentNode.removeChild(a)},filterLineBreak:function(a){this.isCommonBlock(a.previousElementSibling)?a.parentNode.removeChild(a):!this.isCommonBlock(a.parentNode)||a.parentNode.firstChild!==a&&a.parentNode.lastChild!==a?1===a.parentNode.childElementCount&&this.removeWithParent(a):a.parentNode.removeChild(a)},removeWithParent:function(a){a&&a.parentNode&&(a.parentNode.parentNode&&1===a.parentNode.childElementCount?a.parentNode.parentNode.removeChild(a.parentNode):a.parentNode.removeChild(a.parentNode))},cleanupSpans:function(a){var c,d,e,f=a.querySelectorAll(".replace-with");for(c=0;c<f.length;c+=1)d=f[c],e=b.createElement(d.classList.contains("bold")?"b":"i"),e.innerHTML=d.classList.contains("bold")&&d.classList.contains("italic")?"<i>"+d.innerHTML+"</i>":d.innerHTML,d.parentNode.replaceChild(e,d);for(f=a.querySelectorAll("span"),c=0;c<f.length;c+=1)d=f[c],/^\s*$/.test()?d.parentNode.removeChild(d):d.parentNode.replaceChild(b.createTextNode(d.innerText),d)}}}(window,document); |
@@ -6,6 +6,11 @@ /*global module, require*/ | ||
var gruntConfig = { | ||
pkg: grunt.file.readJSON('package.json') | ||
}, | ||
autoprefixerBrowsers = ['last 3 versions', 'ie >= 9']; | ||
var autoprefixerBrowsers = ['last 3 versions', 'ie >= 9'], | ||
globalConfig = { | ||
src: 'src', | ||
dest: 'dev' | ||
}, | ||
gruntConfig = { | ||
pkg: grunt.file.readJSON('package.json'), | ||
globalConfig: globalConfig | ||
}; | ||
@@ -69,2 +74,9 @@ gruntConfig.jsbeautifier = { | ||
} | ||
}, | ||
spec: { | ||
src: 'src/js/**/*.js', | ||
options: { | ||
specs: ['spec/<%= globalConfig.file %>.spec.js'], | ||
helpers: 'spec/helpers/*.js' | ||
} | ||
} | ||
@@ -174,7 +186,12 @@ }; | ||
grunt.registerTask('test', ['jsbeautifier', 'jslint', 'jasmine', 'csslint']); | ||
grunt.registerTask('js', ['jsbeautifier', 'jslint', 'jasmine', 'uglify', 'concat']); | ||
grunt.registerTask('test', ['jsbeautifier', 'jslint', 'jasmine:suite', 'csslint']); | ||
grunt.registerTask('js', ['jsbeautifier', 'jslint', 'jasmine:suite', 'uglify', 'concat']); | ||
grunt.registerTask('css', ['sass', 'autoprefixer', 'csslint']); | ||
grunt.registerTask('default', ['js', 'css']); | ||
grunt.registerTask('spec', 'Runs a task on a specified file', function (taskName, fileName) { | ||
globalConfig.file = fileName; | ||
grunt.task.run(taskName + ':spec'); | ||
}); | ||
}; |
{ | ||
"name": "medium-editor", | ||
"version": "1.7.5", | ||
"version": "1.8.0", | ||
"author": "Davi Ferreira <hi@daviferreira.com>", | ||
@@ -31,3 +31,3 @@ "description": "Medium.com WYSIWYG editor clone.", | ||
"grunt-contrib-watch": "~0.6.1", | ||
"grunt-contrib-concat": "~0.3.0", | ||
"grunt-contrib-concat": "~0.4.0", | ||
"grunt-template-jasmine-istanbul": "~0.3.1", | ||
@@ -34,0 +34,0 @@ "grunt-plato": "~1.0.0", |
108
README.md
@@ -57,2 +57,5 @@ # MediumEditor | ||
* __buttonLabels__: type of labels on the buttons. Values: 'fontawesome', `{'bold': '<b>b</b>', 'italic': '<i>i</i>'}`. Default: false | ||
* __checkLinkFormat__: enables/disables check for http on anchor links. Default: | ||
false | ||
* __cleanPastedHTML__: cleans pasted content from different sources, like google docs etc. Default: false | ||
* __delay__: time in milliseconds to show the toolbar or anchor tag preview. Default: 0 | ||
@@ -68,4 +71,4 @@ * __diffLeft__: value in pixels to be added to the X axis positioning of the toolbar. Default: 0 | ||
* __secondHeader__: HTML tag to be used as second header. Default: h4 | ||
* __targetBlank__: enables/disables target="\_blank" for anchor tags. Default: | ||
false | ||
* __targetBlank__: enables/disables target="\_blank" for anchor tags. Default: false | ||
* __extensions__: extension to use (see _Extensions_) for more. Default: {} | ||
@@ -127,2 +130,54 @@ Example: | ||
## Extensions | ||
To add additional additional functions that are not supported by the native [browser API](https://developer.mozilla.org/de/docs/Rich-Text_Editing_in_Mozilla) you can | ||
write extensions that are then integrated into the toolbar. The Extension API is currently unstable and very minimal. | ||
An extension is an object that has essentially two functions `getButton` and `checkState`. | ||
* `getButton` is called when the editor is initialized and should return a element that is integrated into the toolbar. | ||
Usually this will be a `<button>` element like the onces Medium Editor uses. All event handling on this button is | ||
_entirely up to you_ so you should either keep a reference or bind your eventhandlers before returning it. You can | ||
also return a HTML-String that is then integrated into the toolbar also this is not really useful. | ||
* `checkState` is called whenever a user selects some text in the area where the Medium Editor instance is running. It's | ||
responsability is to toggle the current _state_ of the button. I.e. marking is a _on_ or _off_. Again the method on how | ||
determine the state is entirely up to you. `checkState` will be called multiple times and will receive a [DOM `Element`](https://developer.mozilla.org/en-US/docs/Web/API/element) | ||
as parameter. | ||
### Example | ||
A simple example the uses [rangy](https://code.google.com/p/rangy/) and the [CSS Class Applier Module](https://code.google.com/p/rangy/wiki/CSSClassApplierModule) to support highlighting of text: | ||
rangy.init(); | ||
function Highlighter() { | ||
this.button = document.createElement('button'); | ||
this.button.className = 'medium-editor-action'; | ||
this.button.innerText = 'H'; | ||
this.button.onclick = this.onClick.bind(this); | ||
this.classApplier = rangy.createCssClassApplier("highlight", { | ||
elementTagName: 'mark', | ||
normalize: true | ||
}); | ||
} | ||
Highlighter.prototype.onClick = function() { | ||
this.classApplier.toggleSelection(); | ||
} | ||
Highlighter.prototype.getButton = function() { | ||
return this.button; | ||
} | ||
Highlighter.prototype.checkState = function (node) { | ||
if(node.tagName == 'MARK') { | ||
this.button.classList.add('medium-editor-button-active'); | ||
} | ||
} | ||
var e = new MediumEditor('.editor', { | ||
buttons: ['highlight', 'bold', 'italic', 'underline'], | ||
extensions: { | ||
'highlight': new Highlighter() | ||
} | ||
}); | ||
## Image Upload | ||
@@ -132,3 +187,6 @@ | ||
## Rails Gem | ||
[Ahmet Sezgin Duran](https://github.com/marjinal1st/) gemified Medium Editor for Rails asset pipeline, check it out at [https://github.com/marjinal1st/medium-editor-rails](https://github.com/marjinal1st/medium-editor-rails). | ||
## Angular directive | ||
@@ -172,25 +230,27 @@ | ||
``` | ||
project : medium-editor | ||
repo age : 10 months | ||
active : 116 days | ||
commits : 455 | ||
files : 47 | ||
project : medium-editor | ||
repo age : 11 months | ||
active : 129 days | ||
commits : 493 | ||
files : 50 | ||
authors : | ||
337 Davi Ferreira 74.1% | ||
20 Maxime de Visscher 4.4% | ||
8 Andy Yaco-Mink 1.8% | ||
8 Derek Odegard 1.8% | ||
8 Jarl Gunnar T. Flaten 1.8% | ||
8 Pedro Nasser 1.8% | ||
8 Seif 1.8% | ||
4 Sebastian Zuchmanski 0.9% | ||
4 minikomi 0.9% | ||
3 Andrew Hubbs 0.7% | ||
3 Dmitri Cherniak 0.7% | ||
3 Nikita Korotaev 0.7% | ||
3 Troels Knak-Nielsen 0.7% | ||
3 arol 0.7% | ||
3 ʞuıɯ-oɔɐʎ ʎpuɐ 0.7% | ||
363 Davi Ferreira 73.6% | ||
20 Maxime de Visscher 4.1% | ||
12 Andy Yaco-Mink 2.4% | ||
8 Derek Odegard 1.6% | ||
8 Jarl Gunnar T. Flaten 1.6% | ||
8 Pedro Nasser 1.6% | ||
8 Seif 1.6% | ||
5 Martin Thurau 1.0% | ||
4 Sebastian Zuchmanski 0.8% | ||
4 minikomi 0.8% | ||
3 Andrew Hubbs 0.6% | ||
3 Dmitri Cherniak 0.6% | ||
3 Nikita Korotaev 0.6% | ||
3 Troels Knak-Nielsen 0.6% | ||
3 arol 0.6% | ||
3 ʞuıɯ-oɔɐʎ ʎpuɐ 0.6% | ||
2 Ethan Turkeltaub 0.4% | ||
2 Jacob Magnusson 0.4% | ||
2 OmniaGM 0.4% | ||
1 Adam Mulligan 0.2% | ||
@@ -214,2 +274,3 @@ 1 Alberto Gasparin 0.2% | ||
1 Robert Koritnik 0.2% | ||
1 Scott Carleton 0.2% | ||
1 Søren Torp Petersen 0.2% | ||
@@ -224,4 +285,3 @@ 1 Tom MacWright 0.2% | ||
1 waffleio 0.2% | ||
1 zzjin 0.2% | ||
``` | ||
1 zzjin 0.2%``` | ||
@@ -228,0 +288,0 @@ ## License |
@@ -1,2 +0,2 @@ | ||
/*global MediumEditor, describe, it, expect, spyOn, | ||
/*global MediumEditor, describe, it, expect, spyOn, jasmine, fireEvent, | ||
afterEach, beforeEach, selectElementContents, runs, waitsFor, | ||
@@ -11,2 +11,3 @@ tearDown */ | ||
this.el.className = 'editor'; | ||
this.el.textContent = 'lore ipsum'; | ||
document.body.appendChild(this.el); | ||
@@ -40,2 +41,11 @@ }); | ||
describe('Deactivate', function () { | ||
beforeEach(function () { | ||
jasmine.clock().install(); | ||
}); | ||
afterEach(function () { | ||
jasmine.clock().uninstall(); | ||
}); | ||
it('should remove mediumEditor elements from DOM', function () { | ||
@@ -49,3 +59,17 @@ var editor = new MediumEditor('.editor'); | ||
}); | ||
// regression test for https://github.com/daviferreira/medium-editor/issues/197 | ||
it('should not crash when deactivated immediately after a mouse click', function () { | ||
var editor = new MediumEditor('.editor'); | ||
// selected some content and let the toolbar appear | ||
selectElementContents(editor.elements[0]); | ||
jasmine.clock().tick(501); | ||
// fire a mouse up somewhere else (i.e. a button which click handler could have called deactivate() ) | ||
fireEvent(document.documentElement, 'mouseup'); | ||
editor.deactivate(); | ||
jasmine.clock().tick(501); | ||
}); | ||
}); | ||
}); |
@@ -83,2 +83,13 @@ /*global MediumEditor, describe, it, expect, spyOn, | ||
it('should add http:// if need be and checkLinkFormat option is set to true', function () { | ||
var editor = new MediumEditor('.editor', { | ||
checkLinkFormat: true | ||
}), | ||
input = editor.anchorForm.querySelector('input'); | ||
selectElementContents(editor.elements[0]); | ||
input.value = 'test.com'; | ||
editor.createLink(input); | ||
expect(editor.elements[0].querySelector('a').href).toBe('http://test.com/'); | ||
}); | ||
it('should add target="_blank" when respective option is set to true', function () { | ||
@@ -85,0 +96,0 @@ var editor = new MediumEditor('.editor', { |
@@ -84,2 +84,3 @@ /*global MediumEditor, describe, it, expect, spyOn, | ||
forcePlainText: true, | ||
cleanPastedHTML: false, | ||
allowMultiParagraphSelection: true, | ||
@@ -91,3 +92,5 @@ placeholder: 'Type your text', | ||
targetBlank: false, | ||
anchorPreviewHideDelay: 500 | ||
anchorPreviewHideDelay: 500, | ||
checkLinkFormat: false, | ||
extensions: {} | ||
}, | ||
@@ -94,0 +97,0 @@ editor = new MediumEditor('.editor'); |
@@ -31,3 +31,3 @@ /*global MediumEditor, describe, it, expect, spyOn, | ||
it('should set a custom id when elements have no ids', function () { | ||
this.el.id = null; | ||
this.el.removeAttribute('id'); | ||
var editor = new MediumEditor('.editor'), | ||
@@ -34,0 +34,0 @@ json = editor.serialize(); |
@@ -90,2 +90,7 @@ /*global module, console*/ | ||
// https://github.com/jashkenas/underscore | ||
function isElement(obj) { | ||
return !!(obj && obj.nodeType === 1); | ||
} | ||
MediumEditor.prototype = { | ||
@@ -95,4 +100,7 @@ defaults: { | ||
anchorInputPlaceholder: 'Paste or type a link', | ||
anchorPreviewHideDelay: 500, | ||
buttons: ['bold', 'italic', 'underline', 'anchor', 'header1', 'header2', 'quote'], | ||
buttonLabels: false, | ||
checkLinkFormat: false, | ||
cleanPastedHTML: false, | ||
delay: 0, | ||
@@ -109,3 +117,3 @@ diffLeft: 0, | ||
targetBlank: false, | ||
anchorPreviewHideDelay: 500 | ||
extensions: {} | ||
}, | ||
@@ -149,3 +157,3 @@ | ||
this.elements[i].setAttribute('data-medium-element', true); | ||
this.bindParagraphCreation(i).bindReturn(i).bindTab(i).bindAnchorPreview(i); | ||
this.bindParagraphCreation(i).bindReturn(i).bindTab(i); | ||
if (!this.options.disableToolbar && !this.elements[i].getAttribute('data-disable-toolbar')) { | ||
@@ -159,3 +167,4 @@ addToolbar = true; | ||
.bindButtons() | ||
.bindAnchorForm(); | ||
.bindAnchorForm() | ||
.bindAnchorPreview(); | ||
} | ||
@@ -178,2 +187,28 @@ return this; | ||
/** | ||
* Helper function to call a method with a number of parameters on all registered extensions. | ||
* The function assures that the function exists before calling. | ||
* | ||
* @param {string} funcName name of the function to call | ||
* @param [args] arguments passed into funcName | ||
*/ | ||
callExtensions: function (funcName) { | ||
if (arguments.length < 1) { | ||
return; | ||
} | ||
var args = Array.prototype.slice.call(arguments, 1), | ||
ext, | ||
name; | ||
for (name in this.options.extensions) { | ||
if (this.options.extensions.hasOwnProperty(name)) { | ||
ext = this.options.extensions[name]; | ||
if (ext[funcName] !== undefined) { | ||
ext[funcName].apply(ext, args); | ||
} | ||
} | ||
} | ||
}, | ||
bindParagraphCreation: function (index) { | ||
@@ -255,18 +290,18 @@ var self = this; | ||
buttonTemplates = { | ||
'bold': '<li><button class="medium-editor-action medium-editor-action-bold" data-action="bold" data-element="b">' + buttonLabels.bold + '</button></li>', | ||
'italic': '<li><button class="medium-editor-action medium-editor-action-italic" data-action="italic" data-element="i">' + buttonLabels.italic + '</button></li>', | ||
'underline': '<li><button class="medium-editor-action medium-editor-action-underline" data-action="underline" data-element="u">' + buttonLabels.underline + '</button></li>', | ||
'strikethrough': '<li><button class="medium-editor-action medium-editor-action-strikethrough" data-action="strikethrough" data-element="strike"><strike>A</strike></button></li>', | ||
'superscript': '<li><button class="medium-editor-action medium-editor-action-superscript" data-action="superscript" data-element="sup">' + buttonLabels.superscript + '</button></li>', | ||
'subscript': '<li><button class="medium-editor-action medium-editor-action-subscript" data-action="subscript" data-element="sub">' + buttonLabels.subscript + '</button></li>', | ||
'anchor': '<li><button class="medium-editor-action medium-editor-action-anchor" data-action="anchor" data-element="a">' + buttonLabels.anchor + '</button></li>', | ||
'image': '<li><button class="medium-editor-action medium-editor-action-image" data-action="image" data-element="img">' + buttonLabels.image + '</button></li>', | ||
'header1': '<li><button class="medium-editor-action medium-editor-action-header1" data-action="append-' + this.options.firstHeader + '" data-element="' + this.options.firstHeader + '">' + buttonLabels.header1 + '</button></li>', | ||
'header2': '<li><button class="medium-editor-action medium-editor-action-header2" data-action="append-' + this.options.secondHeader + '" data-element="' + this.options.secondHeader + '">' + buttonLabels.header2 + '</button></li>', | ||
'quote': '<li><button class="medium-editor-action medium-editor-action-quote" data-action="append-blockquote" data-element="blockquote">' + buttonLabels.quote + '</button></li>', | ||
'orderedlist': '<li><button class="medium-editor-action medium-editor-action-orderedlist" data-action="insertorderedlist" data-element="ol">' + buttonLabels.orderedlist + '</button></li>', | ||
'unorderedlist': '<li><button class="medium-editor-action medium-editor-action-unorderedlist" data-action="insertunorderedlist" data-element="ul">' + buttonLabels.unorderedlist + '</button></li>', | ||
'pre': '<li><button class="medium-editor-action medium-editor-action-pre" data-action="append-pre" data-element="pre">' + buttonLabels.pre + '</button></li>', | ||
'indent': '<li><button class="medium-editor-action medium-editor-action-indent" data-action="indent" data-element="ul">' + buttonLabels.indent + '</button></li>', | ||
'outdent': '<li><button class="medium-editor-action medium-editor-action-outdent" data-action="outdent" data-element="ul">' + buttonLabels.outdent + '</button></li>' | ||
'bold': '<button class="medium-editor-action medium-editor-action-bold" data-action="bold" data-element="b">' + buttonLabels.bold + '</button>', | ||
'italic': '<button class="medium-editor-action medium-editor-action-italic" data-action="italic" data-element="i">' + buttonLabels.italic + '</button>', | ||
'underline': '<button class="medium-editor-action medium-editor-action-underline" data-action="underline" data-element="u">' + buttonLabels.underline + '</button>', | ||
'strikethrough': '<button class="medium-editor-action medium-editor-action-strikethrough" data-action="strikethrough" data-element="strike"><strike>A</strike></button>', | ||
'superscript': '<button class="medium-editor-action medium-editor-action-superscript" data-action="superscript" data-element="sup">' + buttonLabels.superscript + '</button>', | ||
'subscript': '<button class="medium-editor-action medium-editor-action-subscript" data-action="subscript" data-element="sub">' + buttonLabels.subscript + '</button>', | ||
'anchor': '<button class="medium-editor-action medium-editor-action-anchor" data-action="anchor" data-element="a">' + buttonLabels.anchor + '</button>', | ||
'image': '<button class="medium-editor-action medium-editor-action-image" data-action="image" data-element="img">' + buttonLabels.image + '</button>', | ||
'header1': '<button class="medium-editor-action medium-editor-action-header1" data-action="append-' + this.options.firstHeader + '" data-element="' + this.options.firstHeader + '">' + buttonLabels.header1 + '</button>', | ||
'header2': '<button class="medium-editor-action medium-editor-action-header2" data-action="append-' + this.options.secondHeader + '" data-element="' + this.options.secondHeader + '">' + buttonLabels.header2 + '</button>', | ||
'quote': '<button class="medium-editor-action medium-editor-action-quote" data-action="append-blockquote" data-element="blockquote">' + buttonLabels.quote + '</button>', | ||
'orderedlist': '<button class="medium-editor-action medium-editor-action-orderedlist" data-action="insertorderedlist" data-element="ol">' + buttonLabels.orderedlist + '</button>', | ||
'unorderedlist': '<button class="medium-editor-action medium-editor-action-unorderedlist" data-action="insertunorderedlist" data-element="ul">' + buttonLabels.unorderedlist + '</button>', | ||
'pre': '<button class="medium-editor-action medium-editor-action-pre" data-action="append-pre" data-element="pre">' + buttonLabels.pre + '</button>', | ||
'indent': '<button class="medium-editor-action medium-editor-action-indent" data-action="indent" data-element="ul">' + buttonLabels.indent + '</button>', | ||
'outdent': '<button class="medium-editor-action medium-editor-action-outdent" data-action="outdent" data-element="ul">' + buttonLabels.outdent + '</button>' | ||
}; | ||
@@ -326,23 +361,2 @@ return buttonTemplates[btnType] || false; | ||
//TODO: actionTemplate | ||
toolbarTemplate: function () { | ||
var btns = this.options.buttons, | ||
html = '<ul id="medium-editor-toolbar-actions" class="medium-editor-toolbar-actions clearfix">', | ||
i, | ||
tpl; | ||
for (i = 0; i < btns.length; i += 1) { | ||
tpl = this.buttonTemplate(btns[i]); | ||
if (tpl) { | ||
html += tpl; | ||
} | ||
} | ||
html += '</ul>' + | ||
'<div class="medium-editor-toolbar-form-anchor" id="medium-editor-toolbar-form-anchor">' + | ||
' <input type="text" value="" placeholder="' + this.options.anchorInputPlaceholder + '">' + | ||
' <a href="#">×</a>' + | ||
'</div>'; | ||
return html; | ||
}, | ||
initToolbar: function () { | ||
@@ -366,3 +380,4 @@ if (this.toolbar) { | ||
toolbar.className = 'medium-editor-toolbar'; | ||
toolbar.innerHTML = this.toolbarTemplate(); | ||
toolbar.appendChild(this.toolbarButtons()); | ||
toolbar.appendChild(this.toolbarFormAnchor()); | ||
document.body.appendChild(toolbar); | ||
@@ -372,2 +387,55 @@ return toolbar; | ||
//TODO: actionTemplate | ||
toolbarButtons: function () { | ||
var btns = this.options.buttons, | ||
ul = document.createElement('ul'), | ||
li, | ||
i, | ||
btn, | ||
ext; | ||
ul.id = 'medium-editor-toolbar-actions'; | ||
ul.className = 'medium-editor-toolbar-actions clearfix'; | ||
for (i = 0; i < btns.length; i += 1) { | ||
if (this.options.extensions.hasOwnProperty(btns[i])) { | ||
ext = this.options.extensions[btns[i]]; | ||
btn = ext.getButton !== undefined ? ext.getButton() : null; | ||
} else { | ||
btn = this.buttonTemplate(btns[i]); | ||
} | ||
if (btn) { | ||
li = document.createElement('li'); | ||
if (isElement(btn)) { | ||
li.appendChild(btn); | ||
} else { | ||
li.innerHTML = btn; | ||
} | ||
ul.appendChild(li); | ||
} | ||
} | ||
return ul; | ||
}, | ||
toolbarFormAnchor: function () { | ||
var anchor = document.createElement('div'), | ||
input = document.createElement('input'), | ||
a = document.createElement('a'); | ||
a.setAttribute('href', '#'); | ||
a.innerHTML = '×'; | ||
input.setAttribute('type', 'text'); | ||
input.setAttribute('placeholder', this.options.anchorInputPlaceholder); | ||
anchor.className = 'medium-editor-toolbar-form-anchor'; | ||
anchor.id = 'medium-editor-toolbar-form-anchor'; | ||
anchor.appendChild(input); | ||
anchor.appendChild(a); | ||
return anchor; | ||
}, | ||
bindSelect: function () { | ||
@@ -453,5 +521,3 @@ var self = this, | ||
var selection = window.getSelection(), | ||
range = selection.getRangeAt(0), | ||
current = range.commonAncestorContainer, | ||
parent = current.parentNode, | ||
range, current, parent, | ||
result, | ||
@@ -471,2 +537,6 @@ getMediumElement = function (e) { | ||
try { | ||
range = selection.getRangeAt(0); | ||
current = range.commonAncestorContainer; | ||
parent = current.parentNode; | ||
if (current.getAttribute('data-medium-element')) { | ||
@@ -525,3 +595,4 @@ result = current; | ||
checkActiveButtons: function () { | ||
var parentNode = this.selection.anchorNode; | ||
var elements = Array.prototype.slice.call(this.elements), | ||
parentNode = this.selection.anchorNode; | ||
if (!parentNode.tagName) { | ||
@@ -532,2 +603,8 @@ parentNode = this.selection.anchorNode.parentNode; | ||
this.activateButton(parentNode.tagName.toLowerCase()); | ||
this.callExtensions('checkState', parentNode); | ||
// we can abort the search upwards if we leave the contentEditable element | ||
if (elements.indexOf(parentNode) !== -1) { | ||
break; | ||
} | ||
parentNode = parentNode.parentNode; | ||
@@ -559,3 +636,5 @@ } | ||
} | ||
self.execAction(this.getAttribute('data-action'), e); | ||
if (this.hasAttribute('data-action')) { | ||
self.execAction(this.getAttribute('data-action'), e); | ||
} | ||
}; | ||
@@ -570,4 +649,6 @@ for (i = 0; i < buttons.length; i += 1) { | ||
setFirstAndLastItems: function (buttons) { | ||
buttons[0].className += ' medium-editor-button-first'; | ||
buttons[buttons.length - 1].className += ' medium-editor-button-last'; | ||
if (buttons.length > 0) { | ||
buttons[0].className += ' medium-editor-button-first'; | ||
buttons[buttons.length - 1].className += ' medium-editor-button-last'; | ||
} | ||
return this; | ||
@@ -659,3 +740,5 @@ }, | ||
this.keepToolbarAlive = false; | ||
this.toolbar.classList.remove('medium-editor-toolbar-active'); | ||
if (this.toolbar !== undefined) { | ||
this.toolbar.classList.remove('medium-editor-toolbar-active'); | ||
} | ||
}, | ||
@@ -671,3 +754,3 @@ | ||
timer = setTimeout(function () { | ||
if (!self.toolbar.classList.contains('medium-editor-toolbar-active')) { | ||
if (self.toolbar && !self.toolbar.classList.contains('medium-editor-toolbar-active')) { | ||
self.toolbar.classList.add('medium-editor-toolbar-active'); | ||
@@ -741,3 +824,3 @@ } | ||
timer = setTimeout(function () { | ||
if (!self.anchorPreview.classList.contains('medium-editor-anchor-preview-active')) { | ||
if (self.anchorPreview && !self.anchorPreview.classList.contains('medium-editor-anchor-preview-active')) { | ||
self.anchorPreview.classList.add('medium-editor-anchor-preview-active'); | ||
@@ -835,3 +918,5 @@ } | ||
setTimeout(function () { | ||
self.showAnchorForm(self.activeAnchor.href); | ||
if (self.activeAnchor) { | ||
self.showAnchorForm(self.activeAnchor.href); | ||
} | ||
self.keepToolbarAlive = false; | ||
@@ -857,3 +942,3 @@ }, 100 + self.options.delay); | ||
// Detect empty href attributes | ||
// The browser will make href="" or href="#top" | ||
// The browser will make href="" or href="#top" | ||
// into absolute urls when accessed as e.targed.href, so check the html | ||
@@ -884,9 +969,20 @@ if (!/href=["']\S+["']/.test(e.target.outerHTML) || /href=["']#\S+["']/.test(e.target.outerHTML)) { | ||
bindAnchorPreview: function (index) { | ||
var self = this; | ||
this.elements[index].addEventListener('mouseover', function (e) { | ||
var i, self = this; | ||
this.editorAnchorObserverWrapper = function (e) { | ||
self.editorAnchorObserver(e); | ||
}); | ||
}; | ||
for (i = 0; i < this.elements.length; i += 1) { | ||
this.elements[i].addEventListener('mouseover', this.editorAnchorObserverWrapper); | ||
} | ||
return this; | ||
}, | ||
checkLinkFormat: function (value) { | ||
var re = /^https?:\/\//; | ||
if (value.match(re)) { | ||
return value; | ||
} | ||
return "http://" + value; | ||
}, | ||
setTargetBlank: function () { | ||
@@ -907,2 +1003,5 @@ var el = getSelectionStart(), | ||
restoreSelection(this.savedSelection); | ||
if (this.options.checkLinkFormat) { | ||
input.value = this.checkLinkFormat(input.value); | ||
} | ||
document.execCommand('createLink', false, input.value); | ||
@@ -958,2 +1057,3 @@ if (this.options.targetBlank) { | ||
for (i = 0; i < this.elements.length; i += 1) { | ||
this.elements[i].removeEventListener('mouseover', this.editorAnchorObserverWrapper); | ||
this.elements[i].removeEventListener('keyup', this.checkSelectionWrapper); | ||
@@ -968,2 +1068,8 @@ this.elements[i].removeEventListener('blur', this.checkSelectionWrapper); | ||
htmlEntities: function (str) { | ||
// converts special characters (like <) into their escaped/encoded values (like <). | ||
// This allows you to show to display the string without the browser reading it as HTML. | ||
return String(str).replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"'); | ||
}, | ||
bindPaste: function () { | ||
@@ -977,3 +1083,3 @@ var i, self = this; | ||
this.classList.remove('medium-editor-placeholder'); | ||
if (!self.options.forcePlainText) { | ||
if (!self.options.forcePlainText && !self.options.cleanPastedHTML) { | ||
return this; | ||
@@ -984,2 +1090,6 @@ } | ||
e.preventDefault(); | ||
if (self.options.cleanPastedHTML && e.clipboardData.getData('text/html')) { | ||
return self.cleanPaste(e.clipboardData.getData('text/html')); | ||
} | ||
if (!self.options.disableReturn) { | ||
@@ -989,3 +1099,7 @@ paragraphs = e.clipboardData.getData('text/plain').split(/[\r\n]/g); | ||
if (paragraphs[p] !== '') { | ||
html += '<p>' + paragraphs[p] + '</p>'; | ||
if (navigator.userAgent.match(/firefox/i) && p === 0) { | ||
html += self.htmlEntities(paragraphs[p]); | ||
} else { | ||
html += '<p>' + self.htmlEntities(paragraphs[p]) + '</p>'; | ||
} | ||
} | ||
@@ -1024,2 +1138,189 @@ } | ||
return this; | ||
}, | ||
cleanPaste: function (text) { | ||
/*jslint regexp: true*/ | ||
/* | ||
jslint does not allow character negation, because the negation | ||
will not match any unicode characters. In the regexes in this | ||
block, negation is used specifically to match the end of an html | ||
tag, and in fact unicode characters *should* be allowed. | ||
*/ | ||
var i, elList, workEl, | ||
el = this.getSelectionElement(), | ||
multiline = /<p|<br|<div/.test(text), | ||
replacements = [ | ||
// replace two bogus tags that begin pastes from google docs | ||
[new RegExp(/<[^>]*docs-internal-guid[^>]*>/gi), ""], | ||
[new RegExp(/<\/b>(<br[^>]*>)?$/gi), ""], | ||
// un-html spaces and newlines inserted by OS X | ||
[new RegExp(/<span class="Apple-converted-space">\s+<\/span>/g), ' '], | ||
[new RegExp(/<br class="Apple-interchange-newline">/g), '<br>'], | ||
// replace google docs italics+bold with a span to be replaced once the html is inserted | ||
[new RegExp(/<span[^>]*(font-style:italic;font-weight:bold|font-weight:bold;font-style:italic)[^>]*>/gi), '<span class="replace-with italic bold">'], | ||
// replace google docs italics with a span to be replaced once the html is inserted | ||
[new RegExp(/<span[^>]*font-style:italic[^>]*>/gi), '<span class="replace-with italic">'], | ||
//[replace google docs bolds with a span to be replaced once the html is inserted | ||
[new RegExp(/<span[^>]*font-weight:bold[^>]*>/gi), '<span class="replace-with bold">'], | ||
// replace manually entered b/i/a tags with real ones | ||
[new RegExp(/<(\/?)(i|b|a)>/gi), '<$1$2>'], | ||
// replace manually a tags with real ones, converting smart-quotes from google docs | ||
[new RegExp(/<a\s+href=("|”|“|“|”)([^&]+)("|”|“|“|”)>/gi), '<a href="$2">'] | ||
]; | ||
/*jslint regexp: false*/ | ||
for (i = 0; i < replacements.length; i += 1) { | ||
text = text.replace(replacements[i][0], replacements[i][1]); | ||
} | ||
if (multiline) { | ||
// double br's aren't converted to p tags, but we want paragraphs. | ||
elList = text.split('<br><br>'); | ||
this.pasteHTML('<p>' + elList.join('</p><p>') + '</p>'); | ||
document.execCommand('insertText', false, "\n"); | ||
// block element cleanup | ||
elList = el.querySelectorAll('p,div,br'); | ||
for (i = 0; i < elList.length; i += 1) { | ||
workEl = elList[i]; | ||
switch (workEl.tagName.toLowerCase()) { | ||
case 'p': | ||
case 'div': | ||
this.filterCommonBlocks(workEl); | ||
break; | ||
case 'br': | ||
this.filterLineBreak(workEl); | ||
break; | ||
} | ||
} | ||
} else { | ||
this.pasteHTML(text); | ||
} | ||
}, | ||
pasteHTML: function (html) { | ||
var elList, workEl, i, fragmentBody, pasteBlock = document.createDocumentFragment(); | ||
pasteBlock.appendChild(document.createElement('body')); | ||
fragmentBody = pasteBlock.querySelector('body'); | ||
fragmentBody.innerHTML = html; | ||
this.cleanupSpans(fragmentBody); | ||
elList = fragmentBody.querySelectorAll('*'); | ||
for (i = 0; i < elList.length; i += 1) { | ||
workEl = elList[i]; | ||
// delete ugly attributes | ||
workEl.removeAttribute('class'); | ||
workEl.removeAttribute('style'); | ||
workEl.removeAttribute('dir'); | ||
if (workEl.tagName.toLowerCase() === 'meta') { | ||
workEl.parentNode.removeChild(workEl); | ||
} | ||
} | ||
document.execCommand('insertHTML', false, fragmentBody.innerHTML.replace(/ /g, ' ')); | ||
}, | ||
isCommonBlock: function (el) { | ||
return (el && (el.tagName.toLowerCase() === 'p' || el.tagName.toLowerCase() === 'div')); | ||
}, | ||
filterCommonBlocks: function (el) { | ||
if (/^\s*$/.test(el.innerText)) { | ||
el.parentNode.removeChild(el); | ||
} | ||
}, | ||
filterLineBreak: function (el) { | ||
if (this.isCommonBlock(el.previousElementSibling)) { | ||
// remove stray br's following common block elements | ||
el.parentNode.removeChild(el); | ||
} else if (this.isCommonBlock(el.parentNode) && (el.parentNode.firstChild === el || el.parentNode.lastChild === el)) { | ||
// remove br's just inside open or close tags of a div/p | ||
el.parentNode.removeChild(el); | ||
} else if (el.parentNode.childElementCount === 1) { | ||
// and br's that are the only child of a div/p | ||
this.removeWithParent(el); | ||
} | ||
}, | ||
// remove an element, including its parent, if it is the only element within its parent | ||
removeWithParent: function (el) { | ||
if (el && el.parentNode) { | ||
if (el.parentNode.parentNode && el.parentNode.childElementCount === 1) { | ||
el.parentNode.parentNode.removeChild(el.parentNode); | ||
} else { | ||
el.parentNode.removeChild(el.parentNode); | ||
} | ||
} | ||
}, | ||
cleanupSpans: function (container_el) { | ||
var i, | ||
el, | ||
new_el, | ||
spans = container_el.querySelectorAll('.replace-with'); | ||
for (i = 0; i < spans.length; i += 1) { | ||
el = spans[i]; | ||
new_el = document.createElement(el.classList.contains('bold') ? 'b' : 'i'); | ||
if (el.classList.contains('bold') && el.classList.contains('italic')) { | ||
// add an i tag as well if this has both italics and bold | ||
new_el.innerHTML = '<i>' + el.innerHTML + '</i>'; | ||
} else { | ||
new_el.innerHTML = el.innerHTML; | ||
} | ||
el.parentNode.replaceChild(new_el, el); | ||
} | ||
spans = container_el.querySelectorAll('span'); | ||
for (i = 0; i < spans.length; i += 1) { | ||
el = spans[i]; | ||
// remove empty spans, replace others with their contents | ||
if (/^\s*$/.test()) { | ||
el.parentNode.removeChild(el); | ||
} else { | ||
el.parentNode.replaceChild(document.createTextNode(el.innerText), el); | ||
} | ||
} | ||
} | ||
@@ -1026,0 +1327,0 @@ |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
610474
50
3692
286