Last active
December 25, 2015 20:39
-
-
Save georgepsarakis/7037153 to your computer and use it in GitHub Desktop.
Extra-Lightweight HTML/Markdown Editor (jQuery Plugin)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
<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, "<").replace(/>/g, ">").replace(/"/g, "'"); | |
} | |
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> */ |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Basic Markdown parsing and auto-save with localStorage.