You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
415 lines
15 KiB
415 lines
15 KiB
/*! UIkit 2.5.0 | http://www.getuikit.com | (c) 2014 YOOtheme | MIT License */
|
|
|
|
(function(addon) {
|
|
|
|
if (typeof define == "function" && define.amd) { // AMD
|
|
define("uikit-markdownarea", ["uikit"], function(){
|
|
return jQuery.UIkit.markdownarea || addon(window, window.jQuery, window.jQuery.UIkit);
|
|
});
|
|
}
|
|
|
|
if(window && window.jQuery && window.jQuery.UIkit) {
|
|
addon(window, window.jQuery, window.jQuery.UIkit);
|
|
}
|
|
|
|
})(function(global, $, UI){
|
|
|
|
var Markdownarea = function(element, options){
|
|
|
|
var $element = $(element);
|
|
|
|
if($element.data("markdownarea")) return;
|
|
|
|
this.element = $element;
|
|
this.options = $.extend({}, Markdownarea.defaults, options);
|
|
|
|
this.marked = this.options.marked || marked;
|
|
this.CodeMirror = this.options.CodeMirror || CodeMirror;
|
|
|
|
this.marked.setOptions({
|
|
gfm: true,
|
|
tables: true,
|
|
breaks: true,
|
|
pedantic: false,
|
|
sanitize: false,
|
|
smartLists: true,
|
|
smartypants: false,
|
|
langPrefix: 'lang-'
|
|
});
|
|
|
|
this.init();
|
|
|
|
this.element.data("markdownarea", this);
|
|
};
|
|
|
|
$.extend(Markdownarea.prototype, {
|
|
|
|
init: function(){
|
|
|
|
var $this = this, tpl = Markdownarea.template;
|
|
|
|
tpl = tpl.replace(/\{\:lblPreview\}/g, this.options.lblPreview);
|
|
tpl = tpl.replace(/\{\:lblCodeview\}/g, this.options.lblCodeview);
|
|
|
|
this.markdownarea = $(tpl);
|
|
this.content = this.markdownarea.find(".uk-markdownarea-content");
|
|
this.toolbar = this.markdownarea.find(".uk-markdownarea-toolbar");
|
|
this.preview = this.markdownarea.find(".uk-markdownarea-preview").children().eq(0);
|
|
this.code = this.markdownarea.find(".uk-markdownarea-code");
|
|
|
|
this.element.before(this.markdownarea).appendTo(this.code);
|
|
|
|
this.editor = this.CodeMirror.fromTextArea(this.element[0], this.options.codemirror);
|
|
|
|
this.editor.markdownarea = this;
|
|
|
|
this.editor.on("change", (function(){
|
|
var render = function(){
|
|
|
|
var value = $this.editor.getValue();
|
|
|
|
$this.currentvalue = String(value);
|
|
|
|
$this.element.trigger("markdownarea-before", [$this]);
|
|
|
|
$this.applyPlugins();
|
|
|
|
$this.marked($this.currentvalue, function (err, markdown) {
|
|
|
|
if (err) throw err;
|
|
|
|
$this.preview.html(markdown);
|
|
$this.element.val($this.editor.getValue()).trigger("markdownarea-update", [$this]);
|
|
});
|
|
};
|
|
render();
|
|
return UI.Utils.debounce(render, 150);
|
|
})());
|
|
|
|
this.code.find(".CodeMirror").css("height", this.options.height);
|
|
|
|
this._buildtoolbar();
|
|
this.fit();
|
|
|
|
$(window).on("resize", UI.Utils.debounce(function(){
|
|
$this.fit();
|
|
}, 200));
|
|
|
|
|
|
var previewContainer = $this.preview.parent(),
|
|
codeContent = this.code.find('.CodeMirror-sizer'),
|
|
codeScroll = this.code.find('.CodeMirror-scroll').on('scroll',UI.Utils.debounce(function() {
|
|
|
|
if($this.markdownarea.attr("data-mode")=="tab") return;
|
|
|
|
// calc position
|
|
var codeHeight = codeContent.height() - codeScroll.height(),
|
|
previewHeight = previewContainer[0].scrollHeight - previewContainer.height(),
|
|
ratio = previewHeight / codeHeight,
|
|
previewPostition = codeScroll.scrollTop() * ratio;
|
|
|
|
// apply new scroll
|
|
previewContainer.scrollTop(previewPostition);
|
|
}, 10));
|
|
|
|
this.markdownarea.on("click", ".uk-markdown-button-markdown, .uk-markdown-button-preview", function(e){
|
|
|
|
e.preventDefault();
|
|
|
|
if($this.markdownarea.attr("data-mode")=="tab") {
|
|
|
|
$this.markdownarea.find(".uk-markdown-button-markdown, .uk-markdown-button-preview").removeClass("uk-active").filter(this).addClass("uk-active");
|
|
|
|
$this.activetab = $(this).hasClass("uk-markdown-button-markdown") ? "code":"preview";
|
|
$this.markdownarea.attr("data-active-tab", $this.activetab);
|
|
}
|
|
});
|
|
|
|
this.preview.parent().css("height", this.code.height());
|
|
},
|
|
|
|
applyPlugins: function(){
|
|
|
|
var $this = this,
|
|
plugins = Object.keys(Markdownarea.plugins),
|
|
plgs = Markdownarea.plugins;
|
|
|
|
this.markers = {};
|
|
|
|
if(plugins.length) {
|
|
|
|
var lines = this.currentvalue.split("\n");
|
|
|
|
plugins.forEach(function(name){
|
|
this.markers[name] = [];
|
|
}, this);
|
|
|
|
for(var line=0,max=lines.length;line<max;line++) {
|
|
|
|
(function(line){
|
|
plugins.forEach(function(name){
|
|
|
|
var i = 0;
|
|
|
|
lines[line] = lines[line].replace(plgs[name].identifier, function(){
|
|
|
|
var replacement = plgs[name].cb({
|
|
"area" : $this,
|
|
"found": arguments,
|
|
"line" : line,
|
|
"pos" : i++,
|
|
"uid" : [name, line, i, (new Date().getTime())+"RAND"+(Math.ceil(Math.random() *100000))].join('-'),
|
|
"replace": function(strwith){
|
|
var src = this.area.editor.getLine(this.line),
|
|
start = src.indexOf(this.found[0]);
|
|
end = this.found[0].length;
|
|
|
|
this.area.editor.replaceRange(strwith, {"line": this.line, "ch":start}, {"line": this.line, "ch":end} );
|
|
}
|
|
});
|
|
|
|
return replacement;
|
|
});
|
|
});
|
|
})(line);
|
|
}
|
|
|
|
this.currentvalue = lines.join("\n");
|
|
|
|
}
|
|
},
|
|
|
|
_buildtoolbar: function(){
|
|
|
|
if(!(this.options.toolbar && this.options.toolbar.length)) return;
|
|
|
|
var $this = this, bar = [];
|
|
|
|
this.options.toolbar.forEach(function(cmd){
|
|
if(Markdownarea.commands[cmd]) {
|
|
|
|
var title = Markdownarea.commands[cmd].title ? Markdownarea.commands[cmd].title : cmd;
|
|
|
|
bar.push('<li><a data-markdownarea-cmd="'+cmd+'" title="'+title+'" data-uk-tooltip>'+Markdownarea.commands[cmd].label+'</a></li>');
|
|
|
|
if(Markdownarea.commands[cmd].shortcut) {
|
|
$this.registerShortcut(Markdownarea.commands[cmd].shortcut, Markdownarea.commands[cmd].action);
|
|
}
|
|
}
|
|
});
|
|
|
|
this.toolbar.html(bar.join("\n"));
|
|
|
|
this.markdownarea.on("click", "a[data-markdownarea-cmd]", function(){
|
|
var cmd = $(this).data("markdownareaCmd");
|
|
|
|
if(cmd && Markdownarea.commands[cmd] && (!$this.activetab || $this.activetab=="code" || cmd=="fullscreen")) {
|
|
Markdownarea.commands[cmd].action.apply($this, [$this.editor])
|
|
}
|
|
|
|
});
|
|
},
|
|
|
|
fit: function() {
|
|
|
|
var mode = this.options.mode;
|
|
|
|
if(mode=="split" && this.markdownarea.width() < this.options.maxsplitsize) {
|
|
mode = "tab";
|
|
}
|
|
|
|
if(mode=="tab") {
|
|
|
|
if(!this.activetab) {
|
|
this.activetab = "code";
|
|
this.markdownarea.attr("data-active-tab", this.activetab);
|
|
}
|
|
|
|
this.markdownarea.find(".uk-markdown-button-markdown, .uk-markdown-button-preview").removeClass("uk-active")
|
|
.filter(this.activetab=="code" ? '.uk-markdown-button-markdown':'.uk-markdown-button-preview').addClass("uk-active");
|
|
|
|
}
|
|
|
|
this.editor.refresh();
|
|
this.preview.parent().css("height", this.code.height());
|
|
|
|
this.markdownarea.attr("data-mode", mode);
|
|
},
|
|
|
|
registerShortcut: function(combination, callback){
|
|
|
|
var $this = this;
|
|
|
|
combination = $.isArray(combination) ? combination : [combination];
|
|
|
|
for(var i=0,max=combination.length;i < max;i++) {
|
|
var map = {};
|
|
|
|
map[combination[i]] = function(){
|
|
callback.apply($this, [$this.editor]);
|
|
};
|
|
|
|
$this.editor.addKeyMap(map);
|
|
}
|
|
}
|
|
});
|
|
|
|
//jQuery plugin
|
|
|
|
$.fn.markdownarea = function(options){
|
|
|
|
return this.each(function(){
|
|
|
|
var ele = $(this);
|
|
|
|
if(!ele.data("markdownarea")) {
|
|
var obj = new Markdownarea(ele, options);
|
|
}
|
|
});
|
|
};
|
|
|
|
var baseReplacer = function(replace, editor){
|
|
var text = editor.getSelection(),
|
|
markdown = replace.replace('$1', text);
|
|
|
|
editor.replaceSelection(markdown, 'end');
|
|
};
|
|
|
|
Markdownarea.commands = {
|
|
"fullscreen": {
|
|
"title" : 'Fullscreen',
|
|
"label" : '<i class="uk-icon-expand"></i>',
|
|
"action" : function(editor){
|
|
|
|
editor.markdownarea.markdownarea.toggleClass("uk-markdownarea-fullscreen");
|
|
|
|
var wrap = editor.getWrapperElement();
|
|
|
|
if(editor.markdownarea.markdownarea.hasClass("uk-markdownarea-fullscreen")) {
|
|
|
|
editor.state.fullScreenRestore = {scrollTop: window.pageYOffset, scrollLeft: window.pageXOffset, width: wrap.style.width, height: wrap.style.height};
|
|
wrap.style.width = "";
|
|
wrap.style.height = editor.markdownarea.content.height()+"px";
|
|
document.documentElement.style.overflow = "hidden";
|
|
|
|
} else {
|
|
|
|
document.documentElement.style.overflow = "";
|
|
var info = editor.state.fullScreenRestore;
|
|
wrap.style.width = info.width; wrap.style.height = info.height;
|
|
window.scrollTo(info.scrollLeft, info.scrollTop);
|
|
}
|
|
|
|
editor.refresh();
|
|
editor.markdownarea.preview.parent().css("height", editor.markdownarea.code.height());
|
|
}
|
|
},
|
|
|
|
"bold" : {
|
|
"title" : "Bold",
|
|
"label" : '<i class="uk-icon-bold"></i>',
|
|
"shortcut": ['Ctrl-B', 'Cmd-B'],
|
|
"action" : function(editor){
|
|
|
|
baseReplacer("**$1**", editor);
|
|
}
|
|
},
|
|
"italic" : {
|
|
"title" : "Italic",
|
|
"label" : '<i class="uk-icon-italic"></i>',
|
|
"action" : function(editor){
|
|
baseReplacer("*$1*", editor);
|
|
}
|
|
},
|
|
"strike" : {
|
|
"title" : "Strikethrough",
|
|
"label" : '<i class="uk-icon-strikethrough"></i>',
|
|
"action" : function(editor){
|
|
baseReplacer("~~$1~~", editor);
|
|
}
|
|
},
|
|
"blockquote" : {
|
|
"title" : "Blockquote",
|
|
"label" : '<i class="uk-icon-quote-right"></i>',
|
|
"action" : function(editor){
|
|
baseReplacer("> $1", editor);
|
|
}
|
|
},
|
|
"link" : {
|
|
"title" : "Link",
|
|
"label" : '<i class="uk-icon-link"></i>',
|
|
"action" : function(editor){
|
|
baseReplacer("[$1](http://)", editor);
|
|
}
|
|
},
|
|
"picture" : {
|
|
"title" : "Picture",
|
|
"label" : '<i class="uk-icon-picture-o"></i>',
|
|
"action" : function(editor){
|
|
baseReplacer("![$1](http://)", editor);
|
|
}
|
|
},
|
|
"listUl" : {
|
|
"title" : "Unordered List",
|
|
"label" : '<i class="uk-icon-list-ul"></i>',
|
|
"action" : function(editor){
|
|
baseReplacer("* $1", editor);
|
|
}
|
|
},
|
|
"listOl" : {
|
|
"title" : "Ordered List",
|
|
"label" : '<i class="uk-icon-list-ol"></i>',
|
|
"action" : function(editor){
|
|
baseReplacer("1. $1", editor);
|
|
}
|
|
}
|
|
}
|
|
|
|
Markdownarea.defaults = {
|
|
"mode" : "split",
|
|
"height" : 500,
|
|
"maxsplitsize" : 1000,
|
|
"codemirror" : { mode: 'gfm', tabMode: 'indent', tabindex: "2", lineWrapping: true, dragDrop: false },
|
|
"toolbar" : [ "bold", "italic", "strike", "link", "picture", "blockquote", "listUl", "listOl" ],
|
|
"lblPreview" : "Preview",
|
|
"lblCodeview" : "Markdown"
|
|
};
|
|
|
|
Markdownarea.template = '<div class="uk-markdownarea uk-clearfix" data-mode="split">' +
|
|
'<div class="uk-markdownarea-navbar">' +
|
|
'<ul class="uk-markdownarea-navbar-nav uk-markdownarea-toolbar"></ul>' +
|
|
'<div class="uk-markdownarea-navbar-flip">' +
|
|
'<ul class="uk-markdownarea-navbar-nav">' +
|
|
'<li class="uk-markdown-button-markdown"><a>{:lblCodeview}</a></li>' +
|
|
'<li class="uk-markdown-button-preview"><a>{:lblPreview}</a></li>' +
|
|
'<li><a data-markdownarea-cmd="fullscreen"><i class="uk-icon-expand"></i></a></li>' +
|
|
'</ul>' +
|
|
'</div>' +
|
|
'</div>' +
|
|
'<div class="uk-markdownarea-content">' +
|
|
'<div class="uk-markdownarea-code"></div>' +
|
|
'<div class="uk-markdownarea-preview"><div></div></div>' +
|
|
'</div>' +
|
|
'</div>';
|
|
|
|
Markdownarea.plugins = {};
|
|
Markdownarea.addPlugin = function(name, identifier, callback) {
|
|
Markdownarea.plugins[name] = {"identifier":identifier, "cb":callback};
|
|
};
|
|
|
|
UI["markdownarea"] = Markdownarea;
|
|
|
|
// init code
|
|
$(function() {
|
|
|
|
$("textarea[data-uk-markdownarea]").each(function() {
|
|
var area = $(this), obj;
|
|
|
|
if (!area.data("markdownarea")) {
|
|
obj = new Markdownarea(area, UI.Utils.options(area.attr("data-uk-markdownarea")));
|
|
}
|
|
});
|
|
});
|
|
|
|
return Markdownarea;
|
|
});
|