/*!
* -------------------------------------------------------
* SIMPLE TEXT SELECTION LIBRARY FOR ONLINE TEXT EDITING
* -------------------------------------------------------
*
* Author => Taufik Nurrohman
* URL => http://www.dte.web.id, http://latitudu.com
*
*/
var Editor = function(source) {
var base = this,
history = [],
undo = 0,
redo = null;
base.area = typeof source != "undefined" ? source : document.getElementsByTagName('textarea')[0];
history[undo] = {
value: base.area.value,
selectionStart: 0,
selectionEnd: 0
};
undo++;
/**
* Collect data from selected text inside a textarea
*
*
* var editor = new Editor(elem);
* elem.onmouseup = function() {
* alert(editor.selection().start);
* alert(editor.selection().end);
* alert(editor.selection().value);
* };
*
*
*/
base.selection = function() {
var start = base.area.selectionStart,
end = base.area.selectionEnd,
value = base.area.value.substring(start, end),
before = base.area.value.substring(0, start),
after = base.area.value.substring(end),
data = {
start: start,
end: end,
value: value,
before: before,
after: after
};
// console.log(data);
return data;
};
/**
* Select portion of text inside a textarea
*
*
* var editor = new Editor(elem);
* editor.select(7, 11);
*
*
*/
base.select = function(start, end, callback) {
base.area.focus();
base.area.setSelectionRange(start, end);
if (typeof callback == "function") callback();
};
/**
* Replace portion of selected text inside a textarea with something
*
*
* var editor = new Editor(elem);
* editor.replace(/foo/, "bar");
*
*
*/
base.replace = function(from, to, callback) {
var sel = base.selection(),
start = sel.start,
end = sel.end,
selections = sel.value.replace(from, to);
base.area.value = sel.before + selections + sel.after;
base.select(start, start + selections.length);
if (typeof callback == "function") {
callback();
} else {
base.updateHistory({
value: base.area.value,
selectionStart: start,
selectionEnd: start + selections.length
});
}
};
/**
* Replace selected text inside a textarea with something
*
*
* var editor = new Editor(elem);
* editor.insert('foo');
*
*
*/
base.insert = function(insertion, callback) {
var sel = base.selection(),
start = sel.start,
end = sel.end;
base.area.value = sel.before + insertion + sel.after;
base.select(start + insertion.length, start + insertion.length);
if (typeof callback == "function") {
callback();
} else {
base.updateHistory({
value: base.area.value,
selectionStart: start + insertion.length,
selectionEnd: start + insertion.length
});
}
};
/**
* Wrap selected text inside a textarea with something
*
*
* var editor = new Editor(elem);
* editor.wrap('', '');
*
*
*/
base.wrap = function(open, close, callback) {
var sel = base.selection(),
selections = sel.value,
before = sel.before,
after = sel.after;
base.area.value = before + open + selections + close + after;
base.select(before.length + open.length, before.length + open.length + selections.length);
if (typeof callback == "function") {
callback();
} else {
base.updateHistory({
value: base.area.value,
selectionStart: before.length + open.length,
selectionEnd: before.length + open.length + selections.length
});
}
};
/**
* Indent selected text inside a textarea with something
*
*
* var editor = new Editor(elem);
* editor.indent('\t');
*
*
*/
base.indent = function(chars, callback) {
var sel = base.selection();
if (sel.value.length > 0) { // Multi line
base.replace(/(^|\n)([^\n])/gm, '$1' + chars + '$2', callback);
} else { // Single line
base.area.value = sel.before + chars + sel.value + sel.after;
base.select(sel.start + chars.length, sel.start + chars.length);
if (typeof callback == "function") {
callback();
} else {
base.updateHistory({
value: base.area.value,
selectionStart: sel.start + chars.length,
selectionEnd: sel.start + chars.length
});
}
}
};
/**
* Outdent selected text inside a textarea from something
*
*
* var editor = new Editor(elem);
* editor.outdent('\t');
*
*
*/
base.outdent = function(chars, callback) {
var sel = base.selection();
if (sel.value.length > 0) { // Multi line
base.replace(new RegExp('(^|\n)' + chars, 'gm'), '$1', callback);
} else { // Single line
var before = sel.before.replace(new RegExp(chars + '$'), "");
base.area.value = before + sel.value + sel.after;
base.select(before.length, before.length);
if (typeof callback == "function") {
callback();
} else {
base.updateHistory({
value: base.area.value,
selectionStart: before.length,
selectionEnd: before.length
});
}
}
};
/**
* Call available history data
*
*
* var editor = new Editor(elem);
* alert(editor.callHistory(2).value);
* alert(editor.callHistory(2).selectionStart);
* alert(editor.callHistory(2).selectionEnd);
*
*
*/
base.callHistory = function(index) {
return (typeof index == "number") ? history[index] : history;
};
/**
* Update history data
*
*
* var editor = new Editor(elem);
* editor.area.onkeydown = function() {
* editor.updateHistory();
* };
*
*
*/
base.updateHistory = function(data, index) {
var value = (typeof data != "undefined") ? data : {
value: base.area.value,
selectionStart: base.selection().start,
selectionEnd: base.selection().end
};
history[typeof index == "number" ? index : undo] = value;
undo++;
};
/**
* Undo from previous action or previous Redo
*
*
* var editor = new Editor(elem);
* editor.undo();
*
*
*/
base.undo = function(callback) {
var data;
if (history.length > 1) {
if (undo > 1) {
undo--;
} else {
undo = 1;
}
data = base.callHistory(undo - 1);
redo = undo <= 0 ? undo - 1 : undo;
} else {
return;
}
base.area.value = data.value;
base.select(data.selectionStart, data.selectionEnd);
if (typeof callback == "function") callback();
};
/**
* Redo from previous Undo
*
*
* var editor = new Editor(elem);
* editor.redo();
*
*
*/
base.redo = function(callback) {
var data;
if (redo !== null) {
data = base.callHistory(redo);
if (redo < history.length - 1) {
redo++;
} else {
redo = history.length - 1;
}
undo = redo >= history.length - 1 ? redo + 1 : redo;
} else {
return;
}
base.area.value = data.value;
base.select(data.selectionStart, data.selectionEnd);
// console.log(redo);
if (typeof callback == "function") callback();
};
};
/*
* -------------------------------------------------------
* BASIC FUNCTIONS
* -------------------------------------------------------
*/
(function() {
// => http://stackoverflow.com/a/7592235/1163000
String.prototype.capitalize = function(lower) {
return (lower ? this.toLowerCase() : this).replace(/(?:^|\s)\S/g, function(a) {
return a.toUpperCase();
});
};
var myTextArea = document.getElementById('editor-area'),
myButton = document.getElementById('editor-control').getElementsByTagName('a'),
myEditor = new Editor(myTextArea);
var controls = {
'bold': function() {
myEditor.wrap('**', '**');
},
'italic': function() {
myEditor.wrap('_', '_');
},
'code': function() {
myEditor.wrap('`', '`');
},
'code-block': function() {
myEditor.indent(' ');
},
'quote': function() {
myEditor.indent('> ');
},
'ul-list': function() {
var sel = myEditor.selection(),
added = "";
if (sel.value.length > 0) {
myEditor.indent('', function() {
myEditor.replace(/^[^\n\r]/gm, function(str) {
added += '- ';
return str.replace(/^/, '- ');
});
myEditor.select(sel.start, sel.end + added.length);
});
} else {
var placeholder = '- List Item';
myEditor.indent(placeholder, function() {
myEditor.select(sel.start + 2, sel.start + placeholder.length);
});
}
},
'ol-list': function() {
var sel = myEditor.selection(),
ol = 0,
added = "";
if (sel.value.length > 0) {
myEditor.indent('', function() {
myEditor.replace(/^[^\n\r]/gm, function(str) {
ol++;
added += ol + '. ';
return str.replace(/^/, ol + '. ');
});
myEditor.select(sel.start, sel.end + added.length);
});
} else {
var placeholder = '1. List Item';
myEditor.indent(placeholder, function() {
myEditor.select(sel.start + 3, sel.start + placeholder.length);
});
}
},
'link': function() {
var sel = myEditor.selection(),
title = prompt('Link Title:', 'Link title goes here...'),
url = prompt('Link URL:', 'http://'),
placeholder = 'Your link text goes here...';
if (url && url !== "" && url !== 'http://') {
myEditor.wrap('[' + (sel.value.length === 0 ? placeholder : ''), '](' + url + (title !== "" ? ' \"' + title + '\"' : '') + ')', function() {
myEditor.select(sel.start + 1, (sel.value.length === 0 ? sel.start + placeholder.length + 1 : sel.end + 1));
});
}
return false;
},
'image': function() {
var url = prompt('Image URL:', 'http://'),
alt = url.substring(url.lastIndexOf('/') + 1, url.lastIndexOf('.')).replace(/[\-\_\+]+/g, " ").capitalize();
alt = alt.indexOf('/') < 0 ? decodeURIComponent(alt) : 'Image';
if (url && url !== "" && url !== 'http://') {
myEditor.insert('\n\n![' + alt + '](' + url + ')\n\n');
}
return false;
},
'h1': function() {
heading('#');
},
'h2': function() {
heading('##');
},
'h3': function() {
heading('###');
},
'h4': function() {
heading('####');
},
'h5': function() {
heading('#####');
},
'h6': function() {
heading('######');
},
'hr': function() {
myEditor.insert('\n\n---\n\n');
},
'undo': function() {
myEditor.undo();
},
'redo': function() {
myEditor.redo();
}
};
function heading(key) {
if (myEditor.selection().value.length > 0) {
myEditor.wrap(key + ' ', "");
} else {
var placeholder = key + ' Heading ' + key.length + '\n\n';
myEditor.insert(placeholder, function() {
var s = myEditor.selection().start;
myEditor.select(s - placeholder.length + key.length + 1, s - 2);
});
}
}
function click(elem) {
var hash = elem.hash.replace('#', "");
if(controls[hash]) {
elem.onclick = function() {
controls[hash]();
return false;
};
}
}
for (var i = 0, len = myButton.length; i < len; ++i) {
click(myButton[i]);
myButton[i].href = 'javascript:void(0)';
}
var pressed = 0;
myEditor.area.onkeydown = function(e) {
// Update history data on every 5 key presses
if (pressed < 5) {
pressed++;
} else {
myEditor.updateHistory();
pressed = 0;
}
// Press `Shift + Tab` to outdent
if (e.shiftKey && e.keyCode == 9) {
// Outdent from quote
// Outdent from ordered list
// Outdent from unordered list
// Outdent from code block
myEditor.outdent('(> |[0-9]+\. |- | )')
return false;
}
// Press `Tab` to indent
if (e.keyCode == 9) {
myEditor.indent(' ');
return false;
}
};
var c = new Showdown.converter(),
e = document.querySelector('#eye'),
i = document.querySelector('#editor-area'),
o = document.querySelector('.result');
e.onclick = function(){
o.innerHTML = c.makeHtml(i.value);
o.classList.toggle('show');
this.classList.toggle('active');
}
})();