Security News
ESLint is Now Language-Agnostic: Linting JSON, Markdown, and Beyond
ESLint has added JSON and Markdown linting support with new officially-supported plugins, expanding its versatility beyond JavaScript.
medium-editor
Advanced tools
This is a clone of medium.com inline editor toolbar.
MediumEditor has been written using vanilla JavaScript, no additional frameworks required.
IE 9+ ✔ | Latest ✔ | Latest ✔ | Latest ✔ | Latest ✔ |
demo: http://daviferreira.github.io/medium-editor/
Via bower:
Run in your console: bower install medium-editor
Manual installation:
Download the latest release and attach medium editor's stylesheets to your page:
<link rel="stylesheet" href="css/medium-editor.css"> <!-- Core -->
<link rel="stylesheet" href="css/themes/default.css"> <!-- or any other theme -->
The next step is to reference the editor's script
<script src="js/medium-editor.js"></script>
You can now instantiate a new MediumEditor object:
<script>var editor = new MediumEditor('.editable');</script>
The above code will transform all the elements with the .editable class into HTML5 editable contents and add the medium editor toolbar to them.
You can also pass a list of HTML elements:
var elements = document.querySelectorAll('.editable'),
editor = new MediumEditor(elements);
If you want to support IE9, you will need to use a classList pollyfill, like Eli Grey's, available at https://github.com/eligrey/classList.js.
{'bold': '<b>b</b>', 'italic': '<i>i</i>'}
. Default: falseleft
|center
|right
- Aligns the toolbar relative to the medium-editor element.Example:
var editor = new MediumEditor('.editable', {
anchorInputPlaceholder: 'Type a link',
buttons: ['bold', 'italic', 'quote'],
diffLeft: 25,
diffTop: 10,
firstHeader: 'h1',
secondHeader: 'h2',
delay: 1000,
targetBlank: true
});
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:
Check out the Wiki page for a list of available themes: https://github.com/daviferreira/medium-editor/wiki/Themes
For observing any changes on contentEditable
$('.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 and Detect changes in the DOM
Patrick Stillhar developed a new and improved way to add buttons to our toolbar. Check it out at: https://stillhart.biz/project/MediumButton/
To add additional functions that are not supported by the native browser API you can write extensions that are then integrated into the toolbar. The Extension API is currently unstable and very minimal.
An extension is an object that has essentially three functions getButton
, getForm
and checkState
.
getButton
is called when the editor is initialized and should return an element that is integrated into the toolbar.
Usually this will be a <button>
element like the ones Medium Editor uses. All event handling on this button is
entirely up to you so you should either keep a reference or bind your eventhandlers before returning it. You can
also return a HTML-String that is then integrated into the toolbar also this is not really useful.
getForm
is called when the editor is initialized and should return an element that is integrated into the toolbar.
Usually this will be a <div>
element that would contain some input
elements. Handling the saving and canceling of the form is entirely up to you so you should either keep a reference or bind your eventhandlers before returning it.
checkState
is called whenever a user selects some text in the area where the Medium Editor instance is running. It's
responsability is to toggle the current state of the button. I.e. marking is a on or off. Again the method on how
determine the state is entirely up to you. checkState
will be called multiple times and will receive a DOM Element
as parameter.
Properties
parent
add this property to your extension class constructor and set it to true
to be able to access the Medium Editor instance through the base
property that will be set during the initialization
hasForm
add this property to your extension class constructor and set it to true
to tell Medium Editor that your extension contains a form. Medium editor will handle opening the form for you on your extension's button click.
A simple example the uses rangy and the CSS Class Applier Module to support highlighting of text:
rangy.init();
function Highlighter() {
this.button = document.createElement('button');
this.button.className = 'medium-editor-action';
this.button.textContent = 'H';
this.button.onclick = this.onClick.bind(this);
this.classApplier = rangy.createCssClassApplier("highlight", {
elementTagName: 'mark',
normalize: true
});
}
Highlighter.prototype.onClick = function() {
this.classApplier.toggleSelection();
};
Highlighter.prototype.getButton = function() {
return this.button;
};
Highlighter.prototype.checkState = function (node) {
if(node.tagName == 'MARK') {
this.button.classList.add('medium-editor-button-active');
}
};
var e = new MediumEditor('.editor', {
buttons: ['highlight', 'bold', 'italic', 'underline'],
extensions: {
'highlight': new Highlighter()
}
});
A simple example that uses the parent
attribute:
function Extension() {
this.parent = true;
this.button = document.createElement('button');
this.button.className = 'medium-editor-action';
this.button.textContent = 'X';
this.button.onclick = this.onClick.bind(this);
}
Extension.prototype.getButton = function() {
return this.button;
};
Extension.prototype.onClick = function() {
alert('This is editor: #' + this.base.id);
};
var one = new MediumEditor('.one', {
buttons: ['extension'],
extensions: {
extension: new Extension()
}
});
var two = new MediumEditor('.two', {
buttons: ['extension'],
extensions: {
extension: new Extension()
}
});
A simple table example that uses the hasForm
attribute:
function Table(options) {
this.parent = true;
this.hasForm = true;
this.options = options;
}
Table.prototype.init = function() {
this.createButton();
this.createForm();
};
Table.prototype.createButton = function() {
this.button = document.createElement('button');
this.button.className = 'medium-editor-action';
this.button.textContent = 'T';
if(this.base.options.buttonLabels === 'fontawesome'){
this.button.innerHTML = '<i class="fa fa-table"></i>';
}
this.button.onclick = this.onClick.bind(this);
};
Table.prototype.createForm = function() {
this.form = document.createElement('div'),
this.close = document.createElement('a'),
this.save = document.createElement('a'),
this.columnInput = document.createElement('input'),
this.rowInput = document.createElement('input');
this.close.setAttribute('href', '#');
this.close.innerHTML = '×';
this.close.onclick = this.onClose.bind(this);
this.save.setAttribute('href', '#');
this.save.innerHTML = '✓';
this.save.onclick = this.onSave.bind(this);
this.columnInput.setAttribute('type', 'text');
// Add the input CSS class for correct styling
this.columnInput.className = 'medium-editor-toolbar-input';
this.columnInput.setAttribute('placeholder', 'Column Count');
this.rowInput.setAttribute('type', 'text');
// Add the input CSS class for correct styling
this.rowInput.className = 'medium-editor-toolbar-input';
this.rowInput.setAttribute('placeholder', 'Row Count');
this.form.appendChild(this.columnInput);
this.form.appendChild(this.rowInput);
this.form.appendChild(this.save);
this.form.appendChild(this.close);
};
Table.prototype.getButton = function() {
return this.button;
};
Table.prototype.getForm = function() {
return this.form;
};
Table.prototype.onClick = function() {
this.columnInput.value = this.options.defaultColumns;
this.rowInput.value = this.options.defaultRows;
};
Table.prototype.onClose = function(e) {
e.preventDefault();
this.base.hideForm(this.form);
};
Table.prototype.onSave = function(e) {
e.preventDefault();
var columnCount = this.columnInput.value;
var rowCount = this.rowInput.value;
var table = this.createTable(columnCount, rowCount);
this.base.pasteHTML(table.innerHTML);
this.base.hideForm(this.form);
};
// Create the table element.
Table.prototype.createTable = function(cols, rows) {
var table = document.createElement('table'),
header = document.createElement('thead'),
headerRow = document.createElement('tr'),
body = document.createElement('tbody'),
wrap = document.createElement('div');
for (var h = 1; h <= cols; h++) {
var headerCol = document.createElement('th');
headerCol.innerHTML = '...';
headerRow.appendChild(headerCol);
}
header.appendChild(headerRow);
for (var r = 1; r <= rows; r++) {
var bodyRow = document.createElement('tr');
for (var c = 1; c <= this.options.defaultColumns; c++) {
var bodyCol = document.createElement('td');
bodyCol.innerHTML = '...';
bodyRow.appendChild(bodyCol);
}
body.appendChild(bodyRow);
}
table.appendChild(header);
table.appendChild(body);
wrap.appendChild(table);
return wrap;
};
// When creating your Medium Editor
var editor = new MediumEditor('.editable', {
buttons: ['table'],
extensions: {
'table': new Table({
defaultColumns: 3,
defaultRows: 2
}),
});
Pavel Linkesch has developed a jQuery plugin to upload images & embed content (from Twitter, Youtube, Vimeo, etc.) following Medium.com functionality. Check it out at http://orthes.github.io/medium-editor-insert-plugin/
Ionică Bizău has created a Medium Editor extension, named Medium Editor Markdown, to add the functionality to render the HTML into Markdown code. Check it out at https://github.com/IonicaBizau/medium-editor-markdown.
A tables support extension is available at https://github.com/daviferreira/medium-editor-tables.
At @jillix, Ionică Bizău developed a Medium Editor extension that allows inserting custom HTML into the editor. Check it out here: https://github.com/jillix/medium-editor-custom-html.
Zvonko Biškup has written an awesome tutorial about how to integrate MediumEditor into your Laravel Project.
Ahmet Sezgin Duran gemified Medium Editor for Rails asset pipeline, check it out at https://github.com/marjinal1st/medium-editor-rails.
Thijs Wijnmaalen hacked together an AngularJS directive. Check it out at https://github.com/thijsw/angular-medium-editor
MediumEditor development tasks are managed by Grunt. To install all the necessary packages, just invoke:
npm install
These are the available grunt tasks:
The source files are located inside the src directory.
git checkout -b my-new-feature
)git commit -am 'Added some feature'
)git push origin my-new-feature
) project : medium-editor
repo age : 1 year, 8 months
active : 239 days
commits : 863
files : 62
authors :
550 Davi Ferreira 63.7%
48 Nate Mielnik 5.6%
30 Andy Yaco-Mink 3.5%
21 Noah Chase 2.4%
20 Maxime de Visscher 2.3%
8 Derek Odegard 0.9%
8 Jarl Gunnar T. Flaten 0.9%
8 Pedro Nasser 0.9%
8 Seif 0.9%
7 Aidan Threadgold 0.8%
7 Alfonso (the fonz) de la Osa 0.8%
7 Charl Gottschalk 0.8%
7 OmniaGM 0.8%
6 Dayjo 0.7%
6 Pascal 0.7%
5 Martin Thurau 0.6%
5 Raul Matei 0.6%
4 Sebastian Zuchmanski 0.5%
4 minikomi 0.5%
3 Andrew Hubbs 0.3%
3 Brian Reavis 0.3%
3 Dmitri Cherniak 0.3%
3 Ionică Bizău 0.3%
3 Javier Marín 0.3%
3 Nikita Korotaev 0.3%
3 Patrick Cavanaugh 0.3%
3 Pavel Linkesch 0.3%
3 Troels Knak-Nielsen 0.3%
3 arol 0.3%
3 ʞuıɯ-oɔɐʎ ʎpuɐ 0.3%
2 Alexander Hofbauer 0.2%
2 David Van Der Beek 0.2%
2 Ethan Turkeltaub 0.2%
2 Jacob Magnusson 0.2%
2 Jeremy 0.2%
2 Joel 0.2%
2 Karl Sander 0.2%
2 Robin Andersson 0.2%
2 Son Tran-Nguyen 0.2%
2 jj 0.2%
2 mako 0.2%
1 Adam Mulligan 0.1%
1 Alberto Gasparin 0.1%
1 Bitdeli Chef 0.1%
1 Brooke McKim 0.1%
1 Bruno Peres 0.1%
1 Carlos Alexandre Fuechter 0.1%
1 Cenk Dölek 0.1%
1 Chris 0.1%
1 Dave Jarvis 0.1%
1 David Collien 0.1%
1 David Hellsing 0.1%
1 Denis Gorbachev 0.1%
1 Diana Liao 0.1%
1 Enrico Berti 0.1%
1 Harry Hogg 0.1%
1 Harshil Shah 0.1%
1 IndieSquidge 0.1%
1 Jack Parker 0.1%
1 Jeff Bellsey 0.1%
1 Jeff Welch 0.1%
1 Johann Troendle 0.1%
1 Mark Kraemer 0.1%
1 Max 0.1%
1 Maxime Dantec 0.1%
1 Maxime De Visscher 0.1%
1 Michael Kay 0.1%
1 Moore Adam 0.1%
1 Nic Malan 0.1%
1 Nick Semenkovich 0.1%
1 Noah Paessel 0.1%
1 Patrick Kempff 0.1%
1 Peleg Rosenthal 0.1%
1 Randson Oliveira 0.1%
1 Richard Park 0.1%
1 Robert Koritnik 0.1%
1 Sarah Squire 0.1%
1 Scott Carleton 0.1%
1 Søren Torp Petersen 0.1%
1 Tom MacWright 0.1%
1 happyaccidents 0.1%
1 mako yass 0.1%
1 mbrookes 0.1%
1 muescha 0.1%
1 shaohua 0.1%
1 t_kjaergaard 0.1%
1 typify 0.1%
1 valentinnew 0.1%
1 waffleio 0.1%
1 zzjin 0.1%
"THE BEER-WARE LICENSE" (Revision 42):
As long as you retain this notice you can do whatever you want with this stuff. If we meet some day, and you think this stuff is worth it, you can buy me a beer in return.
FAQs
Medium.com WYSIWYG editor clone.
The npm package medium-editor receives a total of 17,969 weekly downloads. As such, medium-editor popularity was classified as popular.
We found that medium-editor demonstrated a not healthy version release cadence and project activity because the last version was released a year ago. It has 6 open source maintainers collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Security News
ESLint has added JSON and Markdown linting support with new officially-supported plugins, expanding its versatility beyond JavaScript.
Security News
Members Hub is conducting large-scale campaigns to artificially boost Discord server metrics, undermining community trust and platform integrity.
Security News
NIST has failed to meet its self-imposed deadline of clearing the NVD's backlog by the end of the fiscal year. Meanwhile, CVE's awaiting analysis have increased by 33% since June.