Skip to content

Instantly share code, notes, and snippets.

@georgepsarakis
Last active December 25, 2015 20:39
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save georgepsarakis/7037153 to your computer and use it in GitHub Desktop.
Save georgepsarakis/7037153 to your computer and use it in GitHub Desktop.
Extra-Lightweight HTML/Markdown Editor (jQuery Plugin)
/*
<div id="editor"></div>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js"></script>
<style>
.clearfix:after {
clear: both;
}
.clearfix:before, .clearfix:after {
content: " ";
display: table;
}
*::-moz-focus-inner {
border: 0;
}
*:focus {
outline: none;
}
#lighter-toolbar-container {
width: 500px;
}
.lighter-editors {
width: 500px;
height: 400px;
border: 1px solid #ccc;
padding: 4px 8px;
display:none;
border-radius: 4px;
margin-top: 4px;
}
.lighter-editors, .lighter-editors * {
margin: 0;
line-height: 16px;
font-family: Helvetica, Arial, "sans serif";
}
.lighter-button {
border:medium none;
border-radius:2px;
color:#FFF;
cursor: pointer;
font-weight:400;
line-height:100%;
text-align:center;
background-color: #2242BD;
height: 40px;
margin-right: 4px;
min-width: 140px;
margin-bottom: 2px
}
.lighter-button-disabled {
opacity: 0.7;
cursor: none;
}
.lighter-last-save {
float: right;
opacity: 0.7;
color: #ccc;
line-height: 40px;
}
</style>
<script type="text/javascript">
*/
$(document).ready(function(){
$('#editor').lighter();
});
/*
* Project:
* Description: Lightweight HTML-to-Markdown-to-HTML editor
* Author: George Psarakis
* License: MIT
* Under development/Work-in-progress (not working yet!)
*/
// Based on https://github.com/jquery-boilerplate/jquery-boilerplate/wiki/Another-extending-jQuery-boilerplate
;(function ( $, window, document, undefined ) {
var pluginName = "lighter",
dataPlugin = "plugin_" + pluginName,
defaults = {
html : true,
markdown : true,
storage_key : 'random-lighter-key',
save_interval: 5000
};
/* Helper functions - extending String and Date objects */
Date.prototype.format = function(fmt){
var zeropad = function(n){
return ( parseInt(n, 10) <= 9 ) ? '0' + n : n;
}
if ( typeof fmt === "undefined" ){
fmt = "%Y-%m-%d %H:%i:%s";
}
var replacements = {
"Y" : this.getFullYear(),
"m" : zeropad(this.getMonth() + 1),
"d" : zeropad(this.getDate()),
"H" : zeropad(this.getHours()),
"i" : zeropad(this.getMinutes()),
"s" : zeropad(this.getSeconds())
};
for(var k in replacements){
var re = new RegExp("%+" + k, 'g');
fmt = fmt.sub(re, replacements[k]);
}
return fmt;
}
String.prototype.sub = function(regex, replacement){
var s = this.toString();
var matches = s.match(regex);
if ( matches !== null ){
for(var i in matches){
var item = matches[i];
if ( !item.escaped() ){
s = s.replace(item, replacement);
}
};
}
return s;
}
String.prototype.escaped = function(){
return ( this.toString().match(/^%{2,}/) !== null );
}
String.prototype.html_encode = function(){
return this.toString().replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&apos;");
}
String.prototype.html_decode = function () {
return $('<div/>').html(this.toString()).text();
}
String.prototype.markdown = function (){
var text = this.toString();
text = text.replace(/<br\s*\/*>/g, "\n");
var lines = text.split("\n");
html = "";
$.each(lines, function(index, line){
var header = line.match(/^[#]+/);
if ( header !== null ){
var level = header[0].length;
if ( level > 6 ){
level = 6;
}
line = line.wrap('h' + level);
return false;
}
var strong = line.match(/\*+[^\*]+\*+/g)
if ( strong !== null ){
$.each(strong, function(i,s){
line = line.replace(s, s.replace(/\*+/g,'').wrap('b'));
});
}
var links = line.match(/\[[^\]]+?\]\([^\)]+\)/g);
if ( links !== null ){
$.each(links, function(i, l){
var description = l.match(/\[([^\]]+)\]/)[1];
var href = l.match(/\(([^\)]+)\)/)[1];
line = line.replace(l, description.wrap('a', {'href' : href}));
});
}
html += line.wrap('p');
});
return html;
}
String.prototype.format = function (params){
var s = this.toString();
for(var k in params){
var re = new RegExp("[%]+\\(" + k + "\\)s", 'g');
s = s.sub(re, params[k]);
}
return s;
}
String.prototype.wrap = function wrap(element, attrs){
var content = this.toString();
var html_attrs = "";
if ( typeof attrs !== 'undefined'){
for(var k in attrs){
html_attrs += ' %(name)s="%(value)s"'.format({ 'name' : k, 'value' : escape(attrs[k]) });
}
}
return '<%(0)s%(1)s>%(2)s</%(0)s>'.format([ element, html_attrs, content ]);
}
var lighter = function ( element ) {
this.options = $.extend( {}, defaults );
};
lighter.prototype = {
init: function ( options ) {
$.extend( this.options, options );
this.mode = 'markdown';
if ( !this.element.is('div') ) {
console.log('lighter.js works only on <div> elements.');
}
this.toolbar();
this.editors();
var saved_content = localStorage.getItem(this.options.storage_key);
if ( saved_content !== null ){
$('#lighter-code-editor').html(saved_content);
}
var element = $(this);
var storage_key = this.options.storage_key;
setInterval(function(){
var lastsave = new Date();
$('#lighter-last-saved').text("@" + lastsave.format('%H:%i:%s'));
localStorage.setItem(storage_key, $('#lighter-code-editor').html())
}, this.options.save_interval);
},
toolbar: function () {
var btn_html = '<button id="lighter-code" class="lighter-button">HTML/MARKDOWN</button>';
var btn_preview = '<button id="lighter-preview" class="lighter-button">PREVIEW</button>';
var last_saved = '<div id="lighter-last-saved" class="lighter-last-save"></div>';
var toolbar_html = '<div id="lighter-toolbar-container" class="clearfix">' + btn_html + btn_preview + last_saved + '</div>';
this.element.append(toolbar_html);
var bound_element = this.element;
$('.lighter-button').click(function(e){
$('.lighter-button').removeClass('lighter-button-disabled');
$(this).addClass('lighter-button-disabled');
e.preventDefault();
$(bound_element).lighter('display', $(this).attr('id').replace('lighter-', ''));
return false;
});
},
editors: function() {
var editor = '<div id="%(id)s" class="lighter-editors"></div>';
var code_editor = editor.format({'id':'lighter-code-editor'});
var code_preview = editor.format({'id':'lighter-code-preview'});
this.element.append(code_editor + code_preview);
$('#lighter-code-editor').attr('contenteditable', 'true').show();
},
display: function(element){
$('.lighter-editors').hide();
if ( element == "preview" ){
$('#lighter-code-preview').html(this.contents('html')).show();
} else {
$('#lighter-code-editor').show();
}
},
destroy: function () {
this.element.data( dataPlugin, null );
},
// Returns the processed HTML or Markdown
contents: function (type) {
var content = null;
if ( type == 'html' ){
content = $('#lighter-code-editor').html().markdown().html_encode().html_decode();
} else {
content = $('#lighter-code-editor').html();
}
return content;
}
}
$.fn[pluginName] = function ( arg ) {
var args, instance;
// only allow the plugin to be instantiated once
if (!( this.data( dataPlugin ) instanceof lighter )) {
// if no instance, create one
this.data( dataPlugin, new lighter( this ) );
}
instance = this.data( dataPlugin );
instance.element = this;
if (typeof arg === 'undefined' || typeof arg === 'object') {
if ( typeof instance['init'] === 'function' ) {
instance.init( arg );
}
// checks that the requested public method exists
} else if ( typeof arg === 'string' && typeof instance[arg] === 'function' ) {
// copy arguments & remove function name
args = Array.prototype.slice.call( arguments, 1 );
// call the method
return instance[arg].apply( instance, args );
} else {
$.error('Method ' + arg + ' does not exist on jQuery.' + pluginName);
}
};
}(jQuery, window, document));
/* </script> */
@georgepsarakis
Copy link
Author

Basic Markdown parsing and auto-save with localStorage.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment