Socket
Socket
Sign inDemoInstall

medium-editor

Package Overview
Dependencies
Maintainers
1
Versions
125
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

medium-editor - npm Package Compare versions

Comparing version 1.1.1 to 1.7.1

demo/customtoolbar.html

8

bower.json
{
"name": "medium-editor",
"version": "1.1.1",
"version": "1.7.1",
"homepage": "http://daviferreira.github.io/medium-editor/",

@@ -9,4 +9,4 @@ "authors": [

"description": "Medium.com WYSIWYG editor clone written in pure JavaScript.",
"main": ["dist/js/medium.editor.js", "dist/js/medium.editor.min.js",
"dist/css/medium.editor.css"],
"main": ["dist/js/medium-editor.js", "dist/js/medium-editor.min.js",
"dist/css/medium-editor.css"],
"keywords": [

@@ -30,3 +30,3 @@ "wysiwyg",

"package.json",
"src",
"src/js",
"README.md",

@@ -33,0 +33,0 @@ "CHANGES.md"

@@ -0,1 +1,180 @@

1.7.1 / 2014-03-22
* Prevents new lines with shift+enter when disableReturn is set to true
1.7.0 / 2014-03-22
==================
* Removes compass dependency by using grunt with libsass
* Fixes subscript button markup
* Fixes anchor preview behavior for empty links and anchors
* Adds a new option to disable double returns
1.6.7 / 2014-03-13
==================
* Allows initialization with a single DOM node
* Adds indent and outdent buttons
1.6.5 / 2014-03-08
==================
* fixes some minor paste bugs
* adds a delay option for anchor toolbar
* fixes anchor toolbar initial positioning
* fixes heading and blockquote on IE
1.6.1 / 2014-03-04
==================
* fixes case where clicking anchor preview and then clicking into the anchorInput
causes hideToolbarActions to be called
* fixes window resize when toolbar element is not created
1.6.0 / 2014-02-27
==================
* Reorganizes CSS files
* Removes unused method bindElementToolbarEvents
* Adds a preview toolbar for anchors
* Removes paste event binding on deactivate
1.5.4 / 2014-02-12
==================
* Fixes filenames for main in bower.json
* Removes window resize event listener on deactivate
1.5.3 / 2014-01-22
==================
* Adds bootstrap theme
* Adds image button that converts selected text into an image tag
* Removes normalize.css dependency
1.5.0 / 2014-01-16
==================
* Adds 3 new themes: Roman, Mani e Flat
1.4.5 / 2014-01-13
==================
* Adds ability to set custom labels on buttons
* Updates uglify
* Fixes bug where pressing enter on formatted list item would generate
a new list instead of a new list item
1.4.0 / 2013-12-13
==================
* Adds new extra buttons: pre and strikethrough
* Fixes placeholder bug on paste
* Various code improvements
* Prevents returns using shift when disableReturn is set to true
* Improves CSS to avoid conflicts
1.3.5 / 2013-11-27
==================
* Fixes problem with text selection ending outside the container div
* Implements serialize method
* Adds a targetBlank option
* Fixes Firefox box-sizing declarations
1.3.1 / 2013-11-19
==================
* Fixes toolbar binding button issue with multi-editor mode
1.3.0 / 2013-11-18
==================
* Fixes data-disable-return not preventing paragraph creation
* Improves getSelectionElement() to work in any case
* Fixes multi element selection bug
* Fixes Issues #88 & #89
* Improves binding for multiple editor instance, checkSelection() is called only once per instance
* Improves allowMultiParagraphSelection filter by removing empty tags elements before counting
* Considers header tags has a paragraph too (same as medium)
1.2.2 / 2013-11-07
==================
* Removes blur event listener when disabling the toolbar
* Adds a light gradient opacity to the toolbar
* Fixes bug that would keep toolbar alive when moving out of the anchor input
1.2.1 / 2013-11-07
==================
* Fixes empty selectionNode.el bug
* Prevents toolbar opening when changing to selection elements
with the toolbar disabled
* Adds a transition to the toolbar when moving across elements
1.2.0 / 2013-11-06
==================
* Fixes issue on deactivation without enabled toolbar
* Fixes checkSelection error when disableToolbar option is enabled
* Adds new option to disable multiple paragraph selection
* Prevents paragraph creation on paste when disableReturn is set to true
1.1.6 / 2013-10-24
==================
* Adds extra buttons: superscript, subscript, ordered list and unordered list
1.1.5 / 2013-10-23
==================
* Changes buttons blacklist to whitelist
1.1.4 / 2013-10-13
==================
* Exports MediumEditor as module
* Changes "Underline" button to display "U" instead of "S"
1.1.3 / 2013-10-08
==================
* Pasted text is now wrapped into P elements
1.1.2 / 2013-10-06
==================
* Changes the editor to use the formatBlock command to handle block elements
* Fixes placeholder for empty elements
1.1.1 / 2013-10-04
==================
* Normalizes styles and scripts
* Improves bower manifest
1.1.0 / 2013-10-03

@@ -14,2 +193,3 @@ ==================

1.0.2 / 2013-09-24

@@ -22,2 +202,3 @@ ==================

1.0.1 / 2013-09-20

@@ -29,2 +210,3 @@ ==================

1.0.0 / 2013-08-26

@@ -31,0 +213,0 @@ ==================

@@ -5,2 +5,7 @@ function MediumEditor(elements, options) {

}
if (typeof module === 'object') {
module.exports = MediumEditor;
}
(function (window, document) {

@@ -51,12 +56,2 @@ 'use strict';

// http://stackoverflow.com/questions/6139107/programatically-select-text-in-a-contenteditable-html-element
// by Tim Down & yckart
function selectElementContents(el) {
var selection = window.getSelection(),
range = document.createRange();
range.selectNodeContents(el);
selection.removeAllRanges();
selection.addRange(range);
}
// http://stackoverflow.com/questions/1197401/how-can-i-get-the-element-the-caret-is-in-with-javascript-when-using-contentedi

@@ -70,5 +65,33 @@ // by You

// http://stackoverflow.com/questions/4176923/html-of-selected-text
// by Tim Down
function getSelectionHtml() {
var i,
html = '',
sel,
len,
container;
if (window.getSelection !== undefined) {
sel = window.getSelection();
if (sel.rangeCount) {
container = document.createElement('div');
for (i = 0, len = sel.rangeCount; i < len; i += 1) {
container.appendChild(sel.getRangeAt(i).cloneContents());
}
html = container.innerHTML;
}
} else if (document.selection !== undefined) {
if (document.selection.type === 'Text') {
html = document.selection.createRange().htmlText;
}
}
return html;
}
MediumEditor.prototype = {
defaults: {
allowMultiParagraphSelection: true,
anchorInputPlaceholder: 'Paste or type a link',
buttons: ['bold', 'italic', 'underline', 'anchor', 'header1', 'header2', 'quote'],
buttonLabels: false,
delay: 0,

@@ -78,12 +101,21 @@ diffLeft: 0,

disableReturn: false,
disableDoubleReturn: false,
disableToolbar: false,
excludedActions: [],
firstHeader: 'h3',
forcePlainText: true,
placeholder: 'Type your text',
secondHeader: 'h4'
secondHeader: 'h4',
targetBlank: false,
anchorPreviewHideDelay: 500
},
// http://stackoverflow.com/questions/17907445/how-to-detect-ie11#comment30165888_17907562
// by rg89
isIE: ((navigator.appName === 'Microsoft Internet Explorer') || ((navigator.appName === 'Netscape') && (new RegExp('Trident/.*rv:([0-9]{1,}[.0-9]{0,})').exec(navigator.userAgent) !== null))),
init: function (elements, options) {
this.elements = typeof elements === 'string' ? document.querySelectorAll(elements) : elements;
if (this.elements.nodeType === 1) {
this.elements = [this.elements];
}
if (this.elements.length === 0) {

@@ -93,6 +125,7 @@ return;

this.isActive = true;
this.parentElements = ['p', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'q'];
this.parentElements = ['p', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'blockquote', 'pre'];
this.id = document.querySelectorAll('.medium-editor-toolbar').length + 1;
this.options = extend(options, this.defaults);
return this.initElements()
.bindSelect()
.bindPaste()

@@ -104,3 +137,4 @@ .setPlaceholders()

initElements: function () {
var i;
var i,
addToolbar = false;
for (i = 0; i < this.elements.length; i += 1) {

@@ -111,52 +145,209 @@ this.elements[i].setAttribute('contentEditable', true);

}
this.elements[i].setAttribute('data-medium-element', 'true');
this.bindParagraphCreation(this.elements[i]);
this.elements[i].setAttribute('data-medium-element', true);
this.bindParagraphCreation(i).bindReturn(i).bindTab(i).bindAnchorPreview(i);
if (!this.options.disableToolbar && !this.elements[i].getAttribute('data-disable-toolbar')) {
this.initToolbar()
.bindSelect()
.bindButtons()
.bindAnchorForm();
addToolbar = true;
}
}
// Init toolbar
if (addToolbar) {
this.initToolbar()
.bindButtons()
.bindAnchorForm();
}
return this;
},
bindParagraphCreation: function (el) {
serialize: function () {
var i,
elementid,
content = {};
for (i = 0; i < this.elements.length; i += 1) {
elementid = (this.elements[i].id !== '') ? this.elements[i].id : 'element-' + i;
content[elementid] = {
value: this.elements[i].innerHTML.trim()
};
}
return content;
},
bindParagraphCreation: function (index) {
var self = this;
el.addEventListener('keypress', function (e) {
var node = getSelectionStart();
if (node) {
node = node.tagName.toLowerCase();
this.elements[index].addEventListener('keyup', function (e) {
var node = getSelectionStart(),
tagName;
if (node && node.getAttribute('data-medium-element') && node.children.length === 0 &&
!(self.options.disableReturn || node.getAttribute('data-disable-return'))) {
document.execCommand('formatBlock', false, 'p');
}
if (e.which === 13 && !e.shiftKey) {
if (node !== 'q' && !self.options.disableReturn && !el.getAttribute('data-disable-return')) {
document.execCommand('formatBlock', false, 'p');
} else {
if (e.which === 13) {
node = getSelectionStart();
tagName = node.tagName.toLowerCase();
if (!(self.options.disableReturn || this.getAttribute('data-disable-return')) &&
tagName !== 'li' && !self.isListItemChild(node)) {
if (!e.shiftKey) {
document.execCommand('formatBlock', false, 'p');
}
if (tagName === 'a') {
document.execCommand('unlink', false, null);
}
}
}
});
return this;
},
isListItemChild: function (node) {
var parentNode = node.parentNode,
tagName = parentNode.tagName.toLowerCase();
while (this.parentElements.indexOf(tagName) === -1 && tagName !== 'div') {
if (tagName === 'li') {
return true;
}
parentNode = parentNode.parentNode;
if (parentNode && parentNode.tagName) {
tagName = parentNode.tagName.toLowerCase();
} else {
return false;
}
}
return false;
},
bindReturn: function (index) {
var self = this;
this.elements[index].addEventListener('keypress', function (e) {
if (e.which === 13) {
if (self.options.disableReturn || this.getAttribute('data-disable-return')) {
e.preventDefault();
} else if (self.options.disableDoubleReturn || this.getAttribute('data-disable-double-return')) {
var node = getSelectionStart();
if (node && node.innerText === '\n') {
e.preventDefault();
}
}
}
});
return this;
},
bindTab: function (index) {
this.elements[index].addEventListener('keydown', function (e) {
if (e.which === 9) {
// Override tab only for pre nodes
var tag = getSelectionStart().tagName.toLowerCase();
if (tag === 'pre') {
e.preventDefault();
document.execCommand('insertHtml', null, ' ');
}
}
});
return this;
},
buttonTemplate: function (btnType) {
var buttonLabels = this.getButtonLabels(this.options.buttonLabels),
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>'
};
return buttonTemplates[btnType] || false;
},
// TODO: break method
getButtonLabels: function (buttonLabelType) {
var customButtonLabels,
attrname,
buttonLabels = {
'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>&ldquo;</b>',
'orderedlist': '<b>1.</b>',
'unorderedlist': '<b>&bull;</b>',
'pre': '<b>0101</b>',
'indent': '<b>&rarr;</b>',
'outdent': '<b>&larr;</b>'
};
if (buttonLabelType === 'fontawesome') {
customButtonLabels = {
'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>'
};
} else if (typeof buttonLabelType === 'object') {
customButtonLabels = buttonLabelType;
}
if (typeof customButtonLabels === 'object') {
for (attrname in customButtonLabels) {
if (customButtonLabels.hasOwnProperty(attrname)) {
buttonLabels[attrname] = customButtonLabels[attrname];
}
}
}
return buttonLabels;
},
//TODO: actionTemplate
toolbarTemplate: function () {
return '<ul id="medium-editor-toolbar-actions" class="medium-editor-toolbar-actions clearfix">' +
' <li><button class="medium-editor-action medium-editor-action-bold" data-action="bold" data-element="b">B</button></li>' +
' <li><button class="medium-editor-action medium-editor-action-italic" data-action="italic" data-element="i">I</button></li>' +
' <li><button class="medium-editor-action medium-editor-action-underline" data-action="underline" data-element="u">S</button></li>' +
' <li><button class="medium-editor-action medium-editor-action-anchor" data-action="anchor" data-element="a">#</button></li>' +
' <li><button class="medium-editor-action medium-editor-action-header1" data-action="append-' + this.options.firstHeader + '" data-element="' + this.options.firstHeader + '">h1</button></li>' +
' <li><button class="medium-editor-action medium-editor-action-header2" data-action="append-' + this.options.secondHeader + '" data-element="' + this.options.secondHeader + '">h2</button></li>' +
' <li><button class="medium-editor-action medium-editor-action-quote" data-action="append-q" data-element="q">&ldquo;</button></li>' +
'</ul>' +
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="#">&times;</a>' +
' <input type="text" value="" placeholder="' + this.options.anchorInputPlaceholder + '">' +
' <a href="#">&times;</a>' +
'</div>';
return html;
},
initToolbar: function () {
if (this.toolbar) {
return this;
}
this.toolbar = this.createToolbar();
this.keepToolbarAlive = false;
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();
return this;

@@ -170,3 +361,3 @@ },

toolbar.innerHTML = this.toolbarTemplate();
document.getElementsByTagName('body')[0].appendChild(toolbar);
document.body.appendChild(toolbar);
return toolbar;

@@ -179,11 +370,21 @@ },

i;
this.checkSelectionWrapper = function (e) {
// Do not close the toolbar when bluring the editable area and clicking into the anchor form
if (e && self.clickingIntoArchorForm(e)) {
return false;
}
clearTimeout(timer);
setTimeout(function () {
self.checkSelection(e);
timer = setTimeout(function () {
self.checkSelection();
}, self.options.delay);
};
document.documentElement.addEventListener('mouseup', this.checkSelectionWrapper);
for (i = 0; i < this.elements.length; i += 1) {
this.elements[i].addEventListener('mouseup', this.checkSelectionWrapper);
this.elements[i].addEventListener('keyup', this.checkSelectionWrapper);
this.elements[i].addEventListener('blur', this.checkSelectionWrapper);
}

@@ -194,15 +395,16 @@ return this;

checkSelection: function () {
var newSelection;
if (this.keepToolbarAlive !== true) {
var newSelection,
selectionElement;
if (this.keepToolbarAlive !== true && !this.options.disableToolbar) {
newSelection = window.getSelection();
if (newSelection.toString().trim() === '') {
this.toolbar.style.display = 'none';
if (newSelection.toString().trim() === '' ||
(this.options.allowMultiParagraphSelection === false && this.hasMultiParagraphs())) {
this.hideToolbarActions();
} else {
this.selection = newSelection;
this.selectionRange = this.selection.getRangeAt(0);
if (!this.getSelectionElement().getAttribute('data-disable-toolbar')) {
this.toolbar.style.display = 'block';
this.setToolbarButtonStates()
.setToolbarPosition()
.showToolbarActions();
selectionElement = this.getSelectionElement();
if (!selectionElement || selectionElement.getAttribute('data-disable-toolbar')) {
this.hideToolbarActions();
} else {
this.checkSelectionElement(newSelection, selectionElement);
}

@@ -214,14 +416,61 @@ }

clickingIntoArchorForm: function(e) {
var self = this;
if (e.type && e.type.toLowerCase() === 'blur' && e.relatedTarget && e.relatedTarget === self.anchorInput) {
return true;
}
return false;
},
hasMultiParagraphs: function () {
var selectionHtml = getSelectionHtml().replace(/<[\S]+><\/[\S]+>/gim, ''),
hasMultiParagraphs = selectionHtml.match(/<(p|h[0-6]|blockquote)>([\s\S]*?)<\/(p|h[0-6]|blockquote)>/g);
return (hasMultiParagraphs ? hasMultiParagraphs.length : 0);
},
checkSelectionElement: function (newSelection, selectionElement) {
var i;
this.selection = newSelection;
this.selectionRange = this.selection.getRangeAt(0);
for (i = 0; i < this.elements.length; i += 1) {
if (this.elements[i] === selectionElement) {
this.setToolbarButtonStates()
.setToolbarPosition()
.showToolbarActions();
return;
}
}
this.hideToolbarActions();
},
getSelectionElement: function () {
var selection = window.getSelection(),
range = selection.getRangeAt(0),
parent = range.commonAncestorContainer.parentNode;
current = range.commonAncestorContainer,
parent = current.parentNode,
result,
getMediumElement = function(e) {
var parent = e;
try {
while (!parent.getAttribute('data-medium-element')) {
parent = parent.parentNode;
}
} catch (errb) {
return false;
}
return parent;
};
// First try on current node
try {
while (!parent.getAttribute('data-medium-element')) {
parent = parent.parentNode;
if (current.getAttribute('data-medium-element')) {
result = current;
} else {
result = getMediumElement(parent);
}
// If not search in the parent nodes.
} catch (err) {
return this.elements[0];
result = getMediumElement(parent);
}
return parent;
return result;
},

@@ -253,2 +502,5 @@

}
this.hideAnchorPreview();
return this;

@@ -262,3 +514,2 @@ },

buttons[i].classList.remove('medium-editor-button-active');
this.showHideButton(buttons[i]);
}

@@ -269,10 +520,2 @@ this.checkActiveButtons();

showHideButton: function (button) {
if (this.options.excludedActions.indexOf(button.getAttribute('data-element')) > -1) {
button.style.display = 'none';
} else {
button.style.display = 'block';
}
},
checkActiveButtons: function () {

@@ -283,3 +526,3 @@ var parentNode = this.selection.anchorNode;

}
while (parentNode.tagName !== undefined && this.parentElements.indexOf(parentNode.tagName) === -1) {
while (parentNode.tagName !== undefined && this.parentElements.indexOf(parentNode.tagName.toLowerCase) === -1) {
this.activateButton(parentNode.tagName.toLowerCase());

@@ -305,3 +548,3 @@ parentNode = parentNode.parentNode;

if (self.selection === undefined) {
self.checkSelection(e);
self.checkSelection();
}

@@ -330,8 +573,11 @@ if (this.className.indexOf('medium-editor-button-active') > -1) {

if (action.indexOf('append-') > -1) {
this.appendEl(action.replace('append-', ''));
this.execFormatBlock(action.replace('append-', ''));
this.setToolbarPosition();
this.setToolbarButtonStates();
} else if (action === 'anchor') {
this.triggerAnchorAction(e);
} else if (action === 'image') {
document.execCommand('insertImage', false, window.getSelection());
} else {
document.execCommand(action, null, false);
document.execCommand(action, false, null);
this.setToolbarPosition();

@@ -343,3 +589,3 @@ }

if (this.selection.anchorNode.parentNode.tagName.toLowerCase() === 'a') {
document.execCommand('unlink', null, false);
document.execCommand('unlink', false, null);
} else {

@@ -355,19 +601,25 @@ if (this.anchorForm.style.display === 'block') {

appendEl: function (el) {
execFormatBlock: function (el) {
var selectionData = this.getSelectionData(this.selection.anchorNode);
// FF handles blockquote differently on formatBlock
// allowing nesting, we need to use outdent
// https://developer.mozilla.org/en-US/docs/Rich-Text_Editing_in_Mozilla
if (el === 'blockquote' && selectionData.el &&
selectionData.el.parentNode.tagName.toLowerCase() === 'blockquote') {
return document.execCommand('outdent', false, null);
}
if (selectionData.tagName === el) {
el = 'p';
}
el = document.createElement(el);
if (selectionData.el) {
this.transferAttributes(selectionData.el, el);
el.innerHTML = selectionData.el.innerHTML;
selectionData.el.parentNode.replaceChild(el, selectionData.el);
} else {
el.innerHTML = this.selection.anchorNode.textContent;
this.selection.anchorNode.parentNode.replaceChild(el, this.selection.anchorNode);
// When IE we need to add <> to heading elements and
// blockquote needs to be called as indent
// http://stackoverflow.com/questions/10741831/execcommand-formatblock-headings-in-ie
// http://stackoverflow.com/questions/1816223/rich-text-editor-with-blockquote-function/1821777#1821777
if (this.isIE) {
if (el === 'blockquote') {
return document.execCommand('indent', false, el);
}
el = '<' + el + '>';
}
selectElementContents(el);
this.bindElementToolbarEvents(el);
this.setToolbarPosition();
return document.execCommand('formatBlock', false, el);
},

@@ -395,8 +647,2 @@

transferAttributes: function (elFrom, elTo) {
Array.prototype.slice.call(elFrom.attributes).forEach(function (item) {
elTo.setAttribute(item.name, item.value);
});
},
getFirstChild: function (el) {

@@ -410,10 +656,5 @@ var firstChild = el.firstChild;

bindElementToolbarEvents: function (el) {
var self = this;
el.addEventListener('mouseup', function (e) {
self.checkSelection(e);
});
el.addEventListener('keyup', function (e) {
self.checkSelection(e);
});
hideToolbarActions: function () {
this.keepToolbarAlive = false;
this.toolbar.classList.remove('medium-editor-toolbar-active');
},

@@ -428,13 +669,10 @@

clearTimeout(timer);
timer = setTimeout(function () {
document.addEventListener('click', function () {
self.keepToolbarAlive = false;
self.toolbar.style.display = 'none';
document.removeEventListener('click', this);
});
}, 300);
timer = setTimeout(function() {
if (!self.toolbar.classList.contains('medium-editor-toolbar-active')) {
self.toolbar.classList.add('medium-editor-toolbar-active');
}
}, 100);
},
showAnchorForm: function () {
var input = this.anchorForm.querySelector('input');
showAnchorForm: function (link_value) {
this.toolbarActions.style.display = 'none';

@@ -444,9 +682,8 @@ this.savedSelection = saveSelection();

this.keepToolbarAlive = true;
input.focus();
input.value = '';
this.anchorInput.focus();
this.anchorInput.value = link_value || '';
},
bindAnchorForm: function () {
var input = this.anchorForm.querySelector('input'),
linkCancel = this.anchorForm.querySelector('a'),
var linkCancel = this.anchorForm.querySelector('a'),
self = this;

@@ -456,3 +693,3 @@ this.anchorForm.addEventListener('click', function (e) {

});
input.addEventListener('keyup', function (e) {
this.anchorInput.addEventListener('keyup', function (e) {
if (e.keyCode === 13) {

@@ -463,2 +700,11 @@ e.preventDefault();

});
this.anchorInput.addEventListener('click', function (e) {
// make sure not to hide form when cliking into the input
e.stopPropagation();
self.keepToolbarAlive = true;
});
this.anchorInput.addEventListener('blur', function () {
self.keepToolbarAlive = false;
self.checkSelection();
});
linkCancel.addEventListener('click', function (e) {

@@ -472,5 +718,193 @@ e.preventDefault();

hideAnchorPreview: function() {
this.anchorPreview.classList.remove('medium-editor-anchor-preview-active');
},
// TODO: break method
showAnchorPreview: function (anchor_el) {
if (this.anchorPreview.classList.contains('medium-editor-anchor-preview-active')) {
return true;
}
var self = this,
buttonHeight = 40,
boundary = anchor_el.getBoundingClientRect(),
middleBoundary = (boundary.left + boundary.right) / 2,
halfOffsetWidth,
defaultLeft,
timer;
self.anchorPreview.querySelector('i').innerHTML = anchor_el.href;
halfOffsetWidth = self.anchorPreview.offsetWidth / 2;
defaultLeft = self.options.diffLeft - halfOffsetWidth;
clearTimeout(timer);
timer = setTimeout(function() {
if (!self.anchorPreview.classList.contains('medium-editor-anchor-preview-active')) {
self.anchorPreview.classList.add('medium-editor-anchor-preview-active');
}
}, 100);
self.observeAnchorPreview(anchor_el);
self.anchorPreview.classList.add('medium-toolbar-arrow-over');
self.anchorPreview.classList.remove('medium-toolbar-arrow-under');
self.anchorPreview.style.top = Math.round(buttonHeight + boundary.bottom - self.options.diffTop + window.pageYOffset - self.anchorPreview.offsetHeight) + 'px';
if (middleBoundary < halfOffsetWidth) {
self.anchorPreview.style.left = defaultLeft + halfOffsetWidth + 'px';
} else if ((window.innerWidth - middleBoundary) < halfOffsetWidth) {
self.anchorPreview.style.left = window.innerWidth + defaultLeft - halfOffsetWidth + 'px';
} else {
self.anchorPreview.style.left = defaultLeft + middleBoundary + 'px';
}
return this;
},
// TODO: break method
observeAnchorPreview: function(anchorEl) {
var self = this,
lastOver = (new Date()).getTime(),
over = true,
stamp = function() {
lastOver = (new Date()).getTime();
over = true;
},
unstamp = function(e) {
if (!e.relatedTarget || !/anchor-preview/.test(e.relatedTarget.className)) {
over = false;
}
},
interval_timer = setInterval(function() {
if (over) {
return true;
}
var durr = (new Date()).getTime() - lastOver;
if (durr > self.options.anchorPreviewHideDelay) {
// hide the preview 1/2 second after mouse leaves the link
self.hideAnchorPreview();
// cleanup
clearInterval(interval_timer);
self.anchorPreview.removeEventListener('mouseover', stamp);
self.anchorPreview.removeEventListener('mouseout', unstamp);
anchorEl.removeEventListener('mouseover', stamp);
anchorEl.removeEventListener('mouseout', unstamp);
}
}, 200);
self.anchorPreview.addEventListener('mouseover', stamp);
self.anchorPreview.addEventListener('mouseout', unstamp);
anchorEl.addEventListener('mouseover', stamp);
anchorEl.addEventListener('mouseout', unstamp);
},
createAnchorPreview: function () {
var self = this,
anchorPreview = document.createElement('div');
anchorPreview.id = 'medium-editor-anchor-preview-' + this.id;
anchorPreview.className = 'medium-editor-anchor-preview';
anchorPreview.innerHTML = this.anchorPreviewTemplate();
document.body.appendChild(anchorPreview);
anchorPreview.addEventListener('click', function() {
self.anchorPreviewClickHandler();
});
return anchorPreview;
},
anchorPreviewTemplate: function () {
return '<div class="medium-editor-toolbar-anchor-preview" id="medium-editor-toolbar-anchor-preview">' +
' <i class="medium-editor-toolbar-anchor-preview-inner">http://google.com/</i>' +
'</div>';
},
anchorPreviewClickHandler: function(e) {
if (this.activeAnchor) {
var self = this,
range = document.createRange(),
sel = window.getSelection();
range.selectNodeContents(self.activeAnchor);
sel.removeAllRanges();
sel.addRange(range);
setTimeout(function() {
self.showAnchorForm(self.activeAnchor.href);
self.keepToolbarAlive = false;
}, 100 + self.options.delay);
}
this.hideAnchorPreview();
},
editorAnchorObserver: function(e) {
var self = this,
overAnchor = true,
leaveAnchor = function() {
// mark the anchor as no longer hovered, and stop listening
overAnchor = false;
self.activeAnchor.removeEventListener('mouseout', leaveAnchor);
};
if (e.target && e.target.tagName.toLowerCase() === 'a') {
// Detect empty href attributes
// The browser will make href="" or href="#top"
// into absolute urls when accessed as e.targed.href, so check the html
if (!/href=["']\S+["']/.test(e.target.outerHTML) || /href=["']#\S+["']/.test(e.target.outerHTML)) {
return true;
}
// only show when hovering on anchors
if (this.toolbar.classList.contains('medium-editor-toolbar-active')) {
// only show when toolbar is not present
return true;
}
this.activeAnchor = e.target;
this.activeAnchor.addEventListener('mouseout', leaveAnchor);
// show the anchor preview according to the configured delay
// if the mouse has not left the anchor tag in that time
setTimeout(function() {
if (overAnchor) {
self.showAnchorPreview(e.target);
}
}, self.options.delay);
}
},
bindAnchorPreview: function (index) {
var self = this;
this.elements[index].addEventListener('mouseover', function(e) {
self.editorAnchorObserver(e);
});
return this;
},
setTargetBlank: function () {
var el = getSelectionStart(),
i;
if (el.tagName.toLowerCase() === 'a') {
el.target = '_blank';
} else {
el = el.getElementsByTagName('a');
for (i = 0; i < el.length; i += 1) {
el[i].target = '_blank';
}
}
},
createLink: function (input) {
restoreSelection(this.savedSelection);
document.execCommand('CreateLink', false, input.value);
document.execCommand('createLink', false, input.value);
if (this.options.targetBlank) {
this.setTargetBlank();
}
this.showToolbarActions();

@@ -483,8 +917,11 @@ input.value = '';

self = this;
window.addEventListener('resize', function () {
this.windowResizeHandler = function () {
clearTimeout(timerResize);
timerResize = setTimeout(function () {
self.setToolbarPosition();
if (self.toolbar && self.toolbar.classList.contains('medium-editor-toolbar-active')) {
self.setToolbarPosition();
}
}, 100);
});
};
window.addEventListener('resize', this.windowResizeHandler);
return this;

@@ -498,2 +935,7 @@ },

}
if (this.toolbar !== undefined) {
this.toolbar.style.display = 'block';
}
this.isActive = true;

@@ -503,5 +945,8 @@ for (i = 0; i < this.elements.length; i += 1) {

}
this.bindSelect();
this.bindWindowActions()
.bindSelect();
},
// TODO: break method
deactivate: function () {

@@ -513,24 +958,49 @@ var i;

this.isActive = false;
this.toolbar.style.display = 'none';
if (this.toolbar !== undefined) {
document.body.removeChild(this.anchorPreview);
document.body.removeChild(this.toolbar);
}
document.documentElement.removeEventListener('mouseup', this.checkSelectionWrapper);
window.removeEventListener('resize', this.windowResizeHandler);
for (i = 0; i < this.elements.length; i += 1) {
this.elements[i].removeEventListener('mouseup', this.checkSelectionWrapper);
this.elements[i].removeEventListener('keyup', this.checkSelectionWrapper);
this.elements[i].removeEventListener('blur', this.checkSelectionWrapper);
this.elements[i].removeEventListener('paste', this.pasteWrapper);
this.elements[i].removeAttribute('contentEditable');
}
},
bindPaste: function () {
if (!this.options.forcePlainText) {
return;
}
var i,
pasteWrapper = function (e) {
e.target.classList.remove('medium-editor-placeholder');
if (e.clipboardData && e.clipboardData.getData) {
e.preventDefault();
document.execCommand('insertHTML', false, e.clipboardData.getData('text/plain').replace(/[\r\n]/g, '<br>'));
var i, self = this;
this.pasteWrapper = function (e) {
var paragraphs,
html = '',
p;
this.classList.remove('medium-editor-placeholder');
if (!self.options.forcePlainText) {
return this;
}
if (e.clipboardData && e.clipboardData.getData && !e.defaultPrevented) {
e.preventDefault();
if (!self.options.disableReturn) {
paragraphs = e.clipboardData.getData('text/plain').split(/[\r\n]/g);
for (p = 0; p < paragraphs.length; p += 1) {
if (paragraphs[p] !== '') {
html += '<p>' + paragraphs[p] + '</p>';
}
}
document.execCommand('insertHTML', false, html);
} else {
document.execCommand('insertHTML', false, e.clipboardData.getData('text/plain'));
}
};
}
};
for (i = 0; i < this.elements.length; i += 1) {
this.elements[i].addEventListener('paste', pasteWrapper);
this.elements[i].addEventListener('paste', this.pasteWrapper);
}

@@ -544,3 +1014,2 @@ return this;

if (el.textContent.replace(/^\s+|\s+$/g, '') === '') {
el.innerHTML = '';
el.classList.add('medium-editor-placeholder');

@@ -557,3 +1026,3 @@ }

activatePlaceholder(this.elements[i]);
this.elements[i].addEventListener('focusout', placeholderWrapper);
this.elements[i].addEventListener('blur', placeholderWrapper);
this.elements[i].addEventListener('keypress', placeholderWrapper);

@@ -563,3 +1032,5 @@ }

}
};
}(window, document));

@@ -1,1 +0,1 @@

function MediumEditor(a,b){"use strict";return this.init(a,b)}!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(c){var d=a.getSelection(),e=b.createRange();e.selectNodeContents(c),d.removeAllRanges(),d.addRange(e)}function g(){var a=b.getSelection().anchorNode,c=a&&3===a.nodeType?a.parentNode:a;return c}MediumEditor.prototype={defaults:{anchorInputPlaceholder:"Paste or type a link",delay:0,diffLeft:0,diffTop:-10,disableReturn:!1,disableToolbar:!1,excludedActions:[],firstHeader:"h3",forcePlainText:!0,placeholder:"Type your text",secondHeader:"h4"},init:function(a,d){return this.elements="string"==typeof a?b.querySelectorAll(a):a,0!==this.elements.length?(this.isActive=!0,this.parentElements=["p","h1","h2","h3","h4","h5","h6","q"],this.id=b.querySelectorAll(".medium-editor-toolbar").length+1,this.options=c(d,this.defaults),this.initElements().bindPaste().setPlaceholders().bindWindowActions()):void 0},initElements:function(){var a;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","true"),this.bindParagraphCreation(this.elements[a]),this.options.disableToolbar||this.elements[a].getAttribute("data-disable-toolbar")||this.initToolbar().bindSelect().bindButtons().bindAnchorForm();return this},bindParagraphCreation:function(a){var c=this;a.addEventListener("keypress",function(d){var e=g();e&&(e=e.tagName.toLowerCase()),13!==d.which||d.shiftKey||("q"===e||c.options.disableReturn||a.getAttribute("data-disable-return")?d.preventDefault():b.execCommand("formatBlock",!1,"p"))})},toolbarTemplate:function(){return'<ul id="medium-editor-toolbar-actions" class="medium-editor-toolbar-actions clearfix"> <li><button class="medium-editor-action medium-editor-action-bold" data-action="bold" data-element="b">B</button></li> <li><button class="medium-editor-action medium-editor-action-italic" data-action="italic" data-element="i">I</button></li> <li><button class="medium-editor-action medium-editor-action-underline" data-action="underline" data-element="u">S</button></li> <li><button class="medium-editor-action medium-editor-action-anchor" data-action="anchor" data-element="a">#</button></li> <li><button class="medium-editor-action medium-editor-action-header1" data-action="append-'+this.options.firstHeader+'" data-element="'+this.options.firstHeader+'">h1</button></li>'+' <li><button class="medium-editor-action medium-editor-action-header2" data-action="append-'+this.options.secondHeader+'" data-element="'+this.options.secondHeader+'">h2</button></li>'+' <li><button class="medium-editor-action medium-editor-action-quote" data-action="append-q" data-element="q">&ldquo;</button></li>'+"</ul>"+'<div class="medium-editor-toolbar-form-anchor" id="medium-editor-toolbar-form-anchor">'+' <input type="text" value="" placeholder="'+this.options.anchorInputPlaceholder+'"><a href="#">&times;</a>'+"</div>"},initToolbar:function(){return this.toolbar=this.createToolbar(),this.keepToolbarAlive=!1,this.anchorForm=this.toolbar.querySelector(".medium-editor-toolbar-form-anchor"),this.toolbarActions=this.toolbar.querySelector(".medium-editor-toolbar-actions"),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.getElementsByTagName("body")[0].appendChild(a),a},bindSelect:function(){var a,b=this,c="";for(this.checkSelectionWrapper=function(a){clearTimeout(c),setTimeout(function(){b.checkSelection(a)},b.options.delay)},a=0;a<this.elements.length;a+=1)this.elements[a].addEventListener("mouseup",this.checkSelectionWrapper),this.elements[a].addEventListener("keyup",this.checkSelectionWrapper);return this},checkSelection:function(){var b;return this.keepToolbarAlive!==!0&&(b=a.getSelection(),""===b.toString().trim()?this.toolbar.style.display="none":(this.selection=b,this.selectionRange=this.selection.getRangeAt(0),this.getSelectionElement().getAttribute("data-disable-toolbar")||(this.toolbar.style.display="block",this.setToolbarButtonStates().setToolbarPosition().showToolbarActions()))),this},getSelectionElement:function(){var b=a.getSelection(),c=b.getRangeAt(0),d=c.commonAncestorContainer.parentNode;try{for(;!d.getAttribute("data-medium-element");)d=d.parentNode}catch(e){return this.elements[0]}return d},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},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"),this.showHideButton(b[a]);return this.checkActiveButtons(),this},showHideButton:function(a){a.style.display=this.options.excludedActions.indexOf(a.getAttribute("data-element"))>-1?"none":"block"},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);)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(a),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(a,c){a.indexOf("append-")>-1?(this.appendEl(a.replace("append-","")),this.setToolbarButtonStates()):"anchor"===a?this.triggerAnchorAction(c):(b.execCommand(a,null,!1),this.setToolbarPosition())},triggerAnchorAction:function(){return"a"===this.selection.anchorNode.parentNode.tagName.toLowerCase()?b.execCommand("unlink",null,!1):"block"===this.anchorForm.style.display?this.showToolbarActions():this.showAnchorForm(),this},appendEl:function(a){var c=this.getSelectionData(this.selection.anchorNode);c.tagName===a&&(a="p"),a=b.createElement(a),c.el?(this.transferAttributes(c.el,a),a.innerHTML=c.el.innerHTML,c.el.parentNode.replaceChild(a,c.el)):(a.innerHTML=this.selection.anchorNode.textContent,this.selection.anchorNode.parentNode.replaceChild(a,this.selection.anchorNode)),f(a),this.bindElementToolbarEvents(a),this.setToolbarPosition()},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}},transferAttributes:function(a,b){Array.prototype.slice.call(a.attributes).forEach(function(a){b.setAttribute(a.name,a.value)})},getFirstChild:function(a){for(var b=a.firstChild;null!==b&&1!==b.nodeType;)b=b.nextSibling;return b},bindElementToolbarEvents:function(a){var b=this;a.addEventListener("mouseup",function(a){b.checkSelection(a)}),a.addEventListener("keyup",function(a){b.checkSelection(a)})},showToolbarActions:function(){var a,c=this;this.anchorForm.style.display="none",this.toolbarActions.style.display="block",this.keepToolbarAlive=!1,clearTimeout(a),a=setTimeout(function(){b.addEventListener("click",function(){c.keepToolbarAlive=!1,c.toolbar.style.display="none",b.removeEventListener("click",this)})},300)},showAnchorForm:function(){var a=this.anchorForm.querySelector("input");this.toolbarActions.style.display="none",this.savedSelection=d(),this.anchorForm.style.display="block",this.keepToolbarAlive=!0,a.focus(),a.value=""},bindAnchorForm:function(){var a=this.anchorForm.querySelector("input"),b=this.anchorForm.querySelector("a"),c=this;return this.anchorForm.addEventListener("click",function(a){a.stopPropagation()}),a.addEventListener("keyup",function(a){13===a.keyCode&&(a.preventDefault(),c.createLink(this))}),b.addEventListener("click",function(a){a.preventDefault(),c.showToolbarActions(),e(c.savedSelection)}),this},createLink:function(a){e(this.savedSelection),b.execCommand("CreateLink",!1,a.value),this.showToolbarActions(),a.value=""},bindWindowActions:function(){var b,c=this;return a.addEventListener("resize",function(){clearTimeout(b),b=setTimeout(function(){c.setToolbarPosition()},100)}),this},activate:function(){var a;if(!this.isActive){for(this.isActive=!0,a=0;a<this.elements.length;a+=1)this.elements[a].setAttribute("contentEditable",!0);this.bindSelect()}},deactivate:function(){var a;if(this.isActive)for(this.isActive=!1,this.toolbar.style.display="none",a=0;a<this.elements.length;a+=1)this.elements[a].removeEventListener("mouseup",this.checkSelectionWrapper),this.elements[a].removeEventListener("keyup",this.checkSelectionWrapper),this.elements[a].removeAttribute("contentEditable")},bindPaste:function(){if(this.options.forcePlainText){var a,c=function(a){a.target.classList.remove("medium-editor-placeholder"),a.clipboardData&&a.clipboardData.getData&&(a.preventDefault(),b.execCommand("insertHTML",!1,a.clipboardData.getData("text/plain").replace(/[\r\n]/g,"<br>")))};for(a=0;a<this.elements.length;a+=1)this.elements[a].addEventListener("paste",c);return this}},setPlaceholders:function(){var a,b=function(a){""===a.textContent.replace(/^\s+|\s+$/g,"")&&(a.innerHTML="",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("focusout",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}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.isActive=!0,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.initElements().bindSelect().bindPaste().setPlaceholders().bindWindowActions()):void 0},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>&ldquo;</b>",orderedlist:"<b>1.</b>",unorderedlist:"<b>&bull;</b>",pre:"<b>0101</b>",indent:"<b>&rarr;</b>",outdent:"<b>&larr;</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="#">&times;</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),d=setTimeout(function(){c.checkSelection()},c.options.delay),void 0)},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 this.setToolbarButtonStates().setToolbarPosition().showToolbarActions(),void 0;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">http://google.com/</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(){var a;if(!this.isActive){for(void 0!==this.toolbar&&(this.toolbar.style.display="block"),this.isActive=!0,a=0;a<this.elements.length;a+=1)this.elements[a].setAttribute("contentEditable",!0);this.bindWindowActions().bindSelect()}},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)),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")},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);

@@ -7,4 +7,5 @@ /*global module, require*/

var gruntConfig = {
pkg: grunt.file.readJSON('package.json')
};
pkg: grunt.file.readJSON('package.json')
},
autoprefixerBrowsers = ['last 3 versions', 'ie >= 9'];

@@ -56,3 +57,5 @@ gruntConfig.jslint = {

'box-sizing': false,
'import': 2
'import': 2,
'compatible-vendor-prefixes': false,
'gradients': false
},

@@ -63,9 +66,15 @@ src: 'dist/css/**/*.css'

gruntConfig.compass = {
gruntConfig.sass = {
dist: {
options: {
sassDir: 'src/sass',
cssDir: 'dist/css',
outputStyle: 'compressed',
noLineComments: true
includePaths: ['src/sass/']
},
files: {
'dist/css/medium-editor.css': 'src/sass/medium-editor.scss',
'dist/css/themes/bootstrap.css': 'src/sass/themes/bootstrap.scss',
'dist/css/themes/default.css': 'src/sass/themes/default.scss',
'dist/css/themes/flat.css': 'src/sass/themes/flat.scss',
'dist/css/themes/mani.css': 'src/sass/themes/mani.scss',
'dist/css/themes/roman.css': 'src/sass/themes/roman.scss'
}

@@ -75,2 +84,16 @@ }

gruntConfig.autoprefixer = {
singleFile: {
src: 'dist/css/medium-editor.css',
browsers: autoprefixerBrowsers
},
multipleFiles: {
expand: true,
flatten: true,
src: 'dist/css/themes/*.css',
dest: 'dist/css/themes/',
browsers: autoprefixerBrowsers
}
};
gruntConfig.watch = {

@@ -116,4 +139,5 @@ scripts: {

grunt.loadNpmTasks('grunt-contrib-jasmine');
grunt.loadNpmTasks('grunt-contrib-compass');
grunt.loadNpmTasks('grunt-autoprefixer');
grunt.loadNpmTasks('grunt-contrib-csslint');
grunt.loadNpmTasks('grunt-sass');
grunt.loadNpmTasks('grunt-contrib-watch');

@@ -125,5 +149,5 @@ grunt.loadNpmTasks('grunt-contrib-concat');

grunt.registerTask('js', ['jslint', 'jasmine', 'uglify', 'concat']);
grunt.registerTask('css', ['compass', 'csslint']);
grunt.registerTask('css', ['sass', 'autoprefixer', 'csslint']);
grunt.registerTask('default', ['js', 'css']);
};
{
"name": "medium-editor",
"version": "1.1.1",
"repository": "https://github.com/daviferreira/medium-editor",
"version": "1.7.1",
"author": "Davi Ferreira <hi@daviferreira.com>",
"description": "Medium.com WYSIWYG editor clone.",
"main": "src/js/medium-editor.js",
"repository": {
"type": "git",
"url": "https://github.com/daviferreira/medium-editor"
},
"bugs": {
"url": "https://github.com/daviferreira/medium-editor/issues",
"email": "hi@daviferreira.com"
},
"homepage": "http://daviferreira.github.io/medium-editor/",
"keywords": [
"editor",
"medium",
"wysiwyg",
"rich-text"
],
"license": "THE BEER-WARE LICENSE",
"devDependencies": {
"grunt": "~0.4.1",
"grunt-contrib-uglify": "~0.2.0",
"grunt": "~0.4.4",
"grunt-autoprefixer": "~0.7.2",
"grunt-contrib-uglify": "~0.2.7",
"grunt-contrib-jasmine": "~0.4.2",
"grunt-contrib-compass": "~0.2.0",
"grunt-contrib-csslint": "~0.1.2",

@@ -15,3 +33,5 @@ "grunt-jslint": "~0.2.5a",

"grunt-template-jasmine-istanbul": "~0.2.4",
"grunt-plato": "~0.2.0"
"grunt-plato": "~0.2.0",
"brfs": "0.0.8",
"grunt-sass": "~0.11.0"
},

@@ -18,0 +38,0 @@ "scripts": {

# MediumEditor
[![Build Status](https://travis-ci.org/daviferreira/medium-editor.png?branch=master)](https://travis-ci.org/daviferreira/medium-editor)
This is a clone of [medium.com](https://medium.com) inline editor toolbar.

@@ -11,4 +9,6 @@

Tested on Googe Chrome, Firefox and IE9+.
Tested on Google Chrome, Firefox and IE9+.
[![Build Status](https://travis-ci.org/daviferreira/medium-editor.png?branch=master)](https://travis-ci.org/daviferreira/medium-editor)
# Basic usage

@@ -23,3 +23,4 @@

```html
<link rel="stylesheet" href="css/medium-editor.css">
<link rel="stylesheet" href="css/medium-editor.css"> <!-- Core -->
<link rel="stylesheet" href="css/themes/default.css"> <!-- or any other theme -->
```

@@ -45,9 +46,13 @@

* __allowMultiParagraphSelection__: enables the toolbar when selecting multiple paragraphs/block elements. Default: true
* __anchorInputPlaceholder__: text to be shown as placeholder of the anchor input. Default: _Paste or type a link_
* __delay__: time in milliseconds to show the toolbar. Default: 0
* __anchorPreviewHideDelay__: time in milliseconds to show the anchor tag preview after the mouse has left the anchor tag. Default: 500
* __buttons__: the set of buttons to display on the toolbar. Default: ['bold', 'italic', 'underline', 'anchor', 'header1', 'header2', 'quote']
* __buttonLabels__: type of labels on the buttons. Values: 'fontawesome', `{'bold': '<b>b</b>', 'italic': '<i>i</i>'}`. Default: false
* __delay__: time in milliseconds to show the toolbar or anchor tag preview. Default: 0
* __diffLeft__: value in pixels to be added to the X axis positioning of the toolbar. Default: 0
* __diffTop__: value in pixels to be added to the Y axis positioning of the toolbar. Default: -5
* __diffTop__: value in pixels to be added to the Y axis positioning of the toolbar. Default: -10
* __disableReturn__: enables/disables the use of the return-key. You can also set specific element behavior by using setting a data-disable-return attribute. Default: false
* __disableToolbar__: enables/disables the toolbar, adding only thecontenteditable behavior. You can also set specific element behavior by using setting a data-disable-toolbar attribute. Default: false
* __excludedActions__: list of actions to be excluded from the toolbar. Default: []
* __disableDoubleReturn__: allows/disallows two (or more) empty new lines. You can also set specific element behavior by using setting a data-disable-double-return attribute. Default: false
* __disableToolbar__: enables/disables the toolbar, adding only the contenteditable behavior. You can also set specific element behavior by using setting a data-disable-toolbar attribute. Default: false
* __firstHeader__: HTML tag to be used as first header. Default: h3

@@ -57,2 +62,4 @@ * __forcePlainText__: Forces pasting as plain text. Default: true

* __secondHeader__: HTML tag to be used as second header. Default: h4
* __targetBlank__: enables/disables target="\_blank" for anchor tags. Default:
false

@@ -64,2 +71,3 @@ Example:

anchorInputPlaceholder: 'Type a link',
buttons: ['bold', 'italic', 'quote'],
diffLeft: 25,

@@ -69,7 +77,26 @@ diffTop: 10,

secondHeader: 'h2',
excludedActions: ['q', 's'],
delay: 1000
delay: 1000,
targetBlank: true
});
```
## Extra buttons
Medium Editor, by default, will show only the buttons listed above to avoid a huge toolbar. There are a couple of extra buttons you can use:
* __superscript__
* __subscript__
* __strikethrough__
* __unorderedlist__
* __orderedlist__
* __pre__
* __image__ (this simply converts selected text to an image tag)
* __indent__ (moves the selected text up one level)
* __outdent__ (moves the selected text down one level)
## Themes
Check out the Wiki page for a list of available themes: [https://github.com/daviferreira/medium-editor/wiki/Themes](https://github.com/daviferreira/medium-editor/wiki/Themes)
## API

@@ -79,3 +106,30 @@

* __.activate()__: re-activates the editor
* __.serialize()__: returns a JSON object with elements contents
## Capturing DOM changes
For observing any changes on contentEditable
```js
$('.editable').on('input', function() {
// Do some work
});
```
This is handy when you need to capture modifications other thats outside of `key up`'s scope like clicking on toolbar buttons.
`input` is supported by Chrome, Firefox, IE9 and other modern browsers. If you want to read more or support older browsers, check [Listening to events of a contenteditable HTML element](http://stackoverflow.com/questions/7802784/listening-to-events-of-a-contenteditable-html-element/7804973#7804973) and [Detect changes in the DOM](http://stackoverflow.com/questions/3219758/detect-changes-in-the-dom)
## Image Upload
[Pavel Linkesch](https://github.com/orthes) has developed a jQuery plugin to upload images following Medium.com functionality. Check it out at [http://orthes.github.io/medium-editor-insert-plugin/](http://orthes.github.io/medium-editor-insert-plugin/)
## Angular directive
[Thijs Wijnmaalen](https://github.com/thijsw) hacked together an AngularJS
directive. Check it out at
[https://github.com/thijsw/angular-medium-editor](https://github.com/thijsw/angular-medium-editor)
## Development

@@ -92,8 +146,76 @@

* __js__: runs jslint and jasmine tests and creates minified and concatenated versions of the script;
* __css__: runs compass and csslint
* __css__: runs autoprefixer and csslint
* __test__: runs jasmine tests, jslint and csslint
* __watch__: watch for modifications on script/scss files
The source files are located inside the __src__ directory. MediumEditor stylesheet was created using sass/compass, make sure you have the compass gem installed on your system.
The source files are located inside the __src__ directory.
## Contributing
[![Stories in Ready](https://badge.waffle.io/daviferreira/medium-editor.png)](https://waffle.io/daviferreira/medium-editor)
1. Fork it
2. Create your feature branch (`git checkout -b my-new-feature`)
3. Test your changes to the best of your ability.
4. Update the documentation to reflect your changes if they add or changes current functionality.
5. Commit your changes (`git commit -am 'Added some feature'`)
6. Push to the branch (`git push origin my-new-feature`)
7. Create new Pull Request
## Contributors
```
project : medium-editor
repo age : 10 months
active : 116 days
commits : 446
files : 46
authors :
328 Davi Ferreira 73.5%
20 Maxime de Visscher 4.5%
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%
2 Ethan Turkeltaub 0.4%
2 Jacob Magnusson 0.4%
1 Adam Mulligan 0.2%
1 Alberto Gasparin 0.2%
1 Bitdeli Chef 0.2%
1 David Collien 0.2%
1 David Hellsing 0.2%
1 Denis Gorbachev 0.2%
1 Diana Liao 0.2%
1 Jeff Welch 0.2%
1 Mark Kraemer 0.2%
1 Max 0.2%
1 Maxime Dantec 0.2%
1 Maxime De Visscher 0.2%
1 Michael Kay 0.2%
1 Moore Adam 0.2%
1 Nic Malan 0.2%
1 Noah Paessel 0.2%
1 Pavel Linkesch 0.2%
1 Robert Koritnik 0.2%
1 Søren Torp Petersen 0.2%
1 Tom MacWright 0.2%
1 happyaccidents 0.2%
1 mbrookes 0.2%
1 muescha 0.2%
1 shaohua 0.2%
1 t_kjaergaard 0.2%
1 typify 0.2%
1 waffleio 0.2%
1 zzjin 0.2%
```
## License

@@ -100,0 +222,0 @@

/*global MediumEditor, describe, it, expect, spyOn,
afterEach, beforeEach, selectElementContents,
jasmine, fireEvent*/
jasmine, fireEvent, console, tearDown*/

@@ -10,18 +10,10 @@ describe('Anchor Button TestCase', function () {

jasmine.Clock.useMock();
this.body = document.getElementsByTagName('body')[0];
this.el = document.createElement('div');
this.el.className = 'editor';
this.el.innerHTML = 'lorem ipsum';
this.body.appendChild(this.el);
document.body.appendChild(this.el);
});
afterEach(function () {
var elements = document.querySelectorAll('.medium-editor-toolbar'),
i,
sel = window.getSelection();
for (i = 0; i < elements.length; i += 1) {
this.body.removeChild(elements[i]);
}
this.body.removeChild(this.el);
sel.removeAllRanges();
tearDown(this.el);
});

@@ -90,2 +82,11 @@

});
it('should add target="_blank" when respective option is set to true', function () {
var editor = new MediumEditor('.editor', {targetBlank: true}),
input = editor.anchorForm.querySelector('input');
selectElementContents(editor.elements[0]);
input.value = 'http://test.com';
editor.createLink(input);
expect(editor.elements[0].querySelector('a').target).toBe('_blank');
});
});

@@ -92,0 +93,0 @@

/*global MediumEditor, describe, it, expect, spyOn,
afterEach, beforeEach, selectElementContents,
jasmine, fireEvent*/
jasmine, fireEvent, tearDown*/

@@ -9,18 +9,10 @@ describe('Buttons TestCase', function () {

beforeEach(function () {
this.body = document.getElementsByTagName('body')[0];
this.el = document.createElement('div');
this.el.className = 'editor';
this.el.innerHTML = 'lorem ipsum';
this.body.appendChild(this.el);
document.body.appendChild(this.el);
});
afterEach(function () {
var elements = document.querySelectorAll('.medium-editor-toolbar'),
i,
sel = window.getSelection();
for (i = 0; i < elements.length; i += 1) {
this.body.removeChild(elements[i]);
}
this.body.removeChild(this.el);
sel.removeAllRanges();
tearDown(this.el);
});

@@ -33,16 +25,2 @@

it('should hide buttons if they are in the excludedActions option', function () {
var editor = new MediumEditor('.editor', {excludedActions: ['b', 'i', 'a']});
selectElementContents(editor.elements[0]);
fireEvent(editor.elements[0], 'mouseup');
jasmine.Clock.tick(1);
expect(editor.toolbar.querySelector('[data-element="b"]').style.display).toBe('none');
expect(editor.toolbar.querySelector('[data-element="i"]').style.display).toBe('none');
expect(editor.toolbar.querySelector('[data-element="a"]').style.display).toBe('none');
expect(editor.toolbar.querySelector('[data-element="u"]').style.display).toBe('block');
expect(editor.toolbar.querySelector('[data-element="h3"]').style.display).toBe('block');
expect(editor.toolbar.querySelector('[data-element="h4"]').style.display).toBe('block');
expect(editor.toolbar.querySelector('[data-element="q"]').style.display).toBe('block');
});
it('should activate button if selection already has the element', function () {

@@ -157,4 +135,4 @@ var button,

it('should call the appendEl method when button action is append', function () {
spyOn(MediumEditor.prototype, 'appendEl');
it('should call the execFormatBlock method when button action is append', function () {
spyOn(MediumEditor.prototype, 'execFormatBlock');
var button,

@@ -167,3 +145,3 @@ editor = new MediumEditor('.editor');

fireEvent(button, 'click');
expect(editor.appendEl).toHaveBeenCalled();
expect(editor.execFormatBlock).toHaveBeenCalled();
});

@@ -194,15 +172,2 @@

});
it('should transfer parent element attributes', function () {
this.el.innerHTML = '<p class="test" data-transfer="test"><b>lorem ipsum</b></p>';
var button,
editor = new MediumEditor('.editor');
selectElementContents(editor.elements[0]);
fireEvent(editor.elements[0], 'mouseup');
jasmine.Clock.tick(1);
button = editor.toolbar.querySelector('[data-element="h3"]');
fireEvent(button, 'click');
expect(this.el.innerHTML).toBe('<h3 class="test" data-transfer="test"><b>lorem ipsum</b></h3>');
});
});

@@ -209,0 +174,0 @@

/*global MediumEditor, describe, it, expect, spyOn,
afterEach, beforeEach*/
afterEach, beforeEach, tearDown*/

@@ -9,15 +9,10 @@ describe('Elements TestCase', function () {

beforeEach(function () {
this.body = document.getElementsByTagName('body')[0];
document.body = document.getElementsByTagName('body')[0];
this.el = document.createElement('div');
this.el.className = 'editor';
this.body.appendChild(this.el);
document.body.appendChild(this.el);
});
afterEach(function () {
var elements = document.querySelectorAll('.medium-editor-toolbar'),
i;
for (i = 0; i < elements.length; i += 1) {
this.body.removeChild(elements[i]);
}
this.body.removeChild(this.el);
tearDown(this.el);
});

@@ -24,0 +19,0 @@

@@ -1,2 +0,2 @@

function fireEvent (element, event, keyCode, ctrlKey) {
function fireEvent (element, event, keyCode, ctrlKey, target, relatedTarget) {
if (document.createEvent) {

@@ -12,2 +12,8 @@ // dispatch for firefox + others

}
if (target) {
evt.target = target;
}
if (relatedTarget) {
evt.relatedTarget = relatedTarget;
}
return !element.dispatchEvent(evt);

@@ -28,1 +34,16 @@ } else {

}
function tearDown(el) {
var elements = document.querySelectorAll('.medium-editor-toolbar'),
i,
sel = window.getSelection();
for (i = 0; i < elements.length; i += 1) {
document.body.removeChild(elements[i]);
}
elements = document.querySelectorAll('.medium-editor-anchor-preview');
for (i = 0; i < elements.length; i += 1) {
document.body.removeChild(elements[i]);
}
document.body.removeChild(el);
sel.removeAllRanges();
}
/*global MediumEditor, describe, it, expect, spyOn,
afterEach, beforeEach*/
afterEach, beforeEach, tearDown*/

@@ -54,2 +54,8 @@ describe('Initialization TestCase', function () {

});
it('should allow a single element as parameter', function () {
var element = document.querySelector('span'),
editor = new MediumEditor(element);
expect(editor.elements).toEqual([element]);
});
});

@@ -59,15 +65,9 @@

beforeEach(function () {
this.body = document.getElementsByTagName('body')[0];
this.el = document.createElement('div');
this.el.className = 'editor';
this.body.appendChild(this.el);
document.body.appendChild(this.el);
});
afterEach(function () {
var elements = document.querySelectorAll('.medium-editor-toolbar'),
i;
for (i = 0; i < elements.length; i += 1) {
this.body.removeChild(elements[i]);
}
this.body.removeChild(this.el);
tearDown(this.el);
});

@@ -82,8 +82,13 @@

disableReturn: false,
disableDoubleReturn: false,
disableToolbar: false,
excludedActions: [],
firstHeader: 'h3',
forcePlainText: true,
allowMultiParagraphSelection: true,
placeholder: 'Type your text',
secondHeader: 'h4'
secondHeader: 'h4',
buttons: [ 'bold', 'italic', 'underline', 'anchor', 'header1', 'header2', 'quote' ],
buttonLabels: false,
targetBlank: false,
anchorPreviewHideDelay: 500
},

@@ -96,3 +101,2 @@ editor = new MediumEditor('.editor');

var options = {
excludedActions: ['h1'],
anchorInputPlaceholder: 'test',

@@ -99,0 +103,0 @@ diffLeft: 10,

/*global MediumEditor, describe, it, expect, spyOn,
afterEach, beforeEach, selectElementContents,
jasmine, fireEvent*/
jasmine, fireEvent, tearDown*/

@@ -9,18 +9,10 @@ describe('Placeholder TestCase', function () {

beforeEach(function () {
this.body = document.getElementsByTagName('body')[0];
this.el = document.createElement('div');
this.el.className = 'editor';
this.el.innerHTML = '';
this.body.appendChild(this.el);
document.body.appendChild(this.el);
});
afterEach(function () {
var elements = document.querySelectorAll('.medium-editor-toolbar'),
i,
sel = window.getSelection();
for (i = 0; i < elements.length; i += 1) {
this.body.removeChild(elements[i]);
}
this.body.removeChild(this.el);
sel.removeAllRanges();
tearDown(this.el);
});

@@ -43,3 +35,2 @@

expect(editor.elements[0].className).toContain('medium-editor-placeholder');
expect(editor.elements[0].innerHTML).toBe('');
});

@@ -54,3 +45,3 @@

it('should add a placeholder to empty elements on focusout', function () {
it('should add a placeholder to empty elements on blur', function () {
this.el.innerHTML = 'some text';

@@ -60,11 +51,11 @@ var editor = new MediumEditor('.editor');

editor.elements[0].innerHTML = '';
fireEvent(editor.elements[0], 'focusout');
fireEvent(editor.elements[0], 'blur');
expect(editor.elements[0].className).toContain('medium-editor-placeholder');
});
it('should not add a placeholder to elements with text on focusout', function () {
it('should not add a placeholder to elements with text on blur', function () {
var editor = new MediumEditor('.editor');
expect(editor.elements[0].className).toContain('medium-editor-placeholder');
editor.elements[0].innerHTML = 'some text';
fireEvent(editor.elements[0], 'focusout');
fireEvent(editor.elements[0], 'blur');
expect(editor.elements[0].className).not.toContain('medium-editor-placeholder');

@@ -71,0 +62,0 @@ });

/*global MediumEditor, describe, it, expect, spyOn,
afterEach, beforeEach, fireEvent, waits,
jasmine, selectElementContents*/
jasmine, selectElementContents, tearDown*/

@@ -9,18 +9,10 @@ describe('Selection TestCase', function () {

beforeEach(function () {
this.body = document.getElementsByTagName('body')[0];
this.el = document.createElement('div');
this.el.className = 'editor';
this.el.innerHTML = 'lorem ipsum';
this.body.appendChild(this.el);
document.body.appendChild(this.el);
});
afterEach(function () {
var elements = document.querySelectorAll('.medium-editor-toolbar'),
i,
sel = window.getSelection();
for (i = 0; i < elements.length; i += 1) {
this.body.removeChild(elements[i]);
}
this.body.removeChild(this.el);
sel.removeAllRanges();
tearDown(this.el);
});

@@ -62,4 +54,6 @@

editor.toolbar.style.display = 'block';
editor.toolbar.classList.add('medium-editor-toolbar-active');
expect(editor.toolbar.classList.contains('medium-editor-toolbar-active')).toBe(true);
editor.checkSelection();
expect(editor.toolbar.style.display).toBe('none');
expect(editor.toolbar.classList.contains('medium-editor-toolbar-active')).toBe(false);
expect(editor.setToolbarPosition).not.toHaveBeenCalled();

@@ -72,6 +66,8 @@ expect(editor.setToolbarButtonStates).not.toHaveBeenCalled();

var editor = new MediumEditor('.editor');
expect(editor.toolbar.style.display).toBe('');
jasmine.Clock.useMock();
expect(editor.toolbar.classList.contains('medium-editor-toolbar-active')).toBe(false);
selectElementContents(this.el);
editor.checkSelection();
expect(editor.toolbar.style.display).toBe('block');
jasmine.Clock.tick(501);
expect(editor.toolbar.classList.contains('medium-editor-toolbar-active')).toBe(true);
});

@@ -78,0 +74,0 @@

/*global MediumEditor, describe, it, expect, spyOn,
afterEach, beforeEach, selectElementContents, fireEvent*/
afterEach, beforeEach, selectElementContents, runs,
waitsFor, tearDown */

@@ -8,15 +9,9 @@ describe('Toolbar TestCase', function () {

beforeEach(function () {
this.body = document.getElementsByTagName('body')[0];
this.el = document.createElement('div');
this.el.className = 'editor';
this.body.appendChild(this.el);
document.body.appendChild(this.el);
});
afterEach(function () {
var elements = document.querySelectorAll('.medium-editor-toolbar'),
i;
for (i = 0; i < elements.length; i += 1) {
this.body.removeChild(elements[i]);
}
this.body.removeChild(this.el);
tearDown(this.el);
});

@@ -76,3 +71,3 @@

element.innerHTML = 'lorem ipsum';
this.body.appendChild(element);
document.body.appendChild(element);

@@ -87,6 +82,42 @@ editor = new MediumEditor(document.querySelectorAll('.editor'));

// Remove the new element from the DOM
this.body.removeChild(element);
document.body.removeChild(element);
});
it('should show the toolbar it it\'s text are selected even though one or more elements that has a data attr of disable-toolbar', function () {
var value,
flag,
element = document.createElement('div'),
editor = null;
runs(function() {
flag = false;
element.className = 'editor';
element.setAttribute('data-disable-toolbar', 'true');
this.el.innerHTML = 'lorem ipsum';
document.body.appendChild(element);
editor = new MediumEditor(document.querySelectorAll('.editor'));
expect(editor.elements.length).toEqual(2);
expect(editor.toolbar.style.display).toBe('');
selectElementContents(this.el);
editor.checkSelection();
setTimeout(function() {
flag = true;
}, 500);
});
// Because the toolbar appear after 100ms, waits 150ms...
waitsFor(function() {
value = value + 1; // value += 1 is not accepted by jslint (unused)
return flag;
}, 'The i value should be incremented', 500);
runs(function() {
expect(editor.toolbar.classList.contains('medium-editor-toolbar-active')).toBe(true);
// Remove the new element from the DOM
document.body.removeChild(element);
});
});
it('should not try to toggle toolbar when option disabletoolbar is set to true', function () {
var element = document.createElement('div'),

@@ -96,18 +127,16 @@ editor = null;

element.className = 'editor';
element.setAttribute('data-disable-toolbar', 'true');
this.el.innerHTML = 'lorem ipsum';
this.body.appendChild(element);
document.body.appendChild(element);
editor = new MediumEditor(document.querySelectorAll('.editor'));
editor = new MediumEditor(document.querySelectorAll('.editor'), { disableToolbar: true });
expect(editor.elements.length).toEqual(2);
expect(editor.toolbar.style.display).toBe('');
expect(editor.toolbar).toBe(undefined);
selectElementContents(this.el);
editor.checkSelection();
expect(editor.toolbar.style.display).toBe('block');
// Remove the new element from the DOM
this.body.removeChild(element);
document.body.removeChild(element);
});
});
});

@@ -1,2 +0,2 @@

/*global console*/
/*global module*/

@@ -7,2 +7,7 @@ function MediumEditor(elements, options) {

}
if (typeof module === 'object') {
module.exports = MediumEditor;
}
(function (window, document) {

@@ -53,12 +58,2 @@ 'use strict';

// http://stackoverflow.com/questions/6139107/programatically-select-text-in-a-contenteditable-html-element
// by Tim Down & yckart
function selectElementContents(el) {
var selection = window.getSelection(),
range = document.createRange();
range.selectNodeContents(el);
selection.removeAllRanges();
selection.addRange(range);
}
// http://stackoverflow.com/questions/1197401/how-can-i-get-the-element-the-caret-is-in-with-javascript-when-using-contentedi

@@ -72,5 +67,33 @@ // by You

// http://stackoverflow.com/questions/4176923/html-of-selected-text
// by Tim Down
function getSelectionHtml() {
var i,
html = '',
sel,
len,
container;
if (window.getSelection !== undefined) {
sel = window.getSelection();
if (sel.rangeCount) {
container = document.createElement('div');
for (i = 0, len = sel.rangeCount; i < len; i += 1) {
container.appendChild(sel.getRangeAt(i).cloneContents());
}
html = container.innerHTML;
}
} else if (document.selection !== undefined) {
if (document.selection.type === 'Text') {
html = document.selection.createRange().htmlText;
}
}
return html;
}
MediumEditor.prototype = {
defaults: {
allowMultiParagraphSelection: true,
anchorInputPlaceholder: 'Paste or type a link',
buttons: ['bold', 'italic', 'underline', 'anchor', 'header1', 'header2', 'quote'],
buttonLabels: false,
delay: 0,

@@ -80,12 +103,21 @@ diffLeft: 0,

disableReturn: false,
disableDoubleReturn: false,
disableToolbar: false,
excludedActions: [],
firstHeader: 'h3',
forcePlainText: true,
placeholder: 'Type your text',
secondHeader: 'h4'
secondHeader: 'h4',
targetBlank: false,
anchorPreviewHideDelay: 500
},
// http://stackoverflow.com/questions/17907445/how-to-detect-ie11#comment30165888_17907562
// by rg89
isIE: ((navigator.appName === 'Microsoft Internet Explorer') || ((navigator.appName === 'Netscape') && (new RegExp('Trident/.*rv:([0-9]{1,}[.0-9]{0,})').exec(navigator.userAgent) !== null))),
init: function (elements, options) {
this.elements = typeof elements === 'string' ? document.querySelectorAll(elements) : elements;
if (this.elements.nodeType === 1) {
this.elements = [this.elements];
}
if (this.elements.length === 0) {

@@ -95,6 +127,7 @@ return;

this.isActive = true;
this.parentElements = ['p', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'q'];
this.parentElements = ['p', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'blockquote', 'pre'];
this.id = document.querySelectorAll('.medium-editor-toolbar').length + 1;
this.options = extend(options, this.defaults);
return this.initElements()
.bindSelect()
.bindPaste()

@@ -106,3 +139,4 @@ .setPlaceholders()

initElements: function () {
var i;
var i,
addToolbar = false;
for (i = 0; i < this.elements.length; i += 1) {

@@ -113,52 +147,209 @@ this.elements[i].setAttribute('contentEditable', true);

}
this.elements[i].setAttribute('data-medium-element', 'true');
this.bindParagraphCreation(this.elements[i]);
this.elements[i].setAttribute('data-medium-element', true);
this.bindParagraphCreation(i).bindReturn(i).bindTab(i).bindAnchorPreview(i);
if (!this.options.disableToolbar && !this.elements[i].getAttribute('data-disable-toolbar')) {
this.initToolbar()
.bindSelect()
.bindButtons()
.bindAnchorForm();
addToolbar = true;
}
}
// Init toolbar
if (addToolbar) {
this.initToolbar()
.bindButtons()
.bindAnchorForm();
}
return this;
},
bindParagraphCreation: function (el) {
serialize: function () {
var i,
elementid,
content = {};
for (i = 0; i < this.elements.length; i += 1) {
elementid = (this.elements[i].id !== '') ? this.elements[i].id : 'element-' + i;
content[elementid] = {
value: this.elements[i].innerHTML.trim()
};
}
return content;
},
bindParagraphCreation: function (index) {
var self = this;
el.addEventListener('keypress', function (e) {
var node = getSelectionStart();
if (node) {
node = node.tagName.toLowerCase();
this.elements[index].addEventListener('keyup', function (e) {
var node = getSelectionStart(),
tagName;
if (node && node.getAttribute('data-medium-element') && node.children.length === 0 &&
!(self.options.disableReturn || node.getAttribute('data-disable-return'))) {
document.execCommand('formatBlock', false, 'p');
}
if (e.which === 13 && !e.shiftKey) {
if (node !== 'q' && !self.options.disableReturn && !el.getAttribute('data-disable-return')) {
document.execCommand('formatBlock', false, 'p');
} else {
if (e.which === 13) {
node = getSelectionStart();
tagName = node.tagName.toLowerCase();
if (!(self.options.disableReturn || this.getAttribute('data-disable-return')) &&
tagName !== 'li' && !self.isListItemChild(node)) {
if (!e.shiftKey) {
document.execCommand('formatBlock', false, 'p');
}
if (tagName === 'a') {
document.execCommand('unlink', false, null);
}
}
}
});
return this;
},
isListItemChild: function (node) {
var parentNode = node.parentNode,
tagName = parentNode.tagName.toLowerCase();
while (this.parentElements.indexOf(tagName) === -1 && tagName !== 'div') {
if (tagName === 'li') {
return true;
}
parentNode = parentNode.parentNode;
if (parentNode && parentNode.tagName) {
tagName = parentNode.tagName.toLowerCase();
} else {
return false;
}
}
return false;
},
bindReturn: function (index) {
var self = this;
this.elements[index].addEventListener('keypress', function (e) {
if (e.which === 13) {
if (self.options.disableReturn || this.getAttribute('data-disable-return')) {
e.preventDefault();
} else if (self.options.disableDoubleReturn || this.getAttribute('data-disable-double-return')) {
var node = getSelectionStart();
if (node && node.innerText === '\n') {
e.preventDefault();
}
}
}
});
return this;
},
bindTab: function (index) {
this.elements[index].addEventListener('keydown', function (e) {
if (e.which === 9) {
// Override tab only for pre nodes
var tag = getSelectionStart().tagName.toLowerCase();
if (tag === 'pre') {
e.preventDefault();
document.execCommand('insertHtml', null, ' ');
}
}
});
return this;
},
buttonTemplate: function (btnType) {
var buttonLabels = this.getButtonLabels(this.options.buttonLabels),
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>'
};
return buttonTemplates[btnType] || false;
},
// TODO: break method
getButtonLabels: function (buttonLabelType) {
var customButtonLabels,
attrname,
buttonLabels = {
'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>&ldquo;</b>',
'orderedlist': '<b>1.</b>',
'unorderedlist': '<b>&bull;</b>',
'pre': '<b>0101</b>',
'indent': '<b>&rarr;</b>',
'outdent': '<b>&larr;</b>'
};
if (buttonLabelType === 'fontawesome') {
customButtonLabels = {
'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>'
};
} else if (typeof buttonLabelType === 'object') {
customButtonLabels = buttonLabelType;
}
if (typeof customButtonLabels === 'object') {
for (attrname in customButtonLabels) {
if (customButtonLabels.hasOwnProperty(attrname)) {
buttonLabels[attrname] = customButtonLabels[attrname];
}
}
}
return buttonLabels;
},
//TODO: actionTemplate
toolbarTemplate: function () {
return '<ul id="medium-editor-toolbar-actions" class="medium-editor-toolbar-actions clearfix">' +
' <li><button class="medium-editor-action medium-editor-action-bold" data-action="bold" data-element="b">B</button></li>' +
' <li><button class="medium-editor-action medium-editor-action-italic" data-action="italic" data-element="i">I</button></li>' +
' <li><button class="medium-editor-action medium-editor-action-underline" data-action="underline" data-element="u">S</button></li>' +
' <li><button class="medium-editor-action medium-editor-action-anchor" data-action="anchor" data-element="a">#</button></li>' +
' <li><button class="medium-editor-action medium-editor-action-header1" data-action="append-' + this.options.firstHeader + '" data-element="' + this.options.firstHeader + '">h1</button></li>' +
' <li><button class="medium-editor-action medium-editor-action-header2" data-action="append-' + this.options.secondHeader + '" data-element="' + this.options.secondHeader + '">h2</button></li>' +
' <li><button class="medium-editor-action medium-editor-action-quote" data-action="append-q" data-element="q">&ldquo;</button></li>' +
'</ul>' +
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="#">&times;</a>' +
' <input type="text" value="" placeholder="' + this.options.anchorInputPlaceholder + '">' +
' <a href="#">&times;</a>' +
'</div>';
return html;
},
initToolbar: function () {
if (this.toolbar) {
return this;
}
this.toolbar = this.createToolbar();
this.keepToolbarAlive = false;
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();
return this;

@@ -172,3 +363,3 @@ },

toolbar.innerHTML = this.toolbarTemplate();
document.getElementsByTagName('body')[0].appendChild(toolbar);
document.body.appendChild(toolbar);
return toolbar;

@@ -181,11 +372,21 @@ },

i;
this.checkSelectionWrapper = function (e) {
// Do not close the toolbar when bluring the editable area and clicking into the anchor form
if (e && self.clickingIntoArchorForm(e)) {
return false;
}
clearTimeout(timer);
setTimeout(function () {
self.checkSelection(e);
timer = setTimeout(function () {
self.checkSelection();
}, self.options.delay);
};
document.documentElement.addEventListener('mouseup', this.checkSelectionWrapper);
for (i = 0; i < this.elements.length; i += 1) {
this.elements[i].addEventListener('mouseup', this.checkSelectionWrapper);
this.elements[i].addEventListener('keyup', this.checkSelectionWrapper);
this.elements[i].addEventListener('blur', this.checkSelectionWrapper);
}

@@ -196,15 +397,16 @@ return this;

checkSelection: function () {
var newSelection;
if (this.keepToolbarAlive !== true) {
var newSelection,
selectionElement;
if (this.keepToolbarAlive !== true && !this.options.disableToolbar) {
newSelection = window.getSelection();
if (newSelection.toString().trim() === '') {
this.toolbar.style.display = 'none';
if (newSelection.toString().trim() === '' ||
(this.options.allowMultiParagraphSelection === false && this.hasMultiParagraphs())) {
this.hideToolbarActions();
} else {
this.selection = newSelection;
this.selectionRange = this.selection.getRangeAt(0);
if (!this.getSelectionElement().getAttribute('data-disable-toolbar')) {
this.toolbar.style.display = 'block';
this.setToolbarButtonStates()
.setToolbarPosition()
.showToolbarActions();
selectionElement = this.getSelectionElement();
if (!selectionElement || selectionElement.getAttribute('data-disable-toolbar')) {
this.hideToolbarActions();
} else {
this.checkSelectionElement(newSelection, selectionElement);
}

@@ -216,14 +418,61 @@ }

clickingIntoArchorForm: function(e) {
var self = this;
if (e.type && e.type.toLowerCase() === 'blur' && e.relatedTarget && e.relatedTarget === self.anchorInput) {
return true;
}
return false;
},
hasMultiParagraphs: function () {
var selectionHtml = getSelectionHtml().replace(/<[\S]+><\/[\S]+>/gim, ''),
hasMultiParagraphs = selectionHtml.match(/<(p|h[0-6]|blockquote)>([\s\S]*?)<\/(p|h[0-6]|blockquote)>/g);
return (hasMultiParagraphs ? hasMultiParagraphs.length : 0);
},
checkSelectionElement: function (newSelection, selectionElement) {
var i;
this.selection = newSelection;
this.selectionRange = this.selection.getRangeAt(0);
for (i = 0; i < this.elements.length; i += 1) {
if (this.elements[i] === selectionElement) {
this.setToolbarButtonStates()
.setToolbarPosition()
.showToolbarActions();
return;
}
}
this.hideToolbarActions();
},
getSelectionElement: function () {
var selection = window.getSelection(),
range = selection.getRangeAt(0),
parent = range.commonAncestorContainer.parentNode;
current = range.commonAncestorContainer,
parent = current.parentNode,
result,
getMediumElement = function(e) {
var parent = e;
try {
while (!parent.getAttribute('data-medium-element')) {
parent = parent.parentNode;
}
} catch (errb) {
return false;
}
return parent;
};
// First try on current node
try {
while (!parent.getAttribute('data-medium-element')) {
parent = parent.parentNode;
if (current.getAttribute('data-medium-element')) {
result = current;
} else {
result = getMediumElement(parent);
}
// If not search in the parent nodes.
} catch (err) {
return this.elements[0];
result = getMediumElement(parent);
}
return parent;
return result;
},

@@ -255,2 +504,5 @@

}
this.hideAnchorPreview();
return this;

@@ -264,3 +516,2 @@ },

buttons[i].classList.remove('medium-editor-button-active');
this.showHideButton(buttons[i]);
}

@@ -271,10 +522,2 @@ this.checkActiveButtons();

showHideButton: function (button) {
if (this.options.excludedActions.indexOf(button.getAttribute('data-element')) > -1) {
button.style.display = 'none';
} else {
button.style.display = 'block';
}
},
checkActiveButtons: function () {

@@ -285,3 +528,3 @@ var parentNode = this.selection.anchorNode;

}
while (parentNode.tagName !== undefined && this.parentElements.indexOf(parentNode.tagName) === -1) {
while (parentNode.tagName !== undefined && this.parentElements.indexOf(parentNode.tagName.toLowerCase) === -1) {
this.activateButton(parentNode.tagName.toLowerCase());

@@ -307,3 +550,3 @@ parentNode = parentNode.parentNode;

if (self.selection === undefined) {
self.checkSelection(e);
self.checkSelection();
}

@@ -332,8 +575,11 @@ if (this.className.indexOf('medium-editor-button-active') > -1) {

if (action.indexOf('append-') > -1) {
this.appendEl(action.replace('append-', ''));
this.execFormatBlock(action.replace('append-', ''));
this.setToolbarPosition();
this.setToolbarButtonStates();
} else if (action === 'anchor') {
this.triggerAnchorAction(e);
} else if (action === 'image') {
document.execCommand('insertImage', false, window.getSelection());
} else {
document.execCommand(action, null, false);
document.execCommand(action, false, null);
this.setToolbarPosition();

@@ -345,3 +591,3 @@ }

if (this.selection.anchorNode.parentNode.tagName.toLowerCase() === 'a') {
document.execCommand('unlink', null, false);
document.execCommand('unlink', false, null);
} else {

@@ -357,19 +603,25 @@ if (this.anchorForm.style.display === 'block') {

appendEl: function (el) {
execFormatBlock: function (el) {
var selectionData = this.getSelectionData(this.selection.anchorNode);
// FF handles blockquote differently on formatBlock
// allowing nesting, we need to use outdent
// https://developer.mozilla.org/en-US/docs/Rich-Text_Editing_in_Mozilla
if (el === 'blockquote' && selectionData.el &&
selectionData.el.parentNode.tagName.toLowerCase() === 'blockquote') {
return document.execCommand('outdent', false, null);
}
if (selectionData.tagName === el) {
el = 'p';
}
el = document.createElement(el);
if (selectionData.el) {
this.transferAttributes(selectionData.el, el);
el.innerHTML = selectionData.el.innerHTML;
selectionData.el.parentNode.replaceChild(el, selectionData.el);
} else {
el.innerHTML = this.selection.anchorNode.textContent;
this.selection.anchorNode.parentNode.replaceChild(el, this.selection.anchorNode);
// When IE we need to add <> to heading elements and
// blockquote needs to be called as indent
// http://stackoverflow.com/questions/10741831/execcommand-formatblock-headings-in-ie
// http://stackoverflow.com/questions/1816223/rich-text-editor-with-blockquote-function/1821777#1821777
if (this.isIE) {
if (el === 'blockquote') {
return document.execCommand('indent', false, el);
}
el = '<' + el + '>';
}
selectElementContents(el);
this.bindElementToolbarEvents(el);
this.setToolbarPosition();
return document.execCommand('formatBlock', false, el);
},

@@ -397,8 +649,2 @@

transferAttributes: function (elFrom, elTo) {
Array.prototype.slice.call(elFrom.attributes).forEach(function (item) {
elTo.setAttribute(item.name, item.value);
});
},
getFirstChild: function (el) {

@@ -412,10 +658,5 @@ var firstChild = el.firstChild;

bindElementToolbarEvents: function (el) {
var self = this;
el.addEventListener('mouseup', function (e) {
self.checkSelection(e);
});
el.addEventListener('keyup', function (e) {
self.checkSelection(e);
});
hideToolbarActions: function () {
this.keepToolbarAlive = false;
this.toolbar.classList.remove('medium-editor-toolbar-active');
},

@@ -430,13 +671,10 @@

clearTimeout(timer);
timer = setTimeout(function () {
document.addEventListener('click', function () {
self.keepToolbarAlive = false;
self.toolbar.style.display = 'none';
document.removeEventListener('click', this);
});
}, 300);
timer = setTimeout(function() {
if (!self.toolbar.classList.contains('medium-editor-toolbar-active')) {
self.toolbar.classList.add('medium-editor-toolbar-active');
}
}, 100);
},
showAnchorForm: function () {
var input = this.anchorForm.querySelector('input');
showAnchorForm: function (link_value) {
this.toolbarActions.style.display = 'none';

@@ -446,9 +684,8 @@ this.savedSelection = saveSelection();

this.keepToolbarAlive = true;
input.focus();
input.value = '';
this.anchorInput.focus();
this.anchorInput.value = link_value || '';
},
bindAnchorForm: function () {
var input = this.anchorForm.querySelector('input'),
linkCancel = this.anchorForm.querySelector('a'),
var linkCancel = this.anchorForm.querySelector('a'),
self = this;

@@ -458,3 +695,3 @@ this.anchorForm.addEventListener('click', function (e) {

});
input.addEventListener('keyup', function (e) {
this.anchorInput.addEventListener('keyup', function (e) {
if (e.keyCode === 13) {

@@ -465,2 +702,11 @@ e.preventDefault();

});
this.anchorInput.addEventListener('click', function (e) {
// make sure not to hide form when cliking into the input
e.stopPropagation();
self.keepToolbarAlive = true;
});
this.anchorInput.addEventListener('blur', function () {
self.keepToolbarAlive = false;
self.checkSelection();
});
linkCancel.addEventListener('click', function (e) {

@@ -474,5 +720,193 @@ e.preventDefault();

hideAnchorPreview: function() {
this.anchorPreview.classList.remove('medium-editor-anchor-preview-active');
},
// TODO: break method
showAnchorPreview: function (anchor_el) {
if (this.anchorPreview.classList.contains('medium-editor-anchor-preview-active')) {
return true;
}
var self = this,
buttonHeight = 40,
boundary = anchor_el.getBoundingClientRect(),
middleBoundary = (boundary.left + boundary.right) / 2,
halfOffsetWidth,
defaultLeft,
timer;
self.anchorPreview.querySelector('i').innerHTML = anchor_el.href;
halfOffsetWidth = self.anchorPreview.offsetWidth / 2;
defaultLeft = self.options.diffLeft - halfOffsetWidth;
clearTimeout(timer);
timer = setTimeout(function() {
if (!self.anchorPreview.classList.contains('medium-editor-anchor-preview-active')) {
self.anchorPreview.classList.add('medium-editor-anchor-preview-active');
}
}, 100);
self.observeAnchorPreview(anchor_el);
self.anchorPreview.classList.add('medium-toolbar-arrow-over');
self.anchorPreview.classList.remove('medium-toolbar-arrow-under');
self.anchorPreview.style.top = Math.round(buttonHeight + boundary.bottom - self.options.diffTop + window.pageYOffset - self.anchorPreview.offsetHeight) + 'px';
if (middleBoundary < halfOffsetWidth) {
self.anchorPreview.style.left = defaultLeft + halfOffsetWidth + 'px';
} else if ((window.innerWidth - middleBoundary) < halfOffsetWidth) {
self.anchorPreview.style.left = window.innerWidth + defaultLeft - halfOffsetWidth + 'px';
} else {
self.anchorPreview.style.left = defaultLeft + middleBoundary + 'px';
}
return this;
},
// TODO: break method
observeAnchorPreview: function(anchorEl) {
var self = this,
lastOver = (new Date()).getTime(),
over = true,
stamp = function() {
lastOver = (new Date()).getTime();
over = true;
},
unstamp = function(e) {
if (!e.relatedTarget || !/anchor-preview/.test(e.relatedTarget.className)) {
over = false;
}
},
interval_timer = setInterval(function() {
if (over) {
return true;
}
var durr = (new Date()).getTime() - lastOver;
if (durr > self.options.anchorPreviewHideDelay) {
// hide the preview 1/2 second after mouse leaves the link
self.hideAnchorPreview();
// cleanup
clearInterval(interval_timer);
self.anchorPreview.removeEventListener('mouseover', stamp);
self.anchorPreview.removeEventListener('mouseout', unstamp);
anchorEl.removeEventListener('mouseover', stamp);
anchorEl.removeEventListener('mouseout', unstamp);
}
}, 200);
self.anchorPreview.addEventListener('mouseover', stamp);
self.anchorPreview.addEventListener('mouseout', unstamp);
anchorEl.addEventListener('mouseover', stamp);
anchorEl.addEventListener('mouseout', unstamp);
},
createAnchorPreview: function () {
var self = this,
anchorPreview = document.createElement('div');
anchorPreview.id = 'medium-editor-anchor-preview-' + this.id;
anchorPreview.className = 'medium-editor-anchor-preview';
anchorPreview.innerHTML = this.anchorPreviewTemplate();
document.body.appendChild(anchorPreview);
anchorPreview.addEventListener('click', function() {
self.anchorPreviewClickHandler();
});
return anchorPreview;
},
anchorPreviewTemplate: function () {
return '<div class="medium-editor-toolbar-anchor-preview" id="medium-editor-toolbar-anchor-preview">' +
' <i class="medium-editor-toolbar-anchor-preview-inner">http://google.com/</i>' +
'</div>';
},
anchorPreviewClickHandler: function(e) {
if (this.activeAnchor) {
var self = this,
range = document.createRange(),
sel = window.getSelection();
range.selectNodeContents(self.activeAnchor);
sel.removeAllRanges();
sel.addRange(range);
setTimeout(function() {
self.showAnchorForm(self.activeAnchor.href);
self.keepToolbarAlive = false;
}, 100 + self.options.delay);
}
this.hideAnchorPreview();
},
editorAnchorObserver: function(e) {
var self = this,
overAnchor = true,
leaveAnchor = function() {
// mark the anchor as no longer hovered, and stop listening
overAnchor = false;
self.activeAnchor.removeEventListener('mouseout', leaveAnchor);
};
if (e.target && e.target.tagName.toLowerCase() === 'a') {
// Detect empty href attributes
// The browser will make href="" or href="#top"
// into absolute urls when accessed as e.targed.href, so check the html
if (!/href=["']\S+["']/.test(e.target.outerHTML) || /href=["']#\S+["']/.test(e.target.outerHTML)) {
return true;
}
// only show when hovering on anchors
if (this.toolbar.classList.contains('medium-editor-toolbar-active')) {
// only show when toolbar is not present
return true;
}
this.activeAnchor = e.target;
this.activeAnchor.addEventListener('mouseout', leaveAnchor);
// show the anchor preview according to the configured delay
// if the mouse has not left the anchor tag in that time
setTimeout(function() {
if (overAnchor) {
self.showAnchorPreview(e.target);
}
}, self.options.delay);
}
},
bindAnchorPreview: function (index) {
var self = this;
this.elements[index].addEventListener('mouseover', function(e) {
self.editorAnchorObserver(e);
});
return this;
},
setTargetBlank: function () {
var el = getSelectionStart(),
i;
if (el.tagName.toLowerCase() === 'a') {
el.target = '_blank';
} else {
el = el.getElementsByTagName('a');
for (i = 0; i < el.length; i += 1) {
el[i].target = '_blank';
}
}
},
createLink: function (input) {
restoreSelection(this.savedSelection);
document.execCommand('CreateLink', false, input.value);
document.execCommand('createLink', false, input.value);
if (this.options.targetBlank) {
this.setTargetBlank();
}
this.showToolbarActions();

@@ -485,8 +919,11 @@ input.value = '';

self = this;
window.addEventListener('resize', function () {
this.windowResizeHandler = function () {
clearTimeout(timerResize);
timerResize = setTimeout(function () {
self.setToolbarPosition();
if (self.toolbar && self.toolbar.classList.contains('medium-editor-toolbar-active')) {
self.setToolbarPosition();
}
}, 100);
});
};
window.addEventListener('resize', this.windowResizeHandler);
return this;

@@ -500,2 +937,7 @@ },

}
if (this.toolbar !== undefined) {
this.toolbar.style.display = 'block';
}
this.isActive = true;

@@ -505,5 +947,8 @@ for (i = 0; i < this.elements.length; i += 1) {

}
this.bindSelect();
this.bindWindowActions()
.bindSelect();
},
// TODO: break method
deactivate: function () {

@@ -515,24 +960,49 @@ var i;

this.isActive = false;
this.toolbar.style.display = 'none';
if (this.toolbar !== undefined) {
document.body.removeChild(this.anchorPreview);
document.body.removeChild(this.toolbar);
}
document.documentElement.removeEventListener('mouseup', this.checkSelectionWrapper);
window.removeEventListener('resize', this.windowResizeHandler);
for (i = 0; i < this.elements.length; i += 1) {
this.elements[i].removeEventListener('mouseup', this.checkSelectionWrapper);
this.elements[i].removeEventListener('keyup', this.checkSelectionWrapper);
this.elements[i].removeEventListener('blur', this.checkSelectionWrapper);
this.elements[i].removeEventListener('paste', this.pasteWrapper);
this.elements[i].removeAttribute('contentEditable');
}
},
bindPaste: function () {
if (!this.options.forcePlainText) {
return;
}
var i,
pasteWrapper = function (e) {
e.target.classList.remove('medium-editor-placeholder');
if (e.clipboardData && e.clipboardData.getData) {
e.preventDefault();
document.execCommand('insertHTML', false, e.clipboardData.getData('text/plain').replace(/[\r\n]/g, '<br>'));
var i, self = this;
this.pasteWrapper = function (e) {
var paragraphs,
html = '',
p;
this.classList.remove('medium-editor-placeholder');
if (!self.options.forcePlainText) {
return this;
}
if (e.clipboardData && e.clipboardData.getData && !e.defaultPrevented) {
e.preventDefault();
if (!self.options.disableReturn) {
paragraphs = e.clipboardData.getData('text/plain').split(/[\r\n]/g);
for (p = 0; p < paragraphs.length; p += 1) {
if (paragraphs[p] !== '') {
html += '<p>' + paragraphs[p] + '</p>';
}
}
document.execCommand('insertHTML', false, html);
} else {
document.execCommand('insertHTML', false, e.clipboardData.getData('text/plain'));
}
};
}
};
for (i = 0; i < this.elements.length; i += 1) {
this.elements[i].addEventListener('paste', pasteWrapper);
this.elements[i].addEventListener('paste', this.pasteWrapper);
}

@@ -546,3 +1016,2 @@ return this;

if (el.textContent.replace(/^\s+|\s+$/g, '') === '') {
el.innerHTML = '';
el.classList.add('medium-editor-placeholder');

@@ -559,3 +1028,3 @@ }

activatePlaceholder(this.elements[i]);
this.elements[i].addEventListener('focusout', placeholderWrapper);
this.elements[i].addEventListener('blur', placeholderWrapper);
this.elements[i].addEventListener('keypress', placeholderWrapper);

@@ -565,3 +1034,5 @@ }

}
};
}(window, document));

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc