From 5bc642d02e8955b200bb21cf30e863fdf0c53765 Mon Sep 17 00:00:00 2001 From: BoHong Li Date: Thu, 9 Mar 2017 02:41:05 +0800 Subject: Use JavaScript Standard Style (part 2) Fixed all fail on frontend code. --- public/js/extra.js | 1944 ++++++++++++++++++++++++++-------------------------- 1 file changed, 967 insertions(+), 977 deletions(-) (limited to 'public/js/extra.js') diff --git a/public/js/extra.js b/public/js/extra.js index a3e840d2..844d52c6 100644 --- a/public/js/extra.js +++ b/public/js/extra.js @@ -1,1150 +1,1140 @@ -require('prismjs/themes/prism.css'); -require('prismjs/components/prism-wiki'); -require('prismjs/components/prism-haskell'); -require('prismjs/components/prism-go'); -require('prismjs/components/prism-typescript'); -require('prismjs/components/prism-jsx'); - -import Prism from 'prismjs'; -import hljs from 'highlight.js'; -import PDFObject from 'pdfobject'; -import S from 'string'; -import { saveAs } from 'file-saver'; - -require('./lib/common/login'); -require('../vendor/md-toc'); -var Viz = require("viz.js"); - -//auto update last change -window.createtime = null; -window.lastchangetime = null; +/* eslint-env browser, jquery */ +/* global moment, serverurl */ + +require('prismjs/themes/prism.css') +require('prismjs/components/prism-wiki') +require('prismjs/components/prism-haskell') +require('prismjs/components/prism-go') +require('prismjs/components/prism-typescript') +require('prismjs/components/prism-jsx') + +import Prism from 'prismjs' +import hljs from 'highlight.js' +import PDFObject from 'pdfobject' +import S from 'string' +import { saveAs } from 'file-saver' + +require('./lib/common/login') +require('../vendor/md-toc') +var Viz = require('viz.js') + +// auto update last change +window.createtime = null +window.lastchangetime = null window.lastchangeui = { - status: $(".ui-status-lastchange"), - time: $(".ui-lastchange"), - user: $(".ui-lastchangeuser"), - nouser: $(".ui-no-lastchangeuser") + status: $('.ui-status-lastchange'), + time: $('.ui-lastchange'), + user: $('.ui-lastchangeuser'), + nouser: $('.ui-no-lastchangeuser') } -const ownerui = $(".ui-owner"); +const ownerui = $('.ui-owner') -export function updateLastChange() { - if (!lastchangeui) return; - if (createtime) { - if (createtime && !lastchangetime) { - lastchangeui.status.text('created'); - } else { - lastchangeui.status.text('changed'); - } - const time = lastchangetime || createtime; - lastchangeui.time.html(moment(time).fromNow()); - lastchangeui.time.attr('title', moment(time).format('llll')); +export function updateLastChange () { + if (!window.lastchangeui) return + if (window.createtime) { + if (window.createtime && !window.lastchangetime) { + window.lastchangeui.status.text('created') + } else { + window.lastchangeui.status.text('changed') } + const time = window.lastchangetime || window.createtime + window.lastchangeui.time.html(moment(time).fromNow()) + window.lastchangeui.time.attr('title', moment(time).format('llll')) + } } -setInterval(updateLastChange, 60000); - -window.lastchangeuser = null; -window.lastchangeuserprofile = null; - -export function updateLastChangeUser() { - if (lastchangeui) { - if (lastchangeuser && lastchangeuserprofile) { - const icon = lastchangeui.user.children('i'); - icon.attr('title', lastchangeuserprofile.name).tooltip('fixTitle'); - if (lastchangeuserprofile.photo) - icon.attr('style', `background-image:url(${lastchangeuserprofile.photo})`); - lastchangeui.user.show(); - lastchangeui.nouser.hide(); - } else { - lastchangeui.user.hide(); - lastchangeui.nouser.show(); - } +setInterval(updateLastChange, 60000) + +window.lastchangeuser = null +window.lastchangeuserprofile = null + +export function updateLastChangeUser () { + if (window.lastchangeui) { + if (window.lastchangeuser && window.lastchangeuserprofile) { + const icon = window.lastchangeui.user.children('i') + icon.attr('title', window.lastchangeuserprofile.name).tooltip('fixTitle') + if (window.lastchangeuserprofile.photo) { icon.attr('style', `background-image:url(${window.lastchangeuserprofile.photo})`) } + window.lastchangeui.user.show() + window.lastchangeui.nouser.hide() + } else { + window.lastchangeui.user.hide() + window.lastchangeui.nouser.show() } + } } -window.owner = null; -window.ownerprofile = null; - -export function updateOwner() { - if (ownerui) { - if (owner && ownerprofile && owner !== lastchangeuser) { - const icon = ownerui.children('i'); - icon.attr('title', ownerprofile.name).tooltip('fixTitle'); - const styleString = `background-image:url(${ownerprofile.photo})`; - if (ownerprofile.photo && icon.attr('style') !== styleString) - icon.attr('style', styleString); - ownerui.show(); - } else { - ownerui.hide(); - } +window.owner = null +window.ownerprofile = null + +export function updateOwner () { + if (window.ownerui) { + if (window.owner && window.ownerprofile && window.owner !== window.lastchangeuser) { + const icon = ownerui.children('i') + icon.attr('title', window.ownerprofile.name).tooltip('fixTitle') + const styleString = `background-image:url(${window.ownerprofile.photo})` + if (window.ownerprofile.photo && icon.attr('style') !== styleString) { icon.attr('style', styleString) } + ownerui.show() + } else { + ownerui.hide() } + } } -//get title -function getTitle(view) { - let title = ""; - if (md && md.meta && md.meta.title && (typeof md.meta.title == "string" || typeof md.meta.title == "number")) { - title = md.meta.title; +// get title +function getTitle (view) { + let title = '' + if (md && md.meta && md.meta.title && (typeof md.meta.title === 'string' || typeof md.meta.title === 'number')) { + title = md.meta.title + } else { + const h1s = view.find('h1') + if (h1s.length > 0) { + title = h1s.first().text() } else { - const h1s = view.find("h1"); - if (h1s.length > 0) { - title = h1s.first().text(); - } else { - title = null; - } + title = null } - return title; + } + return title } -//render title -export function renderTitle(view) { - let title = getTitle(view); - if (title) { - title += ' - HackMD'; - } else { - title = 'HackMD - Collaborative markdown notes'; - } - return title; +// render title +export function renderTitle (view) { + let title = getTitle(view) + if (title) { + title += ' - HackMD' + } else { + title = 'HackMD - Collaborative markdown notes' + } + return title } -//render filename -export function renderFilename(view) { - let filename = getTitle(view); - if (!filename) { - filename = 'Untitled'; - } - return filename; +// render filename +export function renderFilename (view) { + let filename = getTitle(view) + if (!filename) { + filename = 'Untitled' + } + return filename } // render tags -export function renderTags(view) { - const tags = []; - const rawtags = []; - if (md && md.meta && md.meta.tags && (typeof md.meta.tags == "string" || typeof md.meta.tags == "number")) { - const metaTags = (`${md.meta.tags}`).split(','); - for (var i = 0; i < metaTags.length; i++) { - const text = metaTags[i].trim(); - if (text) rawtags.push(text); - } - } else { - view.find('h6').each((key, value) => { - if (/^tags/gmi.test($(value).text())) { - const codes = $(value).find("code"); - for (let i = 0; i < codes.length; i++) { - const text = codes[i].innerHTML.trim(); - if (text) rawtags.push(text); - } - } - }); +export function renderTags (view) { + const tags = [] + const rawtags = [] + if (md && md.meta && md.meta.tags && (typeof md.meta.tags === 'string' || typeof md.meta.tags === 'number')) { + const metaTags = (`${md.meta.tags}`).split(',') + for (let i = 0; i < metaTags.length; i++) { + const text = metaTags[i].trim() + if (text) rawtags.push(text) } - for (var i = 0; i < rawtags.length; i++) { - let found = false; - for (let j = 0; j < tags.length; j++) { - if (tags[j] == rawtags[i]) { - found = true; - break; - } + } else { + view.find('h6').each((key, value) => { + if (/^tags/gmi.test($(value).text())) { + const codes = $(value).find('code') + for (let i = 0; i < codes.length; i++) { + const text = codes[i].innerHTML.trim() + if (text) rawtags.push(text) } - if (!found) - tags.push(rawtags[i]); + } + }) + } + for (let i = 0; i < rawtags.length; i++) { + let found = false + for (let j = 0; j < tags.length; j++) { + if (tags[j] === rawtags[i]) { + found = true + break + } } - return tags; + if (!found) { tags.push(rawtags[i]) } + } + return tags } -function slugifyWithUTF8(text) { - let newText = S(text.toLowerCase()).trim().stripTags().dasherize().s; - newText = newText.replace(/([\!\"\#\$\%\&\'\(\)\*\+\,\.\/\:\;\<\=\>\?\@\[\\\]\^\`\{\|\}\~])/g, ''); - return newText; +function slugifyWithUTF8 (text) { + let newText = S(text.toLowerCase()).trim().stripTags().dasherize().s + newText = newText.replace(/([!"#$%&'()*+,./:;<=>?@[\\\]^`{|}~])/g, '') + return newText } -export function isValidURL(str) { - const pattern = new RegExp('^(https?:\\/\\/)?' + // protocol +export function isValidURL (str) { + const pattern = new RegExp('^(https?:\\/\\/)?' + // protocol '((([a-z\\d]([a-z\\d-]*[a-z\\d])*)\\.)+[a-z]{2,}|' + // domain name '((\\d{1,3}\\.){3}\\d{1,3}))' + // OR ip (v4) address '(\\:\\d+)?(\\/[-a-z\\d%_.~+]*)*' + // port and path '(\\?[;&a-z\\d%_.~+=-]*)?' + // query string - '(\\#[-a-z\\d_]*)?$', 'i'); // fragment locator - if (!pattern.test(str)) { - return false; - } else { - return true; - } + '(\\#[-a-z\\d_]*)?$', 'i') // fragment locator + if (!pattern.test(str)) { + return false + } else { + return true + } } -//parse meta -export function parseMeta(md, edit, view, toc, tocAffix) { - let lang = null; - let dir = null; - let breaks = true; - if (md && md.meta) { - const meta = md.meta; - lang = meta.lang; - dir = meta.dir; - breaks = meta.breaks; - } - //text language - if (lang && typeof lang == "string") { - view.attr('lang', lang); - toc.attr('lang', lang); - tocAffix.attr('lang', lang); - if (edit) - edit.attr('lang', lang); - } else { - view.removeAttr('lang'); - toc.removeAttr('lang'); - tocAffix.removeAttr('lang'); - if (edit) - edit.removeAttr('lang', lang); - } - //text direction - if (dir && typeof dir == "string") { - view.attr('dir', dir); - toc.attr('dir', dir); - tocAffix.attr('dir', dir); - } else { - view.removeAttr('dir'); - toc.removeAttr('dir'); - tocAffix.removeAttr('dir'); - } - //breaks - if (typeof breaks === 'boolean' && !breaks) { - md.options.breaks = false; - } else { - md.options.breaks = true; - } +// parse meta +export function parseMeta (md, edit, view, toc, tocAffix) { + let lang = null + let dir = null + let breaks = true + if (md && md.meta) { + const meta = md.meta + lang = meta.lang + dir = meta.dir + breaks = meta.breaks + } + // text language + if (lang && typeof lang === 'string') { + view.attr('lang', lang) + toc.attr('lang', lang) + tocAffix.attr('lang', lang) + if (edit) { edit.attr('lang', lang) } + } else { + view.removeAttr('lang') + toc.removeAttr('lang') + tocAffix.removeAttr('lang') + if (edit) { edit.removeAttr('lang', lang) } + } + // text direction + if (dir && typeof dir === 'string') { + view.attr('dir', dir) + toc.attr('dir', dir) + tocAffix.attr('dir', dir) + } else { + view.removeAttr('dir') + toc.removeAttr('dir') + tocAffix.removeAttr('dir') + } + // breaks + if (typeof breaks === 'boolean' && !breaks) { + md.options.breaks = false + } else { + md.options.breaks = true + } } -window.viewAjaxCallback = null; - -//regex for extra tags -const spaceregex = /\s*/; -const notinhtmltagregex = /(?![^<]*>|[^<>]*<\/)/; -let coloregex = /\[color=([#|\(|\)|\s|\,|\w]*?)\]/; -coloregex = new RegExp(coloregex.source + notinhtmltagregex.source, "g"); -let nameregex = /\[name=(.*?)\]/; -let timeregex = /\[time=([:|,|+|-|\(|\)|\s|\w]*?)\]/; -const nameandtimeregex = new RegExp(nameregex.source + spaceregex.source + timeregex.source + notinhtmltagregex.source, "g"); -nameregex = new RegExp(nameregex.source + notinhtmltagregex.source, "g"); -timeregex = new RegExp(timeregex.source + notinhtmltagregex.source, "g"); - -function replaceExtraTags(html) { - html = html.replace(coloregex, ''); - html = html.replace(nameandtimeregex, ' $1 $2'); - html = html.replace(nameregex, ' $1'); - html = html.replace(timeregex, ' $1'); - return html; +window.viewAjaxCallback = null + +// regex for extra tags +const spaceregex = /\s*/ +const notinhtmltagregex = /(?![^<]*>|[^<>]*<\/)/ +let coloregex = /\[color=([#|(|)|\s|,|\w]*?)\]/ +coloregex = new RegExp(coloregex.source + notinhtmltagregex.source, 'g') +let nameregex = /\[name=(.*?)\]/ +let timeregex = /\[time=([:|,|+|-|(|)|\s|\w]*?)\]/ +const nameandtimeregex = new RegExp(nameregex.source + spaceregex.source + timeregex.source + notinhtmltagregex.source, 'g') +nameregex = new RegExp(nameregex.source + notinhtmltagregex.source, 'g') +timeregex = new RegExp(timeregex.source + notinhtmltagregex.source, 'g') + +function replaceExtraTags (html) { + html = html.replace(coloregex, '') + html = html.replace(nameandtimeregex, ' $1 $2') + html = html.replace(nameregex, ' $1') + html = html.replace(timeregex, ' $1') + return html } -if (typeof mermaid !== 'undefined' && mermaid) mermaid.startOnLoad = false; +if (typeof window.mermaid !== 'undefined' && window.mermaid) window.mermaid.startOnLoad = false -//dynamic event or object binding here -export function finishView(view) { - //todo list - const lis = view.find('li.raw').removeClass("raw").sortByDepth().toArray(); +// dynamic event or object binding here +export function finishView (view) { + // todo list + const lis = view.find('li.raw').removeClass('raw').sortByDepth().toArray() - for (let li of lis) { - let html = $(li).clone()[0].innerHTML; - const p = $(li).children('p'); - if (p.length == 1) { - html = p.html(); - li = p[0]; - } - html = replaceExtraTags(html); - li.innerHTML = html; - let disabled = 'disabled'; - if(typeof editor !== 'undefined' && havePermission()) - disabled = ''; - if (/^\s*\[[x ]\]\s*/.test(html)) { - li.innerHTML = html.replace(/^\s*\[ \]\s*/, ``) - .replace(/^\s*\[x\]\s*/, ``); - li.setAttribute('class', 'task-list-item'); - } - if (typeof editor !== 'undefined' && havePermission()) - $(li).find('input').change(toggleTodoEvent); - //color tag in list will convert it to tag icon with color - const tag_color = $(li).closest('ul').find(".color"); - tag_color.each((key, value) => { - $(value).addClass('fa fa-tag').css('color', $(value).attr('data-color')); - }); + for (let li of lis) { + let html = $(li).clone()[0].innerHTML + const p = $(li).children('p') + if (p.length === 1) { + html = p.html() + li = p[0] } - - //youtube - view.find("div.youtube.raw").removeClass("raw") + html = replaceExtraTags(html) + li.innerHTML = html + let disabled = 'disabled' + if (typeof editor !== 'undefined' && window.havePermission()) { disabled = '' } + if (/^\s*\[[x ]\]\s*/.test(html)) { + li.innerHTML = html.replace(/^\s*\[ \]\s*/, ``) + .replace(/^\s*\[x\]\s*/, ``) + li.setAttribute('class', 'task-list-item') + } + if (typeof editor !== 'undefined' && window.havePermission()) { $(li).find('input').change(toggleTodoEvent) } + // color tag in list will convert it to tag icon with color + const tagColor = $(li).closest('ul').find('.color') + tagColor.each((key, value) => { + $(value).addClass('fa fa-tag').css('color', $(value).attr('data-color')) + }) + } + + // youtube + view.find('div.youtube.raw').removeClass('raw') .click(function () { - imgPlayiframe(this, '//www.youtube.com/embed/'); - }); - //vimeo - view.find("div.vimeo.raw").removeClass("raw") + imgPlayiframe(this, '//www.youtube.com/embed/') + }) + // vimeo + view.find('div.vimeo.raw').removeClass('raw') .click(function () { - imgPlayiframe(this, '//player.vimeo.com/video/'); + imgPlayiframe(this, '//player.vimeo.com/video/') }) .each((key, value) => { - $.ajax({ - type: 'GET', - url: `//vimeo.com/api/v2/video/${$(value).attr('data-videoid')}.json`, - jsonp: 'callback', - dataType: 'jsonp', - success(data) { - const thumbnail_src = data[0].thumbnail_large; - const image = ``; - $(value).prepend(image); - if(viewAjaxCallback) viewAjaxCallback(); - } - }); - }); - //gist - view.find("code[data-gist-id]").each((key, value) => { - if ($(value).children().length == 0) - $(value).gist(viewAjaxCallback); - }); - //sequence diagram - const sequences = view.find("div.sequence-diagram.raw").removeClass("raw"); - sequences.each((key, value) => { - try { - var $value = $(value); - const $ele = $(value).parent().parent(); - - const sequence = $value; - sequence.sequenceDiagram({ - theme: 'simple' - }); - - $ele.addClass('sequence-diagram'); - $value.children().unwrap().unwrap(); - const svg = $ele.find('> svg'); - svg[0].setAttribute('viewBox', `0 0 ${svg.attr('width')} ${svg.attr('height')}`); - svg[0].setAttribute('preserveAspectRatio', 'xMidYMid meet'); - } catch (err) { - $value.unwrap(); - $value.parent().append('
' + err + '
'); - console.warn(err); - } - }); - //flowchart - const flow = view.find("div.flow-chart.raw").removeClass("raw"); - flow.each((key, value) => { - try { - var $value = $(value); - const $ele = $(value).parent().parent(); - - const chart = flowchart.parse($value.text()); - $value.html(''); - chart.drawSVG(value, { - 'line-width': 2, - 'fill': 'none', - 'font-size': '16px', - 'font-family': "'Andale Mono', monospace" - }); - - $ele.addClass('flow-chart'); - $value.children().unwrap().unwrap(); - } catch (err) { - $value.unwrap(); - $value.parent().append('
' + err + '
'); - console.warn(err); - } - }); - //graphviz - var graphvizs = view.find("div.graphviz.raw").removeClass("raw"); - graphvizs.each(function (key, value) { - try { - var $value = $(value); - var $ele = $(value).parent().parent(); - - var graphviz = Viz($value.text()); - if (!graphviz) throw Error('viz.js output empty graph'); - $value.html(graphviz); - - $ele.addClass('graphviz'); - $value.children().unwrap().unwrap(); - } catch (err) { - $value.unwrap(); - $value.parent().append('
' + err + '
'); - console.warn(err); - } - }); - //mermaid - const mermaids = view.find("div.mermaid.raw").removeClass("raw"); - mermaids.each((key, value) => { - try { - var $value = $(value); - const $ele = $(value).closest('pre'); - - let mermaidError = null; - mermaid.parseError = (err, hash) => { - mermaidError = err; - }; - - if (mermaidAPI.parse($value.text())) { - $ele.addClass('mermaid'); - $ele.html($value.text()); - mermaid.init(undefined, $ele); - } else { - throw new Error(mermaidError); + $.ajax({ + type: 'GET', + url: `//vimeo.com/api/v2/video/${$(value).attr('data-videoid')}.json`, + jsonp: 'callback', + dataType: 'jsonp', + success (data) { + const thumbnailSrc = data[0].thumbnail_large + const image = `` + $(value).prepend(image) + if (window.viewAjaxCallback) window.viewAjaxCallback() } - } catch (err) { - $value.unwrap(); - $value.parent().append('
' + err + '
'); - console.warn(err); - } - }); - //image href new window(emoji not included) - const images = view.find("img.raw[src]").removeClass("raw"); - images.each((key, value) => { + }) + }) + // gist + view.find('code[data-gist-id]').each((key, value) => { + if ($(value).children().length === 0) { $(value).gist(window.viewAjaxCallback) } + }) + // sequence diagram + const sequences = view.find('div.sequence-diagram.raw').removeClass('raw') + sequences.each((key, value) => { + try { + var $value = $(value) + const $ele = $(value).parent().parent() + + const sequence = $value + sequence.sequenceDiagram({ + theme: 'simple' + }) + + $ele.addClass('sequence-diagram') + $value.children().unwrap().unwrap() + const svg = $ele.find('> svg') + svg[0].setAttribute('viewBox', `0 0 ${svg.attr('width')} ${svg.attr('height')}`) + svg[0].setAttribute('preserveAspectRatio', 'xMidYMid meet') + } catch (err) { + $value.unwrap() + $value.parent().append('
' + err + '
') + console.warn(err) + } + }) + // flowchart + const flow = view.find('div.flow-chart.raw').removeClass('raw') + flow.each((key, value) => { + try { + var $value = $(value) + const $ele = $(value).parent().parent() + + const chart = window.flowchart.parse($value.text()) + $value.html('') + chart.drawSVG(value, { + 'line-width': 2, + 'fill': 'none', + 'font-size': '16px', + 'font-family': "'Andale Mono', monospace" + }) + + $ele.addClass('flow-chart') + $value.children().unwrap().unwrap() + } catch (err) { + $value.unwrap() + $value.parent().append('
' + err + '
') + console.warn(err) + } + }) + // graphviz + var graphvizs = view.find('div.graphviz.raw').removeClass('raw') + graphvizs.each(function (key, value) { + try { + var $value = $(value) + var $ele = $(value).parent().parent() + + var graphviz = Viz($value.text()) + if (!graphviz) throw Error('viz.js output empty graph') + $value.html(graphviz) + + $ele.addClass('graphviz') + $value.children().unwrap().unwrap() + } catch (err) { + $value.unwrap() + $value.parent().append('
' + err + '
') + console.warn(err) + } + }) + // mermaid + const mermaids = view.find('div.mermaid.raw').removeClass('raw') + mermaids.each((key, value) => { + try { + var $value = $(value) + const $ele = $(value).closest('pre') + + let mermaidError = null + window.mermaid.parseError = (err, hash) => { + mermaidError = err + } + + if (window.mermaidAPI.parse($value.text())) { + $ele.addClass('mermaid') + $ele.html($value.text()) + window.mermaid.init(undefined, $ele) + } else { + throw new Error(mermaidError) + } + } catch (err) { + $value.unwrap() + $value.parent().append('
' + err + '
') + console.warn(err) + } + }) + // image href new window(emoji not included) + const images = view.find('img.raw[src]').removeClass('raw') + images.each((key, value) => { // if it's already wrapped by link, then ignore - const $value = $(value); - $value[0].onload = e => { - if(viewAjaxCallback) viewAjaxCallback(); - }; - }); - //blockquote - const blockquote = view.find("blockquote.raw").removeClass("raw"); - const blockquote_p = blockquote.find("p"); - blockquote_p.each((key, value) => { - let html = $(value).html(); - html = replaceExtraTags(html); - $(value).html(html); - }); - //color tag in blockquote will change its left border color - const blockquote_color = blockquote.find(".color"); - blockquote_color.each((key, value) => { - $(value).closest("blockquote").css('border-left-color', $(value).attr('data-color')); - }); - //slideshare - view.find("div.slideshare.raw").removeClass("raw") + const $value = $(value) + $value[0].onload = e => { + if (window.viewAjaxCallback) window.viewAjaxCallback() + } + }) + // blockquote + const blockquote = view.find('blockquote.raw').removeClass('raw') + const blockquoteP = blockquote.find('p') + blockquoteP.each((key, value) => { + let html = $(value).html() + html = replaceExtraTags(html) + $(value).html(html) + }) + // color tag in blockquote will change its left border color + const blockquoteColor = blockquote.find('.color') + blockquoteColor.each((key, value) => { + $(value).closest('blockquote').css('border-left-color', $(value).attr('data-color')) + }) + // slideshare + view.find('div.slideshare.raw').removeClass('raw') .each((key, value) => { - $.ajax({ - type: 'GET', - url: `//www.slideshare.net/api/oembed/2?url=http://www.slideshare.net/${$(value).attr('data-slideshareid')}&format=json`, - jsonp: 'callback', - dataType: 'jsonp', - success(data) { - const $html = $(data.html); - const iframe = $html.closest('iframe'); - const caption = $html.closest('div'); - const inner = $('
').append(iframe); - const height = iframe.attr('height'); - const width = iframe.attr('width'); - const ratio = (height / width) * 100; - inner.css('padding-bottom', `${ratio}%`); - $(value).html(inner).append(caption); - if(viewAjaxCallback) viewAjaxCallback(); - } - }); - }); - //speakerdeck - view.find("div.speakerdeck.raw").removeClass("raw") + $.ajax({ + type: 'GET', + url: `//www.slideshare.net/api/oembed/2?url=http://www.slideshare.net/${$(value).attr('data-slideshareid')}&format=json`, + jsonp: 'callback', + dataType: 'jsonp', + success (data) { + const $html = $(data.html) + const iframe = $html.closest('iframe') + const caption = $html.closest('div') + const inner = $('
').append(iframe) + const height = iframe.attr('height') + const width = iframe.attr('width') + const ratio = (height / width) * 100 + inner.css('padding-bottom', `${ratio}%`) + $(value).html(inner).append(caption) + if (window.viewAjaxCallback) window.viewAjaxCallback() + } + }) + }) + // speakerdeck + view.find('div.speakerdeck.raw').removeClass('raw') .each((key, value) => { - const url = `https://speakerdeck.com/oembed.json?url=https%3A%2F%2Fspeakerdeck.com%2F${encodeURIComponent($(value).attr('data-speakerdeckid'))}`; - //use yql because speakerdeck not support jsonp - $.ajax({ - url: 'https://query.yahooapis.com/v1/public/yql', - data: { - q: `select * from json where url ='${url}'`, - format: "json" - }, - dataType: "jsonp", - success(data) { - if (!data.query || !data.query.results) return; - const json = data.query.results.json; - const html = json.html; - var ratio = json.height / json.width; - $(value).html(html); - const iframe = $(value).children('iframe'); - const src = iframe.attr('src'); - if (src.indexOf('//') == 0) - iframe.attr('src', `https:${src}`); - const inner = $('
').append(iframe); - const height = iframe.attr('height'); - const width = iframe.attr('width'); - var ratio = (height / width) * 100; - inner.css('padding-bottom', `${ratio}%`); - $(value).html(inner); - if(viewAjaxCallback) viewAjaxCallback(); - } - }); - }); - //pdf - view.find("div.pdf.raw").removeClass("raw") + const url = `https://speakerdeck.com/oembed.json?url=https%3A%2F%2Fspeakerdeck.com%2F${encodeURIComponent($(value).attr('data-speakerdeckid'))}` + // use yql because speakerdeck not support jsonp + $.ajax({ + url: 'https://query.yahooapis.com/v1/public/yql', + data: { + q: `select * from json where url ='${url}'`, + format: 'json' + }, + dataType: 'jsonp', + success (data) { + if (!data.query || !data.query.results) return + const json = data.query.results.json + const html = json.html + var ratio = json.height / json.width + $(value).html(html) + const iframe = $(value).children('iframe') + const src = iframe.attr('src') + if (src.indexOf('//') === 0) { iframe.attr('src', `https:${src}`) } + const inner = $('
').append(iframe) + const height = iframe.attr('height') + const width = iframe.attr('width') + ratio = (height / width) * 100 + inner.css('padding-bottom', `${ratio}%`) + $(value).html(inner) + if (window.viewAjaxCallback) window.viewAjaxCallback() + } + }) + }) + // pdf + view.find('div.pdf.raw').removeClass('raw') .each(function (key, value) { - const url = $(value).attr('data-pdfurl'); - const inner = $('
'); - $(this).append(inner); - PDFObject.embed(url, inner, { - height: '400px' - }); - }); - //syntax highlighting - view.find("code.raw").removeClass("raw") + const url = $(value).attr('data-pdfurl') + const inner = $('
') + $(this).append(inner) + PDFObject.embed(url, inner, { + height: '400px' + }) + }) + // syntax highlighting + view.find('code.raw').removeClass('raw') .each((key, value) => { - const langDiv = $(value); - if (langDiv.length > 0) { - const reallang = langDiv[0].className.replace(/hljs|wrap/g, '').trim(); - const codeDiv = langDiv.find('.code'); - let code = ""; - if (codeDiv.length > 0) code = codeDiv.html(); - else code = langDiv.html(); - if (!reallang) { - var result = { - value: code - }; - } else if (reallang == "haskell" || reallang == "go" || reallang == "typescript" || reallang == "jsx") { - code = S(code).unescapeHTML().s; - var result = { - value: Prism.highlight(code, Prism.languages[reallang]) - }; - } else if (reallang == "tiddlywiki" || reallang == "mediawiki") { - code = S(code).unescapeHTML().s; - var result = { - value: Prism.highlight(code, Prism.languages.wiki) - }; - } else { - code = S(code).unescapeHTML().s; - const languages = hljs.listLanguages(); - if (!languages.includes(reallang)) { - var result = hljs.highlightAuto(code); - } else { - var result = hljs.highlight(reallang, code); - } - } - if (codeDiv.length > 0) codeDiv.html(result.value); - else langDiv.html(result.value); + const langDiv = $(value) + if (langDiv.length > 0) { + const reallang = langDiv[0].className.replace(/hljs|wrap/g, '').trim() + const codeDiv = langDiv.find('.code') + let code = '' + if (codeDiv.length > 0) code = codeDiv.html() + else code = langDiv.html() + var result + if (!reallang) { + result = { + value: code + } + } else if (reallang === 'haskell' || reallang === 'go' || reallang === 'typescript' || reallang === 'jsx') { + code = S(code).unescapeHTML().s + result = { + value: Prism.highlight(code, Prism.languages[reallang]) + } + } else if (reallang === 'tiddlywiki' || reallang === 'mediawiki') { + code = S(code).unescapeHTML().s + result = { + value: Prism.highlight(code, Prism.languages.wiki) + } + } else { + code = S(code).unescapeHTML().s + const languages = hljs.listLanguages() + if (!languages.includes(reallang)) { + result = hljs.highlightAuto(code) + } else { + result = hljs.highlight(reallang, code) + } } - }); - //mathjax - const mathjaxdivs = view.find('span.mathjax.raw').removeClass("raw").toArray(); - try { - if (mathjaxdivs.length > 1) { - MathJax.Hub.Queue(["Typeset", MathJax.Hub, mathjaxdivs]); - MathJax.Hub.Queue(viewAjaxCallback); - } else if (mathjaxdivs.length > 0) { - MathJax.Hub.Queue(["Typeset", MathJax.Hub, mathjaxdivs[0]]); - MathJax.Hub.Queue(viewAjaxCallback); - } - } catch (err) { - console.warn(err); + if (codeDiv.length > 0) codeDiv.html(result.value) + else langDiv.html(result.value) + } + }) + // mathjax + const mathjaxdivs = view.find('span.mathjax.raw').removeClass('raw').toArray() + try { + if (mathjaxdivs.length > 1) { + window.MathJax.Hub.Queue(['Typeset', window.MathJax.Hub, mathjaxdivs]) + window.MathJax.Hub.Queue(window.viewAjaxCallback) + } else if (mathjaxdivs.length > 0) { + window.MathJax.Hub.Queue(['Typeset', window.MathJax.Hub, mathjaxdivs[0]]) + window.MathJax.Hub.Queue(window.viewAjaxCallback) } - //render title - document.title = renderTitle(view); + } catch (err) { + console.warn(err) + } + // render title + document.title = renderTitle(view) } -//only static transform should be here -export function postProcess(code) { - const result = $(`
${code}
`); - //link should open in new window or tab - result.find('a:not([href^="#"]):not([target])').attr('target', '_blank'); - //update continue line numbers - const linenumberdivs = result.find('.gutter.linenumber').toArray(); - for (let i = 0; i < linenumberdivs.length; i++) { - if ($(linenumberdivs[i]).hasClass('continue')) { - const startnumber = linenumberdivs[i - 1] ? parseInt($(linenumberdivs[i - 1]).find('> span').last().attr('data-linenumber')) : 0; - $(linenumberdivs[i]).find('> span').each((key, value) => { - $(value).attr('data-linenumber', startnumber + key + 1); - }); - } - } - // show yaml meta paring error - if (md.metaError) { - var warning = result.find('div#meta-error'); - if (warning && warning.length > 0) { - warning.text(md.metaError) - } else { - warning = $('
' + md.metaError + '
') - result.prepend(warning); - } +// only static transform should be here +export function postProcess (code) { + const result = $(`
${code}
`) + // link should open in new window or tab + result.find('a:not([href^="#"]):not([target])').attr('target', '_blank') + // update continue line numbers + const linenumberdivs = result.find('.gutter.linenumber').toArray() + for (let i = 0; i < linenumberdivs.length; i++) { + if ($(linenumberdivs[i]).hasClass('continue')) { + const startnumber = linenumberdivs[i - 1] ? parseInt($(linenumberdivs[i - 1]).find('> span').last().attr('data-linenumber')) : 0 + $(linenumberdivs[i]).find('> span').each((key, value) => { + $(value).attr('data-linenumber', startnumber + key + 1) + }) + } + } + // show yaml meta paring error + if (md.metaError) { + var warning = result.find('div#meta-error') + if (warning && warning.length > 0) { + warning.text(md.metaError) + } else { + warning = $('
' + md.metaError + '
') + result.prepend(warning) } - return result; + } + return result } -window.postProcess = postProcess; - -function generateCleanHTML(view) { - const src = view.clone(); - const eles = src.find('*'); - //remove syncscroll parts - eles.removeClass('part'); - src.find('*[class=""]').removeAttr('class'); - eles.removeAttr('data-startline data-endline'); - src.find("a[href^='#'][smoothhashscroll]").removeAttr('smoothhashscroll'); - //remove gist content - src.find("code[data-gist-id]").children().remove(); - //disable todo list - src.find("input.task-list-item-checkbox").attr('disabled', ''); - //replace emoji image path - src.find("img.emoji").each((key, value) => { - let name = $(value).attr('alt'); - name = name.substr(1); - name = name.slice(0, name.length - 1); - $(value).attr('src', `https://www.tortue.me/emoji/${name}.png`); - }); - //replace video to iframe - src.find("div[data-videoid]").each((key, value) => { - const id = $(value).attr('data-videoid'); - const style = $(value).attr('style'); - let url = null; - if ($(value).hasClass('youtube')) { - url = 'https://www.youtube.com/embed/'; - } else if ($(value).hasClass('vimeo')) { - url = 'https://player.vimeo.com/video/'; - } - if (url) { - const iframe = $(''); - iframe.attr('src', url + id); - iframe.attr('style', style); - $(value).html(iframe); - } - }); - return src; +window.postProcess = postProcess + +function generateCleanHTML (view) { + const src = view.clone() + const eles = src.find('*') + // remove syncscroll parts + eles.removeClass('part') + src.find('*[class=""]').removeAttr('class') + eles.removeAttr('data-startline data-endline') + src.find("a[href^='#'][smoothhashscroll]").removeAttr('smoothhashscroll') + // remove gist content + src.find('code[data-gist-id]').children().remove() + // disable todo list + src.find('input.task-list-item-checkbox').attr('disabled', '') + // replace emoji image path + src.find('img.emoji').each((key, value) => { + let name = $(value).attr('alt') + name = name.substr(1) + name = name.slice(0, name.length - 1) + $(value).attr('src', `https://www.tortue.me/emoji/${name}.png`) + }) + // replace video to iframe + src.find('div[data-videoid]').each((key, value) => { + const id = $(value).attr('data-videoid') + const style = $(value).attr('style') + let url = null + if ($(value).hasClass('youtube')) { + url = 'https://www.youtube.com/embed/' + } else if ($(value).hasClass('vimeo')) { + url = 'https://player.vimeo.com/video/' + } + if (url) { + const iframe = $('') + iframe.attr('src', url + id) + iframe.attr('style', style) + $(value).html(iframe) + } + }) + return src } -export function exportToRawHTML(view) { - const filename = `${renderFilename(ui.area.markdown)}.html`; - const src = generateCleanHTML(view); - $(src).find('a.anchor').remove(); - const html = src[0].outerHTML; - const blob = new Blob([html], { - type: "text/html;charset=utf-8" - }); - saveAs(blob, filename, true); +export function exportToRawHTML (view) { + const filename = `${renderFilename(window.ui.area.markdown)}.html` + const src = generateCleanHTML(view) + $(src).find('a.anchor').remove() + const html = src[0].outerHTML + const blob = new Blob([html], { + type: 'text/html;charset=utf-8' + }) + saveAs(blob, filename, true) } -//extract markdown body to html and compile to template -export function exportToHTML(view) { - const title = renderTitle(ui.area.markdown); - const filename = `${renderFilename(ui.area.markdown)}.html`; - const src = generateCleanHTML(view); - //generate toc - const toc = $('#ui-toc').clone(); - toc.find('*').removeClass('active').find("a[href^='#'][smoothhashscroll]").removeAttr('smoothhashscroll'); - const tocAffix = $('#ui-toc-affix').clone(); - tocAffix.find('*').removeClass('active').find("a[href^='#'][smoothhashscroll]").removeAttr('smoothhashscroll'); - //generate html via template - $.get(`${serverurl}/build/html.min.css`, css => { - $.get(`${serverurl}/views/html.hbs`, data => { - const template = Handlebars.compile(data); - const context = { - url: serverurl, - title, - css, - html: src[0].outerHTML, - 'ui-toc': toc.html(), - 'ui-toc-affix': tocAffix.html(), - lang: (md && md.meta && md.meta.lang) ? `lang="${md.meta.lang}"` : null, - dir: (md && md.meta && md.meta.dir) ? `dir="${md.meta.dir}"` : null - }; - const html = template(context); +// extract markdown body to html and compile to template +export function exportToHTML (view) { + const title = renderTitle(window.ui.area.markdown) + const filename = `${renderFilename(window.ui.area.markdown)}.html` + const src = generateCleanHTML(view) + // generate toc + const toc = $('#ui-toc').clone() + toc.find('*').removeClass('active').find("a[href^='#'][smoothhashscroll]").removeAttr('smoothhashscroll') + const tocAffix = $('#ui-toc-affix').clone() + tocAffix.find('*').removeClass('active').find("a[href^='#'][smoothhashscroll]").removeAttr('smoothhashscroll') + // generate html via template + $.get(`${serverurl}/build/html.min.css`, css => { + $.get(`${serverurl}/views/html.hbs`, data => { + const template = window.Handlebars.compile(data) + const context = { + url: serverurl, + title, + css, + html: src[0].outerHTML, + 'ui-toc': toc.html(), + 'ui-toc-affix': tocAffix.html(), + lang: (md && md.meta && md.meta.lang) ? `lang="${md.meta.lang}"` : null, + dir: (md && md.meta && md.meta.dir) ? `dir="${md.meta.dir}"` : null + } + const html = template(context) // console.log(html); - const blob = new Blob([html], { - type: "text/html;charset=utf-8" - }); - saveAs(blob, filename, true); - }); - }); + const blob = new Blob([html], { + type: 'text/html;charset=utf-8' + }) + saveAs(blob, filename, true) + }) + }) } -//jQuery sortByDepth +// jQuery sortByDepth $.fn.sortByDepth = function () { - const ar = this.map(function () { - return { - length: $(this).parents().length, - elt: this - } - }).get(); - - const result = []; - let i = ar.length; - ar.sort((a, b) => a.length - b.length); - while (i--) { - result.push(ar[i].elt); - } - return $(result); -}; - -function toggleTodoEvent(e) { - const startline = $(this).closest('li').attr('data-startline') - 1; - const line = editor.getLine(startline); - const matches = line.match(/^[>\s]*[\-\+\*]\s\[([x ])\]/); - if (matches && matches.length >= 2) { - let checked = null; - if (matches[1] == 'x') - checked = true; - else if (matches[1] == ' ') - checked = false; - const replacements = matches[0].match(/(^[>\s]*[\-\+\*]\s\[)([x ])(\])/); - editor.replaceRange(checked ? ' ' : 'x', { - line: startline, - ch: replacements[1].length - }, { - line: startline, - ch: replacements[1].length + 1 - }, '+input'); + const ar = this.map(function () { + return { + length: $(this).parents().length, + elt: this } + }).get() + + const result = [] + let i = ar.length + ar.sort((a, b) => a.length - b.length) + while (i--) { + result.push(ar[i].elt) + } + return $(result) } -//remove hash -function removeHash() { - history.pushState("", document.title, window.location.pathname + window.location.search); +function toggleTodoEvent (e) { + const startline = $(this).closest('li').attr('data-startline') - 1 + const line = window.editor.getLine(startline) + const matches = line.match(/^[>\s]*[-+*]\s\[([x ])\]/) + if (matches && matches.length >= 2) { + let checked = null + if (matches[1] === 'x') { checked = true } else if (matches[1] === ' ') { checked = false } + const replacements = matches[0].match(/(^[>\s]*[-+*]\s\[)([x ])(\])/) + window.editor.replaceRange(checked ? ' ' : 'x', { + line: startline, + ch: replacements[1].length + }, { + line: startline, + ch: replacements[1].length + 1 + }, '+input') + } } -let tocExpand = false; +// remove hash +function removeHash () { + history.pushState('', document.title, window.location.pathname + window.location.search) +} -function checkExpandToggle() { - const toc = $('.ui-toc-dropdown .toc'); - const toggle = $('.expand-toggle'); - if (!tocExpand) { - toc.removeClass('expand'); - toggle.text('Expand all'); - } else { - toc.addClass('expand'); - toggle.text('Collapse all'); - } +let tocExpand = false + +function checkExpandToggle () { + const toc = $('.ui-toc-dropdown .toc') + const toggle = $('.expand-toggle') + if (!tocExpand) { + toc.removeClass('expand') + toggle.text('Expand all') + } else { + toc.addClass('expand') + toggle.text('Collapse all') + } } -//toc -export function generateToc(id) { - const target = $(`#${id}`); - target.html(''); - new Toc('doc', { - 'level': 3, - 'top': -1, - 'class': 'toc', - 'ulClass': 'nav', - 'targetId': id, - 'process': getHeaderContent - }); - if (target.text() == 'undefined') - target.html(''); - const tocMenu = $('
Expand all'); - const backtotop = $('Back to top'); - const gotobottom = $('Go to bottom'); - checkExpandToggle(); - toggle.click(e => { - e.preventDefault(); - e.stopPropagation(); - tocExpand = !tocExpand; - checkExpandToggle(); - }); - backtotop.click(e => { - e.preventDefault(); - e.stopPropagation(); - if (scrollToTop) - scrollToTop(); - removeHash(); - }); - gotobottom.click(e => { - e.preventDefault(); - e.stopPropagation(); - if (scrollToBottom) - scrollToBottom(); - removeHash(); - }); - tocMenu.append(toggle).append(backtotop).append(gotobottom); - target.append(tocMenu); +// toc +export function generateToc (id) { + const target = $(`#${id}`) + target.html('') + /* eslint-disable no-unused-vars */ + var toc = new window.Toc('doc', { + 'level': 3, + 'top': -1, + 'class': 'toc', + 'ulClass': 'nav', + 'targetId': id, + 'process': getHeaderContent + }) + /* eslint-enable no-unsed-vars */ + if (target.text() === 'undefined') { target.html('') } + const tocMenu = $('
Expand all') + const backtotop = $('Back to top') + const gotobottom = $('Go to bottom') + checkExpandToggle() + toggle.click(e => { + e.preventDefault() + e.stopPropagation() + tocExpand = !tocExpand + checkExpandToggle() + }) + backtotop.click(e => { + e.preventDefault() + e.stopPropagation() + if (window.scrollToTop) { window.scrollToTop() } + removeHash() + }) + gotobottom.click(e => { + e.preventDefault() + e.stopPropagation() + if (window.scrollToBottom) { window.scrollToBottom() } + removeHash() + }) + tocMenu.append(toggle).append(backtotop).append(gotobottom) + target.append(tocMenu) } -//smooth all hash trigger scrolling -export function smoothHashScroll() { - const hashElements = $("a[href^='#']:not([smoothhashscroll])").toArray(); +// smooth all hash trigger scrolling +export function smoothHashScroll () { + const hashElements = $("a[href^='#']:not([smoothhashscroll])").toArray() - for (const element of hashElements) { - const $element = $(element); - const hash = element.hash; - if (hash) { - $element.on('click', function (e) { + for (const element of hashElements) { + const $element = $(element) + const hash = element.hash + if (hash) { + $element.on('click', function (e) { // store hash - const hash = decodeURIComponent(this.hash); + const hash = decodeURIComponent(this.hash) // escape special characters in jquery selector - const $hash = $(hash.replace(/(:|\.|\[|\]|,)/g, "\\$1")); + const $hash = $(hash.replace(/(:|\.|\[|\]|,)/g, '\\$1')) // return if no element been selected - if ($hash.length <= 0) return; + if ($hash.length <= 0) return // prevent default anchor click behavior - e.preventDefault(); + e.preventDefault() // animate - $('body, html').stop(true, true).animate({ - scrollTop: $hash.offset().top - }, 100, "linear", () => { + $('body, html').stop(true, true).animate({ + scrollTop: $hash.offset().top + }, 100, 'linear', () => { // when done, add hash to url // (default click behaviour) - window.location.hash = hash; - }); - }); - $element.attr('smoothhashscroll', ''); - } + window.location.hash = hash + }) + }) + $element.attr('smoothhashscroll', '') } + } } -function imgPlayiframe(element, src) { - if (!$(element).attr("data-videoid")) return; - const iframe = $(""); - $(iframe).attr("src", `${src + $(element).attr("data-videoid")}?autoplay=1`); - $(element).find('img').css('visibility', 'hidden'); - $(element).append(iframe); +function imgPlayiframe (element, src) { + if (!$(element).attr('data-videoid')) return + const iframe = $("") + $(iframe).attr('src', `${src + $(element).attr('data-videoid')}?autoplay=1`) + $(element).find('img').css('visibility', 'hidden') + $(element).append(iframe) } const anchorForId = id => { - const anchor = document.createElement("a"); - anchor.className = "anchor hidden-xs"; - anchor.href = `#${id}`; - anchor.innerHTML = ""; - anchor.title = id; - return anchor; -}; + const anchor = document.createElement('a') + anchor.className = 'anchor hidden-xs' + anchor.href = `#${id}` + anchor.innerHTML = '' + anchor.title = id + return anchor +} const linkifyAnchors = (level, containingElement) => { - const headers = containingElement.getElementsByTagName(`h${level}`); - - for (let i = 0, l = headers.length; i < l; i++) { - let header = headers[i]; - if (header.getElementsByClassName("anchor").length == 0) { - if (typeof header.id == "undefined" || header.id == "") { - //to escape characters not allow in css and humanize - const id = slugifyWithUTF8(getHeaderContent(header)); - header.id = id; - } - header.insertBefore(anchorForId(header.id), header.firstChild); - } + const headers = containingElement.getElementsByTagName(`h${level}`) + + for (let i = 0, l = headers.length; i < l; i++) { + let header = headers[i] + if (header.getElementsByClassName('anchor').length === 0) { + if (typeof header.id === 'undefined' || header.id === '') { + // to escape characters not allow in css and humanize + const id = slugifyWithUTF8(getHeaderContent(header)) + header.id = id + } + header.insertBefore(anchorForId(header.id), header.firstChild) } -}; + } +} -export function autoLinkify(view) { - const contentBlock = view[0]; - if (!contentBlock) { - return; - } - for (let level = 1; level <= 6; level++) { - linkifyAnchors(level, contentBlock); - } +export function autoLinkify (view) { + const contentBlock = view[0] + if (!contentBlock) { + return + } + for (let level = 1; level <= 6; level++) { + linkifyAnchors(level, contentBlock) + } } -function getHeaderContent(header) { - const headerHTML = $(header).clone(); - headerHTML.find('.MathJax_Preview').remove(); - headerHTML.find('.MathJax').remove(); - return headerHTML[0].innerHTML; +function getHeaderContent (header) { + const headerHTML = $(header).clone() + headerHTML.find('.MathJax_Preview').remove() + headerHTML.find('.MathJax').remove() + return headerHTML[0].innerHTML } -export function deduplicatedHeaderId(view) { - const headers = view.find(':header.raw').removeClass('raw').toArray(); - for (let i = 0; i < headers.length; i++) { - const id = $(headers[i]).attr('id'); - if (!id) continue; - const duplicatedHeaders = view.find(`:header[id="${id}"]`).toArray(); - for (let j = 0; j < duplicatedHeaders.length; j++) { - if (duplicatedHeaders[j] != headers[i]) { - const newId = id + j; - const $duplicatedHeader = $(duplicatedHeaders[j]); - $duplicatedHeader.attr('id', newId); - const $headerLink = $duplicatedHeader.find(`> a.anchor[href="#${id}"]`); - $headerLink.attr('href', `#${newId}`); - $headerLink.attr('title', newId); - } - } +export function deduplicatedHeaderId (view) { + const headers = view.find(':header.raw').removeClass('raw').toArray() + for (let i = 0; i < headers.length; i++) { + const id = $(headers[i]).attr('id') + if (!id) continue + const duplicatedHeaders = view.find(`:header[id="${id}"]`).toArray() + for (let j = 0; j < duplicatedHeaders.length; j++) { + if (duplicatedHeaders[j] !== headers[i]) { + const newId = id + j + const $duplicatedHeader = $(duplicatedHeaders[j]) + $duplicatedHeader.attr('id', newId) + const $headerLink = $duplicatedHeader.find(`> a.anchor[href="#${id}"]`) + $headerLink.attr('href', `#${newId}`) + $headerLink.attr('title', newId) + } } + } } -export function renderTOC(view) { - const tocs = view.find('.toc').toArray(); - for (let i = 0; i < tocs.length; i++) { - const toc = $(tocs[i]); - const id = `toc${i}`; - toc.attr('id', id); - const target = $(`#${id}`); - target.html(''); - new Toc('doc', { - 'level': 3, - 'top': -1, - 'class': 'toc', - 'targetId': id, - 'process': getHeaderContent - }); - if (target.text() == 'undefined') - target.html(''); - target.replaceWith(target.html()); - } +export function renderTOC (view) { + const tocs = view.find('.toc').toArray() + for (let i = 0; i < tocs.length; i++) { + const toc = $(tocs[i]) + const id = `toc${i}` + toc.attr('id', id) + const target = $(`#${id}`) + target.html('') + /* eslint-disable no-unused-vars */ + var toc = new window.Toc('doc', { + 'level': 3, + 'top': -1, + 'class': 'toc', + 'targetId': id, + 'process': getHeaderContent + }) + /* eslint-enable no-unused-vars */ + if (target.text() === 'undefined') { target.html('') } + target.replaceWith(target.html()) + } } -export function scrollToHash() { - const hash = location.hash; - location.hash = ""; - location.hash = hash; +export function scrollToHash () { + const hash = location.hash + location.hash = '' + location.hash = hash } -function highlightRender(code, lang) { - if (!lang || /no(-?)highlight|plain|text/.test(lang)) - return; - code = S(code).escapeHTML().s - if (lang == 'sequence') { - return `
${code}
`; - } else if (lang == 'flow') { - return `
${code}
`; - } else if (lang == 'graphviz') { - return `
${code}
`; - } else if (lang == 'mermaid') { - return `
${code}
`; +function highlightRender (code, lang) { + if (!lang || /no(-?)highlight|plain|text/.test(lang)) { return } + code = S(code).escapeHTML().s + if (lang === 'sequence') { + return `
${code}
` + } else if (lang === 'flow') { + return `
${code}
` + } else if (lang === 'graphviz') { + return `
${code}
` + } else if (lang === 'mermaid') { + return `
${code}
` + } + const result = { + value: code + } + const showlinenumbers = /=$|=\d+$|=\+$/.test(lang) + if (showlinenumbers) { + let startnumber = 1 + const matches = lang.match(/=(\d+)$/) + if (matches) { startnumber = parseInt(matches[1]) } + const lines = result.value.split('\n') + const linenumbers = [] + for (let i = 0; i < lines.length - 1; i++) { + linenumbers[i] = `` } - const result = { - value: code - }; - const showlinenumbers = /\=$|\=\d+$|\=\+$/.test(lang); - if (showlinenumbers) { - let startnumber = 1; - const matches = lang.match(/\=(\d+)$/); - if (matches) - startnumber = parseInt(matches[1]); - const lines = result.value.split('\n'); - const linenumbers = []; - for (let i = 0; i < lines.length - 1; i++) { - linenumbers[i] = ``; - } - const continuelinenumber = /\=\+$/.test(lang); - const linegutter = `
${linenumbers.join('\n')}
`; - result.value = `
${linegutter}
${result.value}
`; - } - return result.value; + const continuelinenumber = /=\+$/.test(lang) + const linegutter = `
${linenumbers.join('\n')}
` + result.value = `
${linegutter}
${result.value}
` + } + return result.value } -import markdownit from 'markdown-it'; -import markdownitContainer from 'markdown-it-container'; +import markdownit from 'markdown-it' +import markdownitContainer from 'markdown-it-container' export let md = markdownit('default', { - html: true, - breaks: true, - langPrefix: "", - linkify: true, - typographer: true, - highlight: highlightRender -}); -window.md = md; - -md.use(require('markdown-it-abbr')); -md.use(require('markdown-it-footnote')); -md.use(require('markdown-it-deflist')); -md.use(require('markdown-it-mark')); -md.use(require('markdown-it-ins')); -md.use(require('markdown-it-sub')); -md.use(require('markdown-it-sup')); + html: true, + breaks: true, + langPrefix: '', + linkify: true, + typographer: true, + highlight: highlightRender +}) +window.md = md + +md.use(require('markdown-it-abbr')) +md.use(require('markdown-it-footnote')) +md.use(require('markdown-it-deflist')) +md.use(require('markdown-it-mark')) +md.use(require('markdown-it-ins')) +md.use(require('markdown-it-sub')) +md.use(require('markdown-it-sup')) md.use(require('markdown-it-mathjax')({ - beforeMath: '', - afterMath: '', - beforeInlineMath: '\\(', - afterInlineMath: '\\)', - beforeDisplayMath: '\\[', - afterDisplayMath: '\\]' -})); -md.use(require('markdown-it-imsize')); + beforeMath: '', + afterMath: '', + beforeInlineMath: '\\(', + afterInlineMath: '\\)', + beforeDisplayMath: '\\[', + afterDisplayMath: '\\]' +})) +md.use(require('markdown-it-imsize')) md.use(require('markdown-it-emoji'), { - shortcuts: {} -}); - -emojify.setConfig({ - blacklist: { - elements: ['script', 'textarea', 'a', 'pre', 'code', 'svg'], - classes: ['no-emojify'] - }, - img_dir: `${serverurl}/build/emojify.js/dist/images/basic`, - ignore_emoticons: true -}); - -md.renderer.rules.emoji = (token, idx) => emojify.replace(`:${token[idx].markup}:`); - -function renderContainer(tokens, idx, options, env, self) { - tokens[idx].attrJoin('role', 'alert'); - tokens[idx].attrJoin('class', 'alert'); - tokens[idx].attrJoin('class', `alert-${tokens[idx].info.trim()}`); - return self.renderToken(...arguments); + shortcuts: {} +}) + +window.emojify.setConfig({ + blacklist: { + elements: ['script', 'textarea', 'a', 'pre', 'code', 'svg'], + classes: ['no-emojify'] + }, + img_dir: `${serverurl}/build/emojify.js/dist/images/basic`, + ignore_emoticons: true +}) + +md.renderer.rules.emoji = (token, idx) => window.emojify.replace(`:${token[idx].markup}:`) + +function renderContainer (tokens, idx, options, env, self) { + tokens[idx].attrJoin('role', 'alert') + tokens[idx].attrJoin('class', 'alert') + tokens[idx].attrJoin('class', `alert-${tokens[idx].info.trim()}`) + return self.renderToken(...arguments) } -md.use(markdownitContainer, 'success', { render: renderContainer }); -md.use(markdownitContainer, 'info', { render: renderContainer }); -md.use(markdownitContainer, 'warning', { render: renderContainer }); -md.use(markdownitContainer, 'danger', { render: renderContainer }); +md.use(markdownitContainer, 'success', { render: renderContainer }) +md.use(markdownitContainer, 'info', { render: renderContainer }) +md.use(markdownitContainer, 'warning', { render: renderContainer }) +md.use(markdownitContainer, 'danger', { render: renderContainer }) md.renderer.rules.image = function (tokens, idx, options, env, self) { - tokens[idx].attrJoin('class', 'raw'); - return self.renderToken(...arguments); -}; + tokens[idx].attrJoin('class', 'raw') + return self.renderToken(...arguments) +} md.renderer.rules.list_item_open = function (tokens, idx, options, env, self) { - tokens[idx].attrJoin('class', 'raw'); - return self.renderToken(...arguments); -}; + tokens[idx].attrJoin('class', 'raw') + return self.renderToken(...arguments) +} md.renderer.rules.blockquote_open = function (tokens, idx, options, env, self) { - tokens[idx].attrJoin('class', 'raw'); - return self.renderToken(...arguments); -}; + tokens[idx].attrJoin('class', 'raw') + return self.renderToken(...arguments) +} md.renderer.rules.heading_open = function (tokens, idx, options, env, self) { - tokens[idx].attrJoin('class', 'raw'); - return self.renderToken(...arguments); -}; + tokens[idx].attrJoin('class', 'raw') + return self.renderToken(...arguments) +} md.renderer.rules.fence = (tokens, idx, options, env, self) => { - const token = tokens[idx]; - const info = token.info ? md.utils.unescapeAll(token.info).trim() : ''; - let langName = ''; - let highlighted; - - if (info) { - langName = info.split(/\s+/g)[0]; - if (/\!$/.test(info)) token.attrJoin('class', 'wrap'); - token.attrJoin('class', options.langPrefix + langName.replace(/\=$|\=\d+$|\=\+$|\!$|\=\!$/, '')); - token.attrJoin('class', 'hljs'); - token.attrJoin('class', 'raw'); - } - - if (options.highlight) { - highlighted = options.highlight(token.content, langName) || md.utils.escapeHtml(token.content); - } else { - highlighted = md.utils.escapeHtml(token.content); - } - - if (highlighted.indexOf('${highlighted}\n`; -}; + const token = tokens[idx] + const info = token.info ? md.utils.unescapeAll(token.info).trim() : '' + let langName = '' + let highlighted + + if (info) { + langName = info.split(/\s+/g)[0] + if (/!$/.test(info)) token.attrJoin('class', 'wrap') + token.attrJoin('class', options.langPrefix + langName.replace(/=$|=\d+$|=\+$|!$|=!$/, '')) + token.attrJoin('class', 'hljs') + token.attrJoin('class', 'raw') + } + + if (options.highlight) { + highlighted = options.highlight(token.content, langName) || md.utils.escapeHtml(token.content) + } else { + highlighted = md.utils.escapeHtml(token.content) + } + + if (highlighted.indexOf('${highlighted}\n` +} /* Defined regex markdown it plugins */ -import Plugin from 'markdown-it-regexp'; +import Plugin from 'markdown-it-regexp' -//youtube +// youtube const youtubePlugin = new Plugin( // regexp to match /{%youtube\s*([\d\D]*?)\s*%}/, (match, utils) => { - const videoid = match[1]; - if (!videoid) return; - const div = $('
'); - div.attr('data-videoid', videoid); - const thumbnail_src = `//img.youtube.com/vi/${videoid}/hqdefault.jpg`; - const image = ``; - div.append(image); - const icon = ''; - div.append(icon); - return div[0].outerHTML; + const videoid = match[1] + if (!videoid) return + const div = $('
') + div.attr('data-videoid', videoid) + const thumbnailSrc = `//img.youtube.com/vi/${videoid}/hqdefault.jpg` + const image = `` + div.append(image) + const icon = '' + div.append(icon) + return div[0].outerHTML } -); -//vimeo +) +// vimeo const vimeoPlugin = new Plugin( // regexp to match /{%vimeo\s*([\d\D]*?)\s*%}/, (match, utils) => { - const videoid = match[1]; - if (!videoid) return; - const div = $('
'); - div.attr('data-videoid', videoid); - const icon = ''; - div.append(icon); - return div[0].outerHTML; + const videoid = match[1] + if (!videoid) return + const div = $('
') + div.attr('data-videoid', videoid) + const icon = '' + div.append(icon) + return div[0].outerHTML } -); -//gist +) +// gist const gistPlugin = new Plugin( // regexp to match /{%gist\s*([\d\D]*?)\s*%}/, (match, utils) => { - const gistid = match[1]; - const code = ``; - return code; + const gistid = match[1] + const code = `` + return code } -); -//TOC +) +// TOC const tocPlugin = new Plugin( // regexp to match /^\[TOC\]$/i, (match, utils) => '
' -); -//slideshare +) +// slideshare const slidesharePlugin = new Plugin( // regexp to match /{%slideshare\s*([\d\D]*?)\s*%}/, (match, utils) => { - const slideshareid = match[1]; - const div = $('
'); - div.attr('data-slideshareid', slideshareid); - return div[0].outerHTML; + const slideshareid = match[1] + const div = $('
') + div.attr('data-slideshareid', slideshareid) + return div[0].outerHTML } -); -//speakerdeck +) +// speakerdeck const speakerdeckPlugin = new Plugin( // regexp to match /{%speakerdeck\s*([\d\D]*?)\s*%}/, (match, utils) => { - const speakerdeckid = match[1]; - const div = $('
'); - div.attr('data-speakerdeckid', speakerdeckid); - return div[0].outerHTML; + const speakerdeckid = match[1] + const div = $('
') + div.attr('data-speakerdeckid', speakerdeckid) + return div[0].outerHTML } -); -//pdf +) +// pdf const pdfPlugin = new Plugin( // regexp to match /{%pdf\s*([\d\D]*?)\s*%}/, (match, utils) => { - const pdfurl = match[1]; - if (!isValidURL(pdfurl)) return match[0]; - const div = $('
'); - div.attr('data-pdfurl', pdfurl); - return div[0].outerHTML; + const pdfurl = match[1] + if (!isValidURL(pdfurl)) return match[0] + const div = $('
') + div.attr('data-pdfurl', pdfurl) + return div[0].outerHTML } -); +) -//yaml meta, from https://github.com/eugeneware/remarkable-meta -function get(state, line) { - const pos = state.bMarks[line]; - const max = state.eMarks[line]; - return state.src.substr(pos, max - pos); +// yaml meta, from https://github.com/eugeneware/remarkable-meta +function get (state, line) { + const pos = state.bMarks[line] + const max = state.eMarks[line] + return state.src.substr(pos, max - pos) } -function meta(state, start, end, silent) { - if (start !== 0 || state.blkIndent !== 0) return false; - if (state.tShift[start] < 0) return false; - if (!get(state, start).match(/^---$/)) return false; - - const data = []; - for (var line = start + 1; line < end; line++) { - const str = get(state, line); - if (str.match(/^(\.{3}|-{3})$/)) break; - if (state.tShift[line] < 0) break; - data.push(str); - } - - if (line >= end) return false; - - try { - md.meta = jsyaml.safeLoad(data.join('\n')) || {}; - delete md.metaError; - } catch(err) { - md.metaError = err; - console.warn(err); - return false; - } - - state.line = line + 1; - - return true; +function meta (state, start, end, silent) { + if (start !== 0 || state.blkIndent !== 0) return false + if (state.tShift[start] < 0) return false + if (!get(state, start).match(/^---$/)) return false + + const data = [] + for (var line = start + 1; line < end; line++) { + const str = get(state, line) + if (str.match(/^(\.{3}|-{3})$/)) break + if (state.tShift[line] < 0) break + data.push(str) + } + + if (line >= end) return false + + try { + md.meta = window.jsyaml.safeLoad(data.join('\n')) || {} + delete md.metaError + } catch (err) { + md.metaError = err + console.warn(err) + return false + } + + state.line = line + 1 + + return true } -function metaPlugin(md) { - md.meta = md.meta || {}; - md.block.ruler.before('code', 'meta', meta, { - alt: [] - }); +function metaPlugin (md) { + md.meta = md.meta || {} + md.block.ruler.before('code', 'meta', meta, { + alt: [] + }) } -md.use(metaPlugin); -md.use(youtubePlugin); -md.use(vimeoPlugin); -md.use(gistPlugin); -md.use(tocPlugin); -md.use(slidesharePlugin); -md.use(speakerdeckPlugin); -md.use(pdfPlugin); +md.use(metaPlugin) +md.use(youtubePlugin) +md.use(vimeoPlugin) +md.use(gistPlugin) +md.use(tocPlugin) +md.use(slidesharePlugin) +md.use(speakerdeckPlugin) +md.use(pdfPlugin) export default { md -}; +} -- cgit v1.2.3