From 5bc642d02e8955b200bb21cf30e863fdf0c53765 Mon Sep 17 00:00:00 2001 From: BoHong Li Date: Thu, 9 Mar 2017 02:41:05 +0800 Subject: [PATCH] Use JavaScript Standard Style (part 2) Fixed all fail on frontend code. --- public/js/cover.js | 731 ++-- public/js/extra.js | 1944 +++++---- public/js/google-drive-picker.js | 227 +- public/js/google-drive-upload.js | 229 +- public/js/history.js | 568 ++- public/js/htmlExport.js | 12 +- public/js/index.js | 6863 +++++++++++++++--------------- public/js/lib/common/login.js | 133 +- public/js/lib/config/index.js | 28 +- public/js/locale.js | 42 +- public/js/pretty.js | 211 +- public/js/render.js | 86 +- public/js/reveal-markdown.js | 741 ++-- public/js/slide.js | 189 +- public/js/syncscroll.js | 580 +-- public/vendor/md-toc.js | 226 +- webpack.config.js | 60 +- webpack.production.js | 118 +- webpackBaseConfig.js | 834 ++-- 19 files changed, 6790 insertions(+), 7032 deletions(-) diff --git a/public/js/cover.js b/public/js/cover.js index bc6e73f..a45a1c1 100644 --- a/public/js/cover.js +++ b/public/js/cover.js @@ -1,7 +1,10 @@ -require('./locale'); +/* eslint-env browser, jquery */ +/* global moment, serverurl */ -require('../css/cover.css'); -require('../css/site.css'); +require('./locale') + +require('../css/cover.css') +require('../css/site.css') import { checkIfAuth, @@ -9,7 +12,7 @@ import { getLoginState, resetCheckAuth, setloginStateChangeEvent -} from './lib/common/login'; +} from './lib/common/login' import { clearDuplicatedHistory, @@ -23,411 +26,403 @@ import { removeHistory, saveHistory, saveStorageHistoryToServer -} from './history'; +} from './history' -import { saveAs } from 'file-saver'; -import List from 'list.js'; -import S from 'string'; +import { saveAs } from 'file-saver' +import List from 'list.js' +import S from 'string' const options = { - valueNames: ['id', 'text', 'timestamp', 'fromNow', 'time', 'tags', 'pinned'], - item: '
  • \ - \ - \ -
    \ -
    \ -
    \ -
    \ -

    \ -

    \ - visited \ -
    \ - \ - \ -

    \ -

    \ -
    \ -
    \ -
    \ -
  • ', - page: 18, - plugins: [ - ListPagination({ - outerWindow: 1 - }) - ] -}; -const historyList = new List('history', options); + valueNames: ['id', 'text', 'timestamp', 'fromNow', 'time', 'tags', 'pinned'], + item: '
  • ' + + '' + + '' + + '
    ' + + '
    ' + + '
    ' + + '
    ' + + '

    ' + + '

    ' + + ' visited ' + + '
    ' + + '' + + '' + + '

    ' + + '

    ' + + '
    ' + + '
    ' + + '
    ' + + '
  • ', + page: 18, + plugins: [ + window.ListPagination({ + outerWindow: 1 + }) + ] +} +const historyList = new List('history', options) -migrateHistoryFromTempCallback = pageInit; -setloginStateChangeEvent(pageInit); +window.migrateHistoryFromTempCallback = pageInit +setloginStateChangeEvent(pageInit) -pageInit(); +pageInit() -function pageInit() { - checkIfAuth( +function pageInit () { + checkIfAuth( data => { - $('.ui-signin').hide(); - $('.ui-or').hide(); - $('.ui-welcome').show(); - if (data.photo) $('.ui-avatar').prop('src', data.photo).show(); - else $('.ui-avatar').prop('src', '').hide(); - $('.ui-name').html(data.name); - $('.ui-signout').show(); - $(".ui-history").click(); - parseServerToHistory(historyList, parseHistoryCallback); + $('.ui-signin').hide() + $('.ui-or').hide() + $('.ui-welcome').show() + if (data.photo) $('.ui-avatar').prop('src', data.photo).show() + else $('.ui-avatar').prop('src', '').hide() + $('.ui-name').html(data.name) + $('.ui-signout').show() + $('.ui-history').click() + parseServerToHistory(historyList, parseHistoryCallback) }, () => { - $('.ui-signin').show(); - $('.ui-or').show(); - $('.ui-welcome').hide(); - $('.ui-avatar').prop('src', '').hide(); - $('.ui-name').html(''); - $('.ui-signout').hide(); - parseStorageToHistory(historyList, parseHistoryCallback); + $('.ui-signin').show() + $('.ui-or').show() + $('.ui-welcome').hide() + $('.ui-avatar').prop('src', '').hide() + $('.ui-name').html('') + $('.ui-signout').hide() + parseStorageToHistory(historyList, parseHistoryCallback) } - ); + ) } -$(".masthead-nav li").click(function () { - $(this).siblings().removeClass("active"); - $(this).addClass("active"); -}); +$('.masthead-nav li').click(function () { + $(this).siblings().removeClass('active') + $(this).addClass('active') +}) // prevent empty link change hash $('a[href="#"]').click(function (e) { - e.preventDefault(); -}); + e.preventDefault() +}) -$(".ui-home").click(function (e) { - if (!$("#home").is(':visible')) { - $(".section:visible").hide(); - $("#home").fadeIn(); - } -}); +$('.ui-home').click(function (e) { + if (!$('#home').is(':visible')) { + $('.section:visible').hide() + $('#home').fadeIn() + } +}) -$(".ui-history").click(() => { - if (!$("#history").is(':visible')) { - $(".section:visible").hide(); - $("#history").fadeIn(); - } -}); +$('.ui-history').click(() => { + if (!$('#history').is(':visible')) { + $('.section:visible').hide() + $('#history').fadeIn() + } +}) -function checkHistoryList() { - if ($("#history-list").children().length > 0) { - $('.pagination').show(); - $(".ui-nohistory").hide(); - $(".ui-import-from-browser").hide(); - } else if ($("#history-list").children().length == 0) { - $('.pagination').hide(); - $(".ui-nohistory").slideDown(); - getStorageHistory(data => { - if (data && data.length > 0 && getLoginState() && historyList.items.length == 0) { - $(".ui-import-from-browser").slideDown(); - } - }); - } +function checkHistoryList () { + if ($('#history-list').children().length > 0) { + $('.pagination').show() + $('.ui-nohistory').hide() + $('.ui-import-from-browser').hide() + } else if ($('#history-list').children().length === 0) { + $('.pagination').hide() + $('.ui-nohistory').slideDown() + getStorageHistory(data => { + if (data && data.length > 0 && getLoginState() && historyList.items.length === 0) { + $('.ui-import-from-browser').slideDown() + } + }) + } } -function parseHistoryCallback(list, notehistory) { - checkHistoryList(); - //sort by pinned then timestamp - list.sort('', { - sortFunction(a, b) { - const notea = a.values(); - const noteb = b.values(); - if (notea.pinned && !noteb.pinned) { - return -1; - } else if (!notea.pinned && noteb.pinned) { - return 1; - } else { - if (notea.timestamp > noteb.timestamp) { - return -1; - } else if (notea.timestamp < noteb.timestamp) { - return 1; - } else { - return 0; - } - } - } - }); - // parse filter tags - const filtertags = []; - for (let i = 0, l = list.items.length; i < l; i++) { - const tags = list.items[i]._values.tags; - if (tags && tags.length > 0) { - for (let j = 0; j < tags.length; j++) { - //push info filtertags if not found - let found = false; - if (filtertags.includes(tags[j])) - found = true; - if (!found) - filtertags.push(tags[j]); - } +function parseHistoryCallback (list, notehistory) { + checkHistoryList() + // sort by pinned then timestamp + list.sort('', { + sortFunction (a, b) { + const notea = a.values() + const noteb = b.values() + if (notea.pinned && !noteb.pinned) { + return -1 + } else if (!notea.pinned && noteb.pinned) { + return 1 + } else { + if (notea.timestamp > noteb.timestamp) { + return -1 + } else if (notea.timestamp < noteb.timestamp) { + return 1 + } else { + return 0 } + } } - buildTagsFilter(filtertags); + }) + // parse filter tags + const filtertags = [] + for (let i = 0, l = list.items.length; i < l; i++) { + const tags = list.items[i]._values.tags + if (tags && tags.length > 0) { + for (let j = 0; j < tags.length; j++) { + // push info filtertags if not found + let found = false + if (filtertags.includes(tags[j])) { found = true } + if (!found) { filtertags.push(tags[j]) } + } + } + } + buildTagsFilter(filtertags) } // update items whenever list updated historyList.on('updated', e => { - for (let i = 0, l = e.items.length; i < l; i++) { - const item = e.items[i]; - if (item.visible()) { - const itemEl = $(item.elm); - const values = item._values; - const a = itemEl.find("a"); - const pin = itemEl.find(".ui-history-pin"); - const tagsEl = itemEl.find(".tags"); - //parse link to element a - a.attr('href', `${serverurl}/${values.id}`); - //parse pinned - if (values.pinned) { - pin.addClass('active'); - } else { - pin.removeClass('active'); - } - //parse tags - const tags = values.tags; - if (tags && tags.length > 0 && tagsEl.children().length <= 0) { - const labels = []; - for (let j = 0; j < tags.length; j++) { - //push into the item label - labels.push(`${tags[j]}`); - } - tagsEl.html(labels.join(' ')); - } + for (let i = 0, l = e.items.length; i < l; i++) { + const item = e.items[i] + if (item.visible()) { + const itemEl = $(item.elm) + const values = item._values + const a = itemEl.find('a') + const pin = itemEl.find('.ui-history-pin') + const tagsEl = itemEl.find('.tags') + // parse link to element a + a.attr('href', `${serverurl}/${values.id}`) + // parse pinned + if (values.pinned) { + pin.addClass('active') + } else { + pin.removeClass('active') + } + // parse tags + const tags = values.tags + if (tags && tags.length > 0 && tagsEl.children().length <= 0) { + const labels = [] + for (let j = 0; j < tags.length; j++) { + // push into the item label + labels.push(`${tags[j]}`) } + tagsEl.html(labels.join(' ')) + } } - $(".ui-history-close").off('click'); - $(".ui-history-close").on('click', historyCloseClick); - $(".ui-history-pin").off('click'); - $(".ui-history-pin").on('click', historyPinClick); -}); + } + $('.ui-history-close').off('click') + $('.ui-history-close').on('click', historyCloseClick) + $('.ui-history-pin').off('click') + $('.ui-history-pin').on('click', historyPinClick) +}) -function historyCloseClick(e) { - e.preventDefault(); - const id = $(this).closest("a").siblings("span").html(); - const value = historyList.get('id', id)[0]._values; - $('.ui-delete-modal-msg').text('Do you really want to delete below history?'); - $('.ui-delete-modal-item').html(` ${value.text}
    ${value.time}`); - clearHistory = false; - deleteId = id; +function historyCloseClick (e) { + e.preventDefault() + const id = $(this).closest('a').siblings('span').html() + const value = historyList.get('id', id)[0]._values + $('.ui-delete-modal-msg').text('Do you really want to delete below history?') + $('.ui-delete-modal-item').html(` ${value.text}
    ${value.time}`) + clearHistory = false + deleteId = id } -function historyPinClick(e) { - e.preventDefault(); - const $this = $(this); - const id = $this.closest("a").siblings("span").html(); - const item = historyList.get('id', id)[0]; - const values = item._values; - let pinned = values.pinned; - if (!values.pinned) { - pinned = true; - item._values.pinned = true; - } else { - pinned = false; - item._values.pinned = false; - } - checkIfAuth(() => { - postHistoryToServer(id, { - pinned - }, (err, result) => { - if (!err) { - if (pinned) - $this.addClass('active'); - else - $this.removeClass('active'); - } - }); - }, () => { - getHistory(notehistory => { - for(let i = 0; i < notehistory.length; i++) { - if (notehistory[i].id == id) { - notehistory[i].pinned = pinned; - break; - } - } - saveHistory(notehistory); - if (pinned) - $this.addClass('active'); - else - $this.removeClass('active'); - }); - }); +function historyPinClick (e) { + e.preventDefault() + const $this = $(this) + const id = $this.closest('a').siblings('span').html() + const item = historyList.get('id', id)[0] + const values = item._values + let pinned = values.pinned + if (!values.pinned) { + pinned = true + item._values.pinned = true + } else { + pinned = false + item._values.pinned = false + } + checkIfAuth(() => { + postHistoryToServer(id, { + pinned + }, (err, result) => { + if (!err) { + if (pinned) { $this.addClass('active') } else { $this.removeClass('active') } + } + }) + }, () => { + getHistory(notehistory => { + for (let i = 0; i < notehistory.length; i++) { + if (notehistory[i].id === id) { + notehistory[i].pinned = pinned + break + } + } + saveHistory(notehistory) + if (pinned) { $this.addClass('active') } else { $this.removeClass('active') } + }) + }) } -//auto update item fromNow every minutes -setInterval(updateItemFromNow, 60000); +// auto update item fromNow every minutes +setInterval(updateItemFromNow, 60000) -function updateItemFromNow() { - const items = $('.item').toArray(); - for (let i = 0; i < items.length; i++) { - const item = $(items[i]); - const timestamp = parseInt(item.find('.timestamp').text()); - item.find('.fromNow').text(moment(timestamp).fromNow()); - } +function updateItemFromNow () { + const items = $('.item').toArray() + for (let i = 0; i < items.length; i++) { + const item = $(items[i]) + const timestamp = parseInt(item.find('.timestamp').text()) + item.find('.fromNow').text(moment(timestamp).fromNow()) + } } -var clearHistory = false; -var deleteId = null; +var clearHistory = false +var deleteId = null -function deleteHistory() { - checkIfAuth(() => { - deleteServerHistory(deleteId, (err, result) => { - if (!err) { - if (clearHistory) { - historyList.clear(); - checkHistoryList(); - } else { - historyList.remove('id', deleteId); - checkHistoryList(); - } - } - $('.delete-modal').modal('hide'); - deleteId = null; - clearHistory = false; - }); - }, () => { +function deleteHistory () { + checkIfAuth(() => { + deleteServerHistory(deleteId, (err, result) => { + if (!err) { if (clearHistory) { - saveHistory([]); - historyList.clear(); - checkHistoryList(); - deleteId = null; + historyList.clear() + checkHistoryList() } else { - if (!deleteId) return; - getHistory(notehistory => { - const newnotehistory = removeHistory(deleteId, notehistory); - saveHistory(newnotehistory); - historyList.remove('id', deleteId); - checkHistoryList(); - deleteId = null; - }); + historyList.remove('id', deleteId) + checkHistoryList() } - $('.delete-modal').modal('hide'); - clearHistory = false; - }); -} - -$(".ui-delete-modal-confirm").click(() => { - deleteHistory(); -}); - -$(".ui-import-from-browser").click(() => { - saveStorageHistoryToServer(() => { - parseStorageToHistory(historyList, parseHistoryCallback); - }); -}); - -$(".ui-save-history").click(() => { - getHistory(data => { - const history = JSON.stringify(data); - const blob = new Blob([history], { - type: "application/json;charset=utf-8" - }); - saveAs(blob, `hackmd_history_${moment().format('YYYYMMDDHHmmss')}`, true); - }); -}); - -$(".ui-open-history").bind("change", e => { - const files = e.target.files || e.dataTransfer.files; - const file = files[0]; - const reader = new FileReader(); - reader.onload = () => { - const notehistory = JSON.parse(reader.result); - //console.log(notehistory); - if (!reader.result) return; - getHistory(data => { - let mergedata = data.concat(notehistory); - mergedata = clearDuplicatedHistory(mergedata); - saveHistory(mergedata); - parseHistory(historyList, parseHistoryCallback); - }); - $(".ui-open-history").replaceWith($(".ui-open-history").val('').clone(true)); - }; - reader.readAsText(file); -}); - -$(".ui-clear-history").click(() => { - $('.ui-delete-modal-msg').text('Do you really want to clear all history?'); - $('.ui-delete-modal-item').html('There is no turning back.'); - clearHistory = true; - deleteId = null; -}); - -$(".ui-refresh-history").click(() => { - const lastTags = $(".ui-use-tags").select2('val'); - $(".ui-use-tags").select2('val', ''); - historyList.filter(); - const lastKeyword = $('.search').val(); - $('.search').val(''); - historyList.search(); - $('#history-list').slideUp('fast'); - $('.pagination').hide(); - - resetCheckAuth(); - historyList.clear(); - parseHistory(historyList, (list, notehistory) => { - parseHistoryCallback(list, notehistory); - $(".ui-use-tags").select2('val', lastTags); - $(".ui-use-tags").trigger('change'); - historyList.search(lastKeyword); - $('.search').val(lastKeyword); - checkHistoryList(); - $('#history-list').slideDown('fast'); - }); -}); - -$(".ui-logout").click(() => { - clearLoginState(); - location.href = `${serverurl}/logout`; -}); - -let filtertags = []; -$(".ui-use-tags").select2({ - placeholder: $(".ui-use-tags").attr('placeholder'), - multiple: true, - data() { - return { - results: filtertags - }; - } -}); -$('.select2-input').css('width', 'inherit'); -buildTagsFilter([]); - -function buildTagsFilter(tags) { - for (let i = 0; i < tags.length; i++) - tags[i] = { - id: i, - text: S(tags[i]).unescapeHTML().s - }; - filtertags = tags; -} -$(".ui-use-tags").on('change', function () { - const tags = []; - const data = $(this).select2('data'); - for (let i = 0; i < data.length; i++) - tags.push(data[i].text); - if (tags.length > 0) { - historyList.filter(item => { - const values = item.values(); - if (!values.tags) return false; - let found = false; - for (let i = 0; i < tags.length; i++) { - if (values.tags.includes(tags[i])) { - found = true; - break; - } - } - return found; - }); + } + $('.delete-modal').modal('hide') + deleteId = null + clearHistory = false + }) + }, () => { + if (clearHistory) { + saveHistory([]) + historyList.clear() + checkHistoryList() + deleteId = null } else { - historyList.filter(); + if (!deleteId) return + getHistory(notehistory => { + const newnotehistory = removeHistory(deleteId, notehistory) + saveHistory(newnotehistory) + historyList.remove('id', deleteId) + checkHistoryList() + deleteId = null + }) } - checkHistoryList(); -}); + $('.delete-modal').modal('hide') + clearHistory = false + }) +} + +$('.ui-delete-modal-confirm').click(() => { + deleteHistory() +}) + +$('.ui-import-from-browser').click(() => { + saveStorageHistoryToServer(() => { + parseStorageToHistory(historyList, parseHistoryCallback) + }) +}) + +$('.ui-save-history').click(() => { + getHistory(data => { + const history = JSON.stringify(data) + const blob = new Blob([history], { + type: 'application/json;charset=utf-8' + }) + saveAs(blob, `hackmd_history_${moment().format('YYYYMMDDHHmmss')}`, true) + }) +}) + +$('.ui-open-history').bind('change', e => { + const files = e.target.files || e.dataTransfer.files + const file = files[0] + const reader = new FileReader() + reader.onload = () => { + const notehistory = JSON.parse(reader.result) + // console.log(notehistory); + if (!reader.result) return + getHistory(data => { + let mergedata = data.concat(notehistory) + mergedata = clearDuplicatedHistory(mergedata) + saveHistory(mergedata) + parseHistory(historyList, parseHistoryCallback) + }) + $('.ui-open-history').replaceWith($('.ui-open-history').val('').clone(true)) + } + reader.readAsText(file) +}) + +$('.ui-clear-history').click(() => { + $('.ui-delete-modal-msg').text('Do you really want to clear all history?') + $('.ui-delete-modal-item').html('There is no turning back.') + clearHistory = true + deleteId = null +}) + +$('.ui-refresh-history').click(() => { + const lastTags = $('.ui-use-tags').select2('val') + $('.ui-use-tags').select2('val', '') + historyList.filter() + const lastKeyword = $('.search').val() + $('.search').val('') + historyList.search() + $('#history-list').slideUp('fast') + $('.pagination').hide() + + resetCheckAuth() + historyList.clear() + parseHistory(historyList, (list, notehistory) => { + parseHistoryCallback(list, notehistory) + $('.ui-use-tags').select2('val', lastTags) + $('.ui-use-tags').trigger('change') + historyList.search(lastKeyword) + $('.search').val(lastKeyword) + checkHistoryList() + $('#history-list').slideDown('fast') + }) +}) + +$('.ui-logout').click(() => { + clearLoginState() + location.href = `${serverurl}/logout` +}) + +let filtertags = [] +$('.ui-use-tags').select2({ + placeholder: $('.ui-use-tags').attr('placeholder'), + multiple: true, + data () { + return { + results: filtertags + } + } +}) +$('.select2-input').css('width', 'inherit') +buildTagsFilter([]) + +function buildTagsFilter (tags) { + for (let i = 0; i < tags.length; i++) { + tags[i] = { + id: i, + text: S(tags[i]).unescapeHTML().s + } + } + filtertags = tags +} +$('.ui-use-tags').on('change', function () { + const tags = [] + const data = $(this).select2('data') + for (let i = 0; i < data.length; i++) { tags.push(data[i].text) } + if (tags.length > 0) { + historyList.filter(item => { + const values = item.values() + if (!values.tags) return false + let found = false + for (let i = 0; i < tags.length; i++) { + if (values.tags.includes(tags[i])) { + found = true + break + } + } + return found + }) + } else { + historyList.filter() + } + checkHistoryList() +}) $('.search').keyup(() => { - checkHistoryList(); -}); + checkHistoryList() +}) diff --git a/public/js/extra.js b/public/js/extra.js index a3e840d..844d52c 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'); +/* eslint-env browser, jquery */ +/* global moment, serverurl */ -import Prism from 'prismjs'; -import hljs from 'highlight.js'; -import PDFObject from 'pdfobject'; -import S from 'string'; -import { saveAs } from 'file-saver'; +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') -require('./lib/common/login'); -require('../vendor/md-toc'); -var Viz = require("viz.js"); +import Prism from 'prismjs' +import hljs from 'highlight.js' +import PDFObject from 'pdfobject' +import S from 'string' +import { saveAs } from 'file-saver' -//auto update last change -window.createtime = null; -window.lastchangetime = null; +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')); - } -} -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(); - } - } -} - -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(); - } - } -} - -//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; +export function updateLastChange () { + if (!window.lastchangeui) return + if (window.createtime) { + if (window.createtime && !window.lastchangetime) { + window.lastchangeui.status.text('created') } else { - const h1s = view.find("h1"); - if (h1s.length > 0) { - title = h1s.first().text(); - } else { - title = null; - } + window.lastchangeui.status.text('changed') } - return title; + 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) -//render title -export function renderTitle(view) { - let title = getTitle(view); - if (title) { - title += ' - HackMD'; +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 { - title = 'HackMD - Collaborative markdown notes'; + window.lastchangeui.user.hide() + window.lastchangeui.nouser.show() } - return title; + } } -//render filename -export function renderFilename(view) { - let filename = getTitle(view); - if (!filename) { - filename = 'Untitled'; +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() } - return filename; + } +} + +// 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 { + title = null + } + } + 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 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; +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"); +// 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; +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] } + 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") + // 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) => { - // 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") - .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") - .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") - .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") - .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); - } - }); - //mathjax - const mathjaxdivs = view.find('span.mathjax.raw').removeClass("raw").toArray(); + }) + }) + // 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 { - 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); - } + 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) { - console.warn(err); + $value.unwrap() + $value.parent().append('
    ' + err + '
    ') + console.warn(err) } - //render title - document.title = renderTitle(view); -} + }) + // flowchart + const flow = view.find('div.flow-chart.raw').removeClass('raw') + flow.each((key, value) => { + try { + var $value = $(value) + const $ele = $(value).parent().parent() -//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); - } + 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) } - return result; -} -window.postProcess = postProcess; + }) + // graphviz + var graphvizs = view.find('div.graphviz.raw').removeClass('raw') + graphvizs.each(function (key, value) { + try { + var $value = $(value) + var $ele = $(value).parent().parent() -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; -} + var graphviz = Viz($value.text()) + if (!graphviz) throw Error('viz.js output empty graph') + $value.html(graphviz) -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); -} + $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') -//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); - // console.log(html); - const blob = new Blob([html], { - type: "text/html;charset=utf-8" - }); - saveAs(blob, filename, true); - }); - }); -} + let mermaidError = null + window.mermaid.parseError = (err, hash) => { + mermaidError = err + } -//jQuery sortByDepth -$.fn.sortByDepth = function () { - const ar = this.map(function () { - return { - length: $(this).parents().length, - elt: this + 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 (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 (window.viewAjaxCallback) window.viewAjaxCallback() } - }).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'); + }) + }) + // 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') + 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') + .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() + 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) + } + } + 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) } + } catch (err) { + console.warn(err) + } + // render title + document.title = renderTitle(view) } -//remove hash -function removeHash() { - history.pushState("", document.title, window.location.pathname + window.location.search); -} - -let tocExpand = false; - -function checkExpandToggle() { - const toc = $('.ui-toc-dropdown .toc'); - const toggle = $('.expand-toggle'); - if (!tocExpand) { - toc.removeClass('expand'); - toggle.text('Expand all'); +// 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 { - toc.addClass('expand'); - toggle.text('Collapse all'); + warning = $('
    ' + md.metaError + '
    ') + result.prepend(warning) } + } + 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 } -//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); +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) } -//smooth all hash trigger scrolling -export function smoothHashScroll() { - const hashElements = $("a[href^='#']:not([smoothhashscroll])").toArray(); +// 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) + }) + }) +} - for (const element of hashElements) { - const $element = $(element); - const hash = element.hash; - if (hash) { - $element.on('click', function (e) { +// 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 = 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') + } +} + +// remove hash +function removeHash () { + history.pushState('', document.title, window.location.pathname + window.location.search) +} + +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('') + /* 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() + + 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}`); + 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); + 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) } + } } -function getHeaderContent(header) { - const headerHTML = $(header).clone(); - headerHTML.find('.MathJax_Preview').remove(); - headerHTML.find('.MathJax').remove(); - return headerHTML[0].innerHTML; +export function autoLinkify (view) { + const contentBlock = view[0] + if (!contentBlock) { + return + } + for (let level = 1; level <= 6; level++) { + linkifyAnchors(level, contentBlock) + } } -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); - } - } +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 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 +} + +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 continuelinenumber = /=\+$/.test(lang) + const linegutter = `
    ${linenumbers.join('\n')}
    ` + result.value = `
    ${linegutter}
    ${result.value}
    ` + } + return result.value } -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}
    `; - } - 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; -} - -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; + 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-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: {} -}); + 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 -}); +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) => emojify.replace(`:${token[idx].markup}:`); +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); +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; + 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 (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 (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`; -}; + return `
    ${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; +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); - } + 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; + 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; - } + 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; + state.line = line + 1 - return true; + 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 -}; +} diff --git a/public/js/google-drive-picker.js b/public/js/google-drive-picker.js index 94aa77f..5006cd2 100644 --- a/public/js/google-drive-picker.js +++ b/public/js/google-drive-picker.js @@ -1,119 +1,118 @@ -/**! +/** ! * Google Drive File Picker Example * By Daniel Lo Nigro (http://dan.cx/) */ -(function() { - /** - * Initialise a Google Driver file picker - */ - var FilePicker = window.FilePicker = function(options) { - // Config - this.apiKey = options.apiKey; - this.clientId = options.clientId; - - // Elements - this.buttonEl = options.buttonEl; - - // Events - this.onSelect = options.onSelect; - this.buttonEl.on('click', this.open.bind(this)); - - // Disable the button until the API loads, as it won't work properly until then. - this.buttonEl.prop('disabled', true); +(function () { + /** + * Initialise a Google Driver file picker + */ + var FilePicker = window.FilePicker = function (options) { + // Config + this.apiKey = options.apiKey + this.clientId = options.clientId - // Load the drive API - gapi.client.setApiKey(this.apiKey); - gapi.client.load('drive', 'v2', this._driveApiLoaded.bind(this)); - google.load('picker', '1', { callback: this._pickerApiLoaded.bind(this) }); - } + // Elements + this.buttonEl = options.buttonEl - FilePicker.prototype = { - /** - * Open the file picker. - */ - open: function() { - // Check if the user has already authenticated - var token = gapi.auth.getToken(); - if (token) { - this._showPicker(); - } else { - // The user has not yet authenticated with Google - // We need to do the authentication before displaying the Drive picker. - this._doAuth(false, function() { this._showPicker(); }.bind(this)); - } - }, - - /** - * Show the file picker once authentication has been done. - * @private - */ - _showPicker: function() { - var accessToken = gapi.auth.getToken().access_token; - var view = new google.picker.DocsView(); - view.setMimeTypes("text/markdown,text/html"); - view.setIncludeFolders(true); - view.setOwnedByMe(true); - this.picker = new google.picker.PickerBuilder(). - enableFeature(google.picker.Feature.NAV_HIDDEN). - addView(view). - setAppId(this.clientId). - setOAuthToken(accessToken). - setCallback(this._pickerCallback.bind(this)). - build(). - setVisible(true); - }, - - /** - * Called when a file has been selected in the Google Drive file picker. - * @private - */ - _pickerCallback: function(data) { - if (data[google.picker.Response.ACTION] == google.picker.Action.PICKED) { - var file = data[google.picker.Response.DOCUMENTS][0], - id = file[google.picker.Document.ID], - request = gapi.client.drive.files.get({ - fileId: id - }); - - request.execute(this._fileGetCallback.bind(this)); - } - }, - /** - * Called when file details have been retrieved from Google Drive. - * @private - */ - _fileGetCallback: function(file) { - if (this.onSelect) { - this.onSelect(file); - } - }, - - /** - * Called when the Google Drive file picker API has finished loading. - * @private - */ - _pickerApiLoaded: function() { - this.buttonEl.prop('disabled', false); - }, - - /** - * Called when the Google Drive API has finished loading. - * @private - */ - _driveApiLoaded: function() { - this._doAuth(true); - }, - - /** - * Authenticate with Google Drive via the Google JavaScript API. - * @private - */ - _doAuth: function(immediate, callback) { - gapi.auth.authorize({ - client_id: this.clientId, - scope: 'https://www.googleapis.com/auth/drive.readonly', - immediate: immediate - }, callback ? callback : function() {}); - } - }; -}()); + // Events + this.onSelect = options.onSelect + this.buttonEl.on('click', this.open.bind(this)) + + // Disable the button until the API loads, as it won't work properly until then. + this.buttonEl.prop('disabled', true) + + // Load the drive API + window.gapi.client.setApiKey(this.apiKey) + window.gapi.client.load('drive', 'v2', this._driveApiLoaded.bind(this)) + window.google.load('picker', '1', { callback: this._pickerApiLoaded.bind(this) }) + } + + FilePicker.prototype = { + /** + * Open the file picker. + */ + open: function () { + // Check if the user has already authenticated + var token = window.gapi.auth.getToken() + if (token) { + this._showPicker() + } else { + // The user has not yet authenticated with Google + // We need to do the authentication before displaying the Drive picker. + this._doAuth(false, function () { this._showPicker() }.bind(this)) + } + }, + + /** + * Show the file picker once authentication has been done. + * @private + */ + _showPicker: function () { + var accessToken = window.gapi.auth.getToken().access_token + var view = new window.google.picker.DocsView() + view.setMimeTypes('text/markdown,text/html') + view.setIncludeFolders(true) + view.setOwnedByMe(true) + this.picker = new window.google.picker.PickerBuilder() + .enableFeature(window.google.picker.Feature.NAV_HIDDEN) + .addView(view) + .setAppId(this.clientId) + .setOAuthToken(accessToken) + .setCallback(this._pickerCallback.bind(this)) + .build() + .setVisible(true) + }, + + /** + * Called when a file has been selected in the Google Drive file picker. + * @private + */ + _pickerCallback: function (data) { + if (data[window.google.picker.Response.ACTION] === window.google.picker.Action.PICKED) { + var file = data[window.google.picker.Response.DOCUMENTS][0] + var id = file[window.google.picker.Document.ID] + var request = window.gapi.client.drive.files.get({ + fileId: id + }) + request.execute(this._fileGetCallback.bind(this)) + } + }, + /** + * Called when file details have been retrieved from Google Drive. + * @private + */ + _fileGetCallback: function (file) { + if (this.onSelect) { + this.onSelect(file) + } + }, + + /** + * Called when the Google Drive file picker API has finished loading. + * @private + */ + _pickerApiLoaded: function () { + this.buttonEl.prop('disabled', false) + }, + + /** + * Called when the Google Drive API has finished loading. + * @private + */ + _driveApiLoaded: function () { + this._doAuth(true) + }, + + /** + * Authenticate with Google Drive via the Google JavaScript API. + * @private + */ + _doAuth: function (immediate, callback) { + window.gapi.auth.authorize({ + client_id: this.clientId, + scope: 'https://www.googleapis.com/auth/drive.readonly', + immediate: immediate + }, callback || function () {}) + } + } +}()) diff --git a/public/js/google-drive-upload.js b/public/js/google-drive-upload.js index eabc5b7..6c0e8a6 100644 --- a/public/js/google-drive-upload.js +++ b/public/js/google-drive-upload.js @@ -1,30 +1,31 @@ +/* eslint-env browser, jquery */ /** * Helper for implementing retries with backoff. Initial retry * delay is 1 second, increasing by 2x (+jitter) for subsequent retries * * @constructor */ -var RetryHandler = function() { - this.interval = 1000; // Start at one second - this.maxInterval = 60 * 1000; // Don't wait longer than a minute -}; +var RetryHandler = function () { + this.interval = 1000 // Start at one second + this.maxInterval = 60 * 1000 // Don't wait longer than a minute +} /** * Invoke the function after waiting * * @param {function} fn Function to invoke */ -RetryHandler.prototype.retry = function(fn) { - setTimeout(fn, this.interval); - this.interval = this.nextInterval_(); -}; +RetryHandler.prototype.retry = function (fn) { + setTimeout(fn, this.interval) + this.interval = this.nextInterval_() +} /** * Reset the counter (e.g. after successful request.) */ -RetryHandler.prototype.reset = function() { - this.interval = 1000; -}; +RetryHandler.prototype.reset = function () { + this.interval = 1000 +} /** * Calculate the next wait time. @@ -32,10 +33,10 @@ RetryHandler.prototype.reset = function() { * * @private */ -RetryHandler.prototype.nextInterval_ = function() { - var interval = this.interval * 2 + this.getRandomInt_(0, 1000); - return Math.min(interval, this.maxInterval); -}; +RetryHandler.prototype.nextInterval_ = function () { + var interval = this.interval * 2 + this.getRandomInt_(0, 1000) + return Math.min(interval, this.maxInterval) +} /** * Get a random int in the range of min to max. Used to add jitter to wait times. @@ -44,10 +45,9 @@ RetryHandler.prototype.nextInterval_ = function() { * @param {number} max Upper bounds * @private */ -RetryHandler.prototype.getRandomInt_ = function(min, max) { - return Math.floor(Math.random() * (max - min + 1) + min); -}; - +RetryHandler.prototype.getRandomInt_ = function (min, max) { + return Math.floor(Math.random() * (max - min + 1) + min) +} /** * Helper class for resumable uploads using XHR/CORS. Can upload any Blob-like item, whether @@ -75,116 +75,115 @@ RetryHandler.prototype.getRandomInt_ = function(min, max) { * @param {function} [options.onProgress] Callback for status for the in-progress upload * @param {function} [options.onError] Callback if upload fails */ -var MediaUploader = function(options) { - var noop = function() {}; - this.file = options.file; - this.contentType = options.contentType || this.file.type || 'application/octet-stream'; +var MediaUploader = function (options) { + var noop = function () {} + this.file = options.file + this.contentType = options.contentType || this.file.type || 'application/octet-stream' this.metadata = options.metadata || { 'title': this.file.name, 'mimeType': this.contentType - }; - this.token = options.token; - this.onComplete = options.onComplete || noop; - this.onProgress = options.onProgress || noop; - this.onError = options.onError || noop; - this.offset = options.offset || 0; - this.chunkSize = options.chunkSize || 0; - this.retryHandler = new RetryHandler(); - - this.url = options.url; - if (!this.url) { - var params = options.params || {}; - params.uploadType = 'resumable'; - this.url = this.buildUrl_(options.fileId, params, options.baseUrl); } - this.httpMethod = options.fileId ? 'PUT' : 'POST'; -}; + this.token = options.token + this.onComplete = options.onComplete || noop + this.onProgress = options.onProgress || noop + this.onError = options.onError || noop + this.offset = options.offset || 0 + this.chunkSize = options.chunkSize || 0 + this.retryHandler = new RetryHandler() + + this.url = options.url + if (!this.url) { + var params = options.params || {} + params.uploadType = 'resumable' + this.url = this.buildUrl_(options.fileId, params, options.baseUrl) + } + this.httpMethod = options.fileId ? 'PUT' : 'POST' +} /** * Initiate the upload. */ -MediaUploader.prototype.upload = function() { - var self = this; - var xhr = new XMLHttpRequest(); +MediaUploader.prototype.upload = function () { + var xhr = new XMLHttpRequest() - xhr.open(this.httpMethod, this.url, true); - xhr.setRequestHeader('Authorization', 'Bearer ' + this.token); - xhr.setRequestHeader('Content-Type', 'application/json'); - xhr.setRequestHeader('X-Upload-Content-Length', this.file.size); - xhr.setRequestHeader('X-Upload-Content-Type', this.contentType); + xhr.open(this.httpMethod, this.url, true) + xhr.setRequestHeader('Authorization', 'Bearer ' + this.token) + xhr.setRequestHeader('Content-Type', 'application/json') + xhr.setRequestHeader('X-Upload-Content-Length', this.file.size) + xhr.setRequestHeader('X-Upload-Content-Type', this.contentType) - xhr.onload = function(e) { + xhr.onload = function (e) { if (e.target.status < 400) { - var location = e.target.getResponseHeader('Location'); - this.url = location; - this.sendFile_(); + var location = e.target.getResponseHeader('Location') + this.url = location + this.sendFile_() } else { - this.onUploadError_(e); + this.onUploadError_(e) } - }.bind(this); - xhr.onerror = this.onUploadError_.bind(this); - xhr.send(JSON.stringify(this.metadata)); -}; + }.bind(this) + xhr.onerror = this.onUploadError_.bind(this) + xhr.send(JSON.stringify(this.metadata)) +} /** * Send the actual file content. * * @private */ -MediaUploader.prototype.sendFile_ = function() { - var content = this.file; - var end = this.file.size; +MediaUploader.prototype.sendFile_ = function () { + var content = this.file + var end = this.file.size if (this.offset || this.chunkSize) { // Only bother to slice the file if we're either resuming or uploading in chunks if (this.chunkSize) { - end = Math.min(this.offset + this.chunkSize, this.file.size); + end = Math.min(this.offset + this.chunkSize, this.file.size) } - content = content.slice(this.offset, end); + content = content.slice(this.offset, end) } - var xhr = new XMLHttpRequest(); - xhr.open('PUT', this.url, true); - xhr.setRequestHeader('Content-Type', this.contentType); - xhr.setRequestHeader('Content-Range', "bytes " + this.offset + "-" + (end - 1) + "/" + this.file.size); - xhr.setRequestHeader('X-Upload-Content-Type', this.file.type); + var xhr = new XMLHttpRequest() + xhr.open('PUT', this.url, true) + xhr.setRequestHeader('Content-Type', this.contentType) + xhr.setRequestHeader('Content-Range', 'bytes ' + this.offset + '-' + (end - 1) + '/' + this.file.size) + xhr.setRequestHeader('X-Upload-Content-Type', this.file.type) if (xhr.upload) { - xhr.upload.addEventListener('progress', this.onProgress); + xhr.upload.addEventListener('progress', this.onProgress) } - xhr.onload = this.onContentUploadSuccess_.bind(this); - xhr.onerror = this.onContentUploadError_.bind(this); - xhr.send(content); -}; + xhr.onload = this.onContentUploadSuccess_.bind(this) + xhr.onerror = this.onContentUploadError_.bind(this) + xhr.send(content) +} /** * Query for the state of the file for resumption. * * @private */ -MediaUploader.prototype.resume_ = function() { - var xhr = new XMLHttpRequest(); - xhr.open('PUT', this.url, true); - xhr.setRequestHeader('Content-Range', "bytes */" + this.file.size); - xhr.setRequestHeader('X-Upload-Content-Type', this.file.type); +MediaUploader.prototype.resume_ = function () { + var xhr = new XMLHttpRequest() + xhr.open('PUT', this.url, true) + xhr.setRequestHeader('Content-Range', 'bytes */' + this.file.size) + xhr.setRequestHeader('X-Upload-Content-Type', this.file.type) if (xhr.upload) { - xhr.upload.addEventListener('progress', this.onProgress); + xhr.upload.addEventListener('progress', this.onProgress) } - xhr.onload = this.onContentUploadSuccess_.bind(this); - xhr.onerror = this.onContentUploadError_.bind(this); - xhr.send(); -}; + xhr.onload = this.onContentUploadSuccess_.bind(this) + xhr.onerror = this.onContentUploadError_.bind(this) + xhr.send() +} /** * Extract the last saved range if available in the request. * * @param {XMLHttpRequest} xhr Request object */ -MediaUploader.prototype.extractRange_ = function(xhr) { - var range = xhr.getResponseHeader('Range'); +MediaUploader.prototype.extractRange_ = function (xhr) { + var range = xhr.getResponseHeader('Range') if (range) { - this.offset = parseInt(range.match(/\d+/g).pop(), 10) + 1; + this.offset = parseInt(range.match(/\d+/g).pop(), 10) + 1 } -}; +} /** * Handle successful responses for uploads. Depending on the context, @@ -194,17 +193,17 @@ MediaUploader.prototype.extractRange_ = function(xhr) { * @private * @param {object} e XHR event */ -MediaUploader.prototype.onContentUploadSuccess_ = function(e) { - if (e.target.status == 200 || e.target.status == 201) { - this.onComplete(e.target.response); - } else if (e.target.status == 308) { - this.extractRange_(e.target); - this.retryHandler.reset(); - this.sendFile_(); +MediaUploader.prototype.onContentUploadSuccess_ = function (e) { + if (e.target.status === 200 || e.target.status === 201) { + this.onComplete(e.target.response) + } else if (e.target.status === 308) { + this.extractRange_(e.target) + this.retryHandler.reset() + this.sendFile_() } else { - this.onContentUploadError_(e); + this.onContentUploadError_(e) } -}; +} /** * Handles errors for uploads. Either retries or aborts depending @@ -213,13 +212,13 @@ MediaUploader.prototype.onContentUploadSuccess_ = function(e) { * @private * @param {object} e XHR event */ -MediaUploader.prototype.onContentUploadError_ = function(e) { +MediaUploader.prototype.onContentUploadError_ = function (e) { if (e.target.status && e.target.status < 500) { - this.onError(e.target.response); + this.onError(e.target.response) } else { - this.retryHandler.retry(this.resume_.bind(this)); + this.retryHandler.retry(this.resume_.bind(this)) } -}; +} /** * Handles errors for the initial request. @@ -227,9 +226,9 @@ MediaUploader.prototype.onContentUploadError_ = function(e) { * @private * @param {object} e XHR event */ -MediaUploader.prototype.onUploadError_ = function(e) { - this.onError(e.target.response); // TODO - Retries for initial upload -}; +MediaUploader.prototype.onUploadError_ = function (e) { + this.onError(e.target.response) // TODO - Retries for initial upload +} /** * Construct a query string from a hash/object @@ -238,12 +237,12 @@ MediaUploader.prototype.onUploadError_ = function(e) { * @param {object} [params] Key/value pairs for query string * @return {string} query string */ -MediaUploader.prototype.buildQuery_ = function(params) { - params = params || {}; - return Object.keys(params).map(function(key) { - return encodeURIComponent(key) + '=' + encodeURIComponent(params[key]); - }).join('&'); -}; +MediaUploader.prototype.buildQuery_ = function (params) { + params = params || {} + return Object.keys(params).map(function (key) { + return encodeURIComponent(key) + '=' + encodeURIComponent(params[key]) + }).join('&') +} /** * Build the drive upload URL @@ -253,16 +252,16 @@ MediaUploader.prototype.buildQuery_ = function(params) { * @param {object} [params] Query parameters * @return {string} URL */ -MediaUploader.prototype.buildUrl_ = function(id, params, baseUrl) { - var url = baseUrl || 'https://www.googleapis.com/upload/drive/v2/files/'; +MediaUploader.prototype.buildUrl_ = function (id, params, baseUrl) { + var url = baseUrl || 'https://www.googleapis.com/upload/drive/v2/files/' if (id) { - url += id; + url += id } - var query = this.buildQuery_(params); + var query = this.buildQuery_(params) if (query) { - url += '?' + query; + url += '?' + query } - return url; -}; + return url +} -window.MediaUploader = MediaUploader; +window.MediaUploader = MediaUploader diff --git a/public/js/history.js b/public/js/history.js index 34b2cba..e14b80d 100644 --- a/public/js/history.js +++ b/public/js/history.js @@ -1,372 +1,328 @@ -import store from 'store'; -import S from 'string'; +/* eslint-env browser, jquery */ +/* global serverurl, Cookies, moment */ + +import store from 'store' +import S from 'string' import { checkIfAuth -} from './lib/common/login'; +} from './lib/common/login' import { urlpath -} from './lib/config'; +} from './lib/config' -window.migrateHistoryFromTempCallback = null; +window.migrateHistoryFromTempCallback = null -migrateHistoryFromTemp(); +migrateHistoryFromTemp() -function migrateHistoryFromTemp() { - if (url('#tempid')) { - $.get(`${serverurl}/temp`, { - tempid: url('#tempid') - }) - .done(data => { - if (data && data.temp) { - getStorageHistory(olddata => { - if (!olddata || olddata.length == 0) { - saveHistoryToStorage(JSON.parse(data.temp)); - } - }); - } - }) - .always(() => { - let hash = location.hash.split('#')[1]; - hash = hash.split('&'); - for (let i = 0; i < hash.length; i++) - if (hash[i].indexOf('tempid') == 0) { - hash.splice(i, 1); - i--; - } - hash = hash.join('&'); - location.hash = hash; - if (migrateHistoryFromTempCallback) - migrateHistoryFromTempCallback(); - }); - } +function migrateHistoryFromTemp () { + if (window.url('#tempid')) { + $.get(`${serverurl}/temp`, { + tempid: window.url('#tempid') + }) + .done(data => { + if (data && data.temp) { + getStorageHistory(olddata => { + if (!olddata || olddata.length === 0) { + saveHistoryToStorage(JSON.parse(data.temp)) + } + }) + } + }) + .always(() => { + let hash = location.hash.split('#')[1] + hash = hash.split('&') + for (let i = 0; i < hash.length; i++) { + if (hash[i].indexOf('tempid') === 0) { + hash.splice(i, 1) + i-- + } + } + hash = hash.join('&') + location.hash = hash + if (window.migrateHistoryFromTempCallback) { window.migrateHistoryFromTempCallback() } + }) + } } -export function saveHistory(notehistory) { - checkIfAuth( +export function saveHistory (notehistory) { + checkIfAuth( () => { - saveHistoryToServer(notehistory); + saveHistoryToServer(notehistory) }, () => { - saveHistoryToStorage(notehistory); + saveHistoryToStorage(notehistory) } - ); + ) } -function saveHistoryToStorage(notehistory) { - if (store.enabled) - store.set('notehistory', JSON.stringify(notehistory)); - else - saveHistoryToCookie(notehistory); +function saveHistoryToStorage (notehistory) { + if (store.enabled) { store.set('notehistory', JSON.stringify(notehistory)) } else { saveHistoryToCookie(notehistory) } } -function saveHistoryToCookie(notehistory) { - Cookies.set('notehistory', notehistory, { - expires: 365 - }); +function saveHistoryToCookie (notehistory) { + Cookies.set('notehistory', notehistory, { + expires: 365 + }) } -function saveHistoryToServer(notehistory) { +function saveHistoryToServer (notehistory) { + $.post(`${serverurl}/history`, { + history: JSON.stringify(notehistory) + }) +} + +export function saveStorageHistoryToServer (callback) { + const data = store.get('notehistory') + if (data) { $.post(`${serverurl}/history`, { - history: JSON.stringify(notehistory) - }); -} - -function saveCookieHistoryToStorage(callback) { - store.set('notehistory', Cookies.get('notehistory')); - callback(); -} - -export function saveStorageHistoryToServer(callback) { - const data = store.get('notehistory'); - if (data) { - $.post(`${serverurl}/history`, { - history: data - }) + history: data + }) .done(data => { - callback(data); - }); - } + callback(data) + }) + } } -function saveCookieHistoryToServer(callback) { - $.post(`${serverurl}/history`, { - history: Cookies.get('notehistory') - }) - .done(data => { - callback(data); - }); -} - -export function clearDuplicatedHistory(notehistory) { - const newnotehistory = []; - for (let i = 0; i < notehistory.length; i++) { - let found = false; - for (let j = 0; j < newnotehistory.length; j++) { - const id = notehistory[i].id.replace(/\=+$/, ''); - const newId = newnotehistory[j].id.replace(/\=+$/, ''); - if (id == newId || notehistory[i].id == newnotehistory[j].id || !notehistory[i].id || !newnotehistory[j].id) { - const time = (typeof notehistory[i].time === 'number' ? moment(notehistory[i].time) : moment(notehistory[i].time, 'MMMM Do YYYY, h:mm:ss a')); - const newTime = (typeof newnotehistory[i].time === 'number' ? moment(newnotehistory[i].time) : moment(newnotehistory[i].time, 'MMMM Do YYYY, h:mm:ss a')); - if(time >= newTime) { - newnotehistory[j] = notehistory[i]; - } - found = true; - break; - } +export function clearDuplicatedHistory (notehistory) { + const newnotehistory = [] + for (let i = 0; i < notehistory.length; i++) { + let found = false + for (let j = 0; j < newnotehistory.length; j++) { + const id = notehistory[i].id.replace(/=+$/, '') + const newId = newnotehistory[j].id.replace(/=+$/, '') + if (id === newId || notehistory[i].id === newnotehistory[j].id || !notehistory[i].id || !newnotehistory[j].id) { + const time = (typeof notehistory[i].time === 'number' ? moment(notehistory[i].time) : moment(notehistory[i].time, 'MMMM Do YYYY, h:mm:ss a')) + const newTime = (typeof newnotehistory[i].time === 'number' ? moment(newnotehistory[i].time) : moment(newnotehistory[i].time, 'MMMM Do YYYY, h:mm:ss a')) + if (time >= newTime) { + newnotehistory[j] = notehistory[i] } - if (!found) - newnotehistory.push(notehistory[i]); + found = true + break + } } - return newnotehistory; + if (!found) { newnotehistory.push(notehistory[i]) } + } + return newnotehistory } -function addHistory(id, text, time, tags, pinned, notehistory) { +function addHistory (id, text, time, tags, pinned, notehistory) { // only add when note id exists - if (id) { - notehistory.push({ - id, - text, - time, - tags, - pinned - }); - } - return notehistory; + if (id) { + notehistory.push({ + id, + text, + time, + tags, + pinned + }) + } + return notehistory } -export function removeHistory(id, notehistory) { - for (let i = 0; i < notehistory.length; i++) { - if (notehistory[i].id == id) { - notehistory.splice(i, 1); - i -= 1; - } +export function removeHistory (id, notehistory) { + for (let i = 0; i < notehistory.length; i++) { + if (notehistory[i].id === id) { + notehistory.splice(i, 1) + i -= 1 } - return notehistory; + } + return notehistory } -//used for inner -export function writeHistory(title, tags) { - checkIfAuth( +// used for inner +export function writeHistory (title, tags) { + checkIfAuth( () => { // no need to do this anymore, this will count from server-side // writeHistoryToServer(title, tags); }, () => { - writeHistoryToStorage(title, tags); + writeHistoryToStorage(title, tags) } - ); + ) } -function writeHistoryToServer(title, tags) { - $.get(`${serverurl}/history`) - .done(data => { - try { - if (data.history) { - var notehistory = data.history; - } else { - var notehistory = []; - } - } catch (err) { - var notehistory = []; - } - if (!notehistory) - notehistory = []; - - const newnotehistory = generateHistory(title, tags, notehistory); - saveHistoryToServer(newnotehistory); - }) - .fail((xhr, status, error) => { - console.error(xhr.responseText); - }); +function writeHistoryToCookie (title, tags) { + var notehistory + try { + notehistory = Cookies.getJSON('notehistory') + } catch (err) { + notehistory = [] + } + if (!notehistory) { notehistory = [] } + const newnotehistory = generateHistory(title, tags, notehistory) + saveHistoryToCookie(newnotehistory) } -function writeHistoryToCookie(title, tags) { - try { - var notehistory = Cookies.getJSON('notehistory'); - } catch (err) { - var notehistory = []; - } - if (!notehistory) - notehistory = []; - - const newnotehistory = generateHistory(title, tags, notehistory); - saveHistoryToCookie(newnotehistory); -} - -function writeHistoryToStorage(title, tags) { - if (store.enabled) { - let data = store.get('notehistory'); - if (data) { - if (typeof data == "string") - data = JSON.parse(data); - var notehistory = data; - } else - var notehistory = []; - if (!notehistory) - notehistory = []; - - const newnotehistory = generateHistory(title, tags, notehistory); - saveHistoryToStorage(newnotehistory); +function writeHistoryToStorage (title, tags) { + if (store.enabled) { + let data = store.get('notehistory') + var notehistory + if (data) { + if (typeof data === 'string') { data = JSON.parse(data) } + notehistory = data } else { - writeHistoryToCookie(title, tags); + notehistory = [] } + if (!notehistory) { notehistory = [] } + + const newnotehistory = generateHistory(title, tags, notehistory) + saveHistoryToStorage(newnotehistory) + } else { + writeHistoryToCookie(title, tags) + } } if (!Array.isArray) { - Array.isArray = arg => Object.prototype.toString.call(arg) === '[object Array]'; + Array.isArray = arg => Object.prototype.toString.call(arg) === '[object Array]' } -function renderHistory(title, tags) { - //console.debug(tags); - const id = urlpath ? location.pathname.slice(urlpath.length + 1, location.pathname.length).split('/')[1] : location.pathname.split('/')[1]; - return { - id, - text: title, - time: moment().valueOf(), - tags - }; +function renderHistory (title, tags) { + // console.debug(tags); + const id = urlpath ? location.pathname.slice(urlpath.length + 1, location.pathname.length).split('/')[1] : location.pathname.split('/')[1] + return { + id, + text: title, + time: moment().valueOf(), + tags + } } -function generateHistory(title, tags, notehistory) { - const info = renderHistory(title, tags); - //keep any pinned data - let pinned = false; +function generateHistory (title, tags, notehistory) { + const info = renderHistory(title, tags) + // keep any pinned data + let pinned = false + for (let i = 0; i < notehistory.length; i++) { + if (notehistory[i].id === info.id && notehistory[i].pinned) { + pinned = true + break + } + } + notehistory = removeHistory(info.id, notehistory) + notehistory = addHistory(info.id, info.text, info.time, info.tags, pinned, notehistory) + notehistory = clearDuplicatedHistory(notehistory) + return notehistory +} + +// used for outer +export function getHistory (callback) { + checkIfAuth( + () => { + getServerHistory(callback) + }, + () => { + getStorageHistory(callback) + } + ) +} + +function getServerHistory (callback) { + $.get(`${serverurl}/history`) + .done(data => { + if (data.history) { + callback(data.history) + } + }) + .fail((xhr, status, error) => { + console.error(xhr.responseText) + }) +} + +function getCookieHistory (callback) { + callback(Cookies.getJSON('notehistory')) +} + +export function getStorageHistory (callback) { + if (store.enabled) { + let data = store.get('notehistory') + if (data) { + if (typeof data === 'string') { data = JSON.parse(data) } + callback(data) + } else { getCookieHistory(callback) } + } else { + getCookieHistory(callback) + } +} + +export function parseHistory (list, callback) { + checkIfAuth( + () => { + parseServerToHistory(list, callback) + }, + () => { + parseStorageToHistory(list, callback) + } + ) +} + +export function parseServerToHistory (list, callback) { + $.get(`${serverurl}/history`) + .done(data => { + if (data.history) { + parseToHistory(list, data.history, callback) + } + }) + .fail((xhr, status, error) => { + console.error(xhr.responseText) + }) +} + +function parseCookieToHistory (list, callback) { + const notehistory = Cookies.getJSON('notehistory') + parseToHistory(list, notehistory, callback) +} + +export function parseStorageToHistory (list, callback) { + if (store.enabled) { + let data = store.get('notehistory') + if (data) { + if (typeof data === 'string') { data = JSON.parse(data) } + parseToHistory(list, data, callback) + } else { parseCookieToHistory(list, callback) } + } else { + parseCookieToHistory(list, callback) + } +} + +function parseToHistory (list, notehistory, callback) { + if (!callback) return + else if (!list || !notehistory) callback(list, notehistory) + else if (notehistory && notehistory.length > 0) { for (let i = 0; i < notehistory.length; i++) { - if (notehistory[i].id == info.id && notehistory[i].pinned) { - pinned = true; - break; - } - } - notehistory = removeHistory(info.id, notehistory); - notehistory = addHistory(info.id, info.text, info.time, info.tags, pinned, notehistory); - notehistory = clearDuplicatedHistory(notehistory); - return notehistory; -} - -//used for outer -export function getHistory(callback) { - checkIfAuth( - () => { - getServerHistory(callback); - }, - () => { - getStorageHistory(callback); - } - ); -} - -function getServerHistory(callback) { - $.get(`${serverurl}/history`) - .done(data => { - if (data.history) { - callback(data.history); - } - }) - .fail((xhr, status, error) => { - console.error(xhr.responseText); - }); -} - -function getCookieHistory(callback) { - callback(Cookies.getJSON('notehistory')); -} - -export function getStorageHistory(callback) { - if (store.enabled) { - let data = store.get('notehistory'); - if (data) { - if (typeof data == "string") - data = JSON.parse(data); - callback(data); - } else - getCookieHistory(callback); - } else { - getCookieHistory(callback); - } -} - -export function parseHistory(list, callback) { - checkIfAuth( - () => { - parseServerToHistory(list, callback); - }, - () => { - parseStorageToHistory(list, callback); - } - ); -} - -export function parseServerToHistory(list, callback) { - $.get(`${serverurl}/history`) - .done(data => { - if (data.history) { - parseToHistory(list, data.history, callback); - } - }) - .fail((xhr, status, error) => { - console.error(xhr.responseText); - }); -} - -function parseCookieToHistory(list, callback) { - const notehistory = Cookies.getJSON('notehistory'); - parseToHistory(list, notehistory, callback); -} - -export function parseStorageToHistory(list, callback) { - if (store.enabled) { - let data = store.get('notehistory'); - if (data) { - if (typeof data == "string") - data = JSON.parse(data); - parseToHistory(list, data, callback); - } else - parseCookieToHistory(list, callback); - } else { - parseCookieToHistory(list, callback); - } -} - -function parseToHistory(list, notehistory, callback) { - if (!callback) return; - else if (!list || !notehistory) callback(list, notehistory); - else if (notehistory && notehistory.length > 0) { - for (let i = 0; i < notehistory.length; i++) { - //parse time to timestamp and fromNow - const timestamp = (typeof notehistory[i].time === 'number' ? moment(notehistory[i].time) : moment(notehistory[i].time, 'MMMM Do YYYY, h:mm:ss a')); - notehistory[i].timestamp = timestamp.valueOf(); - notehistory[i].fromNow = timestamp.fromNow(); - notehistory[i].time = timestamp.format('llll'); + // parse time to timestamp and fromNow + const timestamp = (typeof notehistory[i].time === 'number' ? moment(notehistory[i].time) : moment(notehistory[i].time, 'MMMM Do YYYY, h:mm:ss a')) + notehistory[i].timestamp = timestamp.valueOf() + notehistory[i].fromNow = timestamp.fromNow() + notehistory[i].time = timestamp.format('llll') // prevent XSS - notehistory[i].text = S(notehistory[i].text).escapeHTML().s; - notehistory[i].tags = (notehistory[i].tags && notehistory[i].tags.length > 0) ? S(notehistory[i].tags).escapeHTML().s.split(',') : []; + notehistory[i].text = S(notehistory[i].text).escapeHTML().s + notehistory[i].tags = (notehistory[i].tags && notehistory[i].tags.length > 0) ? S(notehistory[i].tags).escapeHTML().s.split(',') : [] // add to list - if (notehistory[i].id && list.get('id', notehistory[i].id).length == 0) - list.add(notehistory[i]); - } + if (notehistory[i].id && list.get('id', notehistory[i].id).length === 0) { list.add(notehistory[i]) } } - callback(list, notehistory); + } + callback(list, notehistory) } -export function postHistoryToServer(noteId, data, callback) { - $.post(`${serverurl}/history/${noteId}`, data) +export function postHistoryToServer (noteId, data, callback) { + $.post(`${serverurl}/history/${noteId}`, data) .done(result => callback(null, result)) .fail((xhr, status, error) => { - console.error(xhr.responseText); - return callback(error, null); - }); -} - -export function deleteServerHistory(noteId, callback) { - $.ajax({ - url: `${serverurl}/history${noteId ? '/' + noteId : ""}`, - type: 'DELETE' + console.error(xhr.responseText) + return callback(error, null) }) +} + +export function deleteServerHistory (noteId, callback) { + $.ajax({ + url: `${serverurl}/history${noteId ? '/' + noteId : ''}`, + type: 'DELETE' + }) .done(result => callback(null, result)) .fail((xhr, status, error) => { - console.error(xhr.responseText); - return callback(error, null); - }); + console.error(xhr.responseText) + return callback(error, null) + }) } diff --git a/public/js/htmlExport.js b/public/js/htmlExport.js index 1c2c5eb..1a873ac 100644 --- a/public/js/htmlExport.js +++ b/public/js/htmlExport.js @@ -1,6 +1,6 @@ -require('../css/github-extract.css'); -require('../css/markdown.css'); -require('../css/extra.css'); -require('../css/slide-preview.css'); -require('../css/google-font.css'); -require('../css/site.css'); +require('../css/github-extract.css') +require('../css/markdown.css') +require('../css/extra.css') +require('../css/slide-preview.css') +require('../css/google-font.css') +require('../css/site.css') diff --git a/public/js/index.js b/public/js/index.js index f0c476e..e672a68 100644 --- a/public/js/index.js +++ b/public/js/index.js @@ -1,26 +1,30 @@ -/* jquery and jquery plugins */ -require('../vendor/showup/showup'); +/* eslint-env browser, jquery */ +/* global CodeMirror, Cookies, moment, editor, ui, Spinner, + modeType, Idle, serverurl, key, gapi, Dropbox, FilePicker + ot, MediaUploader, hex2rgb, num_loaded, Visibility */ -require('../css/index.css'); -require('../css/extra.css'); -require('../css/slide-preview.css'); -require('../css/site.css'); +require('../vendor/showup/showup') -require('highlight.js/styles/github-gist.css'); +require('../css/index.css') +require('../css/extra.css') +require('../css/slide-preview.css') +require('../css/site.css') -var toMarkdown = require('to-markdown'); +require('highlight.js/styles/github-gist.css') -var saveAs = require('file-saver').saveAs; -var randomColor = require('randomcolor'); +var toMarkdown = require('to-markdown') -var _ = require("lodash"); +var saveAs = require('file-saver').saveAs +var randomColor = require('randomcolor') -var List = require('list.js'); +var _ = require('lodash') + +var List = require('list.js') import { checkLoginStateChanged, setloginStateChangeEvent -} from './lib/common/login'; +} from './lib/common/login' import { debug, @@ -31,7 +35,7 @@ import { noteurl, urlpath, version -} from './lib/config'; +} from './lib/config' import { autoLinkify, @@ -53,14 +57,14 @@ import { updateLastChange, updateLastChangeUser, updateOwner -} from './extra'; +} from './extra' import { clearMap, setupSyncAreas, syncScrollToEdit, syncScrollToView -} from './syncscroll'; +} from './syncscroll' import { writeHistory, @@ -68,4007 +72,3856 @@ import { getHistory, saveHistory, removeHistory -} from './history'; +} from './history' -var renderer = require('./render'); -var preventXSS = renderer.preventXSS; +var renderer = require('./render') +var preventXSS = renderer.preventXSS -var defaultTextHeight = 20; -var viewportMargin = 20; -var mac = CodeMirror.keyMap["default"] == CodeMirror.keyMap.macDefault; -var defaultEditorMode = 'gfm'; +var defaultTextHeight = 20 +var viewportMargin = 20 +var mac = CodeMirror.keyMap['default'] === CodeMirror.keyMap.macDefault +var defaultEditorMode = 'gfm' var defaultExtraKeys = { - "F10": function (cm) { - cm.setOption("fullScreen", !cm.getOption("fullScreen")); - }, - "Esc": function (cm) { - if (cm.getOption('keyMap').substr(0, 3) === 'vim') return CodeMirror.Pass; - else if (cm.getOption("fullScreen")) cm.setOption("fullScreen", false); - }, - "Cmd-S": function () { - return false; - }, - "Ctrl-S": function () { - return false; - }, - "Enter": "newlineAndIndentContinueMarkdownList", - "Tab": function (cm) { - var tab = '\t'; - var spaces = Array(parseInt(cm.getOption("indentUnit")) + 1).join(" "); - //auto indent whole line when in list or blockquote - var cursor = cm.getCursor(); - var line = cm.getLine(cursor.line); - var regex = /^(\s*)(>[> ]*|[*+-]\s|(\d+)([.)]))/; - var match; - var multiple = cm.getSelection().split('\n').length > 1 || cm.getSelections().length > 1; - if (multiple) { - cm.execCommand('defaultTab'); - } else if ((match = regex.exec(line)) !== null) { - var ch = match[1].length; - var pos = { - line: cursor.line, - ch: ch - }; - if (cm.getOption('indentWithTabs')) - cm.replaceRange(tab, pos, pos, '+input'); - else - cm.replaceRange(spaces, pos, pos, '+input'); - } else { - if (cm.getOption('indentWithTabs')) - cm.execCommand('defaultTab'); - else { - cm.replaceSelection(spaces); - } - } - }, - "Cmd-Left": "goLineLeftSmart", - "Cmd-Right": "goLineRight", - "Ctrl-C": function (cm) { - if (!mac && cm.getOption('keyMap').substr(0, 3) === 'vim') document.execCommand("copy"); - else return CodeMirror.Pass; - }, - "Ctrl-*": function (cm) { - wrapTextWith(cm, '*'); - }, - "Shift-Ctrl-8": function (cm) { - wrapTextWith(cm, '*'); - }, - "Ctrl-_": function (cm) { - wrapTextWith(cm, '_'); - }, - "Shift-Ctrl--": function (cm) { - wrapTextWith(cm, '_'); - }, - "Ctrl-~": function (cm) { - wrapTextWith(cm, '~'); - }, - "Shift-Ctrl-`": function (cm) { - wrapTextWith(cm, '~'); - }, - "Ctrl-^": function (cm) { - wrapTextWith(cm, '^'); - }, - "Shift-Ctrl-6": function (cm) { - wrapTextWith(cm, '^'); - }, - "Ctrl-+": function (cm) { - wrapTextWith(cm, '+'); - }, - "Shift-Ctrl-=": function (cm) { - wrapTextWith(cm, '+'); - }, - "Ctrl-=": function (cm) { - wrapTextWith(cm, '='); - }, - "Shift-Ctrl-Backspace": function (cm) { - wrapTextWith(cm, 'Backspace'); - } -}; - -var wrapSymbols = ['*', '_', '~', '^', '+', '=']; - -function wrapTextWith(cm, symbol) { - if (!cm.getSelection()) { - return CodeMirror.Pass; + 'F10': function (cm) { + cm.setOption('fullScreen', !cm.getOption('fullScreen')) + }, + 'Esc': function (cm) { + if (cm.getOption('keyMap').substr(0, 3) === 'vim') return CodeMirror.Pass + else if (cm.getOption('fullScreen')) cm.setOption('fullScreen', false) + }, + 'Cmd-S': function () { + return false + }, + 'Ctrl-S': function () { + return false + }, + 'Enter': 'newlineAndIndentContinueMarkdownList', + 'Tab': function (cm) { + var tab = '\t' + var spaces = Array(parseInt(cm.getOption('indentUnit')) + 1).join(' ') + // auto indent whole line when in list or blockquote + var cursor = cm.getCursor() + var line = cm.getLine(cursor.line) + var regex = /^(\s*)(>[> ]*|[*+-]\s|(\d+)([.)]))/ + var match + var multiple = cm.getSelection().split('\n').length > 1 || cm.getSelections().length > 1 + if (multiple) { + cm.execCommand('defaultTab') + } else if ((match = regex.exec(line)) !== null) { + var ch = match[1].length + var pos = { + line: cursor.line, + ch: ch + } + if (cm.getOption('indentWithTabs')) { cm.replaceRange(tab, pos, pos, '+input') } else { cm.replaceRange(spaces, pos, pos, '+input') } } else { - var ranges = cm.listSelections(); - for (var i = 0; i < ranges.length; i++) { - var range = ranges[i]; - if (!range.empty()) { - var from = range.from(), to = range.to(); - if (symbol !== 'Backspace') { - cm.replaceRange(symbol, to, to, '+input'); - cm.replaceRange(symbol, from, from, '+input'); + if (cm.getOption('indentWithTabs')) { cm.execCommand('defaultTab') } else { + cm.replaceSelection(spaces) + } + } + }, + 'Cmd-Left': 'goLineLeftSmart', + 'Cmd-Right': 'goLineRight', + 'Ctrl-C': function (cm) { + if (!mac && cm.getOption('keyMap').substr(0, 3) === 'vim') document.execCommand('copy') + else return CodeMirror.Pass + }, + 'Ctrl-*': function (cm) { + wrapTextWith(cm, '*') + }, + 'Shift-Ctrl-8': function (cm) { + wrapTextWith(cm, '*') + }, + 'Ctrl-_': function (cm) { + wrapTextWith(cm, '_') + }, + 'Shift-Ctrl--': function (cm) { + wrapTextWith(cm, '_') + }, + 'Ctrl-~': function (cm) { + wrapTextWith(cm, '~') + }, + 'Shift-Ctrl-`': function (cm) { + wrapTextWith(cm, '~') + }, + 'Ctrl-^': function (cm) { + wrapTextWith(cm, '^') + }, + 'Shift-Ctrl-6': function (cm) { + wrapTextWith(cm, '^') + }, + 'Ctrl-+': function (cm) { + wrapTextWith(cm, '+') + }, + 'Shift-Ctrl-=': function (cm) { + wrapTextWith(cm, '+') + }, + 'Ctrl-=': function (cm) { + wrapTextWith(cm, '=') + }, + 'Shift-Ctrl-Backspace': function (cm) { + wrapTextWith(cm, 'Backspace') + } +} + +var wrapSymbols = ['*', '_', '~', '^', '+', '='] + +function wrapTextWith (cm, symbol) { + if (!cm.getSelection()) { + return CodeMirror.Pass + } else { + var ranges = cm.listSelections() + for (var i = 0; i < ranges.length; i++) { + var range = ranges[i] + if (!range.empty()) { + var from = range.from() + var to = range.to() + if (symbol !== 'Backspace') { + cm.replaceRange(symbol, to, to, '+input') + cm.replaceRange(symbol, from, from, '+input') // workaround selection range not correct after add symbol - var _ranges = cm.listSelections(); - var anchorIndex = editor.indexFromPos(_ranges[i].anchor); - var headIndex = editor.indexFromPos(_ranges[i].head); - if (anchorIndex > headIndex) { - _ranges[i].anchor.ch--; - } else { - _ranges[i].head.ch--; - } - cm.setSelections(_ranges); - } else { - var preEndPos = { - line: to.line, - ch: to.ch + 1 - }; - var preText = cm.getRange(to, preEndPos); - var preIndex = wrapSymbols.indexOf(preText); - var postEndPos = { - line: from.line, - ch: from.ch - 1 - }; - var postText = cm.getRange(postEndPos, from); - var postIndex = wrapSymbols.indexOf(postText); + var _ranges = cm.listSelections() + var anchorIndex = window.editor.indexFromPos(_ranges[i].anchor) + var headIndex = window.editor.indexFromPos(_ranges[i].head) + if (anchorIndex > headIndex) { + _ranges[i].anchor.ch-- + } else { + _ranges[i].head.ch-- + } + cm.setSelections(_ranges) + } else { + var preEndPos = { + line: to.line, + ch: to.ch + 1 + } + var preText = cm.getRange(to, preEndPos) + var preIndex = wrapSymbols.indexOf(preText) + var postEndPos = { + line: from.line, + ch: from.ch - 1 + } + var postText = cm.getRange(postEndPos, from) + var postIndex = wrapSymbols.indexOf(postText) // check if surround symbol are list in array and matched - if (preIndex > -1 && postIndex > -1 && preIndex === postIndex) { - cm.replaceRange("", to, preEndPos, '+delete'); - cm.replaceRange("", postEndPos, from, '+delete'); - } - } - } + if (preIndex > -1 && postIndex > -1 && preIndex === postIndex) { + cm.replaceRange('', to, preEndPos, '+delete') + cm.replaceRange('', postEndPos, from, '+delete') + } } + } } + } } -var idleTime = 300000; //5 mins -var updateViewDebounce = 100; -var cursorMenuThrottle = 50; -var cursorActivityDebounce = 50; -var cursorAnimatePeriod = 100; -var supportContainers = ['success', 'info', 'warning', 'danger']; -var supportCodeModes = ['javascript', 'typescript', 'jsx', 'htmlmixed', 'htmlembedded', 'css', 'xml', 'clike', 'clojure', 'ruby', 'python', 'shell', 'php', 'sql', 'haskell', 'coffeescript', 'yaml', 'pug', 'lua', 'cmake', 'nginx', 'perl', 'sass', 'r', 'dockerfile', 'tiddlywiki', 'mediawiki', 'go']; -var supportCharts = ['sequence', 'flow', 'graphviz', 'mermaid']; +var idleTime = 300000 // 5 mins +var updateViewDebounce = 100 +var cursorMenuThrottle = 50 +var cursorActivityDebounce = 50 +var cursorAnimatePeriod = 100 +var supportContainers = ['success', 'info', 'warning', 'danger'] +var supportCodeModes = ['javascript', 'typescript', 'jsx', 'htmlmixed', 'htmlembedded', 'css', 'xml', 'clike', 'clojure', 'ruby', 'python', 'shell', 'php', 'sql', 'haskell', 'coffeescript', 'yaml', 'pug', 'lua', 'cmake', 'nginx', 'perl', 'sass', 'r', 'dockerfile', 'tiddlywiki', 'mediawiki', 'go'] +var supportCharts = ['sequence', 'flow', 'graphviz', 'mermaid'] var supportHeaders = [ - { - text: '# h1', - search: '#' - }, - { - text: '## h2', - search: '##' - }, - { - text: '### h3', - search: '###' - }, - { - text: '#### h4', - search: '####' - }, - { - text: '##### h5', - search: '#####' - }, - { - text: '###### h6', - search: '######' - }, - { - text: '###### tags: `example`', - search: '###### tags:' - } -]; + { + text: '# h1', + search: '#' + }, + { + text: '## h2', + search: '##' + }, + { + text: '### h3', + search: '###' + }, + { + text: '#### h4', + search: '####' + }, + { + text: '##### h5', + search: '#####' + }, + { + text: '###### h6', + search: '######' + }, + { + text: '###### tags: `example`', + search: '###### tags:' + } +] var supportReferrals = [ - { - text: '[reference link]', - search: '[]' - }, - { - text: '[reference]: https:// "title"', - search: '[]:' - }, - { - text: '[^footnote link]', - search: '[^]' - }, - { - text: '[^footnote reference]: https:// "title"', - search: '[^]:' - }, - { - text: '^[inline footnote]', - search: '^[]' - }, - { - text: '[link text][reference]', - search: '[][]' - }, - { - text: '[link text](https:// "title")', - search: '[]()' - }, - { - text: '![image alt][reference]', - search: '![][]' - }, - { - text: '![image alt](https:// "title")', - search: '![]()' - }, - { - text: '![image alt](https:// "title" =WidthxHeight)', - search: '![]()' - }, - { - text: '[TOC]', - search: '[]' - } -]; + { + text: '[reference link]', + search: '[]' + }, + { + text: '[reference]: https:// "title"', + search: '[]:' + }, + { + text: '[^footnote link]', + search: '[^]' + }, + { + text: '[^footnote reference]: https:// "title"', + search: '[^]:' + }, + { + text: '^[inline footnote]', + search: '^[]' + }, + { + text: '[link text][reference]', + search: '[][]' + }, + { + text: '[link text](https:// "title")', + search: '[]()' + }, + { + text: '![image alt][reference]', + search: '![][]' + }, + { + text: '![image alt](https:// "title")', + search: '![]()' + }, + { + text: '![image alt](https:// "title" =WidthxHeight)', + search: '![]()' + }, + { + text: '[TOC]', + search: '[]' + } +] var supportExternals = [ - { - text: '{%youtube youtubeid %}', - search: 'youtube' - }, - { - text: '{%vimeo vimeoid %}', - search: 'vimeo' - }, - { - text: '{%gist gistid %}', - search: 'gist' - }, - { - text: '{%slideshare slideshareid %}', - search: 'slideshare' - }, - { - text: '{%speakerdeck speakerdeckid %}', - search: 'speakerdeck' - }, - { - text: '{%pdf pdfurl %}', - search: 'pdf' - } -]; + { + text: '{%youtube youtubeid %}', + search: 'youtube' + }, + { + text: '{%vimeo vimeoid %}', + search: 'vimeo' + }, + { + text: '{%gist gistid %}', + search: 'gist' + }, + { + text: '{%slideshare slideshareid %}', + search: 'slideshare' + }, + { + text: '{%speakerdeck speakerdeckid %}', + search: 'speakerdeck' + }, + { + text: '{%pdf pdfurl %}', + search: 'pdf' + } +] var supportExtraTags = [ - { - text: '[name tag]', - search: '[]', - command: function () { - return '[name=' + personalInfo.name + ']'; - }, - }, - { - text: '[time tag]', - search: '[]', - command: function () { - return '[time=' + moment().format('llll') + ']'; - }, - }, - { - text: '[my color tag]', - search: '[]', - command: function () { - return '[color=' + personalInfo.color + ']'; - } - }, - { - text: '[random color tag]', - search: '[]', - command: function () { - var color = randomColor(); - return '[color=' + color + ']'; - } + { + text: '[name tag]', + search: '[]', + command: function () { + return '[name=' + window.personalInfo.name + ']' } -]; + }, + { + text: '[time tag]', + search: '[]', + command: function () { + return '[time=' + moment().format('llll') + ']' + } + }, + { + text: '[my color tag]', + search: '[]', + command: function () { + return '[color=' + window.personalInfo.color + ']' + } + }, + { + text: '[random color tag]', + search: '[]', + command: function () { + var color = randomColor() + return '[color=' + color + ']' + } + } +] window.modeType = { - edit: { - name: "edit" - }, - view: { - name: "view" - }, - both: { - name: "both" - } -}; + edit: { + name: 'edit' + }, + view: { + name: 'view' + }, + both: { + name: 'both' + } +} var statusType = { - connected: { - msg: "CONNECTED", - label: "label-warning", - fa: "fa-wifi" - }, - online: { - msg: "ONLINE", - label: "label-primary", - fa: "fa-users" - }, - offline: { - msg: "OFFLINE", - label: "label-danger", - fa: "fa-plug" - } -}; -var defaultMode = modeType.view; + connected: { + msg: 'CONNECTED', + label: 'label-warning', + fa: 'fa-wifi' + }, + online: { + msg: 'ONLINE', + label: 'label-primary', + fa: 'fa-users' + }, + offline: { + msg: 'OFFLINE', + label: 'label-danger', + fa: 'fa-plug' + } +} +var defaultMode = modeType.view -//global vars -window.loaded = false; -window.needRefresh = false; -window.isDirty = false; -window.editShown = false; -window.visibleXS = false; -window.visibleSM = false; -window.visibleMD = false; -window.visibleLG = false; -window.isTouchDevice = 'ontouchstart' in document.documentElement; -window.currentMode = defaultMode; -window.currentStatus = statusType.offline; +// global vars +window.loaded = false +window.needRefresh = false +window.isDirty = false +window.editShown = false +window.visibleXS = false +window.visibleSM = false +window.visibleMD = false +window.visibleLG = false +window.isTouchDevice = 'ontouchstart' in document.documentElement +window.currentMode = defaultMode +window.currentStatus = statusType.offline window.lastInfo = { - needRestore: false, - cursor: null, - scroll: null, - edit: { - scroll: { - left: null, - top: null - }, - cursor: { - line: null, - ch: null - }, - selections: null + needRestore: false, + cursor: null, + scroll: null, + edit: { + scroll: { + left: null, + top: null }, - view: { - scroll: { - left: null, - top: null - } + cursor: { + line: null, + ch: null }, - history: null -}; -window.personalInfo = {}; -window.onlineUsers = []; + selections: null + }, + view: { + scroll: { + left: null, + top: null + } + }, + history: null +} +window.personalInfo = {} +window.onlineUsers = [] window.fileTypes = { - "pl": "perl", - "cgi": "perl", - "js": "javascript", - "php": "php", - "sh": "bash", - "rb": "ruby", - "html": "html", - "py": "python" -}; + 'pl': 'perl', + 'cgi': 'perl', + 'js': 'javascript', + 'php': 'php', + 'sh': 'bash', + 'rb': 'ruby', + 'html': 'html', + 'py': 'python' +} -//editor settings -var textit = document.getElementById("textit"); -if (!textit) throw new Error("There was no textit area!"); +// editor settings +var textit = document.getElementById('textit') +if (!textit) throw new Error('There was no textit area!') window.editor = CodeMirror.fromTextArea(textit, { - mode: defaultEditorMode, - backdrop: defaultEditorMode, - keyMap: "sublime", - viewportMargin: viewportMargin, - styleActiveLine: true, - lineNumbers: true, - lineWrapping: true, - showCursorWhenSelecting: true, - highlightSelectionMatches: true, - indentUnit: 4, - continueComments: "Enter", - theme: "one-dark", - inputStyle: "textarea", - matchBrackets: true, - autoCloseBrackets: true, - matchTags: { - bothTags: true - }, - autoCloseTags: true, - foldGutter: true, - gutters: ["CodeMirror-linenumbers", "authorship-gutters", "CodeMirror-foldgutter"], - extraKeys: defaultExtraKeys, - flattenSpans: true, - addModeClass: true, - readOnly: true, - autoRefresh: true, - otherCursors: true, - placeholder: "← Start by entering a title here\n===\nVisit /features if you don't know what to do.\nHappy hacking :)" -}); -var inlineAttach = inlineAttachment.editors.codemirror4.attach(editor); -defaultTextHeight = parseInt($(".CodeMirror").css('line-height')); + mode: defaultEditorMode, + backdrop: defaultEditorMode, + keyMap: 'sublime', + viewportMargin: viewportMargin, + styleActiveLine: true, + lineNumbers: true, + lineWrapping: true, + showCursorWhenSelecting: true, + highlightSelectionMatches: true, + indentUnit: 4, + continueComments: 'Enter', + theme: 'one-dark', + inputStyle: 'textarea', + matchBrackets: true, + autoCloseBrackets: true, + matchTags: { + bothTags: true + }, + autoCloseTags: true, + foldGutter: true, + gutters: ['CodeMirror-linenumbers', 'authorship-gutters', 'CodeMirror-foldgutter'], + extraKeys: defaultExtraKeys, + flattenSpans: true, + addModeClass: true, + readOnly: true, + autoRefresh: true, + otherCursors: true, + placeholder: "← Start by entering a title here\n===\nVisit /features if you don't know what to do.\nHappy hacking :)" +}) +var inlineAttach = window.inlineAttachment.editors.codemirror4.attach(editor) +defaultTextHeight = parseInt($('.CodeMirror').css('line-height')) -var statusBarTemplate = null; -var statusBar = null; -var statusPanel = null; -var statusCursor = null; -var statusFile = null; -var statusIndicators = null; -var statusLength = null; -var statusKeymap = null; -var statusIndent = null; -var statusTheme = null; -var statusSpellcheck = null; -var statusPreferences = null; +var statusBarTemplate = null +var statusBar = null +var statusCursor = null +var statusFile = null +var statusIndicators = null +var statusLength = null +var statusTheme = null +var statusSpellcheck = null -function getStatusBarTemplate(callback) { - $.get(serverurl + '/views/statusbar.html', function (template) { - statusBarTemplate = template; - if (callback) callback(); - }); +function getStatusBarTemplate (callback) { + $.get(serverurl + '/views/statusbar.html', function (template) { + statusBarTemplate = template + if (callback) callback() + }) } -getStatusBarTemplate(); +getStatusBarTemplate() -function addStatusBar() { - if (!statusBarTemplate) { - getStatusBarTemplate(addStatusBar); - return; - } - statusBar = $(statusBarTemplate); - statusCursor = statusBar.find('.status-cursor'); - statusFile = statusBar.find('.status-file'); - statusIndicators = statusBar.find('.status-indicators'); - statusIndent = statusBar.find('.status-indent'); - statusKeymap = statusBar.find('.status-keymap'); - statusLength = statusBar.find('.status-length'); - statusTheme = statusBar.find('.status-theme'); - statusSpellcheck = statusBar.find('.status-spellcheck'); - statusPreferences = statusBar.find('.status-preferences'); - statusPanel = editor.addPanel(statusBar[0], { - position: "bottom" - }); +function addStatusBar () { + if (!statusBarTemplate) { + getStatusBarTemplate(addStatusBar) + return + } + statusBar = $(statusBarTemplate) + statusCursor = statusBar.find('.status-cursor') + statusFile = statusBar.find('.status-file') + statusIndicators = statusBar.find('.status-indicators') + statusBar.find('.status-indent') + statusBar.find('.status-keymap') + statusLength = statusBar.find('.status-length') + statusTheme = statusBar.find('.status-theme') + statusSpellcheck = statusBar.find('.status-spellcheck') + statusBar.find('.status-preferences') + editor.addPanel(statusBar[0], { + position: 'bottom' + }) - setIndent(); - setKeymap(); - setTheme(); - setSpellcheck(); - setPreferences(); + setIndent() + setKeymap() + setTheme() + setSpellcheck() + setPreferences() } -function setIndent() { - var cookieIndentType = Cookies.get('indent_type'); - var cookieTabSize = parseInt(Cookies.get('tab_size')); - var cookieSpaceUnits = parseInt(Cookies.get('space_units')); - if (cookieIndentType) { - if (cookieIndentType == 'tab') { - editor.setOption('indentWithTabs', true); - if (cookieTabSize) - editor.setOption('indentUnit', cookieTabSize); - } else if (cookieIndentType == 'space') { - editor.setOption('indentWithTabs', false); - if (cookieSpaceUnits) - editor.setOption('indentUnit', cookieSpaceUnits); - } +function setIndent () { + var cookieIndentType = Cookies.get('indent_type') + var cookieTabSize = parseInt(Cookies.get('tab_size')) + var cookieSpaceUnits = parseInt(Cookies.get('space_units')) + if (cookieIndentType) { + if (cookieIndentType === 'tab') { + editor.setOption('indentWithTabs', true) + if (cookieTabSize) { editor.setOption('indentUnit', cookieTabSize) } + } else if (cookieIndentType === 'space') { + editor.setOption('indentWithTabs', false) + if (cookieSpaceUnits) { editor.setOption('indentUnit', cookieSpaceUnits) } } - if (cookieTabSize) - editor.setOption('tabSize', cookieTabSize); + } + if (cookieTabSize) { editor.setOption('tabSize', cookieTabSize) } - var type = statusIndicators.find('.indent-type'); - var widthLabel = statusIndicators.find('.indent-width-label'); - var widthInput = statusIndicators.find('.indent-width-input'); + var type = statusIndicators.find('.indent-type') + var widthLabel = statusIndicators.find('.indent-width-label') + var widthInput = statusIndicators.find('.indent-width-input') - function setType() { - if (editor.getOption('indentWithTabs')) { - Cookies.set('indent_type', 'tab', { - expires: 365 - }); - type.text('Tab Size:'); - } else { - Cookies.set('indent_type', 'space', { - expires: 365 - }); - type.text('Spaces:'); - } - } - setType(); - - function setUnit() { - var unit = editor.getOption('indentUnit'); - if (editor.getOption('indentWithTabs')) { - Cookies.set('tab_size', unit, { - expires: 365 - }); - } else { - Cookies.set('space_units', unit, { - expires: 365 - }); - } - widthLabel.text(unit); - } - setUnit(); - - type.click(function () { - if (editor.getOption('indentWithTabs')) { - editor.setOption('indentWithTabs', false); - cookieSpaceUnits = parseInt(Cookies.get('space_units')); - if (cookieSpaceUnits) - editor.setOption('indentUnit', cookieSpaceUnits) - } else { - editor.setOption('indentWithTabs', true); - cookieTabSize = parseInt(Cookies.get('tab_size')); - if (cookieTabSize) { - editor.setOption('indentUnit', cookieTabSize); - editor.setOption('tabSize', cookieTabSize); - } - } - setType(); - setUnit(); - }); - widthLabel.click(function () { - if (widthLabel.is(':visible')) { - widthLabel.addClass('hidden'); - widthInput.removeClass('hidden'); - widthInput.val(editor.getOption('indentUnit')); - widthInput.select(); - } else { - widthLabel.removeClass('hidden'); - widthInput.addClass('hidden'); - } - }); - widthInput.on('change', function () { - var val = parseInt(widthInput.val()); - if (!val) val = editor.getOption('indentUnit'); - if (val < 1) val = 1; - else if (val > 10) val = 10; - - if (editor.getOption('indentWithTabs')) { - editor.setOption('tabSize', val); - } - editor.setOption('indentUnit', val); - setUnit(); - }); - widthInput.on('blur', function () { - widthLabel.removeClass('hidden'); - widthInput.addClass('hidden'); - }); -} - -function setKeymap() { - var cookieKeymap = Cookies.get('keymap'); - if (cookieKeymap) - editor.setOption('keyMap', cookieKeymap); - - var label = statusIndicators.find('.ui-keymap-label'); - var sublime = statusIndicators.find('.ui-keymap-sublime'); - var emacs = statusIndicators.find('.ui-keymap-emacs'); - var vim = statusIndicators.find('.ui-keymap-vim'); - - function setKeymapLabel() { - var keymap = editor.getOption('keyMap'); - Cookies.set('keymap', keymap, { - expires: 365 - }); - label.text(keymap); - restoreOverrideEditorKeymap(); - setOverrideBrowserKeymap(); - } - setKeymapLabel(); - - sublime.click(function () { - editor.setOption('keyMap', 'sublime'); - setKeymapLabel(); - }); - emacs.click(function () { - editor.setOption('keyMap', 'emacs'); - setKeymapLabel(); - }); - vim.click(function () { - editor.setOption('keyMap', 'vim'); - setKeymapLabel(); - }); -} - -function setTheme() { - var cookieTheme = Cookies.get('theme'); - if (cookieTheme) { - editor.setOption('theme', cookieTheme); - } - - var themeToggle = statusTheme.find('.ui-theme-toggle'); - themeToggle.click(function () { - var theme = editor.getOption('theme'); - if (theme == "one-dark") { - theme = "default"; - } else { - theme = "one-dark"; - } - editor.setOption('theme', theme); - Cookies.set('theme', theme, { - expires: 365 - }); - checkTheme(); - }); - function checkTheme() { - var theme = editor.getOption('theme'); - if (theme == "one-dark") { - themeToggle.removeClass('active'); - } else { - themeToggle.addClass('active'); - } - } - checkTheme(); -} - -function setSpellcheck() { - var cookieSpellcheck = Cookies.get('spellcheck'); - if (cookieSpellcheck) { - var mode = null; - if (cookieSpellcheck === 'true' || cookieSpellcheck === true) { - mode = 'spell-checker'; - } else { - mode = defaultEditorMode; - } - if (mode && mode !== editor.getOption('mode')) { - editor.setOption('mode', mode); - } - } - - var spellcheckToggle = statusSpellcheck.find('.ui-spellcheck-toggle'); - spellcheckToggle.click(function () { - var mode = editor.getOption('mode'); - if (mode == defaultEditorMode) { - mode = "spell-checker"; - } else { - mode = defaultEditorMode; - } - if (mode && mode !== editor.getOption('mode')) { - editor.setOption('mode', mode); - } - Cookies.set('spellcheck', (mode == "spell-checker"), { - expires: 365 - }); - checkSpellcheck(); - }); - function checkSpellcheck() { - var mode = editor.getOption('mode'); - if (mode == defaultEditorMode) { - spellcheckToggle.removeClass('active'); - } else { - spellcheckToggle.addClass('active'); - } - } - checkSpellcheck(); - - //workaround spellcheck might not activate beacuse the ajax loading - if (num_loaded < 2) { - var spellcheckTimer = setInterval(function () { - if (num_loaded >= 2) { - if (editor.getOption('mode') == "spell-checker") - editor.setOption('mode', "spell-checker"); - clearInterval(spellcheckTimer); - } - }, 100); - } -} - -var jumpToAddressBarKeymapName = mac ? "Cmd-L" : "Ctrl-L"; -var jumpToAddressBarKeymapValue = null; -function resetEditorKeymapToBrowserKeymap() { - var keymap = editor.getOption('keyMap'); - if (!jumpToAddressBarKeymapValue) { - jumpToAddressBarKeymapValue = CodeMirror.keyMap[keymap][jumpToAddressBarKeymapName]; - delete CodeMirror.keyMap[keymap][jumpToAddressBarKeymapName]; - } -} -function restoreOverrideEditorKeymap() { - var keymap = editor.getOption('keyMap'); - if (jumpToAddressBarKeymapValue) { - CodeMirror.keyMap[keymap][jumpToAddressBarKeymapName] = jumpToAddressBarKeymapValue; - jumpToAddressBarKeymapValue = null; - } -} -function setOverrideBrowserKeymap() { - var overrideBrowserKeymap = $('.ui-preferences-override-browser-keymap label > input[type="checkbox"]'); - if(overrideBrowserKeymap.is(":checked")) { - Cookies.set('preferences-override-browser-keymap', true, { - expires: 365 - }); - restoreOverrideEditorKeymap(); + function setType () { + if (editor.getOption('indentWithTabs')) { + Cookies.set('indent_type', 'tab', { + expires: 365 + }) + type.text('Tab Size:') } else { - Cookies.remove('preferences-override-browser-keymap'); - resetEditorKeymapToBrowserKeymap(); + Cookies.set('indent_type', 'space', { + expires: 365 + }) + type.text('Spaces:') } -} + } + setType() -function setPreferences() { - var overrideBrowserKeymap = $('.ui-preferences-override-browser-keymap label > input[type="checkbox"]'); - var cookieOverrideBrowserKeymap = Cookies.get('preferences-override-browser-keymap'); - if (cookieOverrideBrowserKeymap && cookieOverrideBrowserKeymap === "true") { - overrideBrowserKeymap.prop('checked', true); + function setUnit () { + var unit = editor.getOption('indentUnit') + if (editor.getOption('indentWithTabs')) { + Cookies.set('tab_size', unit, { + expires: 365 + }) } else { - overrideBrowserKeymap.prop('checked', false); + Cookies.set('space_units', unit, { + expires: 365 + }) } - setOverrideBrowserKeymap(); + widthLabel.text(unit) + } + setUnit() - overrideBrowserKeymap.change(function() { - setOverrideBrowserKeymap(); - }); + type.click(function () { + if (editor.getOption('indentWithTabs')) { + editor.setOption('indentWithTabs', false) + cookieSpaceUnits = parseInt(Cookies.get('space_units')) + if (cookieSpaceUnits) { editor.setOption('indentUnit', cookieSpaceUnits) } + } else { + editor.setOption('indentWithTabs', true) + cookieTabSize = parseInt(Cookies.get('tab_size')) + if (cookieTabSize) { + editor.setOption('indentUnit', cookieTabSize) + editor.setOption('tabSize', cookieTabSize) + } + } + setType() + setUnit() + }) + widthLabel.click(function () { + if (widthLabel.is(':visible')) { + widthLabel.addClass('hidden') + widthInput.removeClass('hidden') + widthInput.val(editor.getOption('indentUnit')) + widthInput.select() + } else { + widthLabel.removeClass('hidden') + widthInput.addClass('hidden') + } + }) + widthInput.on('change', function () { + var val = parseInt(widthInput.val()) + if (!val) val = editor.getOption('indentUnit') + if (val < 1) val = 1 + else if (val > 10) val = 10 + + if (editor.getOption('indentWithTabs')) { + editor.setOption('tabSize', val) + } + editor.setOption('indentUnit', val) + setUnit() + }) + widthInput.on('blur', function () { + widthLabel.removeClass('hidden') + widthInput.addClass('hidden') + }) } -var selection = null; +function setKeymap () { + var cookieKeymap = Cookies.get('keymap') + if (cookieKeymap) { editor.setOption('keyMap', cookieKeymap) } -function updateStatusBar() { - if (!statusBar) return; - var cursor = editor.getCursor(); - var cursorText = 'Line ' + (cursor.line + 1) + ', Columns ' + (cursor.ch + 1); - if (selection) { - var anchor = selection.anchor; - var head = selection.head; - var start = head.line <= anchor.line ? head : anchor; - var end = head.line >= anchor.line ? head : anchor; - var selectionText = ' — Selected '; - var selectionCharCount = Math.abs(head.ch - anchor.ch); + var label = statusIndicators.find('.ui-keymap-label') + var sublime = statusIndicators.find('.ui-keymap-sublime') + var emacs = statusIndicators.find('.ui-keymap-emacs') + var vim = statusIndicators.find('.ui-keymap-vim') + + function setKeymapLabel () { + var keymap = editor.getOption('keyMap') + Cookies.set('keymap', keymap, { + expires: 365 + }) + label.text(keymap) + restoreOverrideEditorKeymap() + setOverrideBrowserKeymap() + } + setKeymapLabel() + + sublime.click(function () { + editor.setOption('keyMap', 'sublime') + setKeymapLabel() + }) + emacs.click(function () { + editor.setOption('keyMap', 'emacs') + setKeymapLabel() + }) + vim.click(function () { + editor.setOption('keyMap', 'vim') + setKeymapLabel() + }) +} + +function setTheme () { + var cookieTheme = Cookies.get('theme') + if (cookieTheme) { + editor.setOption('theme', cookieTheme) + } + + var themeToggle = statusTheme.find('.ui-theme-toggle') + themeToggle.click(function () { + var theme = editor.getOption('theme') + if (theme === 'one-dark') { + theme = 'default' + } else { + theme = 'one-dark' + } + editor.setOption('theme', theme) + Cookies.set('theme', theme, { + expires: 365 + }) + checkTheme() + }) + function checkTheme () { + var theme = editor.getOption('theme') + if (theme === 'one-dark') { + themeToggle.removeClass('active') + } else { + themeToggle.addClass('active') + } + } + checkTheme() +} + +function setSpellcheck () { + var cookieSpellcheck = Cookies.get('spellcheck') + if (cookieSpellcheck) { + var mode = null + if (cookieSpellcheck === 'true' || cookieSpellcheck === true) { + mode = 'spell-checker' + } else { + mode = defaultEditorMode + } + if (mode && mode !== editor.getOption('mode')) { + editor.setOption('mode', mode) + } + } + + var spellcheckToggle = statusSpellcheck.find('.ui-spellcheck-toggle') + spellcheckToggle.click(function () { + var mode = editor.getOption('mode') + if (mode === defaultEditorMode) { + mode = 'spell-checker' + } else { + mode = defaultEditorMode + } + if (mode && mode !== editor.getOption('mode')) { + editor.setOption('mode', mode) + } + Cookies.set('spellcheck', (mode === 'spell-checker'), { + expires: 365 + }) + checkSpellcheck() + }) + function checkSpellcheck () { + var mode = editor.getOption('mode') + if (mode === defaultEditorMode) { + spellcheckToggle.removeClass('active') + } else { + spellcheckToggle.addClass('active') + } + } + checkSpellcheck() + + // workaround spellcheck might not activate beacuse the ajax loading + /* eslint-disable camelcase */ + if (num_loaded < 2) { + var spellcheckTimer = setInterval(function () { + if (num_loaded >= 2) { + if (editor.getOption('mode') === 'spell-checker') { editor.setOption('mode', 'spell-checker') } + clearInterval(spellcheckTimer) + } + }, 100) + } + /* eslint-endable camelcase */ +} + +var jumpToAddressBarKeymapName = mac ? 'Cmd-L' : 'Ctrl-L' +var jumpToAddressBarKeymapValue = null +function resetEditorKeymapToBrowserKeymap () { + var keymap = editor.getOption('keyMap') + if (!jumpToAddressBarKeymapValue) { + jumpToAddressBarKeymapValue = CodeMirror.keyMap[keymap][jumpToAddressBarKeymapName] + delete CodeMirror.keyMap[keymap][jumpToAddressBarKeymapName] + } +} +function restoreOverrideEditorKeymap () { + var keymap = editor.getOption('keyMap') + if (jumpToAddressBarKeymapValue) { + CodeMirror.keyMap[keymap][jumpToAddressBarKeymapName] = jumpToAddressBarKeymapValue + jumpToAddressBarKeymapValue = null + } +} +function setOverrideBrowserKeymap () { + var overrideBrowserKeymap = $('.ui-preferences-override-browser-keymap label > input[type="checkbox"]') + if (overrideBrowserKeymap.is(':checked')) { + Cookies.set('preferences-override-browser-keymap', true, { + expires: 365 + }) + restoreOverrideEditorKeymap() + } else { + Cookies.remove('preferences-override-browser-keymap') + resetEditorKeymapToBrowserKeymap() + } +} + +function setPreferences () { + var overrideBrowserKeymap = $('.ui-preferences-override-browser-keymap label > input[type="checkbox"]') + var cookieOverrideBrowserKeymap = Cookies.get('preferences-override-browser-keymap') + if (cookieOverrideBrowserKeymap && cookieOverrideBrowserKeymap === 'true') { + overrideBrowserKeymap.prop('checked', true) + } else { + overrideBrowserKeymap.prop('checked', false) + } + setOverrideBrowserKeymap() + + overrideBrowserKeymap.change(function () { + setOverrideBrowserKeymap() + }) +} + +var selection = null + +function updateStatusBar () { + if (!statusBar) return + var cursor = editor.getCursor() + var cursorText = 'Line ' + (cursor.line + 1) + ', Columns ' + (cursor.ch + 1) + if (selection) { + var anchor = selection.anchor + var head = selection.head + var start = head.line <= anchor.line ? head : anchor + var end = head.line >= anchor.line ? head : anchor + var selectionText = ' — Selected ' + var selectionCharCount = Math.abs(head.ch - anchor.ch) // borrow from brackets EditorStatusBar.js - if (start.line !== end.line) { - var lines = end.line - start.line + 1; - if (end.ch === 0) { - lines--; - } - selectionText += lines + ' lines'; - } else if (selectionCharCount > 0) - selectionText += selectionCharCount + ' columns'; - if (start.line !== end.line || selectionCharCount > 0) - cursorText += selectionText; - } - statusCursor.text(cursorText); - var fileText = ' — ' + editor.lineCount() + ' Lines'; - statusFile.text(fileText); - var docLength = editor.getValue().length; - statusLength.text('Length ' + docLength); - if (docLength > (docmaxlength * 0.95)) { - statusLength.css('color', 'red'); - statusLength.attr('title', 'Your almost reach note max length limit.'); - } else if (docLength > (docmaxlength * 0.8)) { - statusLength.css('color', 'orange'); - statusLength.attr('title', 'You nearly fill the note, consider to make more pieces.'); - } else { - statusLength.css('color', 'white'); - statusLength.attr('title', 'You could write up to ' + docmaxlength + ' characters in this note.'); - } + if (start.line !== end.line) { + var lines = end.line - start.line + 1 + if (end.ch === 0) { + lines-- + } + selectionText += lines + ' lines' + } else if (selectionCharCount > 0) { selectionText += selectionCharCount + ' columns' } + if (start.line !== end.line || selectionCharCount > 0) { cursorText += selectionText } + } + statusCursor.text(cursorText) + var fileText = ' — ' + editor.lineCount() + ' Lines' + statusFile.text(fileText) + var docLength = editor.getValue().length + statusLength.text('Length ' + docLength) + if (docLength > (docmaxlength * 0.95)) { + statusLength.css('color', 'red') + statusLength.attr('title', 'Your almost reach note max length limit.') + } else if (docLength > (docmaxlength * 0.8)) { + statusLength.css('color', 'orange') + statusLength.attr('title', 'You nearly fill the note, consider to make more pieces.') + } else { + statusLength.css('color', 'white') + statusLength.attr('title', 'You could write up to ' + docmaxlength + ' characters in this note.') + } } -//ui vars +// ui vars window.ui = { - spinner: $(".ui-spinner"), - content: $(".ui-content"), - toolbar: { - shortStatus: $(".ui-short-status"), - status: $(".ui-status"), - new: $(".ui-new"), - publish: $(".ui-publish"), - extra: { - revision: $(".ui-extra-revision"), - slide: $(".ui-extra-slide") - }, - download: { - markdown: $(".ui-download-markdown"), - html: $(".ui-download-html"), - rawhtml: $(".ui-download-raw-html"), - pdf: $(".ui-download-pdf-beta"), - }, - export: { - dropbox: $(".ui-save-dropbox"), - googleDrive: $(".ui-save-google-drive"), - gist: $(".ui-save-gist"), - snippet: $(".ui-save-snippet") - }, - import: { - dropbox: $(".ui-import-dropbox"), - googleDrive: $(".ui-import-google-drive"), - gist: $(".ui-import-gist"), - snippet: $(".ui-import-snippet"), - clipboard: $(".ui-import-clipboard") - }, - mode: $(".ui-mode"), - edit: $(".ui-edit"), - view: $(".ui-view"), - both: $(".ui-both"), - uploadImage: $(".ui-upload-image") + spinner: $('.ui-spinner'), + content: $('.ui-content'), + toolbar: { + shortStatus: $('.ui-short-status'), + status: $('.ui-status'), + new: $('.ui-new'), + publish: $('.ui-publish'), + extra: { + revision: $('.ui-extra-revision'), + slide: $('.ui-extra-slide') }, - infobar: { - lastchange: $(".ui-lastchange"), - lastchangeuser: $(".ui-lastchangeuser"), - nolastchangeuser: $(".ui-no-lastchangeuser"), - permission: { - permission: $(".ui-permission"), - label: $(".ui-permission-label"), - freely: $(".ui-permission-freely"), - editable: $(".ui-permission-editable"), - locked: $(".ui-permission-locked"), - private: $(".ui-permission-private"), - limited: $(".ui-permission-limited"), - protected: $(".ui-permission-protected") - }, - delete: $(".ui-delete-note") + download: { + markdown: $('.ui-download-markdown'), + html: $('.ui-download-html'), + rawhtml: $('.ui-download-raw-html'), + pdf: $('.ui-download-pdf-beta') }, - toc: { - toc: $('.ui-toc'), - affix: $('.ui-affix-toc'), - label: $('.ui-toc-label'), - dropdown: $('.ui-toc-dropdown') + export: { + dropbox: $('.ui-save-dropbox'), + googleDrive: $('.ui-save-google-drive'), + gist: $('.ui-save-gist'), + snippet: $('.ui-save-snippet') }, - area: { - edit: $(".ui-edit-area"), - view: $(".ui-view-area"), - codemirror: $(".ui-edit-area .CodeMirror"), - codemirrorScroll: $(".ui-edit-area .CodeMirror .CodeMirror-scroll"), - codemirrorSizer: $(".ui-edit-area .CodeMirror .CodeMirror-sizer"), - codemirrorSizerInner: $(".ui-edit-area .CodeMirror .CodeMirror-sizer > div"), - markdown: $(".ui-view-area .markdown-body"), - resize: { - handle: $('.ui-resizable-handle'), - syncToggle: $('.ui-sync-toggle') - } + import: { + dropbox: $('.ui-import-dropbox'), + googleDrive: $('.ui-import-google-drive'), + gist: $('.ui-import-gist'), + snippet: $('.ui-import-snippet'), + clipboard: $('.ui-import-clipboard') }, - modal: { - snippetImportProjects: $("#snippetImportModalProjects"), - snippetImportSnippets: $("#snippetImportModalSnippets"), - revision: $("#revisionModal") + mode: $('.ui-mode'), + edit: $('.ui-edit'), + view: $('.ui-view'), + both: $('.ui-both'), + uploadImage: $('.ui-upload-image') + }, + infobar: { + lastchange: $('.ui-lastchange'), + lastchangeuser: $('.ui-lastchangeuser'), + nolastchangeuser: $('.ui-no-lastchangeuser'), + permission: { + permission: $('.ui-permission'), + label: $('.ui-permission-label'), + freely: $('.ui-permission-freely'), + editable: $('.ui-permission-editable'), + locked: $('.ui-permission-locked'), + private: $('.ui-permission-private'), + limited: $('.ui-permission-limited'), + protected: $('.ui-permission-protected') + }, + delete: $('.ui-delete-note') + }, + toc: { + toc: $('.ui-toc'), + affix: $('.ui-affix-toc'), + label: $('.ui-toc-label'), + dropdown: $('.ui-toc-dropdown') + }, + area: { + edit: $('.ui-edit-area'), + view: $('.ui-view-area'), + codemirror: $('.ui-edit-area .CodeMirror'), + codemirrorScroll: $('.ui-edit-area .CodeMirror .CodeMirror-scroll'), + codemirrorSizer: $('.ui-edit-area .CodeMirror .CodeMirror-sizer'), + codemirrorSizerInner: $('.ui-edit-area .CodeMirror .CodeMirror-sizer > div'), + markdown: $('.ui-view-area .markdown-body'), + resize: { + handle: $('.ui-resizable-handle'), + syncToggle: $('.ui-sync-toggle') } -}; + }, + modal: { + snippetImportProjects: $('#snippetImportModalProjects'), + snippetImportSnippets: $('#snippetImportModalSnippets'), + revision: $('#revisionModal') + } +} -//page actions +// page actions var opts = { - lines: 11, // The number of lines to draw - length: 20, // The length of each line - width: 2, // The line thickness - radius: 30, // The radius of the inner circle - corners: 0, // Corner roundness (0..1) - rotate: 0, // The rotation offset - direction: 1, // 1: clockwise, -1: counterclockwise - color: '#000', // #rgb or #rrggbb or array of colors - speed: 1.1, // Rounds per second - trail: 60, // Afterglow percentage - shadow: false, // Whether to render a shadow - hwaccel: true, // Whether to use hardware acceleration - className: 'spinner', // The CSS class to assign to the spinner - zIndex: 2e9, // The z-index (defaults to 2000000000) - top: '50%', // Top position relative to parent - left: '50%' // Left position relative to parent -}; -var spinner = new Spinner(opts).spin(ui.spinner[0]); + lines: 11, // The number of lines to draw + length: 20, // The length of each line + width: 2, // The line thickness + radius: 30, // The radius of the inner circle + corners: 0, // Corner roundness (0..1) + rotate: 0, // The rotation offset + direction: 1, // 1: clockwise, -1: counterclockwise + color: '#000', // #rgb or #rrggbb or array of colors + speed: 1.1, // Rounds per second + trail: 60, // Afterglow percentage + shadow: false, // Whether to render a shadow + hwaccel: true, // Whether to use hardware acceleration + className: 'spinner', // The CSS class to assign to the spinner + zIndex: 2e9, // The z-index (defaults to 2000000000) + top: '50%', // Top position relative to parent + left: '50%' // Left position relative to parent +} -//idle +/* eslint-disable no-unused-vars */ +var spinner = new Spinner(opts).spin(ui.spinner[0]) +/* eslint-enable no-unused-vars */ + +// idle var idle = new Idle({ - onAway: function () { - idle.isAway = true; - emitUserStatus(); - updateOnlineStatus(); - }, - onAwayBack: function () { - idle.isAway = false; - emitUserStatus(); - updateOnlineStatus(); - setHaveUnreadChanges(false); - updateTitleReminder(); - }, - awayTimeout: idleTime -}); + onAway: function () { + idle.isAway = true + emitUserStatus() + updateOnlineStatus() + }, + onAwayBack: function () { + idle.isAway = false + emitUserStatus() + updateOnlineStatus() + setHaveUnreadChanges(false) + updateTitleReminder() + }, + awayTimeout: idleTime +}) ui.area.codemirror.on('touchstart', function () { - idle.onActive(); -}); + idle.onActive() +}) -var haveUnreadChanges = false; +var haveUnreadChanges = false -function setHaveUnreadChanges(bool) { - if (!loaded) return; - if (bool && (idle.isAway || Visibility.hidden())) { - haveUnreadChanges = true; - } else if (!bool && !idle.isAway && !Visibility.hidden()) { - haveUnreadChanges = false; - } +function setHaveUnreadChanges (bool) { + if (!window.loaded) return + if (bool && (idle.isAway || Visibility.hidden())) { + haveUnreadChanges = true + } else if (!bool && !idle.isAway && !Visibility.hidden()) { + haveUnreadChanges = false + } } -function updateTitleReminder() { - if (!loaded) return; - if (haveUnreadChanges) { - document.title = '• ' + renderTitle(ui.area.markdown); - } else { - document.title = renderTitle(ui.area.markdown); - } +function updateTitleReminder () { + if (!window.loaded) return + if (haveUnreadChanges) { + document.title = '• ' + renderTitle(ui.area.markdown) + } else { + document.title = renderTitle(ui.area.markdown) + } } -function setRefreshModal(status) { - $('#refreshModal').modal('show'); - $('#refreshModal').find('.modal-body > div').hide(); - $('#refreshModal').find('.' + status).show(); +function setRefreshModal (status) { + $('#refreshModal').modal('show') + $('#refreshModal').find('.modal-body > div').hide() + $('#refreshModal').find('.' + status).show() } -function setNeedRefresh() { - needRefresh = true; - editor.setOption('readOnly', true); - socket.disconnect(); - showStatus(statusType.offline); +function setNeedRefresh () { + window.needRefresh = true + editor.setOption('readOnly', true) + socket.disconnect() + showStatus(statusType.offline) } setloginStateChangeEvent(function () { - setRefreshModal('user-state-changed'); - setNeedRefresh(); -}); + setRefreshModal('user-state-changed') + setNeedRefresh() +}) -//visibility -var wasFocus = false; +// visibility +var wasFocus = false Visibility.change(function (e, state) { - var hidden = Visibility.hidden(); - if (hidden) { - if (editorHasFocus()) { - wasFocus = true; - editor.getInputField().blur(); - } - } else { - if (wasFocus) { - if (!visibleXS) { - editor.focus(); - editor.refresh(); - } - wasFocus = false; - } - setHaveUnreadChanges(false); + var hidden = Visibility.hidden() + if (hidden) { + if (editorHasFocus()) { + wasFocus = true + editor.getInputField().blur() } - updateTitleReminder(); -}); + } else { + if (wasFocus) { + if (!window.visibleXS) { + editor.focus() + editor.refresh() + } + wasFocus = false + } + setHaveUnreadChanges(false) + } + updateTitleReminder() +}) -//when page ready +// when page ready $(document).ready(function () { - idle.checkAway(); - checkResponsive(); - //if in smaller screen, we don't need advanced scrollbar - var scrollbarStyle; - if (visibleXS) { - scrollbarStyle = 'native'; - } else { - scrollbarStyle = 'overlay'; - } - if (scrollbarStyle != editor.getOption('scrollbarStyle')) { - editor.setOption('scrollbarStyle', scrollbarStyle); - clearMap(); - } - checkEditorStyle(); + idle.checkAway() + checkResponsive() + // if in smaller screen, we don't need advanced scrollbar + var scrollbarStyle + if (window.visibleXS) { + scrollbarStyle = 'native' + } else { + scrollbarStyle = 'overlay' + } + if (scrollbarStyle !== editor.getOption('scrollbarStyle')) { + editor.setOption('scrollbarStyle', scrollbarStyle) + clearMap() + } + checkEditorStyle() /* we need this only on touch devices */ - if (isTouchDevice) { + if (window.isTouchDevice) { /* cache dom references */ - var $body = jQuery('body'); + var $body = jQuery('body') /* bind events */ - $(document) + $(document) .on('focus', 'textarea, input', function () { - $body.addClass('fixfixed'); + $body.addClass('fixfixed') }) .on('blur', 'textarea, input', function () { - $body.removeClass('fixfixed'); - }); - } - //showup - $().showUp('.navbar', { - upClass: 'navbar-hide', - downClass: 'navbar-show' - }); - //tooltip - $('[data-toggle="tooltip"]').tooltip(); + $body.removeClass('fixfixed') + }) + } + // showup + $().showUp('.navbar', { + upClass: 'navbar-hide', + downClass: 'navbar-show' + }) + // tooltip + $('[data-toggle="tooltip"]').tooltip() // shortcuts // allow on all tags - key.filter = function (e) { return true; }; - key('ctrl+alt+e', function (e) { - changeMode(modeType.edit); - }); - key('ctrl+alt+v', function (e) { - changeMode(modeType.view); - }); - key('ctrl+alt+b', function (e) { - changeMode(modeType.both); - }); + key.filter = function (e) { return true } + key('ctrl+alt+e', function (e) { + changeMode(modeType.edit) + }) + key('ctrl+alt+v', function (e) { + changeMode(modeType.view) + }) + key('ctrl+alt+b', function (e) { + changeMode(modeType.both) + }) // toggle-dropdown - $(document).on('click', '.toggle-dropdown .dropdown-menu', function (e) { - e.stopPropagation(); - }); -}); -//when page resize + $(document).on('click', '.toggle-dropdown .dropdown-menu', function (e) { + e.stopPropagation() + }) +}) +// when page resize $(window).resize(function () { - checkLayout(); - checkEditorStyle(); - checkTocStyle(); - checkCursorMenu(); - windowResize(); -}); -//when page unload + checkLayout() + checkEditorStyle() + checkTocStyle() + checkCursorMenu() + windowResize() +}) +// when page unload $(window).on('unload', function () { - //updateHistoryInner(); -}); + // updateHistoryInner(); +}) $(window).on('error', function () { - //setNeedRefresh(); -}); + // setNeedRefresh(); +}) -setupSyncAreas(ui.area.codemirrorScroll, ui.area.view, ui.area.markdown); +setupSyncAreas(ui.area.codemirrorScroll, ui.area.view, ui.area.markdown) -function autoSyncscroll() { - if (editorHasFocus()) { - syncScrollToView(); - } else { - syncScrollToEdit(); - } +function autoSyncscroll () { + if (editorHasFocus()) { + syncScrollToView() + } else { + syncScrollToEdit() + } } -var windowResizeDebounce = 200; -var windowResize = _.debounce(windowResizeInner, windowResizeDebounce); +var windowResizeDebounce = 200 +var windowResize = _.debounce(windowResizeInner, windowResizeDebounce) -function windowResizeInner(callback) { - checkLayout(); - checkResponsive(); - checkEditorStyle(); - checkTocStyle(); - checkCursorMenu(); - //refresh editor - if (loaded) { - if (editor.getOption('scrollbarStyle') === 'native') { - setTimeout(function () { - clearMap(); - autoSyncscroll(); - updateScrollspy(); - if (callback && typeof callback === 'function') - callback(); - }, 1); - } else { +function windowResizeInner (callback) { + checkLayout() + checkResponsive() + checkEditorStyle() + checkTocStyle() + checkCursorMenu() + // refresh editor + if (window.loaded) { + if (editor.getOption('scrollbarStyle') === 'native') { + setTimeout(function () { + clearMap() + autoSyncscroll() + updateScrollspy() + if (callback && typeof callback === 'function') { callback() } + }, 1) + } else { // force it load all docs at once to prevent scroll knob blink - editor.setOption('viewportMargin', Infinity); - setTimeout(function () { - clearMap(); - autoSyncscroll(); - editor.setOption('viewportMargin', viewportMargin); - //add or update user cursors - for (var i = 0; i < onlineUsers.length; i++) { - if (onlineUsers[i].id != personalInfo.id) - buildCursor(onlineUsers[i]); - } - updateScrollspy(); - if (callback && typeof callback === 'function') - callback(); - }, 1); + editor.setOption('viewportMargin', Infinity) + setTimeout(function () { + clearMap() + autoSyncscroll() + editor.setOption('viewportMargin', viewportMargin) + // add or update user cursors + for (var i = 0; i < window.onlineUsers.length; i++) { + if (window.onlineUsers[i].id !== window.personalInfo.id) { buildCursor(window.onlineUsers[i]) } } + updateScrollspy() + if (callback && typeof callback === 'function') { callback() } + }, 1) } + } } -function checkLayout() { - var navbarHieght = $('.navbar').outerHeight(); - $('body').css('padding-top', navbarHieght + 'px'); +function checkLayout () { + var navbarHieght = $('.navbar').outerHeight() + $('body').css('padding-top', navbarHieght + 'px') } -function editorHasFocus() { - return $(editor.getInputField()).is(":focus"); +function editorHasFocus () { + return $(editor.getInputField()).is(':focus') } -//768-792px have a gap -function checkResponsive() { - visibleXS = $(".visible-xs").is(":visible"); - visibleSM = $(".visible-sm").is(":visible"); - visibleMD = $(".visible-md").is(":visible"); - visibleLG = $(".visible-lg").is(":visible"); +// 768-792px have a gap +function checkResponsive () { + window.visibleXS = $('.visible-xs').is(':visible') + window.visibleSM = $('.visible-sm').is(':visible') + window.visibleMD = $('.visible-md').is(':visible') + window.visibleLG = $('.visible-lg').is(':visible') - if (visibleXS && currentMode == modeType.both) - if (editorHasFocus()) - changeMode(modeType.edit); - else - changeMode(modeType.view); + if (window.visibleXS && window.currentMode === modeType.both) { + if (editorHasFocus()) { changeMode(modeType.edit) } else { changeMode(modeType.view) } + } - emitUserStatus(); + emitUserStatus() } -var lastEditorWidth = 0; -var previousFocusOnEditor = null; +var lastEditorWidth = 0 +var previousFocusOnEditor = null -function checkEditorStyle() { - var desireHeight = statusBar ? (ui.area.edit.height() - statusBar.outerHeight()) : ui.area.edit.height(); +function checkEditorStyle () { + var desireHeight = statusBar ? (ui.area.edit.height() - statusBar.outerHeight()) : ui.area.edit.height() // set editor height and min height based on scrollbar style and mode - var scrollbarStyle = editor.getOption('scrollbarStyle'); - if (scrollbarStyle == 'overlay' || currentMode == modeType.both) { - ui.area.codemirrorScroll.css('height', desireHeight + 'px'); - ui.area.codemirrorScroll.css('min-height', ''); - checkEditorScrollbar(); - } else if (scrollbarStyle == 'native') { - ui.area.codemirrorScroll.css('height', ''); - ui.area.codemirrorScroll.css('min-height', desireHeight + 'px'); - } + var scrollbarStyle = editor.getOption('scrollbarStyle') + if (scrollbarStyle === 'overlay' || window.currentMode === modeType.both) { + ui.area.codemirrorScroll.css('height', desireHeight + 'px') + ui.area.codemirrorScroll.css('min-height', '') + checkEditorScrollbar() + } else if (scrollbarStyle === 'native') { + ui.area.codemirrorScroll.css('height', '') + ui.area.codemirrorScroll.css('min-height', desireHeight + 'px') + } // workaround editor will have wrong doc height when editor height changed - editor.setSize(null, ui.area.edit.height()); - //make editor resizable - if (!ui.area.resize.handle.length) { - ui.area.edit.resizable({ - handles: 'e', - maxWidth: $(window).width() * 0.7, - minWidth: $(window).width() * 0.2, - create: function (e, ui) { - $(this).parent().on('resize', function (e) { - e.stopPropagation(); - }); - }, - start: function (e) { - editor.setOption('viewportMargin', Infinity); - }, - resize: function (e) { - ui.area.resize.syncToggle.stop(true, true).show(); - checkTocStyle(); - }, - stop: function (e) { - lastEditorWidth = ui.area.edit.width(); + editor.setSize(null, ui.area.edit.height()) + // make editor resizable + if (!ui.area.resize.handle.length) { + ui.area.edit.resizable({ + handles: 'e', + maxWidth: $(window).width() * 0.7, + minWidth: $(window).width() * 0.2, + create: function (e, ui) { + $(this).parent().on('resize', function (e) { + e.stopPropagation() + }) + }, + start: function (e) { + editor.setOption('viewportMargin', Infinity) + }, + resize: function (e) { + ui.area.resize.syncToggle.stop(true, true).show() + checkTocStyle() + }, + stop: function (e) { + lastEditorWidth = ui.area.edit.width() // workaround that scroll event bindings - preventSyncScrollToView = 2; - preventSyncScrollToEdit = true; - editor.setOption('viewportMargin', viewportMargin); - if (editorHasFocus()) { - windowResizeInner(function () { - ui.area.codemirrorScroll.scroll(); - }); - } else { - windowResizeInner(function () { - ui.area.view.scroll(); - }); - } - checkEditorScrollbar(); - } - }); - ui.area.resize.handle = $('.ui-resizable-handle'); - } - if (!ui.area.resize.syncToggle.length) { - ui.area.resize.syncToggle = $(''); - ui.area.resize.syncToggle.hover(function () { - previousFocusOnEditor = editorHasFocus(); - }, function () { - previousFocusOnEditor = null; - }); - ui.area.resize.syncToggle.click(function () { - syncscroll = !syncscroll; - checkSyncToggle(); - }); - ui.area.resize.handle.append(ui.area.resize.syncToggle); - ui.area.resize.syncToggle.hide(); - ui.area.resize.handle.hover(function () { - ui.area.resize.syncToggle.stop(true, true).delay(200).fadeIn(100); - }, function () { - ui.area.resize.syncToggle.stop(true, true).delay(300).fadeOut(300); - }); - } + window.preventSyncScrollToView = 2 + window.preventSyncScrollToEdit = true + editor.setOption('viewportMargin', viewportMargin) + if (editorHasFocus()) { + windowResizeInner(function () { + ui.area.codemirrorScroll.scroll() + }) + } else { + windowResizeInner(function () { + ui.area.view.scroll() + }) + } + checkEditorScrollbar() + } + }) + ui.area.resize.handle = $('.ui-resizable-handle') + } + if (!ui.area.resize.syncToggle.length) { + ui.area.resize.syncToggle = $('') + ui.area.resize.syncToggle.hover(function () { + previousFocusOnEditor = editorHasFocus() + }, function () { + previousFocusOnEditor = null + }) + ui.area.resize.syncToggle.click(function () { + window.syncscroll = !window.syncscroll + checkSyncToggle() + }) + ui.area.resize.handle.append(ui.area.resize.syncToggle) + ui.area.resize.syncToggle.hide() + ui.area.resize.handle.hover(function () { + ui.area.resize.syncToggle.stop(true, true).delay(200).fadeIn(100) + }, function () { + ui.area.resize.syncToggle.stop(true, true).delay(300).fadeOut(300) + }) + } } -function checkSyncToggle() { - if (syncscroll) { - if (previousFocusOnEditor) { - preventSyncScrollToView = false; - syncScrollToView(); - } else { - preventSyncScrollToEdit = false; - syncScrollToEdit(); - } - ui.area.resize.syncToggle.find('i').removeClass('fa-unlink').addClass('fa-link'); +function checkSyncToggle () { + if (window.syncscroll) { + if (previousFocusOnEditor) { + window.preventSyncScrollToView = false + syncScrollToView() } else { - ui.area.resize.syncToggle.find('i').removeClass('fa-link').addClass('fa-unlink'); + window.preventSyncScrollToEdit = false + syncScrollToEdit() } + ui.area.resize.syncToggle.find('i').removeClass('fa-unlink').addClass('fa-link') + } else { + ui.area.resize.syncToggle.find('i').removeClass('fa-link').addClass('fa-unlink') + } } var checkEditorScrollbar = _.debounce(function () { - editor.operation(checkEditorScrollbarInner); -}, 50); + editor.operation(checkEditorScrollbarInner) +}, 50) -function checkEditorScrollbarInner() { +function checkEditorScrollbarInner () { // workaround simple scroll bar knob // will get wrong position when editor height changed - var scrollInfo = editor.getScrollInfo(); - editor.scrollTo(null, scrollInfo.top - 1); - editor.scrollTo(null, scrollInfo.top); + var scrollInfo = editor.getScrollInfo() + editor.scrollTo(null, scrollInfo.top - 1) + editor.scrollTo(null, scrollInfo.top) } -function checkTocStyle() { - //toc right - var paddingRight = parseFloat(ui.area.markdown.css('padding-right')); - var right = ($(window).width() - (ui.area.markdown.offset().left + ui.area.markdown.outerWidth() - paddingRight)); - ui.toc.toc.css('right', right + 'px'); - //affix toc left - var newbool; - var rightMargin = (ui.area.markdown.parent().outerWidth() - ui.area.markdown.outerWidth()) / 2; - //for ipad or wider device - if (rightMargin >= 133) { - newbool = true; - var affixLeftMargin = (ui.toc.affix.outerWidth() - ui.toc.affix.width()) / 2; - var left = ui.area.markdown.offset().left + ui.area.markdown.outerWidth() - affixLeftMargin; - ui.toc.affix.css('left', left + 'px'); - ui.toc.affix.css('width', rightMargin + 'px'); - } else { - newbool = false; - } - //toc scrollspy - ui.toc.toc.removeClass('scrollspy-body, scrollspy-view'); - ui.toc.affix.removeClass('scrollspy-body, scrollspy-view'); - if (currentMode == modeType.both) { - ui.toc.toc.addClass('scrollspy-view'); - ui.toc.affix.addClass('scrollspy-view'); - } else if (currentMode != modeType.both && !newbool) { - ui.toc.toc.addClass('scrollspy-body'); - ui.toc.affix.addClass('scrollspy-body'); - } else { - ui.toc.toc.addClass('scrollspy-view'); - ui.toc.affix.addClass('scrollspy-body'); - } - if (newbool != enoughForAffixToc) { - enoughForAffixToc = newbool; - generateScrollspy(); - } +function checkTocStyle () { + // toc right + var paddingRight = parseFloat(ui.area.markdown.css('padding-right')) + var right = ($(window).width() - (ui.area.markdown.offset().left + ui.area.markdown.outerWidth() - paddingRight)) + ui.toc.toc.css('right', right + 'px') + // affix toc left + var newbool + var rightMargin = (ui.area.markdown.parent().outerWidth() - ui.area.markdown.outerWidth()) / 2 + // for ipad or wider device + if (rightMargin >= 133) { + newbool = true + var affixLeftMargin = (ui.toc.affix.outerWidth() - ui.toc.affix.width()) / 2 + var left = ui.area.markdown.offset().left + ui.area.markdown.outerWidth() - affixLeftMargin + ui.toc.affix.css('left', left + 'px') + ui.toc.affix.css('width', rightMargin + 'px') + } else { + newbool = false + } + // toc scrollspy + ui.toc.toc.removeClass('scrollspy-body, scrollspy-view') + ui.toc.affix.removeClass('scrollspy-body, scrollspy-view') + if (window.currentMode === modeType.both) { + ui.toc.toc.addClass('scrollspy-view') + ui.toc.affix.addClass('scrollspy-view') + } else if (window.currentMode !== modeType.both && !newbool) { + ui.toc.toc.addClass('scrollspy-body') + ui.toc.affix.addClass('scrollspy-body') + } else { + ui.toc.toc.addClass('scrollspy-view') + ui.toc.affix.addClass('scrollspy-body') + } + if (newbool !== enoughForAffixToc) { + enoughForAffixToc = newbool + generateScrollspy() + } } -function showStatus(type, num) { - currentStatus = type; - var shortStatus = ui.toolbar.shortStatus; - var status = ui.toolbar.status; - var label = $(''); - var fa = $(''); - var msg = ""; - var shortMsg = ""; +function showStatus (type, num) { + window.currentStatus = type + var shortStatus = ui.toolbar.shortStatus + var status = ui.toolbar.status + var label = $('') + var fa = $('') + var msg = '' + var shortMsg = '' - shortStatus.html(""); - status.html(""); + shortStatus.html('') + status.html('') - switch (currentStatus) { - case statusType.connected: - label.addClass(statusType.connected.label); - fa.addClass(statusType.connected.fa); - msg = statusType.connected.msg; - break; - case statusType.online: - label.addClass(statusType.online.label); - fa.addClass(statusType.online.fa); - shortMsg = num; - msg = num + " " + statusType.online.msg; - break; - case statusType.offline: - label.addClass(statusType.offline.label); - fa.addClass(statusType.offline.fa); - msg = statusType.offline.msg; - break; - } + switch (window.currentStatus) { + case statusType.connected: + label.addClass(statusType.connected.label) + fa.addClass(statusType.connected.fa) + msg = statusType.connected.msg + break + case statusType.online: + label.addClass(statusType.online.label) + fa.addClass(statusType.online.fa) + shortMsg = num + msg = num + ' ' + statusType.online.msg + break + case statusType.offline: + label.addClass(statusType.offline.label) + fa.addClass(statusType.offline.fa) + msg = statusType.offline.msg + break + } - label.append(fa); - var shortLabel = label.clone(); + label.append(fa) + var shortLabel = label.clone() - shortLabel.append(" " + shortMsg); - shortStatus.append(shortLabel); + shortLabel.append(' ' + shortMsg) + shortStatus.append(shortLabel) - label.append(" " + msg); - status.append(label); + label.append(' ' + msg) + status.append(label) } -function toggleMode() { - switch (currentMode) { - case modeType.edit: - changeMode(modeType.view); - break; - case modeType.view: - changeMode(modeType.edit); - break; - case modeType.both: - changeMode(modeType.view); - break; - } +function toggleMode () { + switch (window.currentMode) { + case modeType.edit: + changeMode(modeType.view) + break + case modeType.view: + changeMode(modeType.edit) + break + case modeType.both: + changeMode(modeType.view) + break + } } -var lastMode = null; +var lastMode = null -function changeMode(type) { +function changeMode (type) { // lock navbar to prevent it hide after changeMode - lockNavbar(); - saveInfo(); - if (type) { - lastMode = currentMode; - currentMode = type; - } - var responsiveClass = "col-lg-6 col-md-6 col-sm-6"; - var scrollClass = "ui-scrollable"; - ui.area.codemirror.removeClass(scrollClass); - ui.area.edit.removeClass(responsiveClass); - ui.area.view.removeClass(scrollClass); - ui.area.view.removeClass(responsiveClass); - switch (currentMode) { - case modeType.edit: - ui.area.edit.show(); - ui.area.view.hide(); - if (!editShown) { - editor.refresh(); - editShown = true; - } - break; - case modeType.view: - ui.area.edit.hide(); - ui.area.view.show(); - break; - case modeType.both: - ui.area.codemirror.addClass(scrollClass); - ui.area.edit.addClass(responsiveClass).show(); - ui.area.view.addClass(scrollClass); - ui.area.view.show(); - break; - } + lockNavbar() + saveInfo() + if (type) { + lastMode = window.currentMode + window.currentMode = type + } + var responsiveClass = 'col-lg-6 col-md-6 col-sm-6' + var scrollClass = 'ui-scrollable' + ui.area.codemirror.removeClass(scrollClass) + ui.area.edit.removeClass(responsiveClass) + ui.area.view.removeClass(scrollClass) + ui.area.view.removeClass(responsiveClass) + switch (window.currentMode) { + case modeType.edit: + ui.area.edit.show() + ui.area.view.hide() + if (!window.editShown) { + editor.refresh() + window.editShown = true + } + break + case modeType.view: + ui.area.edit.hide() + ui.area.view.show() + break + case modeType.both: + ui.area.codemirror.addClass(scrollClass) + ui.area.edit.addClass(responsiveClass).show() + ui.area.view.addClass(scrollClass) + ui.area.view.show() + break + } // save mode to url - if (history.replaceState && loaded) history.replaceState(null, "", serverurl + '/' + noteid + '?' + currentMode.name); - if (currentMode == modeType.view) { - editor.getInputField().blur(); - } - if (currentMode == modeType.edit || currentMode == modeType.both) { - ui.toolbar.uploadImage.fadeIn(); - //add and update status bar - if (!statusBar) { - addStatusBar(); - updateStatusBar(); - } - //work around foldGutter might not init properly - editor.setOption('foldGutter', false); - editor.setOption('foldGutter', true); - } else { - ui.toolbar.uploadImage.fadeOut(); - } - if (currentMode != modeType.edit) { - $(document.body).css('background-color', 'white'); - updateView(); - } else { - $(document.body).css('background-color', ui.area.codemirror.css('background-color')); - } - //check resizable editor style - if (currentMode == modeType.both) { - if (lastEditorWidth > 0) - ui.area.edit.css('width', lastEditorWidth + 'px'); - else - ui.area.edit.css('width', ''); - ui.area.resize.handle.show(); - } else { - ui.area.edit.css('width', ''); - ui.area.resize.handle.hide(); + if (history.replaceState && window.loaded) history.replaceState(null, '', serverurl + '/' + noteid + '?' + window.currentMode.name) + if (window.currentMode === modeType.view) { + editor.getInputField().blur() + } + if (window.currentMode === modeType.edit || window.currentMode === modeType.both) { + ui.toolbar.uploadImage.fadeIn() + // add and update status bar + if (!statusBar) { + addStatusBar() + updateStatusBar() } + // work around foldGutter might not init properly + editor.setOption('foldGutter', false) + editor.setOption('foldGutter', true) + } else { + ui.toolbar.uploadImage.fadeOut() + } + if (window.currentMode !== modeType.edit) { + $(document.body).css('background-color', 'white') + updateView() + } else { + $(document.body).css('background-color', ui.area.codemirror.css('background-color')) + } + // check resizable editor style + if (window.currentMode === modeType.both) { + if (lastEditorWidth > 0) { ui.area.edit.css('width', lastEditorWidth + 'px') } else { ui.area.edit.css('width', '') } + ui.area.resize.handle.show() + } else { + ui.area.edit.css('width', '') + ui.area.resize.handle.hide() + } - windowResizeInner(); + windowResizeInner() - restoreInfo(); + restoreInfo() - if (lastMode == modeType.view && currentMode == modeType.both) { - preventSyncScrollToView = 2; - syncScrollToEdit(null, true); - } + if (lastMode === modeType.view && window.currentMode === modeType.both) { + window.preventSyncScrollToView = 2 + syncScrollToEdit(null, true) + } - if (lastMode == modeType.edit && currentMode == modeType.both) { - preventSyncScrollToEdit = 2; - syncScrollToView(null, true); - } + if (lastMode === modeType.edit && window.currentMode === modeType.both) { + window.preventSyncScrollToEdit = 2 + syncScrollToView(null, true) + } - if (lastMode == modeType.both && currentMode != modeType.both) { - preventSyncScrollToView = false; - preventSyncScrollToEdit = false; - } + if (lastMode === modeType.both && window.currentMode !== modeType.both) { + window.preventSyncScrollToView = false + window.preventSyncScrollToEdit = false + } - if (lastMode != modeType.edit && currentMode == modeType.edit) { - editor.refresh(); - } + if (lastMode !== modeType.edit && window.currentMode === modeType.edit) { + editor.refresh() + } - $(document.body).scrollspy('refresh'); - ui.area.view.scrollspy('refresh'); + $(document.body).scrollspy('refresh') + ui.area.view.scrollspy('refresh') - ui.toolbar.both.removeClass("active"); - ui.toolbar.edit.removeClass("active"); - ui.toolbar.view.removeClass("active"); - var modeIcon = ui.toolbar.mode.find('i'); - modeIcon.removeClass('fa-pencil').removeClass('fa-eye'); - if (ui.area.edit.is(":visible") && ui.area.view.is(":visible")) { //both - ui.toolbar.both.addClass("active"); - modeIcon.addClass('fa-eye'); - } else if (ui.area.edit.is(":visible")) { //edit - ui.toolbar.edit.addClass("active"); - modeIcon.addClass('fa-eye'); - } else if (ui.area.view.is(":visible")) { //view - ui.toolbar.view.addClass("active"); - modeIcon.addClass('fa-pencil'); - } - unlockNavbar(); + ui.toolbar.both.removeClass('active') + ui.toolbar.edit.removeClass('active') + ui.toolbar.view.removeClass('active') + var modeIcon = ui.toolbar.mode.find('i') + modeIcon.removeClass('fa-pencil').removeClass('fa-eye') + if (ui.area.edit.is(':visible') && ui.area.view.is(':visible')) { // both + ui.toolbar.both.addClass('active') + modeIcon.addClass('fa-eye') + } else if (ui.area.edit.is(':visible')) { // edit + ui.toolbar.edit.addClass('active') + modeIcon.addClass('fa-eye') + } else if (ui.area.view.is(':visible')) { // view + ui.toolbar.view.addClass('active') + modeIcon.addClass('fa-pencil') + } + unlockNavbar() } -function lockNavbar() { - $('.navbar').addClass('locked'); +function lockNavbar () { + $('.navbar').addClass('locked') } var unlockNavbar = _.debounce(function () { - $('.navbar').removeClass('locked'); -}, 200); + $('.navbar').removeClass('locked') +}, 200) -function closestIndex(arr, closestTo) { - var closest = Math.max.apply(null, arr); //Get the highest number in arr in case it match nothing. - var index = 0; - for (var i = 0; i < arr.length; i++) { //Loop the array - if (arr[i] >= closestTo && arr[i] < closest) { - closest = arr[i]; //Check if it's higher than your number, but lower than your closest value - index = i; - } - } - return index; // return the value -} - -function showMessageModal(title, header, href, text, success) { - var modal = $('.message-modal'); - modal.find('.modal-title').html(title); - modal.find('.modal-body h5').html(header); - if (href) - modal.find('.modal-body a').attr('href', href).text(text); - else - modal.find('.modal-body a').removeAttr('href').text(text); - modal.find('.modal-footer button').removeClass('btn-default btn-success btn-danger') - if (success) - modal.find('.modal-footer button').addClass('btn-success'); - else - modal.find('.modal-footer button').addClass('btn-danger'); - modal.modal('show'); +function showMessageModal (title, header, href, text, success) { + var modal = $('.message-modal') + modal.find('.modal-title').html(title) + modal.find('.modal-body h5').html(header) + if (href) { modal.find('.modal-body a').attr('href', href).text(text) } else { modal.find('.modal-body a').removeAttr('href').text(text) } + modal.find('.modal-footer button').removeClass('btn-default btn-success btn-danger') + if (success) { modal.find('.modal-footer button').addClass('btn-success') } else { modal.find('.modal-footer button').addClass('btn-danger') } + modal.modal('show') } // check if dropbox app key is set and load scripts if (DROPBOX_APP_KEY) { - $('' ); - - var leadingWs = text.match( /^\n?(\s*)/ )[1].length, - leadingTabs = text.match( /^\n?(\t*)/ )[1].length; - - if( leadingTabs > 0 ) { - text = text.replace( new RegExp('\\n?\\t{' + leadingTabs + '}','g'), '\n' ); - } - else if( leadingWs > 1 ) { - text = text.replace( new RegExp('\\n? {' + leadingWs + '}', 'g'), '\n' ); - } - - return text; - - } - - /** - * Given a markdown slide section element, this will - * return all arguments that aren't related to markdown - * parsing. Used to forward any other user-defined arguments - * to the output markdown slide. - */ - function getForwardedAttributes( section ) { - - var attributes = section.attributes; - var result = []; - - for( var i = 0, len = attributes.length; i < len; i++ ) { - var name = attributes[i].name, - value = attributes[i].value; - - // disregard attributes that are used for markdown loading/parsing - if( /data\-(markdown|separator|vertical|notes)/gi.test( name ) ) continue; - - if( value ) { - result.push( name + '="' + value + '"' ); - } - else { - result.push( name ); - } - } - - return result.join( ' ' ); - - } - - /** - * Inspects the given options and fills out default - * values for what's not defined. - */ - function getSlidifyOptions( options ) { - - options = options || {}; - options.separator = options.separator || DEFAULT_SLIDE_SEPARATOR; - options.notesSeparator = options.notesSeparator || DEFAULT_NOTES_SEPARATOR; - options.attributes = options.attributes || ''; - - return options; - - } - - /** - * Helper function for constructing a markdown slide. - */ - function createMarkdownSlide( content, options ) { - - options = getSlidifyOptions( options ); - - var notesMatch = content.split( new RegExp( options.notesSeparator, 'mgi' ) ); - - if( notesMatch.length === 2 ) { - content = notesMatch[0] + ''; - } - - // prevent script end tags in the content from interfering - // with parsing - content = content.replace( /<\/script>/g, SCRIPT_END_PLACEHOLDER ); - - return ''; - - } - - /** - * Parses a data string into multiple slides based - * on the passed in separator arguments. - */ - function slidify( markdown, options ) { - - options = getSlidifyOptions( options ); - - var separatorRegex = new RegExp( options.separator + ( options.verticalSeparator ? '|' + options.verticalSeparator : '' ), 'mg' ), - horizontalSeparatorRegex = new RegExp( options.separator ); - - var matches, - lastIndex = 0, - isHorizontal, - wasHorizontal = true, - content, - sectionStack = []; - - // iterate until all blocks between separators are stacked up - while( matches = separatorRegex.exec( markdown ) ) { - notes = null; - - // determine direction (horizontal by default) - isHorizontal = horizontalSeparatorRegex.test( matches[0] ); - - if( !isHorizontal && wasHorizontal ) { - // create vertical stack - sectionStack.push( [] ); - } - - // pluck slide content from markdown input - content = markdown.substring( lastIndex, matches.index ); - - if( isHorizontal && wasHorizontal ) { - // add to horizontal stack - sectionStack.push( content ); - } - else { - // add to vertical stack - sectionStack[sectionStack.length-1].push( content ); - } - - lastIndex = separatorRegex.lastIndex; - wasHorizontal = isHorizontal; - } - - // add the remaining slide - ( wasHorizontal ? sectionStack : sectionStack[sectionStack.length-1] ).push( markdown.substring( lastIndex ) ); - - var markdownSections = ''; - - // flatten the hierarchical stack, and insert
    tags - for( var i = 0, len = sectionStack.length; i < len; i++ ) { - // vertical - if( sectionStack[i] instanceof Array ) { - markdownSections += '
    '; - - sectionStack[i].forEach( function( child ) { - markdownSections += '
    ' + createMarkdownSlide( child, options ) + '
    '; - } ); - - markdownSections += '
    '; - } - else { - markdownSections += '
    ' + createMarkdownSlide( sectionStack[i], options ) + '
    '; - } - } - - return markdownSections; - - } - - /** - * Parses any current data-markdown slides, splits - * multi-slide markdown into separate sections and - * handles loading of external markdown. - */ - function processSlides() { - - var sections = document.querySelectorAll( '[data-markdown]'), - section; - - for( var i = 0, len = sections.length; i < len; i++ ) { - - section = sections[i]; - - if( section.getAttribute( 'data-markdown' ).length ) { - - var xhr = new XMLHttpRequest(), - url = section.getAttribute( 'data-markdown' ); - - datacharset = section.getAttribute( 'data-charset' ); - - // see https://developer.mozilla.org/en-US/docs/Web/API/element.getAttribute#Notes - if( datacharset != null && datacharset != '' ) { - xhr.overrideMimeType( 'text/html; charset=' + datacharset ); - } - - xhr.onreadystatechange = function() { - if( xhr.readyState === 4 ) { - // file protocol yields status code 0 (useful for local debug, mobile applications etc.) - if ( ( xhr.status >= 200 && xhr.status < 300 ) || xhr.status === 0 ) { - - section.outerHTML = slidify( xhr.responseText, { - separator: section.getAttribute( 'data-separator' ), - verticalSeparator: section.getAttribute( 'data-separator-vertical' ), - notesSeparator: section.getAttribute( 'data-separator-notes' ), - attributes: getForwardedAttributes( section ) - }); - - } - else { - - section.outerHTML = '
    ' + - 'ERROR: The attempt to fetch ' + url + ' failed with HTTP status ' + xhr.status + '.' + - 'Check your browser\'s JavaScript console for more details.' + - '

    Remember that you need to serve the presentation HTML from a HTTP server.

    ' + - '
    '; - - } - } - }; - - xhr.open( 'GET', url, false ); - - try { - xhr.send(); - } - catch ( e ) { - alert( 'Failed to get the Markdown file ' + url + '. Make sure that the presentation and the file are served by a HTTP server and the file can be found there. ' + e ); - } - - } - else if( section.getAttribute( 'data-separator' ) || section.getAttribute( 'data-separator-vertical' ) || section.getAttribute( 'data-separator-notes' ) ) { - - section.outerHTML = slidify( getMarkdownFromSlide( section ), { - separator: section.getAttribute( 'data-separator' ), - verticalSeparator: section.getAttribute( 'data-separator-vertical' ), - notesSeparator: section.getAttribute( 'data-separator-notes' ), - attributes: getForwardedAttributes( section ) - }); - - } - else { - section.innerHTML = createMarkdownSlide( getMarkdownFromSlide( section ) ); - } - } - - } - - /** - * Check if a node value has the attributes pattern. - * If yes, extract it and add that value as one or several attributes - * the the terget element. - * - * You need Cache Killer on Chrome to see the effect on any FOM transformation - * directly on refresh (F5) - * http://stackoverflow.com/questions/5690269/disabling-chrome-cache-for-website-development/7000899#answer-11786277 - */ - function addAttributeInElement( node, elementTarget, separator ) { - - var mardownClassesInElementsRegex = new RegExp( separator, 'mg' ); - var mardownClassRegex = new RegExp( "([^\"= ]+?)=\"([^\"=]+?)\"", 'mg' ); - var nodeValue = node.nodeValue; - if( matches = mardownClassesInElementsRegex.exec( nodeValue ) ) { - - var classes = matches[1]; - nodeValue = nodeValue.substring( 0, matches.index ) + nodeValue.substring( mardownClassesInElementsRegex.lastIndex ); - node.nodeValue = nodeValue; - while( matchesClass = mardownClassRegex.exec( classes ) ) { - var name = matchesClass[1]; - var value = matchesClass[2]; - if (name.substr(0, 5) === 'data-' || whiteListAttr.indexOf(name) !== -1) - elementTarget.setAttribute( name, filterXSS.escapeAttrValue(value) ); - } - return true; - } - return false; - } - - /** - * Add attributes to the parent element of a text node, - * or the element of an attribute node. - */ - function addAttributes( section, element, previousElement, separatorElementAttributes, separatorSectionAttributes ) { - - if ( element != null && element.childNodes != undefined && element.childNodes.length > 0 ) { - previousParentElement = element; - for( var i = 0; i < element.childNodes.length; i++ ) { - childElement = element.childNodes[i]; - if ( i > 0 ) { - j = i - 1; - while ( j >= 0 ) { - aPreviousChildElement = element.childNodes[j]; - if ( typeof aPreviousChildElement.setAttribute == 'function' && aPreviousChildElement.tagName != "BR" ) { - previousParentElement = aPreviousChildElement; - break; - } - j = j - 1; - } - } - parentSection = section; - if( childElement.nodeName == "section" ) { - parentSection = childElement ; - previousParentElement = childElement ; - } - if ( typeof childElement.setAttribute == 'function' || childElement.nodeType == Node.COMMENT_NODE ) { - addAttributes( parentSection, childElement, previousParentElement, separatorElementAttributes, separatorSectionAttributes ); - } - } - } - - if ( element.nodeType == Node.COMMENT_NODE ) { - if ( addAttributeInElement( element, previousElement, separatorElementAttributes ) == false ) { - addAttributeInElement( element, section, separatorSectionAttributes ); - } - } - } - - /** - * Converts any current data-markdown slides in the - * DOM to HTML. - */ - function convertSlides() { - - var sections = document.querySelectorAll( '[data-markdown]'); - - for( var i = 0, len = sections.length; i < len; i++ ) { - - var section = sections[i]; - - // Only parse the same slide once - if( !section.getAttribute( 'data-markdown-parsed' ) ) { - - section.setAttribute( 'data-markdown-parsed', true ) - - var notes = section.querySelector( 'aside.notes' ); - var markdown = getMarkdownFromSlide( section ); - - var rendered = md.render(markdown); - rendered = preventXSS(rendered); - var result = postProcess(rendered); - section.innerHTML = result[0].outerHTML; - addAttributes( section, section, null, section.getAttribute( 'data-element-attributes' ) || - section.parentNode.getAttribute( 'data-element-attributes' ) || - DEFAULT_ELEMENT_ATTRIBUTES_SEPARATOR, - section.getAttribute( 'data-attributes' ) || - section.parentNode.getAttribute( 'data-attributes' ) || - DEFAULT_SLIDE_ATTRIBUTES_SEPARATOR); - - // If there were notes, we need to re-add them after - // having overwritten the section's HTML - if( notes ) { - section.appendChild( notes ); - } - - } - - } - - } - - // API - return { - - initialize: function() { - processSlides(); - convertSlides(); - }, - - // TODO: Do these belong in the API? - processSlides: processSlides, - convertSlides: convertSlides, - slidify: slidify - - }; - -})); +(function (root, factory) { + if (typeof exports === 'object') { + module.exports = factory() + } else { + // Browser globals (root is window) + root.RevealMarkdown = factory() + root.RevealMarkdown.initialize() + } +}(this, function () { + var DEFAULT_SLIDE_SEPARATOR = '^\r?\n---\r?\n$' + var DEFAULT_NOTES_SEPARATOR = 'note:' + var DEFAULT_ELEMENT_ATTRIBUTES_SEPARATOR = '\\.element\\s*?(.+?)$' + var DEFAULT_SLIDE_ATTRIBUTES_SEPARATOR = '\\.slide:\\s*?(\\S.+?)$' + + var SCRIPT_END_PLACEHOLDER = '__SCRIPT_END__' + + /** + * Retrieves the markdown contents of a slide section + * element. Normalizes leading tabs/whitespace. + */ + function getMarkdownFromSlide (section) { + var template = section.querySelector('script') + + // strip leading whitespace so it isn't evaluated as code + var text = (template || section).textContent + + // restore script end tags + text = text.replace(new RegExp(SCRIPT_END_PLACEHOLDER, 'g'), '') + + var leadingWs = text.match(/^\n?(\s*)/)[1].length + var leadingTabs = text.match(/^\n?(\t*)/)[1].length + + if (leadingTabs > 0) { + text = text.replace(new RegExp('\\n?\\t{' + leadingTabs + '}', 'g'), '\n') + } else if (leadingWs > 1) { + text = text.replace(new RegExp('\\n? {' + leadingWs + '}', 'g'), '\n') + } + + return text + } + + /** + * Given a markdown slide section element, this will + * return all arguments that aren't related to markdown + * parsing. Used to forward any other user-defined arguments + * to the output markdown slide. + */ + function getForwardedAttributes (section) { + var attributes = section.attributes + var result = [] + + for (var i = 0, len = attributes.length; i < len; i++) { + var name = attributes[i].name + var value = attributes[i].value + + // disregard attributes that are used for markdown loading/parsing + if (/data-(markdown|separator|vertical|notes)/gi.test(name)) continue + + if (value) { + result.push(name + '="' + value + '"') + } else { + result.push(name) + } + } + + return result.join(' ') + } + + /** + * Inspects the given options and fills out default + * values for what's not defined. + */ + function getSlidifyOptions (options) { + options = options || {} + options.separator = options.separator || DEFAULT_SLIDE_SEPARATOR + options.notesSeparator = options.notesSeparator || DEFAULT_NOTES_SEPARATOR + options.attributes = options.attributes || '' + + return options + } + + /** + * Helper function for constructing a markdown slide. + */ + function createMarkdownSlide (content, options) { + options = getSlidifyOptions(options) + + var notesMatch = content.split(new RegExp(options.notesSeparator, 'mgi')) + + if (notesMatch.length === 2) { + content = notesMatch[0] + '' + } + + // prevent script end tags in the content from interfering + // with parsing + content = content.replace(/<\/script>/g, SCRIPT_END_PLACEHOLDER) + + return '' + } + + /** + * Parses a data string into multiple slides based + * on the passed in separator arguments. + */ + function slidify (markdown, options) { + options = getSlidifyOptions(options) + + var separatorRegex = new RegExp(options.separator + (options.verticalSeparator ? '|' + options.verticalSeparator : ''), 'mg') + var horizontalSeparatorRegex = new RegExp(options.separator) + + var matches + var lastIndex = 0 + var isHorizontal + var wasHorizontal = true + var content + var sectionStack = [] + + // iterate until all blocks between separators are stacked up + while ((matches = separatorRegex.exec(markdown)) !== null) { + // determine direction (horizontal by default) + isHorizontal = horizontalSeparatorRegex.test(matches[0]) + + if (!isHorizontal && wasHorizontal) { + // create vertical stack + sectionStack.push([]) + } + + // pluck slide content from markdown input + content = markdown.substring(lastIndex, matches.index) + + if (isHorizontal && wasHorizontal) { + // add to horizontal stack + sectionStack.push(content) + } else { + // add to vertical stack + sectionStack[sectionStack.length - 1].push(content) + } + + lastIndex = separatorRegex.lastIndex + wasHorizontal = isHorizontal + } + + // add the remaining slide + (wasHorizontal ? sectionStack : sectionStack[sectionStack.length - 1]).push(markdown.substring(lastIndex)) + + var markdownSections = '' + + // flatten the hierarchical stack, and insert
    tags + for (var i = 0, len = sectionStack.length; i < len; i++) { + // vertical + if (sectionStack[i] instanceof Array) { + markdownSections += '
    ' + + sectionStack[i].forEach(function (child) { + markdownSections += '
    ' + createMarkdownSlide(child, options) + '
    ' + }) + + markdownSections += '
    ' + } else { + markdownSections += '
    ' + createMarkdownSlide(sectionStack[i], options) + '
    ' + } + } + + return markdownSections + } + + /** + * Parses any current data-markdown slides, splits + * multi-slide markdown into separate sections and + * handles loading of external markdown. + */ + function processSlides () { + var sections = document.querySelectorAll('[data-markdown]') + var section + + for (var i = 0, len = sections.length; i < len; i++) { + section = sections[i] + + if (section.getAttribute('data-markdown').length) { + var xhr = new XMLHttpRequest() + var url = section.getAttribute('data-markdown') + + var datacharset = section.getAttribute('data-charset') + + // see https://developer.mozilla.org/en-US/docs/Web/API/element.getAttribute#Notes + if (datacharset !== null && datacharset !== '') { + xhr.overrideMimeType('text/html; charset=' + datacharset) + } + + xhr.onreadystatechange = function () { + if (xhr.readyState === 4) { + // file protocol yields status code 0 (useful for local debug, mobile applications etc.) + if ((xhr.status >= 200 && xhr.status < 300) || xhr.status === 0) { + section.outerHTML = slidify(xhr.responseText, { + separator: section.getAttribute('data-separator'), + verticalSeparator: section.getAttribute('data-separator-vertical'), + notesSeparator: section.getAttribute('data-separator-notes'), + attributes: getForwardedAttributes(section) + }) + } else { + section.outerHTML = '
    ' + + 'ERROR: The attempt to fetch ' + url + ' failed with HTTP status ' + xhr.status + '.' + + 'Check your browser\'s JavaScript console for more details.' + + '

    Remember that you need to serve the presentation HTML from a HTTP server.

    ' + + '
    ' + } + } + } + + xhr.open('GET', url, false) + + try { + xhr.send() + } catch (e) { + alert('Failed to get the Markdown file ' + url + '. Make sure that the presentation and the file are served by a HTTP server and the file can be found there. ' + e) + } + } else if (section.getAttribute('data-separator') || section.getAttribute('data-separator-vertical') || section.getAttribute('data-separator-notes')) { + section.outerHTML = slidify(getMarkdownFromSlide(section), { + separator: section.getAttribute('data-separator'), + verticalSeparator: section.getAttribute('data-separator-vertical'), + notesSeparator: section.getAttribute('data-separator-notes'), + attributes: getForwardedAttributes(section) + }) + } else { + section.innerHTML = createMarkdownSlide(getMarkdownFromSlide(section)) + } + } + } + + /** + * Check if a node value has the attributes pattern. + * If yes, extract it and add that value as one or several attributes + * the the terget element. + * + * You need Cache Killer on Chrome to see the effect on any FOM transformation + * directly on refresh (F5) + * http://stackoverflow.com/questions/5690269/disabling-chrome-cache-for-website-development/7000899#answer-11786277 + */ + function addAttributeInElement (node, elementTarget, separator) { + var mardownClassesInElementsRegex = new RegExp(separator, 'mg') + var mardownClassRegex = new RegExp('([^"= ]+?)="([^"=]+?)"', 'mg') + var nodeValue = node.nodeValue + var matches + var matchesClass + if ((matches = mardownClassesInElementsRegex.exec(nodeValue))) { + var classes = matches[1] + nodeValue = nodeValue.substring(0, matches.index) + nodeValue.substring(mardownClassesInElementsRegex.lastIndex) + node.nodeValue = nodeValue + while ((matchesClass = mardownClassRegex.exec(classes))) { + var name = matchesClass[1] + var value = matchesClass[2] + if (name.substr(0, 5) === 'data-' || window.whiteListAttr.indexOf(name) !== -1) { elementTarget.setAttribute(name, window.filterXSS.escapeAttrValue(value)) } + } + return true + } + return false + } + + /** + * Add attributes to the parent element of a text node, + * or the element of an attribute node. + */ + function addAttributes (section, element, previousElement, separatorElementAttributes, separatorSectionAttributes) { + if (element != null && element.childNodes !== undefined && element.childNodes.length > 0) { + var previousParentElement = element + for (var i = 0; i < element.childNodes.length; i++) { + var childElement = element.childNodes[i] + if (i > 0) { + let j = i - 1 + while (j >= 0) { + var aPreviousChildElement = element.childNodes[j] + if (typeof aPreviousChildElement.setAttribute === 'function' && aPreviousChildElement.tagName !== 'BR') { + previousParentElement = aPreviousChildElement + break + } + j = j - 1 + } + } + var parentSection = section + if (childElement.nodeName === 'section') { + parentSection = childElement + previousParentElement = childElement + } + if (typeof childElement.setAttribute === 'function' || childElement.nodeType === Node.COMMENT_NODE) { + addAttributes(parentSection, childElement, previousParentElement, separatorElementAttributes, separatorSectionAttributes) + } + } + } + + if (element.nodeType === Node.COMMENT_NODE) { + if (addAttributeInElement(element, previousElement, separatorElementAttributes) === false) { + addAttributeInElement(element, section, separatorSectionAttributes) + } + } + } + + /** + * Converts any current data-markdown slides in the + * DOM to HTML. + */ + function convertSlides () { + var sections = document.querySelectorAll('[data-markdown]') + + for (var i = 0, len = sections.length; i < len; i++) { + var section = sections[i] + + // Only parse the same slide once + if (!section.getAttribute('data-markdown-parsed')) { + section.setAttribute('data-markdown-parsed', true) + + var notes = section.querySelector('aside.notes') + var markdown = getMarkdownFromSlide(section) + + var rendered = md.render(markdown) + rendered = preventXSS(rendered) + var result = window.postProcess(rendered) + section.innerHTML = result[0].outerHTML + addAttributes(section, section, null, section.getAttribute('data-element-attributes') || + section.parentNode.getAttribute('data-element-attributes') || + DEFAULT_ELEMENT_ATTRIBUTES_SEPARATOR, + section.getAttribute('data-attributes') || + section.parentNode.getAttribute('data-attributes') || + DEFAULT_SLIDE_ATTRIBUTES_SEPARATOR) + + // If there were notes, we need to re-add them after + // having overwritten the section's HTML + if (notes) { + section.appendChild(notes) + } + } + } + } + + // API + return { + initialize: function () { + processSlides() + convertSlides() + }, + // TODO: Do these belong in the API? + processSlides: processSlides, + convertSlides: convertSlides, + slidify: slidify + } +})) diff --git a/public/js/slide.js b/public/js/slide.js index 63cf64c..e743bb5 100644 --- a/public/js/slide.js +++ b/public/js/slide.js @@ -1,138 +1,139 @@ -require('../css/extra.css'); -require('../css/site.css'); +/* eslint-env browser, jquery */ +/* global serverurl, Reveal */ -import { md, updateLastChange, finishView } from './extra'; +require('../css/extra.css') +require('../css/site.css') -import { preventXSS } from './render'; +import { md, updateLastChange, finishView } from './extra' -const body = $(".slides").text(); +const body = $('.slides').text() -createtime = lastchangeui.time.attr('data-createtime'); -lastchangetime = lastchangeui.time.attr('data-updatetime'); -updateLastChange(); -const url = window.location.pathname; -$('.ui-edit').attr('href', `${url}/edit`); +window.createtime = window.lastchangeui.time.attr('data-createtime') +window.lastchangetime = window.lastchangeui.time.attr('data-updatetime') +updateLastChange() +const url = window.location.pathname +$('.ui-edit').attr('href', `${url}/edit`) $(document).ready(() => { - //tooltip - $('[data-toggle="tooltip"]').tooltip(); -}); + // tooltip + $('[data-toggle="tooltip"]').tooltip() +}) -function extend() { - const target = {}; +function extend () { + const target = {} - for (const source of arguments) { - for (const key in source) { - if (source.hasOwnProperty(key)) { - target[key] = source[key]; - } - } + for (const source of arguments) { + for (const key in source) { + if (source.hasOwnProperty(key)) { + target[key] = source[key] + } } + } - return target; + return target } // Optional libraries used to extend on reveal.js const deps = [{ - src: `${serverurl}/build/reveal.js/lib/js/classList.js`, - condition() { - return !document.body.classList; - } + src: `${serverurl}/build/reveal.js/lib/js/classList.js`, + condition () { + return !document.body.classList + } }, { - src: `${serverurl}/js/reveal-markdown.js`, - callback() { - const slideOptions = { - separator: '^(\r\n?|\n)---(\r\n?|\n)$', - verticalSeparator: '^(\r\n?|\n)----(\r\n?|\n)$' - }; - const slides = RevealMarkdown.slidify(body, slideOptions); - $(".slides").html(slides); - RevealMarkdown.initialize(); - $(".slides").show(); + src: `${serverurl}/js/reveal-markdown.js`, + callback () { + const slideOptions = { + separator: '^(\r\n?|\n)---(\r\n?|\n)$', + verticalSeparator: '^(\r\n?|\n)----(\r\n?|\n)$' } + const slides = window.RevealMarkdown.slidify(body, slideOptions) + $('.slides').html(slides) + window.RevealMarkdown.initialize() + $('.slides').show() + } }, { - src: `${serverurl}/build/reveal.js/plugin/notes/notes.js`, - async: true, - condition() { - return !!document.body.classList; - } -}]; + src: `${serverurl}/build/reveal.js/plugin/notes/notes.js`, + async: true, + condition () { + return !!document.body.classList + } +}] // default options to init reveal.js const defaultOptions = { - controls: true, - progress: true, - slideNumber: true, - history: true, - center: true, - transition: 'none', - dependencies: deps -}; + controls: true, + progress: true, + slideNumber: true, + history: true, + center: true, + transition: 'none', + dependencies: deps +} // options from yaml meta -const meta = JSON.parse($("#meta").text()); -var options = meta.slideOptions || {}; +const meta = JSON.parse($('#meta').text()) +var options = meta.slideOptions || {} -const view = $('.reveal'); +const view = $('.reveal') -//text language -if (meta.lang && typeof meta.lang == "string") { - view.attr('lang', meta.lang); +// text language +if (meta.lang && typeof meta.lang === 'string') { + view.attr('lang', meta.lang) } else { - view.removeAttr('lang'); + view.removeAttr('lang') } -//text direction -if (meta.dir && typeof meta.dir == "string" && meta.dir == "rtl") { - options.rtl = true; +// text direction +if (meta.dir && typeof meta.dir === 'string' && meta.dir === 'rtl') { + options.rtl = true } else { - options.rtl = false; + options.rtl = false } -//breaks +// breaks if (typeof meta.breaks === 'boolean' && !meta.breaks) { - md.options.breaks = false; + md.options.breaks = false } else { - md.options.breaks = true; + md.options.breaks = true } // options from URL query string -const queryOptions = Reveal.getQueryHash() || {}; +const queryOptions = Reveal.getQueryHash() || {} -var options = extend(defaultOptions, options, queryOptions); -Reveal.initialize(options); +options = extend(defaultOptions, options, queryOptions) +Reveal.initialize(options) window.viewAjaxCallback = () => { - Reveal.layout(); -}; + Reveal.layout() +} -function renderSlide(event) { - if (window.location.search.match( /print-pdf/gi )) { - const slides = $('.slides'); - var title = document.title; - finishView(slides); - document.title = title; - Reveal.layout(); - } else { - const markdown = $(event.currentSlide); - if (!markdown.attr('data-rendered')) { - var title = document.title; - finishView(markdown); - markdown.attr('data-rendered', 'true'); - document.title = title; - Reveal.layout(); - } +function renderSlide (event) { + if (window.location.search.match(/print-pdf/gi)) { + const slides = $('.slides') + let title = document.title + finishView(slides) + document.title = title + Reveal.layout() + } else { + const markdown = $(event.currentSlide) + if (!markdown.attr('data-rendered')) { + let title = document.title + finishView(markdown) + markdown.attr('data-rendered', 'true') + document.title = title + Reveal.layout() } + } } Reveal.addEventListener('ready', event => { - renderSlide(event); - const markdown = $(event.currentSlide); + renderSlide(event) + const markdown = $(event.currentSlide) // force browser redraw - setTimeout(() => { - markdown.hide().show(0); - }, 0); -}); -Reveal.addEventListener('slidechanged', renderSlide); + setTimeout(() => { + markdown.hide().show(0) + }, 0) +}) +Reveal.addEventListener('slidechanged', renderSlide) -const isMacLike = navigator.platform.match(/(Mac|iPhone|iPod|iPad)/i) ? true : false; +const isMacLike = !!navigator.platform.match(/(Mac|iPhone|iPod|iPad)/i) -if (!isMacLike) $('.container').addClass('hidescrollbar'); +if (!isMacLike) $('.container').addClass('hidescrollbar') diff --git a/public/js/syncscroll.js b/public/js/syncscroll.js index c969317..c227f83 100644 --- a/public/js/syncscroll.js +++ b/public/js/syncscroll.js @@ -1,365 +1,367 @@ +/* eslint-env browser, jquery */ +/* global _ */ // Inject line numbers for sync scroll. -import markdownitContainer from 'markdown-it-container'; +import markdownitContainer from 'markdown-it-container' -import { md } from './extra'; +import { md } from './extra' -function addPart(tokens, idx) { - if (tokens[idx].map && tokens[idx].level === 0) { - const startline = tokens[idx].map[0] + 1; - const endline = tokens[idx].map[1]; - tokens[idx].attrJoin('class', 'part'); - tokens[idx].attrJoin('data-startline', startline); - tokens[idx].attrJoin('data-endline', endline); - } +function addPart (tokens, idx) { + if (tokens[idx].map && tokens[idx].level === 0) { + const startline = tokens[idx].map[0] + 1 + const endline = tokens[idx].map[1] + tokens[idx].attrJoin('class', 'part') + tokens[idx].attrJoin('data-startline', startline) + tokens[idx].attrJoin('data-endline', endline) + } } md.renderer.rules.blockquote_open = function (tokens, idx, options, env, self) { - tokens[idx].attrJoin('class', 'raw'); - addPart(tokens, idx); - return self.renderToken(...arguments); -}; + tokens[idx].attrJoin('class', 'raw') + addPart(tokens, idx) + return self.renderToken(...arguments) +} md.renderer.rules.table_open = function (tokens, idx, options, env, self) { - addPart(tokens, idx); - return self.renderToken(...arguments); -}; + addPart(tokens, idx) + return self.renderToken(...arguments) +} md.renderer.rules.bullet_list_open = function (tokens, idx, options, env, self) { - addPart(tokens, idx); - return self.renderToken(...arguments); -}; + addPart(tokens, idx) + return self.renderToken(...arguments) +} md.renderer.rules.list_item_open = function (tokens, idx, options, env, self) { - tokens[idx].attrJoin('class', 'raw'); - if (tokens[idx].map) { - const startline = tokens[idx].map[0] + 1; - const endline = tokens[idx].map[1]; - tokens[idx].attrJoin('data-startline', startline); - tokens[idx].attrJoin('data-endline', endline); - } - return self.renderToken(...arguments); -}; + tokens[idx].attrJoin('class', 'raw') + if (tokens[idx].map) { + const startline = tokens[idx].map[0] + 1 + const endline = tokens[idx].map[1] + tokens[idx].attrJoin('data-startline', startline) + tokens[idx].attrJoin('data-endline', endline) + } + return self.renderToken(...arguments) +} md.renderer.rules.ordered_list_open = function (tokens, idx, options, env, self) { - addPart(tokens, idx); - return self.renderToken(...arguments); -}; + addPart(tokens, idx) + return self.renderToken(...arguments) +} md.renderer.rules.link_open = function (tokens, idx, options, env, self) { - addPart(tokens, idx); - return self.renderToken(...arguments); -}; + addPart(tokens, idx) + return self.renderToken(...arguments) +} md.renderer.rules.paragraph_open = function (tokens, idx, options, env, self) { - addPart(tokens, idx); - return self.renderToken(...arguments); -}; + addPart(tokens, idx) + return self.renderToken(...arguments) +} md.renderer.rules.heading_open = function (tokens, idx, options, env, self) { - tokens[idx].attrJoin('class', 'raw'); - addPart(tokens, idx); - return self.renderToken(...arguments); -}; + tokens[idx].attrJoin('class', 'raw') + addPart(tokens, idx) + 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; + 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 (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 (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`; - } + if (tokens[idx].map && tokens[idx].level === 0) { + const startline = tokens[idx].map[0] + 1 + const endline = tokens[idx].map[1] + return `
    ${highlighted}
    \n` + } - return `
    ${highlighted}
    \n`; -}; + return `
    ${highlighted}
    \n` +} md.renderer.rules.code_block = (tokens, idx, options, env, self) => { - if (tokens[idx].map && tokens[idx].level === 0) { - const startline = tokens[idx].map[0] + 1; - const endline = tokens[idx].map[1]; - return `
    ${md.utils.escapeHtml(tokens[idx].content)}
    \n`; - } - return `
    ${md.utils.escapeHtml(tokens[idx].content)}
    \n`; -}; -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()}`); - addPart(tokens, idx); - return self.renderToken(...arguments); + if (tokens[idx].map && tokens[idx].level === 0) { + const startline = tokens[idx].map[0] + 1 + const endline = tokens[idx].map[1] + return `
    ${md.utils.escapeHtml(tokens[idx].content)}
    \n` + } + return `
    ${md.utils.escapeHtml(tokens[idx].content)}
    \n` +} +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()}`) + addPart(tokens, idx) + 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 }) // FIXME: expose syncscroll to window -window.syncscroll = true; +window.syncscroll = true -window.preventSyncScrollToEdit = false; -window.preventSyncScrollToView = false; +window.preventSyncScrollToEdit = false +window.preventSyncScrollToView = false -const editScrollThrottle = 5; -const viewScrollThrottle = 5; -const buildMapThrottle = 100; +const editScrollThrottle = 5 +const viewScrollThrottle = 5 +const buildMapThrottle = 100 -let viewScrolling = false; -let editScrolling = false; +let viewScrolling = false +let editScrolling = false -let editArea = null; -let viewArea = null; -let markdownArea = null; +let editArea = null +let viewArea = null +let markdownArea = null -export function setupSyncAreas(edit, view, markdown) { - editArea = edit; - viewArea = view; - markdownArea = markdown; - editArea.on('scroll', _.throttle(syncScrollToView, editScrollThrottle)); - viewArea.on('scroll', _.throttle(syncScrollToEdit, viewScrollThrottle)); +export function setupSyncAreas (edit, view, markdown) { + editArea = edit + viewArea = view + markdownArea = markdown + editArea.on('scroll', _.throttle(syncScrollToView, editScrollThrottle)) + viewArea.on('scroll', _.throttle(syncScrollToEdit, viewScrollThrottle)) } -let scrollMap, lineHeightMap, viewTop, viewBottom; +let scrollMap, lineHeightMap, viewTop, viewBottom -export function clearMap() { - scrollMap = null; - lineHeightMap = null; - viewTop = null; - viewBottom = null; +export function clearMap () { + scrollMap = null + lineHeightMap = null + viewTop = null + viewBottom = null } -window.viewAjaxCallback = clearMap; +window.viewAjaxCallback = clearMap -const buildMap = _.throttle(buildMapInner, buildMapThrottle); +const buildMap = _.throttle(buildMapInner, buildMapThrottle) // Build offsets for each line (lines can be wrapped) // That's a bit dirty to process each line everytime, but ok for demo. // Optimizations are required only for big texts. -function buildMapInner(callback) { - if (!viewArea || !markdownArea) return; - let i, offset, nonEmptyList, pos, a, b, _lineHeightMap, linesCount, acc, _scrollMap; +function buildMapInner (callback) { + if (!viewArea || !markdownArea) return + let i, offset, nonEmptyList, pos, a, b, _lineHeightMap, linesCount, acc, _scrollMap - offset = viewArea.scrollTop() - viewArea.offset().top; - _scrollMap = []; - nonEmptyList = []; - _lineHeightMap = []; - viewTop = 0; - viewBottom = viewArea[0].scrollHeight - viewArea.height(); + offset = viewArea.scrollTop() - viewArea.offset().top + _scrollMap = [] + nonEmptyList = [] + _lineHeightMap = [] + viewTop = 0 + viewBottom = viewArea[0].scrollHeight - viewArea.height() - acc = 0; - const lines = editor.getValue().split('\n'); - const lineHeight = editor.defaultTextHeight(); - for (i = 0; i < lines.length; i++) { - const str = lines[i]; + acc = 0 + const lines = window.editor.getValue().split('\n') + const lineHeight = window.editor.defaultTextHeight() + for (i = 0; i < lines.length; i++) { + const str = lines[i] - _lineHeightMap.push(acc); + _lineHeightMap.push(acc) - if (str.length === 0) { - acc++; - continue; - } - - const h = editor.heightAtLine(i + 1) - editor.heightAtLine(i); - acc += Math.round(h / lineHeight); - } - _lineHeightMap.push(acc); - linesCount = acc; - - for (i = 0; i < linesCount; i++) { - _scrollMap.push(-1); + if (str.length === 0) { + acc++ + continue } - nonEmptyList.push(0); + const h = window.editor.heightAtLine(i + 1) - window.editor.heightAtLine(i) + acc += Math.round(h / lineHeight) + } + _lineHeightMap.push(acc) + linesCount = acc + + for (i = 0; i < linesCount; i++) { + _scrollMap.push(-1) + } + + nonEmptyList.push(0) // make the first line go top - _scrollMap[0] = viewTop; + _scrollMap[0] = viewTop - const parts = markdownArea.find('.part').toArray(); - for (i = 0; i < parts.length; i++) { - const $el = $(parts[i]); - let t = $el.attr('data-startline') - 1; - if (t === '') { - return; - } - t = _lineHeightMap[t]; - if (t !== 0 && t !== nonEmptyList[nonEmptyList.length - 1]) { - nonEmptyList.push(t); - } - _scrollMap[t] = Math.round($el.offset().top + offset - 10); + const parts = markdownArea.find('.part').toArray() + for (i = 0; i < parts.length; i++) { + const $el = $(parts[i]) + let t = $el.attr('data-startline') - 1 + if (t === '') { + return + } + t = _lineHeightMap[t] + if (t !== 0 && t !== nonEmptyList[nonEmptyList.length - 1]) { + nonEmptyList.push(t) + } + _scrollMap[t] = Math.round($el.offset().top + offset - 10) + } + + nonEmptyList.push(linesCount) + _scrollMap[linesCount] = viewArea[0].scrollHeight + + pos = 0 + for (i = 1; i < linesCount; i++) { + if (_scrollMap[i] !== -1) { + pos++ + continue } - nonEmptyList.push(linesCount); - _scrollMap[linesCount] = viewArea[0].scrollHeight; + a = nonEmptyList[pos] + b = nonEmptyList[pos + 1] + _scrollMap[i] = Math.round((_scrollMap[b] * (i - a) + _scrollMap[a] * (b - i)) / (b - a)) + } - pos = 0; - for (i = 1; i < linesCount; i++) { - if (_scrollMap[i] !== -1) { - pos++; - continue; - } + _scrollMap[0] = 0 - a = nonEmptyList[pos]; - b = nonEmptyList[pos + 1]; - _scrollMap[i] = Math.round((_scrollMap[b] * (i - a) + _scrollMap[a] * (b - i)) / (b - a)); - } + scrollMap = _scrollMap + lineHeightMap = _lineHeightMap - _scrollMap[0] = 0; - - scrollMap = _scrollMap; - lineHeightMap = _lineHeightMap; - - if (loaded && callback) callback(); + if (window.loaded && callback) callback() } // sync view scroll progress to edit -let viewScrollingTimer = null; +let viewScrollingTimer = null -export function syncScrollToEdit(event, preventAnimate) { - if (currentMode != modeType.both || !syncscroll || !editArea) return; - if (preventSyncScrollToEdit) { - if (typeof preventSyncScrollToEdit === 'number') { - preventSyncScrollToEdit--; - } else { - preventSyncScrollToEdit = false; - } - return; - } - if (!scrollMap || !lineHeightMap) { - buildMap(() => { - syncScrollToEdit(event, preventAnimate); - }); - return; - } - if (editScrolling) return; - - const scrollTop = viewArea[0].scrollTop; - let lineIndex = 0; - for (var i = 0, l = scrollMap.length; i < l; i++) { - if (scrollMap[i] > scrollTop) { - break; - } else { - lineIndex = i; - } - } - let lineNo = 0; - let lineDiff = 0; - for (var i = 0, l = lineHeightMap.length; i < l; i++) { - if (lineHeightMap[i] > lineIndex) { - break; - } else { - lineNo = lineHeightMap[i]; - lineDiff = lineHeightMap[i + 1] - lineNo; - } - } - - let posTo = 0; - let topDiffPercent = 0; - let posToNextDiff = 0; - const scrollInfo = editor.getScrollInfo(); - const textHeight = editor.defaultTextHeight(); - const preLastLineHeight = scrollInfo.height - scrollInfo.clientHeight - textHeight; - const preLastLineNo = Math.round(preLastLineHeight / textHeight); - const preLastLinePos = scrollMap[preLastLineNo]; - - if (scrollInfo.height > scrollInfo.clientHeight && scrollTop >= preLastLinePos) { - posTo = preLastLineHeight; - topDiffPercent = (scrollTop - preLastLinePos) / (viewBottom - preLastLinePos); - posToNextDiff = textHeight * topDiffPercent; - posTo += Math.ceil(posToNextDiff); +export function syncScrollToEdit (event, preventAnimate) { + if (window.currentMode !== window.modeType.both || !window.syncscroll || !editArea) return + if (window.preventSyncScrollToEdit) { + if (typeof window.preventSyncScrollToEdit === 'number') { + window.preventSyncScrollToEdit-- } else { - posTo = lineNo * textHeight; - topDiffPercent = (scrollTop - scrollMap[lineNo]) / (scrollMap[lineNo + lineDiff] - scrollMap[lineNo]); - posToNextDiff = textHeight * lineDiff * topDiffPercent; - posTo += Math.ceil(posToNextDiff); + window.preventSyncScrollToEdit = false } + return + } + if (!scrollMap || !lineHeightMap) { + buildMap(() => { + syncScrollToEdit(event, preventAnimate) + }) + return + } + if (editScrolling) return - if (preventAnimate) { - editArea.scrollTop(posTo); + const scrollTop = viewArea[0].scrollTop + let lineIndex = 0 + for (let i = 0, l = scrollMap.length; i < l; i++) { + if (scrollMap[i] > scrollTop) { + break } else { - const posDiff = Math.abs(scrollInfo.top - posTo); - var duration = posDiff / 50; - duration = duration >= 100 ? duration : 100; - editArea.stop(true, true).animate({ - scrollTop: posTo - }, duration, "linear"); + lineIndex = i } + } + let lineNo = 0 + let lineDiff = 0 + for (let i = 0, l = lineHeightMap.length; i < l; i++) { + if (lineHeightMap[i] > lineIndex) { + break + } else { + lineNo = lineHeightMap[i] + lineDiff = lineHeightMap[i + 1] - lineNo + } + } - viewScrolling = true; - clearTimeout(viewScrollingTimer); - viewScrollingTimer = setTimeout(viewScrollingTimeoutInner, duration * 1.5); + let posTo = 0 + let topDiffPercent = 0 + let posToNextDiff = 0 + const scrollInfo = window.editor.getScrollInfo() + const textHeight = window.editor.defaultTextHeight() + const preLastLineHeight = scrollInfo.height - scrollInfo.clientHeight - textHeight + const preLastLineNo = Math.round(preLastLineHeight / textHeight) + const preLastLinePos = scrollMap[preLastLineNo] + + if (scrollInfo.height > scrollInfo.clientHeight && scrollTop >= preLastLinePos) { + posTo = preLastLineHeight + topDiffPercent = (scrollTop - preLastLinePos) / (viewBottom - preLastLinePos) + posToNextDiff = textHeight * topDiffPercent + posTo += Math.ceil(posToNextDiff) + } else { + posTo = lineNo * textHeight + topDiffPercent = (scrollTop - scrollMap[lineNo]) / (scrollMap[lineNo + lineDiff] - scrollMap[lineNo]) + posToNextDiff = textHeight * lineDiff * topDiffPercent + posTo += Math.ceil(posToNextDiff) + } + + if (preventAnimate) { + editArea.scrollTop(posTo) + } else { + const posDiff = Math.abs(scrollInfo.top - posTo) + var duration = posDiff / 50 + duration = duration >= 100 ? duration : 100 + editArea.stop(true, true).animate({ + scrollTop: posTo + }, duration, 'linear') + } + + viewScrolling = true + clearTimeout(viewScrollingTimer) + viewScrollingTimer = setTimeout(viewScrollingTimeoutInner, duration * 1.5) } -function viewScrollingTimeoutInner() { - viewScrolling = false; +function viewScrollingTimeoutInner () { + viewScrolling = false } // sync edit scroll progress to view -let editScrollingTimer = null; +let editScrollingTimer = null -export function syncScrollToView(event, preventAnimate) { - if (currentMode != modeType.both || !syncscroll || !viewArea) return; - if (preventSyncScrollToView) { - if (typeof preventSyncScrollToView === 'number') { - preventSyncScrollToView--; - } else { - preventSyncScrollToView = false; - } - return; +export function syncScrollToView (event, preventAnimate) { + if (window.currentMode !== window.modeType.both || !window.syncscroll || !viewArea) return + if (window.preventSyncScrollToView) { + if (typeof preventSyncScrollToView === 'number') { + window.preventSyncScrollToView-- + } else { + window.preventSyncScrollToView = false } - if (!scrollMap || !lineHeightMap) { - buildMap(() => { - syncScrollToView(event, preventAnimate); - }); - return; - } - if (viewScrolling) return; + return + } + if (!scrollMap || !lineHeightMap) { + buildMap(() => { + syncScrollToView(event, preventAnimate) + }) + return + } + if (viewScrolling) return - let lineNo, posTo; - let topDiffPercent, posToNextDiff; - const scrollInfo = editor.getScrollInfo(); - const textHeight = editor.defaultTextHeight(); - lineNo = Math.floor(scrollInfo.top / textHeight); + let lineNo, posTo + let topDiffPercent, posToNextDiff + const scrollInfo = window.editor.getScrollInfo() + const textHeight = window.editor.defaultTextHeight() + lineNo = Math.floor(scrollInfo.top / textHeight) // if reach the last line, will start lerp to the bottom - const diffToBottom = (scrollInfo.top + scrollInfo.clientHeight) - (scrollInfo.height - textHeight); - if (scrollInfo.height > scrollInfo.clientHeight && diffToBottom > 0) { - topDiffPercent = diffToBottom / textHeight; - posTo = scrollMap[lineNo + 1]; - posToNextDiff = (viewBottom - posTo) * topDiffPercent; - posTo += Math.floor(posToNextDiff); - } else { - topDiffPercent = (scrollInfo.top % textHeight) / textHeight; - posTo = scrollMap[lineNo]; - posToNextDiff = (scrollMap[lineNo + 1] - posTo) * topDiffPercent; - posTo += Math.floor(posToNextDiff); - } + const diffToBottom = (scrollInfo.top + scrollInfo.clientHeight) - (scrollInfo.height - textHeight) + if (scrollInfo.height > scrollInfo.clientHeight && diffToBottom > 0) { + topDiffPercent = diffToBottom / textHeight + posTo = scrollMap[lineNo + 1] + posToNextDiff = (viewBottom - posTo) * topDiffPercent + posTo += Math.floor(posToNextDiff) + } else { + topDiffPercent = (scrollInfo.top % textHeight) / textHeight + posTo = scrollMap[lineNo] + posToNextDiff = (scrollMap[lineNo + 1] - posTo) * topDiffPercent + posTo += Math.floor(posToNextDiff) + } - if (preventAnimate) { - viewArea.scrollTop(posTo); - } else { - const posDiff = Math.abs(viewArea.scrollTop() - posTo); - var duration = posDiff / 50; - duration = duration >= 100 ? duration : 100; - viewArea.stop(true, true).animate({ - scrollTop: posTo - }, duration, "linear"); - } + if (preventAnimate) { + viewArea.scrollTop(posTo) + } else { + const posDiff = Math.abs(viewArea.scrollTop() - posTo) + var duration = posDiff / 50 + duration = duration >= 100 ? duration : 100 + viewArea.stop(true, true).animate({ + scrollTop: posTo + }, duration, 'linear') + } - editScrolling = true; - clearTimeout(editScrollingTimer); - editScrollingTimer = setTimeout(editScrollingTimeoutInner, duration * 1.5); + editScrolling = true + clearTimeout(editScrollingTimer) + editScrollingTimer = setTimeout(editScrollingTimeoutInner, duration * 1.5) } -function editScrollingTimeoutInner() { - editScrolling = false; +function editScrollingTimeoutInner () { + editScrolling = false } diff --git a/public/vendor/md-toc.js b/public/vendor/md-toc.js index 200275a..f93f792 100755 --- a/public/vendor/md-toc.js +++ b/public/vendor/md-toc.js @@ -1,129 +1,123 @@ +/* eslint-env browser, jquery */ /** * md-toc.js v1.0.2 * https://github.com/yijian166/md-toc.js */ (function (window) { - function Toc(id, options) { - this.el = document.getElementById(id); - if (!this.el) return; - this.options = options || {}; - this.tocLevel = parseInt(options.level) || 0; - this.tocClass = options['class'] || 'toc'; - this.ulClass = options['ulClass']; - this.tocTop = parseInt(options.top) || 0; - this.elChilds = this.el.children; - this.process = options['process']; - if (!this.elChilds.length) return; - this._init(); + function Toc (id, options) { + this.el = document.getElementById(id) + if (!this.el) return + this.options = options || {} + this.tocLevel = parseInt(options.level) || 0 + this.tocClass = options['class'] || 'toc' + this.ulClass = options['ulClass'] + this.tocTop = parseInt(options.top) || 0 + this.elChilds = this.el.children + this.process = options['process'] + if (!this.elChilds.length) return + this._init() + } + + Toc.prototype._init = function () { + this._collectTitleElements() + this._createTocContent() + this._showToc() + } + + Toc.prototype._collectTitleElements = function () { + this._elTitlesNames = [] + this.elTitleElements = [] + for (var i = 1; i < 7; i++) { + if (this.el.getElementsByTagName('h' + i).length) { + this._elTitlesNames.push('h' + i) + } } - Toc.prototype._init = function () { - this._collectTitleElements(); - this._createTocContent(); - this._showToc(); - }; + this._elTitlesNames.length = this._elTitlesNames.length > this.tocLevel ? this.tocLevel : this._elTitlesNames.length - Toc.prototype._collectTitleElements = function () { - this._elTitlesNames = [], - this.elTitleElements = []; - for (var i = 1; i < 7; i++) { - if (this.el.getElementsByTagName('h' + i).length) { - this._elTitlesNames.push('h' + i); + for (var j = 0; j < this.elChilds.length; j++) { + this._elChildName = this.elChilds[j].tagName.toLowerCase() + if (this._elTitlesNames.toString().match(this._elChildName)) { + this.elTitleElements.push(this.elChilds[j]) + } + } + } + + Toc.prototype._createTocContent = function () { + this._elTitleElementsLen = this.elTitleElements.length + if (!this._elTitleElementsLen) return + this.tocContent = '' + this._tempLists = [] + + for (var i = 0; i < this._elTitleElementsLen; i++) { + var j = i + 1 + this._elTitleElement = this.elTitleElements[i] + this._elTitleElementName = this._elTitleElement.tagName + this._elTitleElementText = (typeof this.process === 'function' ? this.process(this._elTitleElement) : this._elTitleElement.innerHTML).replace(/<(?:.|\n)*?>/gm, '') + var id = this._elTitleElement.getAttribute('id') + if (!id) { + this._elTitleElement.setAttribute('id', 'tip' + i) + id = '#tip' + i + } else { + id = '#' + id + } + + this.tocContent += '
  • ' + this._elTitleElementText + '' + + if (j !== this._elTitleElementsLen) { + this._elNextTitleElementName = this.elTitleElements[j].tagName + if (this._elTitleElementName !== this._elNextTitleElementName) { + var checkColse = false + var y = 1 + for (var t = this._tempLists.length - 1; t >= 0; t--) { + if (this._tempLists[t].tagName === this._elNextTitleElementName) { + checkColse = true + break } - } - - this._elTitlesNames.length = this._elTitlesNames.length > this.tocLevel ? this.tocLevel : this._elTitlesNames.length; - - for (var j = 0; j < this.elChilds.length; j++) { - this._elChildName = this.elChilds[j].tagName.toLowerCase(); - if (this._elTitlesNames.toString().match(this._elChildName)) { - this.elTitleElements.push(this.elChilds[j]); - } - } - }; - - Toc.prototype._createTocContent = function () { - this._elTitleElementsLen = this.elTitleElements.length; - if (!this._elTitleElementsLen) return; - this.tocContent = ''; - this._tempLists = []; - - var url = location.origin + location.pathname; - for (var i = 0; i < this._elTitleElementsLen; i++) { - var j = i + 1; - this._elTitleElement = this.elTitleElements[i]; - this._elTitleElementName = this._elTitleElement.tagName; - this._elTitleElementText = (typeof this.process === 'function' ? this.process(this._elTitleElement) : this._elTitleElement.innerHTML).replace(/<(?:.|\n)*?>/gm, ''); - var id = this._elTitleElement.getAttribute('id'); - if (!id) { - this._elTitleElement.setAttribute('id', 'tip' + i); - id = '#tip' + i; - } else { - id = '#' + id; - } - - this.tocContent += '
  • ' + this._elTitleElementText + ''; - - if (j != this._elTitleElementsLen) { - this._elNextTitleElementName = this.elTitleElements[j].tagName; - if (this._elTitleElementName != this._elNextTitleElementName) { - var checkColse = false, - y = 1; - for (var t = this._tempLists.length - 1; t >= 0; t--) { - if (this._tempLists[t].tagName == this._elNextTitleElementName) { - checkColse = true; - break; - } - y++; - } - if (checkColse) { - this.tocContent += new Array(y + 1).join('
  • '); - this._tempLists.length = this._tempLists.length - y; - } else { - this._tempLists.push(this._elTitleElement); - if (this.ulClass) - this.tocContent += '
      '; - else - this.tocContent += '
        '; - } - } else { - this.tocContent += ''; - } - } else { - if (this._tempLists.length) { - this.tocContent += new Array(this._tempLists.length + 1).join('
      '); - } else { - this.tocContent += ''; - } - } - } - if (this.ulClass) - this.tocContent = '
        ' + this.tocContent + '
      '; - else - this.tocContent = '
        ' + this.tocContent + '
      '; - }; - - Toc.prototype._showToc = function () { - this.toc = document.createElement('div'); - this.toc.innerHTML = this.tocContent; - this.toc.setAttribute('class', this.tocClass); - if (!this.options.targetId) { - this.el.appendChild(this.toc); + y++ + } + if (checkColse) { + this.tocContent += new Array(y + 1).join('
    ') + this._tempLists.length = this._tempLists.length - y + } else { + this._tempLists.push(this._elTitleElement) + if (this.ulClass) { this.tocContent += '
      ' } else { this.tocContent += '
        ' } + } } else { - document.getElementById(this.options.targetId).appendChild(this.toc); + this.tocContent += '' } - var self = this; - if (this.tocTop > -1) { - window.onscroll = function () { - var t = document.documentElement.scrollTop || document.body.scrollTop; - if (t < self.tocTop) { - self.toc.setAttribute('style', 'position:absolute;top:' + self.tocTop + 'px;'); - } else { - self.toc.setAttribute('style', 'position:fixed;top:10px;'); - } - } + } else { + if (this._tempLists.length) { + this.tocContent += new Array(this._tempLists.length + 1).join('
      ') + } else { + this.tocContent += '' } - }; - window.Toc = Toc; -})(window); \ No newline at end of file + } + } + if (this.ulClass) { this.tocContent = '
        ' + this.tocContent + '
      ' } else { this.tocContent = '
        ' + this.tocContent + '
      ' } + } + + Toc.prototype._showToc = function () { + this.toc = document.createElement('div') + this.toc.innerHTML = this.tocContent + this.toc.setAttribute('class', this.tocClass) + if (!this.options.targetId) { + this.el.appendChild(this.toc) + } else { + document.getElementById(this.options.targetId).appendChild(this.toc) + } + var self = this + if (this.tocTop > -1) { + window.onscroll = function () { + var t = document.documentElement.scrollTop || document.body.scrollTop + if (t < self.tocTop) { + self.toc.setAttribute('style', 'position:absolute;top:' + self.tocTop + 'px;') + } else { + self.toc.setAttribute('style', 'position:fixed;top:10px;') + } + } + } + } + window.Toc = Toc +})(window) diff --git a/webpack.config.js b/webpack.config.js index 236490b..f9f0a1c 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -1,33 +1,33 @@ -var baseConfig = require('./webpackBaseConfig'); -var ExtractTextPlugin = require("extract-text-webpack-plugin"); -var path = require('path'); +var baseConfig = require('./webpackBaseConfig') +var ExtractTextPlugin = require('extract-text-webpack-plugin') +var path = require('path') module.exports = [Object.assign({}, baseConfig, { - plugins: baseConfig.plugins.concat([ - new ExtractTextPlugin("[name].css") - ]) + plugins: baseConfig.plugins.concat([ + new ExtractTextPlugin('[name].css') + ]) }), { - entry: { - htmlExport: path.join(__dirname, 'public/js/htmlExport.js') - }, - module: { - loaders: [{ - test: /\.css$/, - loader: ExtractTextPlugin.extract('style-loader', 'css-loader') - }, { - test: /\.scss$/, - loader: ExtractTextPlugin.extract('style-loader', 'sass-loader') - }, { - test: /\.less$/, - loader: ExtractTextPlugin.extract('style-loader', 'less-loader') - }] - }, - output: { - path: path.join(__dirname, 'public/build'), - publicPath: '/build/', - filename: '[name].js' - }, - plugins: [ - new ExtractTextPlugin("html.min.css") - ] -}]; + entry: { + htmlExport: path.join(__dirname, 'public/js/htmlExport.js') + }, + module: { + loaders: [{ + test: /\.css$/, + loader: ExtractTextPlugin.extract('style-loader', 'css-loader') + }, { + test: /\.scss$/, + loader: ExtractTextPlugin.extract('style-loader', 'sass-loader') + }, { + test: /\.less$/, + loader: ExtractTextPlugin.extract('style-loader', 'less-loader') + }] + }, + output: { + path: path.join(__dirname, 'public/build'), + publicPath: '/build/', + filename: '[name].js' + }, + plugins: [ + new ExtractTextPlugin('html.min.css') + ] +}] diff --git a/webpack.production.js b/webpack.production.js index 7c690d2..7b42843 100644 --- a/webpack.production.js +++ b/webpack.production.js @@ -1,63 +1,63 @@ -var baseConfig = require('./webpackBaseConfig'); -var webpack = require('webpack'); -var path = require('path'); -var ExtractTextPlugin = require("extract-text-webpack-plugin"); -var OptimizeCssAssetsPlugin = require('optimize-css-assets-webpack-plugin'); -var ParallelUglifyPlugin = require('webpack-parallel-uglify-plugin'); +var baseConfig = require('./webpackBaseConfig') +var webpack = require('webpack') +var path = require('path') +var ExtractTextPlugin = require('extract-text-webpack-plugin') +var OptimizeCssAssetsPlugin = require('optimize-css-assets-webpack-plugin') +var ParallelUglifyPlugin = require('webpack-parallel-uglify-plugin') module.exports = [Object.assign({}, baseConfig, { - plugins: baseConfig.plugins.concat([ - new webpack.DefinePlugin({ - 'process.env': { - 'NODE_ENV': JSON.stringify('production') - } - }), - new ParallelUglifyPlugin({ - uglifyJS: { - compress: { - warnings: false - }, - mangle: false, - sourceMap: false - } - }), - new ExtractTextPlugin("[name].[hash].css") - ]), + plugins: baseConfig.plugins.concat([ + new webpack.DefinePlugin({ + 'process.env': { + 'NODE_ENV': JSON.stringify('production') + } + }), + new ParallelUglifyPlugin({ + uglifyJS: { + compress: { + warnings: false + }, + mangle: false, + sourceMap: false + } + }), + new ExtractTextPlugin('[name].[hash].css') + ]), - output: { - path: path.join(__dirname, 'public/build'), - publicPath: '/build/', - filename: '[id].[name].[hash].js', - baseUrl: '<%- url %>' - } + output: { + path: path.join(__dirname, 'public/build'), + publicPath: '/build/', + filename: '[id].[name].[hash].js', + baseUrl: '<%- url %>' + } }), { - entry: { - htmlExport: path.join(__dirname, 'public/js/htmlExport.js') - }, - module: { - loaders: [{ - test: /\.css$/, - loader: ExtractTextPlugin.extract('style-loader', 'css-loader') - }, { - test: /\.scss$/, - loader: ExtractTextPlugin.extract('style-loader', 'sass-loader') - }, { - test: /\.less$/, - loader: ExtractTextPlugin.extract('style-loader', 'less-loader') - }] - }, - output: { - path: path.join(__dirname, 'public/build'), - publicPath: '/build/', - filename: '[name].js' - }, - plugins: [ - new webpack.DefinePlugin({ - 'process.env': { - 'NODE_ENV': JSON.stringify('production') - } - }), - new ExtractTextPlugin("html.min.css"), - new OptimizeCssAssetsPlugin() - ] -}]; + entry: { + htmlExport: path.join(__dirname, 'public/js/htmlExport.js') + }, + module: { + loaders: [{ + test: /\.css$/, + loader: ExtractTextPlugin.extract('style-loader', 'css-loader') + }, { + test: /\.scss$/, + loader: ExtractTextPlugin.extract('style-loader', 'sass-loader') + }, { + test: /\.less$/, + loader: ExtractTextPlugin.extract('style-loader', 'less-loader') + }] + }, + output: { + path: path.join(__dirname, 'public/build'), + publicPath: '/build/', + filename: '[name].js' + }, + plugins: [ + new webpack.DefinePlugin({ + 'process.env': { + 'NODE_ENV': JSON.stringify('production') + } + }), + new ExtractTextPlugin('html.min.css'), + new OptimizeCssAssetsPlugin() + ] +}] diff --git a/webpackBaseConfig.js b/webpackBaseConfig.js index 419149c..8279005 100644 --- a/webpackBaseConfig.js +++ b/webpackBaseConfig.js @@ -1,423 +1,423 @@ -var webpack = require('webpack'); -var path = require('path'); -var ExtractTextPlugin = require("extract-text-webpack-plugin"); -var HtmlWebpackPlugin = require('html-webpack-plugin'); -var CopyWebpackPlugin = require('copy-webpack-plugin'); +var webpack = require('webpack') +var path = require('path') +var ExtractTextPlugin = require('extract-text-webpack-plugin') +var HtmlWebpackPlugin = require('html-webpack-plugin') +var CopyWebpackPlugin = require('copy-webpack-plugin') module.exports = { - plugins: [ - new webpack.ProvidePlugin({ - Visibility: "visibilityjs", - Cookies: "js-cookie", - key: "keymaster", - $: "jquery", - jQuery: "jquery", - "window.jQuery": "jquery", - "moment": "moment", - "Handlebars": "handlebars" - }), - new webpack.optimize.OccurrenceOrderPlugin(true), - new webpack.optimize.CommonsChunkPlugin({ - names: ["cover", "index", "pretty", "slide", "vendor"], - children: true, - async: true, - filename: '[name].js', - minChunks: Infinity - }), - new HtmlWebpackPlugin({ - template: 'public/views/includes/header.ejs', - chunks: ['font', 'index-styles', 'index'], - filename: path.join(__dirname, 'public/views/build/index-header.ejs'), - inject: false - }), - new HtmlWebpackPlugin({ - template: 'public/views/includes/header.ejs', - chunks: ['font-pack', 'index-styles-pack', 'index-styles', 'index'], - filename: path.join(__dirname, 'public/views/build/index-pack-header.ejs'), - inject: false - }), - new HtmlWebpackPlugin({ - template: 'public/views/includes/scripts.ejs', - chunks: ['index'], - filename: path.join(__dirname, 'public/views/build/index-scripts.ejs'), - inject: false - }), - new HtmlWebpackPlugin({ - template: 'public/views/includes/scripts.ejs', - chunks: ['common', 'index-pack'], - filename: path.join(__dirname, 'public/views/build/index-pack-scripts.ejs'), - inject: false - }), - new HtmlWebpackPlugin({ - template: 'public/views/includes/header.ejs', - chunks: ['font', 'cover'], - filename: path.join(__dirname, 'public/views/build/cover-header.ejs'), - inject: false - }), - new HtmlWebpackPlugin({ - template: 'public/views/includes/header.ejs', - chunks: ['font-pack', 'cover-styles-pack', 'cover'], - filename: path.join(__dirname, 'public/views/build/cover-pack-header.ejs'), - inject: false - }), - new HtmlWebpackPlugin({ - template: 'public/views/includes/scripts.ejs', - chunks: ['cover'], - filename: path.join(__dirname, 'public/views/build/cover-scripts.ejs'), - inject: false - }), - new HtmlWebpackPlugin({ - template: 'public/views/includes/scripts.ejs', - chunks: ['common', 'cover-pack'], - filename: path.join(__dirname, 'public/views/build/cover-pack-scripts.ejs'), - inject: false - }), - new HtmlWebpackPlugin({ - template: 'public/views/includes/header.ejs', - chunks: ['font', 'pretty-styles', 'pretty'], - filename: path.join(__dirname, 'public/views/build/pretty-header.ejs'), - inject: false - }), - new HtmlWebpackPlugin({ - template: 'public/views/includes/header.ejs', - chunks: ['font-pack', 'pretty-styles-pack', 'pretty-styles', 'pretty'], - filename: path.join(__dirname, 'public/views/build/pretty-pack-header.ejs'), - inject: false - }), - new HtmlWebpackPlugin({ - template: 'public/views/includes/scripts.ejs', - chunks: ['pretty'], - filename: path.join(__dirname, 'public/views/build/pretty-scripts.ejs'), - inject: false - }), - new HtmlWebpackPlugin({ - template: 'public/views/includes/scripts.ejs', - chunks: ['common', 'pretty-pack'], - filename: path.join(__dirname, 'public/views/build/pretty-pack-scripts.ejs'), - inject: false - }), - new HtmlWebpackPlugin({ - template: 'public/views/includes/header.ejs', - chunks: ['font', 'slide-styles', 'slide'], - filename: path.join(__dirname, 'public/views/build/slide-header.ejs'), - inject: false - }), - new HtmlWebpackPlugin({ - template: 'public/views/includes/header.ejs', - chunks: ['font-pack', 'slide-styles-pack', 'slide-styles', 'slide'], - filename: path.join(__dirname, 'public/views/build/slide-pack-header.ejs'), - inject: false - }), - new HtmlWebpackPlugin({ - template: 'public/views/includes/scripts.ejs', - chunks: ['slide'], - filename: path.join(__dirname, 'public/views/build/slide-scripts.ejs'), - inject: false - }), - new HtmlWebpackPlugin({ - template: 'public/views/includes/scripts.ejs', - chunks: ['slide-pack'], - filename: path.join(__dirname, 'public/views/build/slide-pack-scripts.ejs'), - inject: false - }), - new CopyWebpackPlugin([ - { - context: path.join(__dirname, 'node_modules/mathjax'), - from: { - glob: '**/*', - dot: false - }, - to: 'MathJax/' - }, - { - context: path.join(__dirname, 'node_modules/emojify.js'), - from: { - glob: '**/*', - dot: false - }, - to: 'emojify.js/' - }, - { - context: path.join(__dirname, 'node_modules/reveal.js'), - from: { - glob: '**/*', - dot: false - }, - to: 'reveal.js/' - } - ]) + plugins: [ + new webpack.ProvidePlugin({ + Visibility: 'visibilityjs', + Cookies: 'js-cookie', + key: 'keymaster', + $: 'jquery', + jQuery: 'jquery', + 'window.jQuery': 'jquery', + 'moment': 'moment', + 'Handlebars': 'handlebars' + }), + new webpack.optimize.OccurrenceOrderPlugin(true), + new webpack.optimize.CommonsChunkPlugin({ + names: ['cover', 'index', 'pretty', 'slide', 'vendor'], + children: true, + async: true, + filename: '[name].js', + minChunks: Infinity + }), + new HtmlWebpackPlugin({ + template: 'public/views/includes/header.ejs', + chunks: ['font', 'index-styles', 'index'], + filename: path.join(__dirname, 'public/views/build/index-header.ejs'), + inject: false + }), + new HtmlWebpackPlugin({ + template: 'public/views/includes/header.ejs', + chunks: ['font-pack', 'index-styles-pack', 'index-styles', 'index'], + filename: path.join(__dirname, 'public/views/build/index-pack-header.ejs'), + inject: false + }), + new HtmlWebpackPlugin({ + template: 'public/views/includes/scripts.ejs', + chunks: ['index'], + filename: path.join(__dirname, 'public/views/build/index-scripts.ejs'), + inject: false + }), + new HtmlWebpackPlugin({ + template: 'public/views/includes/scripts.ejs', + chunks: ['common', 'index-pack'], + filename: path.join(__dirname, 'public/views/build/index-pack-scripts.ejs'), + inject: false + }), + new HtmlWebpackPlugin({ + template: 'public/views/includes/header.ejs', + chunks: ['font', 'cover'], + filename: path.join(__dirname, 'public/views/build/cover-header.ejs'), + inject: false + }), + new HtmlWebpackPlugin({ + template: 'public/views/includes/header.ejs', + chunks: ['font-pack', 'cover-styles-pack', 'cover'], + filename: path.join(__dirname, 'public/views/build/cover-pack-header.ejs'), + inject: false + }), + new HtmlWebpackPlugin({ + template: 'public/views/includes/scripts.ejs', + chunks: ['cover'], + filename: path.join(__dirname, 'public/views/build/cover-scripts.ejs'), + inject: false + }), + new HtmlWebpackPlugin({ + template: 'public/views/includes/scripts.ejs', + chunks: ['common', 'cover-pack'], + filename: path.join(__dirname, 'public/views/build/cover-pack-scripts.ejs'), + inject: false + }), + new HtmlWebpackPlugin({ + template: 'public/views/includes/header.ejs', + chunks: ['font', 'pretty-styles', 'pretty'], + filename: path.join(__dirname, 'public/views/build/pretty-header.ejs'), + inject: false + }), + new HtmlWebpackPlugin({ + template: 'public/views/includes/header.ejs', + chunks: ['font-pack', 'pretty-styles-pack', 'pretty-styles', 'pretty'], + filename: path.join(__dirname, 'public/views/build/pretty-pack-header.ejs'), + inject: false + }), + new HtmlWebpackPlugin({ + template: 'public/views/includes/scripts.ejs', + chunks: ['pretty'], + filename: path.join(__dirname, 'public/views/build/pretty-scripts.ejs'), + inject: false + }), + new HtmlWebpackPlugin({ + template: 'public/views/includes/scripts.ejs', + chunks: ['common', 'pretty-pack'], + filename: path.join(__dirname, 'public/views/build/pretty-pack-scripts.ejs'), + inject: false + }), + new HtmlWebpackPlugin({ + template: 'public/views/includes/header.ejs', + chunks: ['font', 'slide-styles', 'slide'], + filename: path.join(__dirname, 'public/views/build/slide-header.ejs'), + inject: false + }), + new HtmlWebpackPlugin({ + template: 'public/views/includes/header.ejs', + chunks: ['font-pack', 'slide-styles-pack', 'slide-styles', 'slide'], + filename: path.join(__dirname, 'public/views/build/slide-pack-header.ejs'), + inject: false + }), + new HtmlWebpackPlugin({ + template: 'public/views/includes/scripts.ejs', + chunks: ['slide'], + filename: path.join(__dirname, 'public/views/build/slide-scripts.ejs'), + inject: false + }), + new HtmlWebpackPlugin({ + template: 'public/views/includes/scripts.ejs', + chunks: ['slide-pack'], + filename: path.join(__dirname, 'public/views/build/slide-pack-scripts.ejs'), + inject: false + }), + new CopyWebpackPlugin([ + { + context: path.join(__dirname, 'node_modules/mathjax'), + from: { + glob: '**/*', + dot: false + }, + to: 'MathJax/' + }, + { + context: path.join(__dirname, 'node_modules/emojify.js'), + from: { + glob: '**/*', + dot: false + }, + to: 'emojify.js/' + }, + { + context: path.join(__dirname, 'node_modules/reveal.js'), + from: { + glob: '**/*', + dot: false + }, + to: 'reveal.js/' + } + ]) + ], + + entry: { + font: path.join(__dirname, 'public/css/google-font.css'), + 'font-pack': path.join(__dirname, 'public/css/font.css'), + common: [ + 'expose?jQuery!expose?$!jquery', + 'velocity-animate', + 'imports?$=jquery!jquery-mousewheel', + 'bootstrap' ], + cover: [ + 'babel-polyfill', + path.join(__dirname, 'public/js/cover.js') + ], + 'cover-styles-pack': [ + path.join(__dirname, 'node_modules/bootstrap/dist/css/bootstrap.min.css'), + path.join(__dirname, 'node_modules/font-awesome/css/font-awesome.min.css'), + path.join(__dirname, 'public/css/bootstrap-social.css'), + path.join(__dirname, 'node_modules/select2/select2.css'), + path.join(__dirname, 'node_modules/select2/select2-bootstrap.css') + ], + 'cover-pack': [ + 'babel-polyfill', + 'bootstrap-validator', + 'script!listPagnation', + 'expose?select2!select2', + 'expose?moment!moment', + 'script!js-url', + path.join(__dirname, 'public/js/cover.js') + ], + index: [ + 'babel-polyfill', + 'script!jquery-ui-resizable', + 'script!js-url', + 'expose?filterXSS!xss', + 'script!Idle.Js', + 'expose?LZString!lz-string', + 'script!codemirror', + 'script!inlineAttachment', + 'script!jqueryTextcomplete', + 'script!codemirrorSpellChecker', + 'script!codemirrorInlineAttachment', + 'script!ot', + 'flowchart.js', + 'js-sequence-diagrams', + 'expose?RevealMarkdown!reveal-markdown', + path.join(__dirname, 'public/js/google-drive-upload.js'), + path.join(__dirname, 'public/js/google-drive-picker.js'), + path.join(__dirname, 'public/js/index.js') + ], + 'index-styles': [ + path.join(__dirname, 'public/vendor/jquery-ui/jquery-ui.min.css'), + path.join(__dirname, 'public/vendor/codemirror-spell-checker/spell-checker.min.css'), + path.join(__dirname, 'node_modules/codemirror/lib/codemirror.css'), + path.join(__dirname, 'node_modules/codemirror/addon/fold/foldgutter.css'), + path.join(__dirname, 'node_modules/codemirror/addon/display/fullscreen.css'), + path.join(__dirname, 'node_modules/codemirror/addon/dialog/dialog.css'), + path.join(__dirname, 'node_modules/codemirror/addon/scroll/simplescrollbars.css'), + path.join(__dirname, 'node_modules/codemirror/addon/search/matchesonscrollbar.css'), + path.join(__dirname, 'node_modules/codemirror/theme/monokai.css'), + path.join(__dirname, 'node_modules/codemirror/theme/one-dark.css'), + path.join(__dirname, 'node_modules/codemirror/mode/tiddlywiki/tiddlywiki.css'), + path.join(__dirname, 'node_modules/codemirror/mode/mediawiki/mediawiki.css'), + path.join(__dirname, 'public/css/github-extract.css'), + path.join(__dirname, 'public/vendor/showup/showup.css'), + path.join(__dirname, 'public/css/mermaid.css'), + path.join(__dirname, 'public/css/markdown.css'), + path.join(__dirname, 'public/css/slide-preview.css') + ], + 'index-styles-pack': [ + path.join(__dirname, 'node_modules/bootstrap/dist/css/bootstrap.min.css'), + path.join(__dirname, 'node_modules/font-awesome/css/font-awesome.min.css'), + path.join(__dirname, 'public/css/bootstrap-social.css'), + path.join(__dirname, 'node_modules/ionicons/css/ionicons.min.css'), + path.join(__dirname, 'node_modules/octicons/octicons/octicons.css') + ], + 'index-pack': [ + 'babel-polyfill', + 'expose?Spinner!spin.js', + 'script!jquery-ui-resizable', + 'bootstrap-validator', + 'expose?jsyaml!js-yaml', + 'script!mermaid', + 'expose?moment!moment', + 'script!js-url', + 'script!handlebars', + 'expose?hljs!highlight.js', + 'expose?emojify!emojify.js', + 'expose?filterXSS!xss', + 'script!Idle.Js', + 'script!gist-embed', + 'expose?LZString!lz-string', + 'script!codemirror', + 'script!inlineAttachment', + 'script!jqueryTextcomplete', + 'script!codemirrorSpellChecker', + 'script!codemirrorInlineAttachment', + 'script!ot', + 'flowchart.js', + 'js-sequence-diagrams', + 'expose?Viz!viz.js', + 'expose?io!socket.io-client', + 'expose?RevealMarkdown!reveal-markdown', + path.join(__dirname, 'public/js/google-drive-upload.js'), + path.join(__dirname, 'public/js/google-drive-picker.js'), + path.join(__dirname, 'public/js/index.js') + ], + pretty: [ + 'babel-polyfill', + 'expose?filterXSS!xss', + 'flowchart.js', + 'js-sequence-diagrams', + 'expose?RevealMarkdown!reveal-markdown', + path.join(__dirname, 'public/js/pretty.js') + ], + 'pretty-styles': [ + path.join(__dirname, 'public/css/github-extract.css'), + path.join(__dirname, 'public/css/mermaid.css'), + path.join(__dirname, 'public/css/markdown.css'), + path.join(__dirname, 'public/css/slide-preview.css') + ], + 'pretty-styles-pack': [ + path.join(__dirname, 'node_modules/bootstrap/dist/css/bootstrap.min.css'), + path.join(__dirname, 'node_modules/font-awesome/css/font-awesome.min.css'), + path.join(__dirname, 'node_modules/ionicons/css/ionicons.min.css'), + path.join(__dirname, 'node_modules/octicons/octicons/octicons.css') + ], + 'pretty-pack': [ + 'babel-polyfill', + 'expose?jsyaml!js-yaml', + 'script!mermaid', + 'expose?moment!moment', + 'script!handlebars', + 'expose?hljs!highlight.js', + 'expose?emojify!emojify.js', + 'expose?filterXSS!xss', + 'script!gist-embed', + 'flowchart.js', + 'js-sequence-diagrams', + 'expose?Viz!viz.js', + 'expose?RevealMarkdown!reveal-markdown', + path.join(__dirname, 'public/js/pretty.js') + ], + slide: [ + 'babel-polyfill', + 'bootstrap-tooltip', + 'expose?filterXSS!xss', + 'flowchart.js', + 'js-sequence-diagrams', + 'expose?RevealMarkdown!reveal-markdown', + path.join(__dirname, 'public/js/slide.js') + ], + 'slide-styles': [ + path.join(__dirname, 'public/vendor/bootstrap/tooltip.min.css'), + path.join(__dirname, 'public/css/github-extract.css'), + path.join(__dirname, 'public/css/mermaid.css'), + path.join(__dirname, 'public/css/markdown.css') + ], + 'slide-styles-pack': [ + path.join(__dirname, 'node_modules/font-awesome/css/font-awesome.min.css'), + path.join(__dirname, 'node_modules/ionicons/css/ionicons.min.css'), + path.join(__dirname, 'node_modules/octicons/octicons/octicons.css') + ], + 'slide-pack': [ + 'babel-polyfill', + 'expose?jQuery!expose?$!jquery', + 'velocity-animate', + 'imports?$=jquery!jquery-mousewheel', + 'bootstrap-tooltip', + 'expose?jsyaml!js-yaml', + 'script!mermaid', + 'expose?moment!moment', + 'script!handlebars', + 'expose?hljs!highlight.js', + 'expose?emojify!emojify.js', + 'expose?filterXSS!xss', + 'script!gist-embed', + 'flowchart.js', + 'js-sequence-diagrams', + 'expose?Viz!viz.js', + 'headjs', + 'expose?Reveal!reveal.js', + 'expose?RevealMarkdown!reveal-markdown', + path.join(__dirname, 'public/js/slide.js') + ] + }, - entry: { - font: path.join(__dirname, 'public/css/google-font.css'), - "font-pack": path.join(__dirname, 'public/css/font.css'), - common: [ - "expose?jQuery!expose?$!jquery", - "velocity-animate", - "imports?$=jquery!jquery-mousewheel", - "bootstrap" - ], - cover: [ - "babel-polyfill", - path.join(__dirname, 'public/js/cover.js') - ], - "cover-styles-pack": [ - path.join(__dirname, 'node_modules/bootstrap/dist/css/bootstrap.min.css'), - path.join(__dirname, 'node_modules/font-awesome/css/font-awesome.min.css'), - path.join(__dirname, 'public/css/bootstrap-social.css'), - path.join(__dirname, 'node_modules/select2/select2.css'), - path.join(__dirname, 'node_modules/select2/select2-bootstrap.css'), - ], - "cover-pack": [ - "babel-polyfill", - "bootstrap-validator", - "script!listPagnation", - "expose?select2!select2", - "expose?moment!moment", - "script!js-url", - path.join(__dirname, 'public/js/cover.js') - ], - index: [ - "babel-polyfill", - "script!jquery-ui-resizable", - "script!js-url", - "expose?filterXSS!xss", - "script!Idle.Js", - "expose?LZString!lz-string", - "script!codemirror", - "script!inlineAttachment", - "script!jqueryTextcomplete", - "script!codemirrorSpellChecker", - "script!codemirrorInlineAttachment", - "script!ot", - "flowchart.js", - "js-sequence-diagrams", - "expose?RevealMarkdown!reveal-markdown", - path.join(__dirname, 'public/js/google-drive-upload.js'), - path.join(__dirname, 'public/js/google-drive-picker.js'), - path.join(__dirname, 'public/js/index.js') - ], - "index-styles": [ - path.join(__dirname, 'public/vendor/jquery-ui/jquery-ui.min.css'), - path.join(__dirname, 'public/vendor/codemirror-spell-checker/spell-checker.min.css'), - path.join(__dirname, 'node_modules/codemirror/lib/codemirror.css'), - path.join(__dirname, 'node_modules/codemirror/addon/fold/foldgutter.css'), - path.join(__dirname, 'node_modules/codemirror/addon/display/fullscreen.css'), - path.join(__dirname, 'node_modules/codemirror/addon/dialog/dialog.css'), - path.join(__dirname, 'node_modules/codemirror/addon/scroll/simplescrollbars.css'), - path.join(__dirname, 'node_modules/codemirror/addon/search/matchesonscrollbar.css'), - path.join(__dirname, 'node_modules/codemirror/theme/monokai.css'), - path.join(__dirname, 'node_modules/codemirror/theme/one-dark.css'), - path.join(__dirname, 'node_modules/codemirror/mode/tiddlywiki/tiddlywiki.css'), - path.join(__dirname, 'node_modules/codemirror/mode/mediawiki/mediawiki.css'), - path.join(__dirname, 'public/css/github-extract.css'), - path.join(__dirname, 'public/vendor/showup/showup.css'), - path.join(__dirname, 'public/css/mermaid.css'), - path.join(__dirname, 'public/css/markdown.css'), - path.join(__dirname, 'public/css/slide-preview.css') - ], - "index-styles-pack": [ - path.join(__dirname, 'node_modules/bootstrap/dist/css/bootstrap.min.css'), - path.join(__dirname, 'node_modules/font-awesome/css/font-awesome.min.css'), - path.join(__dirname, 'public/css/bootstrap-social.css'), - path.join(__dirname, 'node_modules/ionicons/css/ionicons.min.css'), - path.join(__dirname, 'node_modules/octicons/octicons/octicons.css') - ], - "index-pack": [ - "babel-polyfill", - "expose?Spinner!spin.js", - "script!jquery-ui-resizable", - "bootstrap-validator", - "expose?jsyaml!js-yaml", - "script!mermaid", - "expose?moment!moment", - "script!js-url", - "script!handlebars", - "expose?hljs!highlight.js", - "expose?emojify!emojify.js", - "expose?filterXSS!xss", - "script!Idle.Js", - "script!gist-embed", - "expose?LZString!lz-string", - "script!codemirror", - "script!inlineAttachment", - "script!jqueryTextcomplete", - "script!codemirrorSpellChecker", - "script!codemirrorInlineAttachment", - "script!ot", - "flowchart.js", - "js-sequence-diagrams", - "expose?Viz!viz.js", - "expose?io!socket.io-client", - "expose?RevealMarkdown!reveal-markdown", - path.join(__dirname, 'public/js/google-drive-upload.js'), - path.join(__dirname, 'public/js/google-drive-picker.js'), - path.join(__dirname, 'public/js/index.js') - ], - pretty: [ - "babel-polyfill", - "expose?filterXSS!xss", - "flowchart.js", - "js-sequence-diagrams", - "expose?RevealMarkdown!reveal-markdown", - path.join(__dirname, 'public/js/pretty.js') - ], - "pretty-styles": [ - path.join(__dirname, 'public/css/github-extract.css'), - path.join(__dirname, 'public/css/mermaid.css'), - path.join(__dirname, 'public/css/markdown.css'), - path.join(__dirname, 'public/css/slide-preview.css') - ], - "pretty-styles-pack": [ - path.join(__dirname, 'node_modules/bootstrap/dist/css/bootstrap.min.css'), - path.join(__dirname, 'node_modules/font-awesome/css/font-awesome.min.css'), - path.join(__dirname, 'node_modules/ionicons/css/ionicons.min.css'), - path.join(__dirname, 'node_modules/octicons/octicons/octicons.css') - ], - "pretty-pack": [ - "babel-polyfill", - "expose?jsyaml!js-yaml", - "script!mermaid", - "expose?moment!moment", - "script!handlebars", - "expose?hljs!highlight.js", - "expose?emojify!emojify.js", - "expose?filterXSS!xss", - "script!gist-embed", - "flowchart.js", - "js-sequence-diagrams", - "expose?Viz!viz.js", - "expose?RevealMarkdown!reveal-markdown", - path.join(__dirname, 'public/js/pretty.js') - ], - slide: [ - "babel-polyfill", - "bootstrap-tooltip", - "expose?filterXSS!xss", - "flowchart.js", - "js-sequence-diagrams", - "expose?RevealMarkdown!reveal-markdown", - path.join(__dirname, 'public/js/slide.js') - ], - "slide-styles": [ - path.join(__dirname, 'public/vendor/bootstrap/tooltip.min.css'), - path.join(__dirname, 'public/css/github-extract.css'), - path.join(__dirname, 'public/css/mermaid.css'), - path.join(__dirname, 'public/css/markdown.css') - ], - "slide-styles-pack": [ - path.join(__dirname, 'node_modules/font-awesome/css/font-awesome.min.css'), - path.join(__dirname, 'node_modules/ionicons/css/ionicons.min.css'), - path.join(__dirname, 'node_modules/octicons/octicons/octicons.css') - ], - "slide-pack": [ - "babel-polyfill", - "expose?jQuery!expose?$!jquery", - "velocity-animate", - "imports?$=jquery!jquery-mousewheel", - "bootstrap-tooltip", - "expose?jsyaml!js-yaml", - "script!mermaid", - "expose?moment!moment", - "script!handlebars", - "expose?hljs!highlight.js", - "expose?emojify!emojify.js", - "expose?filterXSS!xss", - "script!gist-embed", - "flowchart.js", - "js-sequence-diagrams", - "expose?Viz!viz.js", - "headjs", - "expose?Reveal!reveal.js", - "expose?RevealMarkdown!reveal-markdown", - path.join(__dirname, 'public/js/slide.js') - ] - }, + output: { + path: path.join(__dirname, 'public/build'), + publicPath: '/build/', + filename: '[name].js', + baseUrl: '<%- url %>' + }, - output: { - path: path.join(__dirname, 'public/build'), - publicPath: '/build/', - filename: '[name].js', - baseUrl: '<%- url %>' - }, - - resolve: { - modulesDirectories: [ - path.resolve(__dirname, 'src'), - path.resolve(__dirname, 'node_modules') - ], - extensions: ["", ".js"], - alias: { - codemirror: path.join(__dirname, 'node_modules/codemirror/codemirror.min.js'), - inlineAttachment: path.join(__dirname, 'public/vendor/inlineAttachment/inline-attachment.js'), - jqueryTextcomplete: path.join(__dirname, 'public/vendor/jquery-textcomplete/jquery.textcomplete.js'), - codemirrorSpellChecker: path.join(__dirname, 'public/vendor/codemirror-spell-checker/spell-checker.min.js'), - codemirrorInlineAttachment: path.join(__dirname, 'public/vendor/inlineAttachment/codemirror.inline-attachment.js'), - ot: path.join(__dirname, 'public/vendor/ot/ot.min.js'), - listPagnation: path.join(__dirname, 'node_modules/list.pagination.js/dist/list.pagination.min.js'), - mermaid: path.join(__dirname, 'node_modules/mermaid/dist/mermaid.min.js'), - handlebars: path.join(__dirname, 'node_modules/handlebars/dist/handlebars.min.js'), - "jquery-ui-resizable": path.join(__dirname, 'public/vendor/jquery-ui/jquery-ui.min.js'), - "gist-embed": path.join(__dirname, 'node_modules/gist-embed/gist-embed.min.js'), - "bootstrap-tooltip": path.join(__dirname, 'public/vendor/bootstrap/tooltip.min.js'), - "headjs": path.join(__dirname, 'node_modules/reveal.js/lib/js/head.min.js'), - "reveal-markdown": path.join(__dirname, 'public/js/reveal-markdown.js') - } - }, - - externals: { - "viz.js": "Viz", - "socket.io-client": "io", - "lodash": "_", - "jquery": "$", - "moment": "moment", - "handlebars": "Handlebars", - "highlight.js": "hljs", - "select2": "select2" - }, - - module: { - loaders: [{ - test: /\.json$/, - loader: 'json-loader' - }, { - test: /\.js$/, - loader: 'babel', - exclude: [/node_modules/, /public\/vendor/] - }, { - test: /\.css$/, - loader: ExtractTextPlugin.extract('style-loader', 'css-loader') - }, { - test: /\.scss$/, - loader: ExtractTextPlugin.extract('style-loader', 'sass-loader') - }, { - test: /\.less$/, - loader: ExtractTextPlugin.extract('style-loader', 'less-loader') - }, { - test: require.resolve("js-sequence-diagrams"), - loader: "imports?Raphael=raphael" - }, { - test: /\.eot(\?v=\d+\.\d+\.\d+)?$/, - loader: "file" - }, { - test: /\.(woff|woff2)(\?v=\d+\.\d+\.\d+)?$/, - loader: "url?prefix=font/&limit=5000" - }, { - test: /\.ttf(\?v=\d+\.\d+\.\d+)?$/, - loader: "url?limit=10000&mimetype=application/octet-stream" - }, { - test: /\.svg(\?v=\d+\.\d+\.\d+)?$/, - loader: "url?limit=10000&mimetype=image/svg+xml" - }, { - test: /\.png(\?v=\d+\.\d+\.\d+)?$/, - loader: "url?limit=10000&mimetype=image/png" - }, { - test: /\.gif(\?v=\d+\.\d+\.\d+)?$/, - loader: "url?limit=10000&mimetype=image/gif" - }] - }, - - node: { - fs: "empty" + resolve: { + modulesDirectories: [ + path.resolve(__dirname, 'src'), + path.resolve(__dirname, 'node_modules') + ], + extensions: ['', '.js'], + alias: { + codemirror: path.join(__dirname, 'node_modules/codemirror/codemirror.min.js'), + inlineAttachment: path.join(__dirname, 'public/vendor/inlineAttachment/inline-attachment.js'), + jqueryTextcomplete: path.join(__dirname, 'public/vendor/jquery-textcomplete/jquery.textcomplete.js'), + codemirrorSpellChecker: path.join(__dirname, 'public/vendor/codemirror-spell-checker/spell-checker.min.js'), + codemirrorInlineAttachment: path.join(__dirname, 'public/vendor/inlineAttachment/codemirror.inline-attachment.js'), + ot: path.join(__dirname, 'public/vendor/ot/ot.min.js'), + listPagnation: path.join(__dirname, 'node_modules/list.pagination.js/dist/list.pagination.min.js'), + mermaid: path.join(__dirname, 'node_modules/mermaid/dist/mermaid.min.js'), + handlebars: path.join(__dirname, 'node_modules/handlebars/dist/handlebars.min.js'), + 'jquery-ui-resizable': path.join(__dirname, 'public/vendor/jquery-ui/jquery-ui.min.js'), + 'gist-embed': path.join(__dirname, 'node_modules/gist-embed/gist-embed.min.js'), + 'bootstrap-tooltip': path.join(__dirname, 'public/vendor/bootstrap/tooltip.min.js'), + 'headjs': path.join(__dirname, 'node_modules/reveal.js/lib/js/head.min.js'), + 'reveal-markdown': path.join(__dirname, 'public/js/reveal-markdown.js') } -}; + }, + + externals: { + 'viz.js': 'Viz', + 'socket.io-client': 'io', + 'lodash': '_', + 'jquery': '$', + 'moment': 'moment', + 'handlebars': 'Handlebars', + 'highlight.js': 'hljs', + 'select2': 'select2' + }, + + module: { + loaders: [{ + test: /\.json$/, + loader: 'json-loader' + }, { + test: /\.js$/, + loader: 'babel', + exclude: [/node_modules/, /public\/vendor/] + }, { + test: /\.css$/, + loader: ExtractTextPlugin.extract('style-loader', 'css-loader') + }, { + test: /\.scss$/, + loader: ExtractTextPlugin.extract('style-loader', 'sass-loader') + }, { + test: /\.less$/, + loader: ExtractTextPlugin.extract('style-loader', 'less-loader') + }, { + test: require.resolve('js-sequence-diagrams'), + loader: 'imports?Raphael=raphael' + }, { + test: /\.eot(\?v=\d+\.\d+\.\d+)?$/, + loader: 'file' + }, { + test: /\.(woff|woff2)(\?v=\d+\.\d+\.\d+)?$/, + loader: 'url?prefix=font/&limit=5000' + }, { + test: /\.ttf(\?v=\d+\.\d+\.\d+)?$/, + loader: 'url?limit=10000&mimetype=application/octet-stream' + }, { + test: /\.svg(\?v=\d+\.\d+\.\d+)?$/, + loader: 'url?limit=10000&mimetype=image/svg+xml' + }, { + test: /\.png(\?v=\d+\.\d+\.\d+)?$/, + loader: 'url?limit=10000&mimetype=image/png' + }, { + test: /\.gif(\?v=\d+\.\d+\.\d+)?$/, + loader: 'url?limit=10000&mimetype=image/gif' + }] + }, + + node: { + fs: 'empty' + } +}