DotaNoobs main site.
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.

414 lines
15 KiB

  1. /*! UIkit 2.5.0 | http://www.getuikit.com | (c) 2014 YOOtheme | MIT License */
  2. (function(addon) {
  3. if (typeof define == "function" && define.amd) { // AMD
  4. define("uikit-markdownarea", ["uikit"], function(){
  5. return jQuery.UIkit.markdownarea || addon(window, window.jQuery, window.jQuery.UIkit);
  6. });
  7. }
  8. if(window && window.jQuery && window.jQuery.UIkit) {
  9. addon(window, window.jQuery, window.jQuery.UIkit);
  10. }
  11. })(function(global, $, UI){
  12. var Markdownarea = function(element, options){
  13. var $element = $(element);
  14. if($element.data("markdownarea")) return;
  15. this.element = $element;
  16. this.options = $.extend({}, Markdownarea.defaults, options);
  17. this.marked = this.options.marked || marked;
  18. this.CodeMirror = this.options.CodeMirror || CodeMirror;
  19. this.marked.setOptions({
  20. gfm: true,
  21. tables: true,
  22. breaks: true,
  23. pedantic: false,
  24. sanitize: false,
  25. smartLists: true,
  26. smartypants: false,
  27. langPrefix: 'lang-'
  28. });
  29. this.init();
  30. this.element.data("markdownarea", this);
  31. };
  32. $.extend(Markdownarea.prototype, {
  33. init: function(){
  34. var $this = this, tpl = Markdownarea.template;
  35. tpl = tpl.replace(/\{\:lblPreview\}/g, this.options.lblPreview);
  36. tpl = tpl.replace(/\{\:lblCodeview\}/g, this.options.lblCodeview);
  37. this.markdownarea = $(tpl);
  38. this.content = this.markdownarea.find(".uk-markdownarea-content");
  39. this.toolbar = this.markdownarea.find(".uk-markdownarea-toolbar");
  40. this.preview = this.markdownarea.find(".uk-markdownarea-preview").children().eq(0);
  41. this.code = this.markdownarea.find(".uk-markdownarea-code");
  42. this.element.before(this.markdownarea).appendTo(this.code);
  43. this.editor = this.CodeMirror.fromTextArea(this.element[0], this.options.codemirror);
  44. this.editor.markdownarea = this;
  45. this.editor.on("change", (function(){
  46. var render = function(){
  47. var value = $this.editor.getValue();
  48. $this.currentvalue = String(value);
  49. $this.element.trigger("markdownarea-before", [$this]);
  50. $this.applyPlugins();
  51. $this.marked($this.currentvalue, function (err, markdown) {
  52. if (err) throw err;
  53. $this.preview.html(markdown);
  54. $this.element.val($this.editor.getValue()).trigger("markdownarea-update", [$this]);
  55. });
  56. };
  57. render();
  58. return UI.Utils.debounce(render, 150);
  59. })());
  60. this.code.find(".CodeMirror").css("height", this.options.height);
  61. this._buildtoolbar();
  62. this.fit();
  63. $(window).on("resize", UI.Utils.debounce(function(){
  64. $this.fit();
  65. }, 200));
  66. var previewContainer = $this.preview.parent(),
  67. codeContent = this.code.find('.CodeMirror-sizer'),
  68. codeScroll = this.code.find('.CodeMirror-scroll').on('scroll',UI.Utils.debounce(function() {
  69. if($this.markdownarea.attr("data-mode")=="tab") return;
  70. // calc position
  71. var codeHeight = codeContent.height() - codeScroll.height(),
  72. previewHeight = previewContainer[0].scrollHeight - previewContainer.height(),
  73. ratio = previewHeight / codeHeight,
  74. previewPostition = codeScroll.scrollTop() * ratio;
  75. // apply new scroll
  76. previewContainer.scrollTop(previewPostition);
  77. }, 10));
  78. this.markdownarea.on("click", ".uk-markdown-button-markdown, .uk-markdown-button-preview", function(e){
  79. e.preventDefault();
  80. if($this.markdownarea.attr("data-mode")=="tab") {
  81. $this.markdownarea.find(".uk-markdown-button-markdown, .uk-markdown-button-preview").removeClass("uk-active").filter(this).addClass("uk-active");
  82. $this.activetab = $(this).hasClass("uk-markdown-button-markdown") ? "code":"preview";
  83. $this.markdownarea.attr("data-active-tab", $this.activetab);
  84. }
  85. });
  86. this.preview.parent().css("height", this.code.height());
  87. },
  88. applyPlugins: function(){
  89. var $this = this,
  90. plugins = Object.keys(Markdownarea.plugins),
  91. plgs = Markdownarea.plugins;
  92. this.markers = {};
  93. if(plugins.length) {
  94. var lines = this.currentvalue.split("\n");
  95. plugins.forEach(function(name){
  96. this.markers[name] = [];
  97. }, this);
  98. for(var line=0,max=lines.length;line<max;line++) {
  99. (function(line){
  100. plugins.forEach(function(name){
  101. var i = 0;
  102. lines[line] = lines[line].replace(plgs[name].identifier, function(){
  103. var replacement = plgs[name].cb({
  104. "area" : $this,
  105. "found": arguments,
  106. "line" : line,
  107. "pos" : i++,
  108. "uid" : [name, line, i, (new Date().getTime())+"RAND"+(Math.ceil(Math.random() *100000))].join('-'),
  109. "replace": function(strwith){
  110. var src = this.area.editor.getLine(this.line),
  111. start = src.indexOf(this.found[0]);
  112. end = this.found[0].length;
  113. this.area.editor.replaceRange(strwith, {"line": this.line, "ch":start}, {"line": this.line, "ch":end} );
  114. }
  115. });
  116. return replacement;
  117. });
  118. });
  119. })(line);
  120. }
  121. this.currentvalue = lines.join("\n");
  122. }
  123. },
  124. _buildtoolbar: function(){
  125. if(!(this.options.toolbar && this.options.toolbar.length)) return;
  126. var $this = this, bar = [];
  127. this.options.toolbar.forEach(function(cmd){
  128. if(Markdownarea.commands[cmd]) {
  129. var title = Markdownarea.commands[cmd].title ? Markdownarea.commands[cmd].title : cmd;
  130. bar.push('<li><a data-markdownarea-cmd="'+cmd+'" title="'+title+'" data-uk-tooltip>'+Markdownarea.commands[cmd].label+'</a></li>');
  131. if(Markdownarea.commands[cmd].shortcut) {
  132. $this.registerShortcut(Markdownarea.commands[cmd].shortcut, Markdownarea.commands[cmd].action);
  133. }
  134. }
  135. });
  136. this.toolbar.html(bar.join("\n"));
  137. this.markdownarea.on("click", "a[data-markdownarea-cmd]", function(){
  138. var cmd = $(this).data("markdownareaCmd");
  139. if(cmd && Markdownarea.commands[cmd] && (!$this.activetab || $this.activetab=="code" || cmd=="fullscreen")) {
  140. Markdownarea.commands[cmd].action.apply($this, [$this.editor])
  141. }
  142. });
  143. },
  144. fit: function() {
  145. var mode = this.options.mode;
  146. if(mode=="split" && this.markdownarea.width() < this.options.maxsplitsize) {
  147. mode = "tab";
  148. }
  149. if(mode=="tab") {
  150. if(!this.activetab) {
  151. this.activetab = "code";
  152. this.markdownarea.attr("data-active-tab", this.activetab);
  153. }
  154. this.markdownarea.find(".uk-markdown-button-markdown, .uk-markdown-button-preview").removeClass("uk-active")
  155. .filter(this.activetab=="code" ? '.uk-markdown-button-markdown':'.uk-markdown-button-preview').addClass("uk-active");
  156. }
  157. this.editor.refresh();
  158. this.preview.parent().css("height", this.code.height());
  159. this.markdownarea.attr("data-mode", mode);
  160. },
  161. registerShortcut: function(combination, callback){
  162. var $this = this;
  163. combination = $.isArray(combination) ? combination : [combination];
  164. for(var i=0,max=combination.length;i < max;i++) {
  165. var map = {};
  166. map[combination[i]] = function(){
  167. callback.apply($this, [$this.editor]);
  168. };
  169. $this.editor.addKeyMap(map);
  170. }
  171. }
  172. });
  173. //jQuery plugin
  174. $.fn.markdownarea = function(options){
  175. return this.each(function(){
  176. var ele = $(this);
  177. if(!ele.data("markdownarea")) {
  178. var obj = new Markdownarea(ele, options);
  179. }
  180. });
  181. };
  182. var baseReplacer = function(replace, editor){
  183. var text = editor.getSelection(),
  184. markdown = replace.replace('$1', text);
  185. editor.replaceSelection(markdown, 'end');
  186. };
  187. Markdownarea.commands = {
  188. "fullscreen": {
  189. "title" : 'Fullscreen',
  190. "label" : '<i class="uk-icon-expand"></i>',
  191. "action" : function(editor){
  192. editor.markdownarea.markdownarea.toggleClass("uk-markdownarea-fullscreen");
  193. var wrap = editor.getWrapperElement();
  194. if(editor.markdownarea.markdownarea.hasClass("uk-markdownarea-fullscreen")) {
  195. editor.state.fullScreenRestore = {scrollTop: window.pageYOffset, scrollLeft: window.pageXOffset, width: wrap.style.width, height: wrap.style.height};
  196. wrap.style.width = "";
  197. wrap.style.height = editor.markdownarea.content.height()+"px";
  198. document.documentElement.style.overflow = "hidden";
  199. } else {
  200. document.documentElement.style.overflow = "";
  201. var info = editor.state.fullScreenRestore;
  202. wrap.style.width = info.width; wrap.style.height = info.height;
  203. window.scrollTo(info.scrollLeft, info.scrollTop);
  204. }
  205. editor.refresh();
  206. editor.markdownarea.preview.parent().css("height", editor.markdownarea.code.height());
  207. }
  208. },
  209. "bold" : {
  210. "title" : "Bold",
  211. "label" : '<i class="uk-icon-bold"></i>',
  212. "shortcut": ['Ctrl-B', 'Cmd-B'],
  213. "action" : function(editor){
  214. baseReplacer("**$1**", editor);
  215. }
  216. },
  217. "italic" : {
  218. "title" : "Italic",
  219. "label" : '<i class="uk-icon-italic"></i>',
  220. "action" : function(editor){
  221. baseReplacer("*$1*", editor);
  222. }
  223. },
  224. "strike" : {
  225. "title" : "Strikethrough",
  226. "label" : '<i class="uk-icon-strikethrough"></i>',
  227. "action" : function(editor){
  228. baseReplacer("~~$1~~", editor);
  229. }
  230. },
  231. "blockquote" : {
  232. "title" : "Blockquote",
  233. "label" : '<i class="uk-icon-quote-right"></i>',
  234. "action" : function(editor){
  235. baseReplacer("> $1", editor);
  236. }
  237. },
  238. "link" : {
  239. "title" : "Link",
  240. "label" : '<i class="uk-icon-link"></i>',
  241. "action" : function(editor){
  242. baseReplacer("[$1](http://)", editor);
  243. }
  244. },
  245. "picture" : {
  246. "title" : "Picture",
  247. "label" : '<i class="uk-icon-picture-o"></i>',
  248. "action" : function(editor){
  249. baseReplacer("![$1](http://)", editor);
  250. }
  251. },
  252. "listUl" : {
  253. "title" : "Unordered List",
  254. "label" : '<i class="uk-icon-list-ul"></i>',
  255. "action" : function(editor){
  256. baseReplacer("* $1", editor);
  257. }
  258. },
  259. "listOl" : {
  260. "title" : "Ordered List",
  261. "label" : '<i class="uk-icon-list-ol"></i>',
  262. "action" : function(editor){
  263. baseReplacer("1. $1", editor);
  264. }
  265. }
  266. }
  267. Markdownarea.defaults = {
  268. "mode" : "split",
  269. "height" : 500,
  270. "maxsplitsize" : 1000,
  271. "codemirror" : { mode: 'gfm', tabMode: 'indent', tabindex: "2", lineWrapping: true, dragDrop: false },
  272. "toolbar" : [ "bold", "italic", "strike", "link", "picture", "blockquote", "listUl", "listOl" ],
  273. "lblPreview" : "Preview",
  274. "lblCodeview" : "Markdown"
  275. };
  276. Markdownarea.template = '<div class="uk-markdownarea uk-clearfix" data-mode="split">' +
  277. '<div class="uk-markdownarea-navbar">' +
  278. '<ul class="uk-markdownarea-navbar-nav uk-markdownarea-toolbar"></ul>' +
  279. '<div class="uk-markdownarea-navbar-flip">' +
  280. '<ul class="uk-markdownarea-navbar-nav">' +
  281. '<li class="uk-markdown-button-markdown"><a>{:lblCodeview}</a></li>' +
  282. '<li class="uk-markdown-button-preview"><a>{:lblPreview}</a></li>' +
  283. '<li><a data-markdownarea-cmd="fullscreen"><i class="uk-icon-expand"></i></a></li>' +
  284. '</ul>' +
  285. '</div>' +
  286. '</div>' +
  287. '<div class="uk-markdownarea-content">' +
  288. '<div class="uk-markdownarea-code"></div>' +
  289. '<div class="uk-markdownarea-preview"><div></div></div>' +
  290. '</div>' +
  291. '</div>';
  292. Markdownarea.plugins = {};
  293. Markdownarea.addPlugin = function(name, identifier, callback) {
  294. Markdownarea.plugins[name] = {"identifier":identifier, "cb":callback};
  295. };
  296. UI["markdownarea"] = Markdownarea;
  297. // init code
  298. $(function() {
  299. $("textarea[data-uk-markdownarea]").each(function() {
  300. var area = $(this), obj;
  301. if (!area.data("markdownarea")) {
  302. obj = new Markdownarea(area, UI.Utils.options(area.attr("data-uk-markdownarea")));
  303. }
  304. });
  305. });
  306. return Markdownarea;
  307. });