diff options
Diffstat (limited to 'public/js')
-rw-r--r-- | public/js/cover.js | 100 | ||||
-rw-r--r-- | public/js/extra.js | 468 | ||||
-rw-r--r-- | public/js/history.js | 158 | ||||
-rw-r--r-- | public/js/index.js | 1139 | ||||
-rw-r--r-- | public/js/lib/common/login.js | 32 | ||||
-rw-r--r-- | public/js/lib/editor/index.js | 11 | ||||
-rw-r--r-- | public/js/lib/editor/toolbar.html | 31 | ||||
-rw-r--r-- | public/js/lib/editor/ui-elements.js | 3 | ||||
-rw-r--r-- | public/js/lib/editor/utils.js | 14 | ||||
-rw-r--r-- | public/js/lib/syncscroll.js | 4 | ||||
-rw-r--r-- | public/js/pretty.js | 48 | ||||
-rw-r--r-- | public/js/render.js | 2 | ||||
-rw-r--r-- | public/js/slide.js | 10 |
13 files changed, 1003 insertions, 1017 deletions
diff --git a/public/js/cover.js b/public/js/cover.js index 79fb3a2a..94748cdc 100644 --- a/public/js/cover.js +++ b/public/js/cover.js @@ -1,37 +1,37 @@ /* eslint-env browser, jquery */ /* global moment, serverurl */ -require('./locale') - -require('../css/cover.css') -require('../css/site.css') - import { - checkIfAuth, - clearLoginState, - getLoginState, - resetCheckAuth, - setloginStateChangeEvent + checkIfAuth, + clearLoginState, + getLoginState, + resetCheckAuth, + setloginStateChangeEvent } from './lib/common/login' import { - clearDuplicatedHistory, - deleteServerHistory, - getHistory, - getStorageHistory, - parseHistory, - parseServerToHistory, - parseStorageToHistory, - postHistoryToServer, - removeHistory, - saveHistory, - saveStorageHistoryToServer + clearDuplicatedHistory, + deleteServerHistory, + getHistory, + getStorageHistory, + parseHistory, + parseServerToHistory, + parseStorageToHistory, + postHistoryToServer, + removeHistory, + saveHistory, + saveStorageHistoryToServer } from './history' import { saveAs } from 'file-saver' import List from 'list.js' import S from 'string' +require('./locale') + +require('../css/cover.css') +require('../css/site.css') + const options = { valueNames: ['id', 'text', 'timestamp', 'fromNow', 'time', 'tags', 'pinned'], item: `<li class="col-xs-12 col-sm-6 col-md-6 col-lg-4"> @@ -67,27 +67,27 @@ pageInit() 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').show() - $('.ui-or').show() - $('.ui-welcome').hide() - $('.ui-avatar').prop('src', '').hide() - $('.ui-name').html('') - $('.ui-signout').hide() - parseStorageToHistory(historyList, parseHistoryCallback) - } - ) + 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').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 () { @@ -132,7 +132,7 @@ function checkHistoryList () { function parseHistoryCallback (list, notehistory) { checkHistoryList() - // sort by pinned then timestamp + // sort by pinned then timestamp list.sort('', { sortFunction (a, b) { const notea = a.values() @@ -152,13 +152,13 @@ function parseHistoryCallback (list, notehistory) { } } }) - // parse filter tags + // 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 + // push info filtertags if not found let found = false if (filtertags.includes(tags[j])) { found = true } if (!found) { filtertags.push(tags[j]) } @@ -178,20 +178,20 @@ historyList.on('updated', e => { const a = itemEl.find('a') const pin = itemEl.find('.ui-history-pin') const tagsEl = itemEl.find('.tags') - // parse link to element a + // parse link to element a a.attr('href', `${serverurl}/${values.id}`) - // parse pinned + // parse pinned if (values.pinned) { pin.addClass('active') } else { pin.removeClass('active') } - // parse tags + // 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 + // push into the item label labels.push(`<span class='label label-default'>${tags[j]}</span>`) } tagsEl.html(labels.join(' ')) @@ -328,7 +328,7 @@ $('.ui-open-history').bind('change', e => { const reader = new FileReader() reader.onload = () => { const notehistory = JSON.parse(reader.result) - // console.log(notehistory); + // console.log(notehistory); if (!reader.result) return getHistory(data => { let mergedata = data.concat(notehistory) diff --git a/public/js/extra.js b/public/js/extra.js index 76e95635..4431513d 100644 --- a/public/js/extra.js +++ b/public/js/extra.js @@ -1,6 +1,22 @@ /* eslint-env browser, jquery */ +/* eslint no-console: ["error", { allow: ["warn", "error"] }] */ /* 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' +import escapeHTML from 'escape-html' + +import getUIElements from './lib/editor/ui-elements' + +import markdownit from 'markdown-it' +import markdownitContainer from 'markdown-it-container' + +/* Defined regex markdown it plugins */ +import Plugin from 'markdown-it-regexp' + require('prismjs/themes/prism.css') require('prismjs/components/prism-wiki') require('prismjs/components/prism-haskell') @@ -10,17 +26,9 @@ require('prismjs/components/prism-jsx') require('prismjs/components/prism-makefile') require('prismjs/components/prism-gherkin') -import Prism from 'prismjs' -import hljs from 'highlight.js' -import PDFObject from 'pdfobject' -import S from 'string' -import { saveAs } from 'file-saver' - require('./lib/common/login') require('../vendor/md-toc') var Viz = require('viz.js') - -import getUIElements from './lib/editor/ui-elements' const ui = getUIElements() // auto update last change @@ -190,7 +198,7 @@ export function parseMeta (md, edit, view, toc, tocAffix) { dir = meta.dir breaks = meta.breaks } - // text language + // text language if (lang && typeof lang === 'string') { view.attr('lang', lang) toc.attr('lang', lang) @@ -202,7 +210,7 @@ export function parseMeta (md, edit, view, toc, tocAffix) { tocAffix.removeAttr('lang') if (edit) { edit.removeAttr('lang', lang) } } - // text direction + // text direction if (dir && typeof dir === 'string') { view.attr('dir', dir) toc.attr('dir', dir) @@ -212,7 +220,7 @@ export function parseMeta (md, edit, view, toc, tocAffix) { toc.removeAttr('dir') tocAffix.removeAttr('dir') } - // breaks + // breaks if (typeof breaks === 'boolean' && !breaks) { md.options.breaks = false } else { @@ -245,7 +253,7 @@ if (typeof window.mermaid !== 'undefined' && window.mermaid) window.mermaid.star // dynamic event or object binding here export function finishView (view) { - // todo list + // todo list const lis = view.find('li.raw').removeClass('raw').sortByDepth().toArray() for (let li of lis) { @@ -261,7 +269,7 @@ export function finishView (view) { if (typeof editor !== 'undefined' && window.havePermission()) { disabled = '' } if (/^\s*\[[x ]\]\s*/.test(html)) { li.innerHTML = html.replace(/^\s*\[ \]\s*/, `<input type="checkbox" class="task-list-item-checkbox "${disabled}><label></label>`) - .replace(/^\s*\[x\]\s*/, `<input type="checkbox" class="task-list-item-checkbox" checked ${disabled}><label></label>`) + .replace(/^\s*\[x\]\s*/, `<input type="checkbox" class="task-list-item-checkbox" checked ${disabled}><label></label>`) if (li.tagName.toLowerCase() !== 'li') { li.parentElement.setAttribute('class', 'task-list-item') } else { @@ -269,42 +277,42 @@ export function finishView (view) { } } if (typeof editor !== 'undefined' && window.havePermission()) { $(li).find('input').change(toggleTodoEvent) } - // color tag in list will convert it to tag icon with color + // 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 + // youtube view.find('div.youtube.raw').removeClass('raw') - .click(function () { - imgPlayiframe(this, '//www.youtube.com/embed/') - }) + .click(function () { + imgPlayiframe(this, '//www.youtube.com/embed/') + }) // vimeo view.find('div.vimeo.raw').removeClass('raw') - .click(function () { - 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 thumbnailSrc = data[0].thumbnail_large - const image = `<img src="${thumbnailSrc}" />` - $(value).prepend(image) - if (window.viewAjaxCallback) window.viewAjaxCallback() - } - }) - }) + .click(function () { + 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 thumbnailSrc = data[0].thumbnail_large + const image = `<img src="${thumbnailSrc}" />` + $(value).prepend(image) + if (window.viewAjaxCallback) window.viewAjaxCallback() + } + }) + }) // gist view.find('code[data-gist-id]').each((key, value) => { if ($(value).children().length === 0) { $(value).gist(window.viewAjaxCallback) } }) - // sequence diagram + // sequence diagram const sequences = view.find('div.sequence-diagram.raw').removeClass('raw') sequences.each((key, value) => { try { @@ -323,11 +331,11 @@ export function finishView (view) { svg[0].setAttribute('preserveAspectRatio', 'xMidYMid meet') } catch (err) { $value.unwrap() - $value.parent().append('<div class="alert alert-warning">' + err + '</div>') + $value.parent().append(`<div class="alert alert-warning">${escapeHTML(err)}</div>`) console.warn(err) } }) - // flowchart + // flowchart const flow = view.find('div.flow-chart.raw').removeClass('raw') flow.each((key, value) => { try { @@ -347,11 +355,11 @@ export function finishView (view) { $value.children().unwrap().unwrap() } catch (err) { $value.unwrap() - $value.parent().append('<div class="alert alert-warning">' + err + '</div>') + $value.parent().append(`<div class="alert alert-warning">${escapeHTML(err)}</div>`) console.warn(err) } }) - // graphviz + // graphviz var graphvizs = view.find('div.graphviz.raw').removeClass('raw') graphvizs.each(function (key, value) { try { @@ -366,11 +374,11 @@ export function finishView (view) { $value.children().unwrap().unwrap() } catch (err) { $value.unwrap() - $value.parent().append('<div class="alert alert-warning">' + err + '</div>') + $value.parent().append(`<div class="alert alert-warning">${escapeHTML(err)}</div>`) console.warn(err) } }) - // mermaid + // mermaid const mermaids = view.find('div.mermaid.raw').removeClass('raw') mermaids.each((key, value) => { try { @@ -388,7 +396,7 @@ export function finishView (view) { } $value.unwrap() - $value.parent().append('<div class="alert alert-warning">' + errormessage + '</div>') + $value.parent().append(`<div class="alert alert-warning">${escapeHTML(errormessage)}</div>`) console.warn(errormessage) } }) @@ -408,20 +416,20 @@ export function finishView (view) { svg[0].setAttribute('preserveAspectRatio', 'xMidYMid meet') } catch (err) { $value.unwrap() - $value.parent().append('<div class="alert alert-warning">' + err + '</div>') + $value.parent().append(`<div class="alert alert-warning">${escapeHTML(err)}</div>`) console.warn(err) } }) - // image href new window(emoji not included) + // 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 + // if it's already wrapped by link, then ignore const $value = $(value) $value[0].onload = e => { if (window.viewAjaxCallback) window.viewAjaxCallback() } }) - // blockquote + // blockquote const blockquote = view.find('blockquote.raw').removeClass('raw') const blockquoteP = blockquote.find('p') blockquoteP.each((key, value) => { @@ -429,117 +437,96 @@ export function finishView (view) { html = replaceExtraTags(html) $(value).html(html) }) - // color tag in blockquote will change its left border color + // 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 + // 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 = $('<div class="inner"></div>').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() - } - }) - }) + .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 = $('<div class="inner"></div>').append(iframe) + const height = iframe.attr('height') + const width = iframe.attr('width') + const ratio = (height / width) * 100 + inner.css('padding-bottom', `${ratio}%`) + $(value).html(inner).append(caption) + if (window.viewAjaxCallback) window.viewAjaxCallback() + } + }) + }) // speakerdeck view.find('div.speakerdeck.raw').removeClass('raw') - .each((key, value) => { - const url = `https://speakerdeck.com/oembed.json?url=https%3A%2F%2Fspeakerdeck.com%2F${encodeURIComponent($(value).attr('data-speakerdeckid'))}` - // use yql because speakerdeck not support jsonp - $.ajax({ - url: 'https://query.yahooapis.com/v1/public/yql', - data: { - q: `select * from json where url ='${url}'`, - format: 'json' - }, - dataType: 'jsonp', - success (data) { - if (!data.query || !data.query.results) return - const json = data.query.results.json - const html = json.html - var ratio = json.height / json.width - $(value).html(html) - const iframe = $(value).children('iframe') - const src = iframe.attr('src') - if (src.indexOf('//') === 0) { iframe.attr('src', `https:${src}`) } - const inner = $('<div class="inner"></div>').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() - } - }) - }) + .each((key, value) => { + const url = `https://speakerdeck.com/${$(value).attr('data-speakerdeckid')}` + const inner = $('<a>Speakerdeck</a>') + inner.attr('href', url) + inner.attr('rel', 'noopener noreferrer') + inner.attr('target', '_blank') + $(value).append(inner) + }) // pdf view.find('div.pdf.raw').removeClass('raw') - .each(function (key, value) { - const url = $(value).attr('data-pdfurl') - const inner = $('<div></div>') - $(this).append(inner) - PDFObject.embed(url, inner, { - height: '400px' - }) - }) + .each(function (key, value) { + const url = $(value).attr('data-pdfurl') + const inner = $('<div></div>') + $(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' || reallang === 'gherkin') { - 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 if (reallang === 'cmake') { - code = S(code).unescapeHTML().s - result = { - value: Prism.highlight(code, Prism.languages.makefile) - } - } 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) + .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' || reallang === 'gherkin') { + 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 if (reallang === 'cmake') { + code = S(code).unescapeHTML().s + result = { + value: Prism.highlight(code, Prism.languages.makefile) + } + } 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 { @@ -553,7 +540,7 @@ export function finishView (view) { } catch (err) { console.warn(err) } - // render title + // render title document.title = renderTitle(view) } @@ -589,7 +576,7 @@ export function postProcess (code) { if (warning && warning.length > 0) { warning.text(md.metaError) } else { - warning = $('<div id="meta-error" class="alert alert-warning">' + md.metaError + '</div>') + warning = $(`<div id="meta-error" class="alert alert-warning">${escapeHTML(md.metaError)}</div>`) result.prepend(warning) } } @@ -613,23 +600,23 @@ window.removeDOMEvents = removeDOMEvents function generateCleanHTML (view) { const src = view.clone() const eles = src.find('*') - // remove syncscroll parts + // 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 + // remove gist content src.find('code[data-gist-id]').children().remove() - // disable todo list + // disable todo list src.find('input.task-list-item-checkbox').attr('disabled', '') - // replace emoji image path + // 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://cdnjs.cloudflare.com/ajax/libs/emojify.js/1.1.0/images/basic/${name}.png`) }) - // replace video to iframe + // replace video to iframe src.find('div[data-videoid]').each((key, value) => { const id = $(value).attr('data-videoid') const style = $(value).attr('style') @@ -665,12 +652,12 @@ export function exportToHTML (view) { const title = renderTitle(ui.area.markdown) const filename = `${renderFilename(ui.area.markdown)}.html` const src = generateCleanHTML(view) - // generate toc + // 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 + // generate html via template $.get(`${serverurl}/build/html.min.css`, css => { $.get(`${serverurl}/views/html.hbs`, data => { const template = window.Handlebars.compile(data) @@ -685,7 +672,6 @@ export function exportToHTML (view) { 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' }) @@ -800,20 +786,20 @@ export function smoothHashScroll () { const hash = element.hash if (hash) { $element.on('click', function (e) { - // store hash + // store hash const hash = decodeURIComponent(this.hash) - // escape special characters in jquery selector + // escape special characters in jquery selector const $hash = $(hash.replace(/(:|\.|\[|\]|,)/g, '\\$1')) - // return if no element been selected + // return if no element been selected if ($hash.length <= 0) return - // prevent default anchor click behavior + // prevent default anchor click behavior e.preventDefault() - // animate + // animate $('body, html').stop(true, true).animate({ scrollTop: $hash.offset().top }, 100, 'linear', () => { - // when done, add hash to url - // (default click behaviour) + // when done, add hash to url + // (default click behaviour) window.location.hash = hash }) }) @@ -955,9 +941,6 @@ function highlightRender (code, lang) { return result.value } -import markdownit from 'markdown-it' -import markdownitContainer from 'markdown-it-container' - export let md = markdownit('default', { html: true, breaks: true, @@ -1055,109 +1038,106 @@ md.renderer.rules.fence = (tokens, idx, options, env, self) => { return `<pre><code${self.renderAttrs(token)}>${highlighted}</code></pre>\n` } -/* Defined regex markdown it plugins */ -import Plugin from 'markdown-it-regexp' - // 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 class="youtube raw"></div>') - div.attr('data-videoid', videoid) - const thumbnailSrc = `//img.youtube.com/vi/${videoid}/hqdefault.jpg` - const image = `<img src="${thumbnailSrc}" />` - div.append(image) - const icon = '<i class="icon fa fa-youtube-play fa-5x"></i>' - div.append(icon) - return div[0].outerHTML - } + // regexp to match + /{%youtube\s*([\d\D]*?)\s*%}/, + + (match, utils) => { + const videoid = match[1] + if (!videoid) return + const div = $('<div class="youtube raw"></div>') + div.attr('data-videoid', videoid) + const thumbnailSrc = `//img.youtube.com/vi/${videoid}/hqdefault.jpg` + const image = `<img src="${thumbnailSrc}" />` + div.append(image) + const icon = '<i class="icon fa fa-youtube-play fa-5x"></i>' + div.append(icon) + return div[0].outerHTML + } ) // 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 class="vimeo raw"></div>') - div.attr('data-videoid', videoid) - const icon = '<i class="icon fa fa-vimeo-square fa-5x"></i>' - div.append(icon) - return div[0].outerHTML - } + // regexp to match + /{%vimeo\s*([\d\D]*?)\s*%}/, + + (match, utils) => { + const videoid = match[1] + if (!videoid) return + const div = $('<div class="vimeo raw"></div>') + div.attr('data-videoid', videoid) + const icon = '<i class="icon fa fa-vimeo-square fa-5x"></i>' + div.append(icon) + return div[0].outerHTML + } ) // gist const gistPlugin = new Plugin( - // regexp to match - /{%gist\s*([\d\D]*?)\s*%}/, + // regexp to match + /{%gist\s*([\d\D]*?)\s*%}/, - (match, utils) => { - const gistid = match[1] - const code = `<code data-gist-id="${gistid}"></code>` - return code - } + (match, utils) => { + const gistid = match[1] + const code = `<code data-gist-id="${gistid}"></code>` + return code + } ) // TOC const tocPlugin = new Plugin( - // regexp to match - /^\[TOC\]$/i, + // regexp to match + /^\[TOC\]$/i, - (match, utils) => '<div class="toc"></div>' + (match, utils) => '<div class="toc"></div>' ) // slideshare const slidesharePlugin = new Plugin( - // regexp to match - /{%slideshare\s*([\d\D]*?)\s*%}/, - - (match, utils) => { - const slideshareid = match[1] - const div = $('<div class="slideshare raw"></div>') - div.attr('data-slideshareid', slideshareid) - return div[0].outerHTML - } + // regexp to match + /{%slideshare\s*([\d\D]*?)\s*%}/, + + (match, utils) => { + const slideshareid = match[1] + const div = $('<div class="slideshare raw"></div>') + div.attr('data-slideshareid', slideshareid) + return div[0].outerHTML + } ) // speakerdeck const speakerdeckPlugin = new Plugin( - // regexp to match - /{%speakerdeck\s*([\d\D]*?)\s*%}/, - - (match, utils) => { - const speakerdeckid = match[1] - const div = $('<div class="speakerdeck raw"></div>') - div.attr('data-speakerdeckid', speakerdeckid) - return div[0].outerHTML - } + // regexp to match + /{%speakerdeck\s*([\d\D]*?)\s*%}/, + + (match, utils) => { + const speakerdeckid = match[1] + const div = $('<div class="speakerdeck raw"></div>') + div.attr('data-speakerdeckid', speakerdeckid) + return div[0].outerHTML + } ) // 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 class="pdf raw"></div>') - div.attr('data-pdfurl', pdfurl) - return div[0].outerHTML - } + // regexp to match + /{%pdf\s*([\d\D]*?)\s*%}/, + + (match, utils) => { + const pdfurl = match[1] + if (!isValidURL(pdfurl)) return match[0] + const div = $('<div class="pdf raw"></div>') + div.attr('data-pdfurl', pdfurl) + return div[0].outerHTML + } ) const emojijsPlugin = new Plugin( - // regexp to match emoji shortcodes :something: - // We generate an universal regex that guaranteed only contains the - // emojies we have available. This should prevent all false-positives - new RegExp(':(' + window.emojify.emojiNames.map((item) => { return RegExp.escape(item) }).join('|') + '):', 'i'), - - (match, utils) => { - const emoji = match[1].toLowerCase() - const div = $(`<img class="emoji" src="${serverurl}/build/emojify.js/dist/images/basic/${emoji}.png"></img>`) - return div[0].outerHTML - } + // regexp to match emoji shortcodes :something: + // We generate an universal regex that guaranteed only contains the + // emojies we have available. This should prevent all false-positives + new RegExp(':(' + window.emojify.emojiNames.map((item) => { return RegExp.escape(item) }).join('|') + '):', 'i'), + + (match, utils) => { + const emoji = match[1].toLowerCase() + const div = $(`<img class="emoji" alt=":${emoji}:" src="${serverurl}/build/emojify.js/dist/images/basic/${emoji}.png"></img>`) + return div[0].outerHTML + } ) // yaml meta, from https://github.com/eugeneware/remarkable-meta diff --git a/public/js/history.js b/public/js/history.js index 6007bef4..e0154185 100644 --- a/public/js/history.js +++ b/public/js/history.js @@ -1,9 +1,11 @@ /* eslint-env browser, jquery */ +/* eslint no-console: ["error", { allow: ["warn", "error", "debug"] }] */ /* global serverurl, moment */ import store from 'store' import S from 'string' import LZString from 'lz-string' +import url from 'wurl' import { checkNoteIdValid, @@ -11,11 +13,11 @@ import { } from './utils' import { - checkIfAuth + checkIfAuth } from './lib/common/login' import { - urlpath + urlpath } from './lib/config' window.migrateHistoryFromTempCallback = null @@ -23,44 +25,44 @@ window.migrateHistoryFromTempCallback = null migrateHistoryFromTemp() function migrateHistoryFromTemp () { - if (window.url('#tempid')) { + if (url('#tempid')) { $.get(`${serverurl}/temp`, { - tempid: window.url('#tempid') + tempid: url('#tempid') }) - .done(data => { - if (data && data.temp) { - getStorageHistory(olddata => { - if (!olddata || olddata.length === 0) { - saveHistoryToStorage(JSON.parse(data.temp)) + .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-- } - }) - } - }) - .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() } - }) + hash = hash.join('&') + location.hash = hash + if (window.migrateHistoryFromTempCallback) { window.migrateHistoryFromTempCallback() } + }) } } export function saveHistory (notehistory) { checkIfAuth( - () => { - saveHistoryToServer(notehistory) - }, - () => { - saveHistoryToStorage(notehistory) - } - ) + () => { + saveHistoryToServer(notehistory) + }, + () => { + saveHistoryToStorage(notehistory) + } + ) } function saveHistoryToStorage (notehistory) { @@ -79,9 +81,9 @@ export function saveStorageHistoryToServer (callback) { $.post(`${serverurl}/history`, { history: data }) - .done(data => { - callback(data) - }) + .done(data => { + callback(data) + }) } } @@ -108,7 +110,7 @@ export function clearDuplicatedHistory (notehistory) { } function addHistory (id, text, time, tags, pinned, notehistory) { - // only add when note id exists + // only add when note id exists if (id) { notehistory.push({ id, @@ -134,14 +136,14 @@ export function removeHistory (id, notehistory) { // 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) - } - ) + () => { + // no need to do this anymore, this will count from server-side + // writeHistoryToServer(title, tags); + }, + () => { + writeHistoryToStorage(title, tags) + } + ) } function writeHistoryToStorage (title, tags) { @@ -162,7 +164,7 @@ if (!Array.isArray) { } function renderHistory (title, tags) { - // console.debug(tags); + // console.debug(tags); const id = urlpath ? location.pathname.slice(urlpath.length + 1, location.pathname.length).split('/')[1] : location.pathname.split('/')[1] return { id, @@ -174,7 +176,7 @@ function renderHistory (title, tags) { function generateHistory (title, tags, notehistory) { const info = renderHistory(title, tags) - // keep any pinned data + // keep any pinned data let pinned = false for (let i = 0; i < notehistory.length; i++) { if (notehistory[i].id === info.id && notehistory[i].pinned) { @@ -191,25 +193,25 @@ function generateHistory (title, tags, notehistory) { // used for outer export function getHistory (callback) { checkIfAuth( - () => { - getServerHistory(callback) - }, - () => { - getStorageHistory(callback) - } - ) + () => { + 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) - }) + .done(data => { + if (data.history) { + callback(data.history) + } + }) + .fail((xhr, status, error) => { + console.error(xhr.responseText) + }) } export function getStorageHistory (callback) { @@ -224,25 +226,25 @@ export function getStorageHistory (callback) { export function parseHistory (list, callback) { checkIfAuth( - () => { - parseServerToHistory(list, callback) - }, - () => { - parseStorageToHistory(list, callback) - } - ) + () => { + 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) - }) + .done(data => { + if (data.history) { + parseToHistory(list, data.history, callback) + } + }) + .fail((xhr, status, error) => { + console.error(xhr.responseText) + }) } export function parseStorageToHistory (list, callback) { @@ -268,15 +270,15 @@ function parseToHistory (list, notehistory, callback) { } catch (err) { console.error(err) } - // parse time to timestamp and fromNow + // 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 + // 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(',') : [] - // add to list + // add to list if (notehistory[i].id && list.get('id', notehistory[i].id).length === 0) { list.add(notehistory[i]) } } } diff --git a/public/js/index.js b/public/js/index.js index a845b5d3..6ba174a5 100644 --- a/public/js/index.js +++ b/public/js/index.js @@ -1,16 +1,7 @@ /* 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('../vendor/showup/showup') - -require('../css/index.css') -require('../css/extra.css') -require('../css/slide-preview.css') -require('../css/site.css') - -require('highlight.js/styles/github-gist.css') +/* eslint no-console: ["error", { allow: ["warn", "error", "debug"] }] */ +/* global CodeMirror, Cookies, moment, Spinner, Idle, serverurl, + key, Dropbox, ot, hex2rgb, Visibility */ import TurndownService from 'turndown' @@ -18,61 +9,62 @@ import { saveAs } from 'file-saver' import randomColor from 'randomcolor' import store from 'store' import hljs from 'highlight.js' +import url from 'wurl' import _ from 'lodash' import List from 'list.js' import { - checkLoginStateChanged, - setloginStateChangeEvent + checkLoginStateChanged, + setloginStateChangeEvent } from './lib/common/login' import { - debug, - DROPBOX_APP_KEY, - noteid, - noteurl, - urlpath, - version + debug, + DROPBOX_APP_KEY, + noteid, + noteurl, + urlpath, + version } from './lib/config' import { - autoLinkify, - deduplicatedHeaderId, - exportToHTML, - exportToRawHTML, - removeDOMEvents, - finishView, - generateToc, - isValidURL, - md, - parseMeta, - postProcess, - renderFilename, - renderTOC, - renderTags, - renderTitle, - scrollToHash, - smoothHashScroll, - updateLastChange, - updateLastChangeUser, - updateOwner + autoLinkify, + deduplicatedHeaderId, + exportToHTML, + exportToRawHTML, + removeDOMEvents, + finishView, + generateToc, + isValidURL, + md, + parseMeta, + postProcess, + renderFilename, + renderTOC, + renderTags, + renderTitle, + scrollToHash, + smoothHashScroll, + updateLastChange, + updateLastChangeUser, + updateOwner } from './extra' import { - clearMap, - setupSyncAreas, - syncScrollToEdit, - syncScrollToView + clearMap, + setupSyncAreas, + syncScrollToEdit, + syncScrollToView } from './lib/syncscroll' import { - writeHistory, - deleteServerHistory, - getHistory, - saveHistory, - removeHistory + writeHistory, + deleteServerHistory, + getHistory, + saveHistory, + removeHistory } from './history' import { preventXSS } from './render' @@ -83,6 +75,15 @@ import getUIElements from './lib/editor/ui-elements' import modeType from './lib/modeType' import appState from './lib/appState' +require('../vendor/showup/showup') + +require('../css/index.css') +require('../css/extra.css') +require('../css/slide-preview.css') +require('../css/site.css') + +require('highlight.js/styles/github-gist.css') + var defaultTextHeight = 20 var viewportMargin = 20 var defaultEditorMode = 'gfm' @@ -305,7 +306,6 @@ var editor = editorInstance.init(textit) // FIXME: global referncing in jquery-textcomplete patch window.editor = editor -var inlineAttach = inlineAttachment.editors.codemirror4.attach(editor) defaultTextHeight = parseInt($('.CodeMirror').css('line-height')) // initalize ui reference @@ -419,7 +419,7 @@ Visibility.change(function (e, state) { $(document).ready(function () { idle.checkAway() checkResponsive() - // if in smaller screen, we don't need advanced scrollbar + // if in smaller screen, we don't need advanced scrollbar var scrollbarStyle if (visibleXS) { scrollbarStyle = 'native' @@ -439,12 +439,12 @@ $(document).ready(function () { if (isTouchDevice) { /* bind events */ $(document) - .on('focus', 'textarea, input', function () { - $body.addClass('fixfixed') - }) - .on('blur', 'textarea, input', function () { - $body.removeClass('fixfixed') - }) + .on('focus', 'textarea, input', function () { + $body.addClass('fixfixed') + }) + .on('blur', 'textarea, input', function () { + $body.removeClass('fixfixed') + }) } // Re-enable nightmode @@ -669,14 +669,14 @@ function checkEditorScrollbarInner () { } function checkTocStyle () { - // toc right + // 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 + // affix toc left var newbool var rightMargin = (ui.area.markdown.parent().outerWidth() - ui.area.markdown.outerWidth()) / 2 - // for ipad or wider device + // for ipad or wider device if (rightMargin >= 133) { newbool = true var affixLeftMargin = (ui.toc.affix.outerWidth() - ui.toc.affix.width()) / 2 @@ -763,7 +763,7 @@ function toggleMode () { var lastMode = null function changeMode (type) { - // lock navbar to prevent it hide after changeMode + // lock navbar to prevent it hide after changeMode lockNavbar() saveInfo() if (type) { @@ -802,7 +802,6 @@ function changeMode (type) { editor.getInputField().blur() } if (appState.currentMode === modeType.edit || appState.currentMode === modeType.both) { - ui.toolbar.uploadImage.fadeIn() // add and update status bar if (!editorInstance.statusBar) { editorInstance.addStatusBar() @@ -815,8 +814,6 @@ function changeMode (type) { // work around foldGutter might not init properly editor.setOption('foldGutter', false) editor.setOption('foldGutter', true) - } else { - ui.toolbar.uploadImage.fadeOut() } if (appState.currentMode !== modeType.edit) { $(document.body).css('background-color', 'white') @@ -824,7 +821,7 @@ function changeMode (type) { } else { $(document.body).css('background-color', ui.area.codemirror.css('background-color')) } - // check resizable editor style + // check resizable editor style if (appState.currentMode === modeType.both) { if (lastEditorWidth > 0) { ui.area.edit.css('width', lastEditorWidth + 'px') @@ -902,13 +899,13 @@ function showMessageModal (title, header, href, text, success) { // check if dropbox app key is set and load scripts if (DROPBOX_APP_KEY) { $('<script>') - .attr('type', 'text/javascript') - .attr('src', 'https://www.dropbox.com/static/api/2/dropins.js') - .attr('id', 'dropboxjs') - .attr('data-app-key', DROPBOX_APP_KEY) - .prop('async', true) - .prop('defer', true) - .appendTo('body') + .attr('type', 'text/javascript') + .attr('src', 'https://www.dropbox.com/static/api/2/dropins.js') + .attr('id', 'dropboxjs') + .attr('data-app-key', DROPBOX_APP_KEY) + .prop('async', true) + .prop('defer', true) + .appendTo('body') } else { ui.toolbar.import.dropbox.hide() ui.toolbar.export.dropbox.hide() @@ -968,35 +965,35 @@ ui.toolbar.export.gist.attr('href', noteurl + '/gist') ui.toolbar.export.snippet.click(function () { ui.spinner.show() $.get(serverurl + '/auth/gitlab/callback/' + noteid + '/projects') - .done(function (data) { - $('#snippetExportModalAccessToken').val(data.accesstoken) - $('#snippetExportModalBaseURL').val(data.baseURL) - $('#snippetExportModalVersion').val(data.version) - $('#snippetExportModalLoading').hide() - $('#snippetExportModal').modal('toggle') - $('#snippetExportModalProjects').find('option').remove().end().append('<option value="init" selected="selected" disabled="disabled">Select From Available Projects</option>') - if (data.projects) { - data.projects.sort(function (a, b) { - return (a.path_with_namespace < b.path_with_namespace) ? -1 : ((a.path_with_namespace > b.path_with_namespace) ? 1 : 0) - }) - data.projects.forEach(function (project) { - if (!project.snippets_enabled || + .done(function (data) { + $('#snippetExportModalAccessToken').val(data.accesstoken) + $('#snippetExportModalBaseURL').val(data.baseURL) + $('#snippetExportModalVersion').val(data.version) + $('#snippetExportModalLoading').hide() + $('#snippetExportModal').modal('toggle') + $('#snippetExportModalProjects').find('option').remove().end().append('<option value="init" selected="selected" disabled="disabled">Select From Available Projects</option>') + if (data.projects) { + data.projects.sort(function (a, b) { + return (a.path_with_namespace < b.path_with_namespace) ? -1 : ((a.path_with_namespace > b.path_with_namespace) ? 1 : 0) + }) + data.projects.forEach(function (project) { + if (!project.snippets_enabled || (project.permissions.project_access === null && project.permissions.group_access === null) || (project.permissions.project_access !== null && project.permissions.project_access.access_level < 20)) { - return - } - $('<option>').val(project.id).text(project.path_with_namespace).appendTo('#snippetExportModalProjects') - }) - $('#snippetExportModalProjects').prop('disabled', false) + return } - $('#snippetExportModalLoading').hide() - }) - .fail(function (data) { - showMessageModal('<i class="fa fa-gitlab"></i> Import from Snippet', 'Unable to fetch gitlab parameters :(', '', '', false) - }) - .always(function () { - ui.spinner.hide() + $('<option>').val(project.id).text(project.path_with_namespace).appendTo('#snippetExportModalProjects') }) + $('#snippetExportModalProjects').prop('disabled', false) + } + $('#snippetExportModalLoading').hide() + }) + .fail(function (data) { + showMessageModal('<i class="fa fa-gitlab"></i> Import from Snippet', 'Unable to fetch gitlab parameters :(', '', '', false) + }) + .always(function () { + ui.spinner.hide() + }) }) // import from dropbox ui.toolbar.import.dropbox.click(function () { @@ -1014,54 +1011,43 @@ ui.toolbar.import.dropbox.click(function () { }) // import from gist ui.toolbar.import.gist.click(function () { - // na + // na }) // import from snippet ui.toolbar.import.snippet.click(function () { ui.spinner.show() $.get(serverurl + '/auth/gitlab/callback/' + noteid + '/projects') - .done(function (data) { - $('#snippetImportModalAccessToken').val(data.accesstoken) - $('#snippetImportModalBaseURL').val(data.baseURL) - $('#snippetImportModalVersion').val(data.version) - $('#snippetImportModalContent').prop('disabled', false) - $('#snippetImportModalConfirm').prop('disabled', false) - $('#snippetImportModalLoading').hide() - $('#snippetImportModal').modal('toggle') - $('#snippetImportModalProjects').find('option').remove().end().append('<option value="init" selected="selected" disabled="disabled">Select From Available Projects</option>') - if (data.projects) { - data.projects.sort(function (a, b) { - return (a.path_with_namespace < b.path_with_namespace) ? -1 : ((a.path_with_namespace > b.path_with_namespace) ? 1 : 0) - }) - data.projects.forEach(function (project) { - if (!project.snippets_enabled || + .done(function (data) { + $('#snippetImportModalAccessToken').val(data.accesstoken) + $('#snippetImportModalBaseURL').val(data.baseURL) + $('#snippetImportModalVersion').val(data.version) + $('#snippetImportModalContent').prop('disabled', false) + $('#snippetImportModalConfirm').prop('disabled', false) + $('#snippetImportModalLoading').hide() + $('#snippetImportModal').modal('toggle') + $('#snippetImportModalProjects').find('option').remove().end().append('<option value="init" selected="selected" disabled="disabled">Select From Available Projects</option>') + if (data.projects) { + data.projects.sort(function (a, b) { + return (a.path_with_namespace < b.path_with_namespace) ? -1 : ((a.path_with_namespace > b.path_with_namespace) ? 1 : 0) + }) + data.projects.forEach(function (project) { + if (!project.snippets_enabled || (project.permissions.project_access === null && project.permissions.group_access === null) || (project.permissions.project_access !== null && project.permissions.project_access.access_level < 20)) { - return - } - $('<option>').val(project.id).text(project.path_with_namespace).appendTo('#snippetImportModalProjects') - }) - $('#snippetImportModalProjects').prop('disabled', false) + return } - $('#snippetImportModalLoading').hide() - }) - .fail(function (data) { - showMessageModal('<i class="fa fa-gitlab"></i> Import from Snippet', 'Unable to fetch gitlab parameters :(', '', '', false) + $('<option>').val(project.id).text(project.path_with_namespace).appendTo('#snippetImportModalProjects') }) - .always(function () { - ui.spinner.hide() - }) -}) -// import from clipboard -ui.toolbar.import.clipboard.click(function () { - // na -}) -// upload image -ui.toolbar.uploadImage.bind('change', function (e) { - var files = e.target.files || e.dataTransfer.files - e.dataTransfer = {} - e.dataTransfer.files = files - inlineAttach.onDrop(e) + $('#snippetImportModalProjects').prop('disabled', false) + } + $('#snippetImportModalLoading').hide() + }) + .fail(function (data) { + showMessageModal('<i class="fa fa-gitlab"></i> Import from Snippet', 'Unable to fetch gitlab parameters :(', '', '', false) + }) + .always(function () { + ui.spinner.hide() + }) }) // toc ui.toc.dropdown.click(function (e) { @@ -1084,18 +1070,19 @@ var revision = null var revisionTime = null ui.modal.revision.on('show.bs.modal', function (e) { $.get(noteurl + '/revision') - .done(function (data) { - parseRevisions(data.revision) - initRevisionViewer() - }) - .fail(function (err) { - if (debug) { - console.log(err) - } - }) - .always(function () { - // na - }) + .done(function (data) { + parseRevisions(data.revision) + initRevisionViewer() + }) + .fail(function (err) { + if (debug) { + // eslint-disable-next-line no-console + console.debug(err) + } + }) + .always(function () { + // na + }) }) function checkRevisionViewer () { if (revisionViewer) { @@ -1138,74 +1125,75 @@ function parseRevisions (_revisions) { function selectRevision (time) { if (time === revisionTime) return $.get(noteurl + '/revision/' + time) - .done(function (data) { - revision = data - revisionTime = time - var lastScrollInfo = revisionViewer.getScrollInfo() - revisionList.children().removeClass('active') - revisionList.find('[data-revision-time="' + time + '"]').addClass('active') - var content = revision.content - revisionViewer.setValue(content) - revisionViewer.scrollTo(null, lastScrollInfo.top) - revisionInsert = [] - revisionDelete = [] - // mark the text which have been insert or delete - if (revision.patch.length > 0) { - var bias = 0 - for (var j = 0; j < revision.patch.length; j++) { - var patch = revision.patch[j] - var currIndex = patch.start1 + bias - for (var i = 0; i < patch.diffs.length; i++) { - var diff = patch.diffs[i] - // ignore if diff only contains line breaks - if ((diff[1].match(/\n/g) || []).length === diff[1].length) continue - var prePos - var postPos - switch (diff[0]) { - case 0: // retain - currIndex += diff[1].length - break - case 1: // insert - prePos = revisionViewer.posFromIndex(currIndex) - postPos = revisionViewer.posFromIndex(currIndex + diff[1].length) - revisionInsert.push({ - from: prePos, - to: postPos - }) - revisionViewer.markText(prePos, postPos, { - css: 'background-color: rgba(230,255,230,0.7); text-decoration: underline;' - }) - currIndex += diff[1].length - break - case -1: // delete - prePos = revisionViewer.posFromIndex(currIndex) - revisionViewer.replaceRange(diff[1], prePos) - postPos = revisionViewer.posFromIndex(currIndex + diff[1].length) - revisionDelete.push({ - from: prePos, - to: postPos - }) - revisionViewer.markText(prePos, postPos, { - css: 'background-color: rgba(255,230,230,0.7); text-decoration: line-through;' - }) - bias += diff[1].length - currIndex += diff[1].length - break - } - } + .done(function (data) { + revision = data + revisionTime = time + var lastScrollInfo = revisionViewer.getScrollInfo() + revisionList.children().removeClass('active') + revisionList.find('[data-revision-time="' + time + '"]').addClass('active') + var content = revision.content + revisionViewer.setValue(content) + revisionViewer.scrollTo(null, lastScrollInfo.top) + revisionInsert = [] + revisionDelete = [] + // mark the text which have been insert or delete + if (revision.patch.length > 0) { + var bias = 0 + for (var j = 0; j < revision.patch.length; j++) { + var patch = revision.patch[j] + var currIndex = patch.start1 + bias + for (var i = 0; i < patch.diffs.length; i++) { + var diff = patch.diffs[i] + // ignore if diff only contains line breaks + if ((diff[1].match(/\n/g) || []).length === diff[1].length) continue + var prePos + var postPos + switch (diff[0]) { + case 0: // retain + currIndex += diff[1].length + break + case 1: // insert + prePos = revisionViewer.posFromIndex(currIndex) + postPos = revisionViewer.posFromIndex(currIndex + diff[1].length) + revisionInsert.push({ + from: prePos, + to: postPos + }) + revisionViewer.markText(prePos, postPos, { + css: 'background-color: rgba(230,255,230,0.7); text-decoration: underline;' + }) + currIndex += diff[1].length + break + case -1: // delete + prePos = revisionViewer.posFromIndex(currIndex) + revisionViewer.replaceRange(diff[1], prePos) + postPos = revisionViewer.posFromIndex(currIndex + diff[1].length) + revisionDelete.push({ + from: prePos, + to: postPos + }) + revisionViewer.markText(prePos, postPos, { + css: 'background-color: rgba(255,230,230,0.7); text-decoration: line-through;' + }) + bias += diff[1].length + currIndex += diff[1].length + break } } - revisionInsertAnnotation.update(revisionInsert) - revisionDeleteAnnotation.update(revisionDelete) - }) - .fail(function (err) { - if (debug) { - console.log(err) - } - }) - .always(function () { - // na - }) + } + } + revisionInsertAnnotation.update(revisionInsert) + revisionDeleteAnnotation.update(revisionDelete) + }) + .fail(function (err) { + if (debug) { + // eslint-disable-next-line no-console + console.debug(err) + } + }) + .always(function () { + // na + }) } function initRevisionViewer () { if (revisionViewer) return @@ -1250,22 +1238,23 @@ ui.modal.snippetImportProjects.change(function () { $('#snippetImportModalLoading').show() $('#snippetImportModalContent').val('/projects/' + project) $.get(baseURL + '/api/' + version + '/projects/' + project + '/snippets?access_token=' + accesstoken) - .done(function (data) { - $('#snippetImportModalSnippets').find('option').remove().end().append('<option value="init" selected="selected" disabled="disabled">Select From Available Snippets</option>') - data.forEach(function (snippet) { - $('<option>').val(snippet.id).text(snippet.title).appendTo($('#snippetImportModalSnippets')) - }) - $('#snippetImportModalLoading').hide() - $('#snippetImportModalSnippets').prop('disabled', false) - }) - .fail(function (err) { - if (debug) { - console.log(err) - } - }) - .always(function () { - // na - }) + .done(function (data) { + $('#snippetImportModalSnippets').find('option').remove().end().append('<option value="init" selected="selected" disabled="disabled">Select From Available Snippets</option>') + data.forEach(function (snippet) { + $('<option>').val(snippet.id).text(snippet.title).appendTo($('#snippetImportModalSnippets')) + }) + $('#snippetImportModalLoading').hide() + $('#snippetImportModalSnippets').prop('disabled', false) + }) + .fail(function (err) { + if (debug) { + // eslint-disable-next-line no-console + console.debug(err) + } + }) + .always(function () { + // na + }) }) // snippet snippets ui.modal.snippetImportSnippets.change(function () { @@ -1325,8 +1314,8 @@ function generateScrollspy () { ui.toc.affix.hide() ui.toc.toc.show() } - // $(document.body).scroll(); - // ui.area.view.scroll(); + // $(document.body).scroll(); + // ui.area.view.scroll(); } function updateScrollspy () { @@ -1336,10 +1325,10 @@ function updateScrollspy () { headerMap.push($(headers[i]).offset().top - parseInt($(headers[i]).css('margin-top'))) } applyScrollspyActive($(window).scrollTop(), headerMap, headers, - $('.scrollspy-body'), 0) + $('.scrollspy-body'), 0) var offset = ui.area.view.scrollTop() - ui.area.view.offset().top applyScrollspyActive(ui.area.view.scrollTop(), headerMap, headers, - $('.scrollspy-view'), offset - 10) + $('.scrollspy-view'), offset - 10) } function applyScrollspyActive (top, headerMap, headers, target, offset) { @@ -1389,32 +1378,32 @@ $('#gistImportModalConfirm').click(function () { if (!isValidURL(gisturl)) { showMessageModal('<i class="fa fa-github"></i> Import from Gist', 'Not a valid URL :(', '', '', false) } else { - var hostname = window.url('hostname', gisturl) + var hostname = url('hostname', gisturl) if (hostname !== 'gist.github.com') { showMessageModal('<i class="fa fa-github"></i> Import from Gist', 'Not a valid Gist URL :(', '', '', false) } else { ui.spinner.show() - $.get('https://api.github.com/gists/' + window.url('-1', gisturl)) - .done(function (data) { - if (data.files) { - var contents = '' - Object.keys(data.files).forEach(function (key) { - contents += key - contents += '\n---\n' - contents += data.files[key].content - contents += '\n\n' - }) - replaceAll(contents) - } else { - showMessageModal('<i class="fa fa-github"></i> Import from Gist', 'Unable to fetch gist files :(', '', '', false) - } - }) - .fail(function (data) { - showMessageModal('<i class="fa fa-github"></i> Import from Gist', 'Not a valid Gist URL :(', '', JSON.stringify(data), false) - }) - .always(function () { - ui.spinner.hide() - }) + $.get('https://api.github.com/gists/' + url('-1', gisturl)) + .done(function (data) { + if (data.files) { + var contents = '' + Object.keys(data.files).forEach(function (key) { + contents += key + contents += '\n---\n' + contents += data.files[key].content + contents += '\n\n' + }) + replaceAll(contents) + } else { + showMessageModal('<i class="fa fa-github"></i> Import from Gist', 'Unable to fetch gist files :(', '', '', false) + } + }) + .fail(function (data) { + showMessageModal('<i class="fa fa-github"></i> Import from Gist', 'Not a valid Gist URL :(', '', JSON.stringify(data), false) + }) + .always(function () { + ui.spinner.hide() + }) } } }) @@ -1438,34 +1427,34 @@ $('#snippetImportModalConfirm').click(function () { var accessToken = '?access_token=' + $('#snippetImportModalAccessToken').val() var fullURL = $('#snippetImportModalBaseURL').val() + '/api/' + $('#snippetImportModalVersion').val() + snippeturl $.get(fullURL + accessToken) - .done(function (data) { - var content = '# ' + (data.title || 'Snippet Import') - var fileInfo = data.file_name.split('.') - fileInfo[1] = (fileInfo[1]) ? fileInfo[1] : 'md' - $.get(fullURL + '/raw' + accessToken) - .done(function (raw) { - if (raw) { - content += '\n\n' - if (fileInfo[1] !== 'md') { - content += '```' + fileTypes[fileInfo[1]] + '\n' - } - content += raw - if (fileInfo[1] !== 'md') { - content += '\n```' - } - replaceAll(content) - } - }) - .fail(function (data) { - showMessageModal('<i class="fa fa-gitlab"></i> Import from Snippet', 'Not a valid Snippet URL :(', '', JSON.stringify(data), false) - }) - .always(function () { - ui.spinner.hide() - }) - }) - .fail(function (data) { - showMessageModal('<i class="fa fa-gitlab"></i> Import from Snippet', 'Not a valid Snippet URL :(', '', JSON.stringify(data), false) - }) + .done(function (data) { + var content = '# ' + (data.title || 'Snippet Import') + var fileInfo = data.file_name.split('.') + fileInfo[1] = (fileInfo[1]) ? fileInfo[1] : 'md' + $.get(fullURL + '/raw' + accessToken) + .done(function (raw) { + if (raw) { + content += '\n\n' + if (fileInfo[1] !== 'md') { + content += '```' + fileTypes[fileInfo[1]] + '\n' + } + content += raw + if (fileInfo[1] !== 'md') { + content += '\n```' + } + replaceAll(content) + } + }) + .fail(function (data) { + showMessageModal('<i class="fa fa-gitlab"></i> Import from Snippet', 'Not a valid Snippet URL :(', '', JSON.stringify(data), false) + }) + .always(function () { + ui.spinner.hide() + }) + }) + .fail(function (data) { + showMessageModal('<i class="fa fa-gitlab"></i> Import from Snippet', 'Not a valid Snippet URL :(', '', JSON.stringify(data), false) + }) } }) @@ -1487,14 +1476,14 @@ $('#snippetExportModalConfirm').click(function () { $('#snippetExportModalLoading').show() var fullURL = baseURL + '/api/' + version + '/projects/' + $('#snippetExportModalProjects').val() + '/snippets?access_token=' + accesstoken $.post(fullURL - , data - , function (ret) { - $('#snippetExportModalLoading').hide() - $('#snippetExportModal').modal('hide') - var redirect = baseURL + '/' + $("#snippetExportModalProjects option[value='" + $('#snippetExportModalProjects').val() + "']").text() + '/snippets/' + ret.id - showMessageModal('<i class="fa fa-gitlab"></i> Export to Snippet', 'Export Successful!', redirect, 'View Snippet Here', true) - } - ) + , data + , function (ret) { + $('#snippetExportModalLoading').hide() + $('#snippetExportModal').modal('hide') + var redirect = baseURL + '/' + $("#snippetExportModalProjects option[value='" + $('#snippetExportModalProjects').val() + "']").text() + '/snippets/' + ret.id + showMessageModal('<i class="fa fa-gitlab"></i> Export to Snippet', 'Export Successful!', redirect, 'View Snippet Here', true) + } + ) }) function parseToEditor (data) { @@ -1518,7 +1507,7 @@ function replaceAll (data) { } function importFromUrl (url) { - // console.log(url); + // console.debug(url); if (!url) return if (!isValidURL(url)) { showMessageModal('<i class="fa fa-cloud-download"></i> Import from URL', 'Not a valid URL :(', '', '', false) @@ -1756,7 +1745,7 @@ socket.on('disconnect', function (data) { } }) socket.on('reconnect', function (data) { - // sync back any change in offline + // sync back any change in offline emitUserStatus(true) cursorActivity(editor) socket.emit('online users') @@ -1783,7 +1772,7 @@ var authorship = [] var authorMarks = {} // temp variable var addTextMarkers = [] // temp variable function updateInfo (data) { - // console.log(data); + // console.debug(data); if (data.hasOwnProperty('createtime') && window.createtime !== data.createtime) { window.createtime = data.createtime updateLastChange() @@ -1846,7 +1835,7 @@ var addStyleRule = (function () { } }()) function updateAuthorshipInner () { - // ignore when ot not synced yet + // ignore when ot not synced yet if (havePendingOperation()) return authorMarks = {} for (let i = 0; i < authorship.length; i++) { @@ -2008,7 +1997,7 @@ editorInstance.on('update', function () { }) }) socket.on('check', function (data) { - // console.log(data); + // console.debug(data); updateInfo(data) }) socket.on('permission', function (data) { @@ -2017,13 +2006,13 @@ socket.on('permission', function (data) { var permission = null socket.on('refresh', function (data) { - // console.log(data); + // console.debug(data); editorInstance.config.docmaxlength = data.docmaxlength editor.setOption('maxLength', editorInstance.config.docmaxlength) updateInfo(data) updatePermission(data.permission) if (!window.loaded) { - // auto change mode if no content detected + // auto change mode if no content detected var nocontent = editor.getValue().length <= 0 if (nocontent) { if (visibleXS) { appState.currentMode = modeType.edit } else { appState.currentMode = modeType.both } @@ -2162,7 +2151,7 @@ socket.on('cursor focus', function (data) { } } if (data.id !== socket.id) { buildCursor(data) } - // force show + // force show var cursor = $('div[data-clientid="' + data.id + '"]') if (cursor.length > 0) { cursor.stop(true).fadeIn() @@ -2185,7 +2174,7 @@ socket.on('cursor blur', function (data) { } } if (data.id !== socket.id) { buildCursor(data) } - // force hide + // force hide var cursor = $('div[data-clientid="' + data.id + '"]') if (cursor.length > 0) { cursor.stop(true).fadeOut() @@ -2209,7 +2198,7 @@ function updateOnlineStatus () { var _onlineUsers = deduplicateOnlineUsers(onlineUsers) showStatus(statusType.online, _onlineUsers.length) var items = onlineUserList.items - // update or remove current list items + // update or remove current list items for (let i = 0; i < items.length; i++) { let found = false let foundindex = null @@ -2229,7 +2218,7 @@ function updateOnlineStatus () { shortOnlineUserList.remove('id', id) } } - // add not in list items + // add not in list items for (let i = 0; i < _onlineUsers.length; i++) { let found = false for (let j = 0; j < items.length; j++) { @@ -2243,16 +2232,16 @@ function updateOnlineStatus () { shortOnlineUserList.add(_onlineUsers[i]) } } - // sorting + // sorting sortOnlineUserList(onlineUserList) sortOnlineUserList(shortOnlineUserList) - // render list items + // render list items renderUserStatusList(onlineUserList) renderUserStatusList(shortOnlineUserList) } function sortOnlineUserList (list) { - // sort order by isSelf, login state, idle state, alphabet name, color brightness + // sort order by isSelf, login state, idle state, alphabet name, color brightness list.sort('', { sortFunction: function (a, b) { var usera = a.values() @@ -2288,7 +2277,7 @@ function renderUserStatusList (list) { var usericon = $(item.elm).find('.ui-user-icon') if (item.values().login && item.values().photo) { usericon.css('background-image', 'url(' + item.values().photo + ')') - // add 1px more to right, make it feel aligned + // add 1px more to right, make it feel aligned usericon.css('margin-right', '6px') $(item.elm).css('border-left', '4px solid ' + item.values().color) usericon.css('margin-left', '-4px') @@ -2569,15 +2558,15 @@ editorInstance.on('beforeChange', function (cm, change) { if (cmClient && !socket.connected) { cmClient.editorAdapter.ignoreNextChange = true } }) editorInstance.on('cut', function () { - // na + // na }) editorInstance.on('paste', function () { - // na + // na }) editorInstance.on('changes', function (editor, changes) { updateHistory() var docLength = editor.getValue().length - // workaround for big documents + // workaround for big documents var newViewportMargin = 20 if (docLength > 20000) { newViewportMargin = 1 @@ -2755,6 +2744,7 @@ function updateViewInner () { delete md.metaError var rendered = md.render(value) if (md.meta.type && md.meta.type === 'slide') { + ui.area.view.addClass('black') var slideOptions = { separator: '^(\r\n?|\n)---(\r\n?|\n)$', verticalSeparator: '^(\r\n?|\n)----(\r\n?|\n)$' @@ -2762,7 +2752,7 @@ function updateViewInner () { var slides = window.RevealMarkdown.slidify(editor.getValue(), slideOptions) ui.area.markdown.html(slides) window.RevealMarkdown.initialize() - // prevent XSS + // prevent XSS ui.area.markdown.html(preventXSS(ui.area.markdown.html())) ui.area.markdown.addClass('slides') appState.syncscroll = false @@ -2771,15 +2761,16 @@ function updateViewInner () { if (lastMeta.type && lastMeta.type === 'slide') { refreshView() ui.area.markdown.removeClass('slides') + ui.area.view.removeClass('black') appState.syncscroll = true checkSyncToggle() } - // only render again when meta changed + // only render again when meta changed if (JSON.stringify(md.meta) !== JSON.stringify(lastMeta)) { parseMeta(md, ui.area.codemirror, ui.area.markdown, $('#ui-toc'), $('#ui-toc-affix')) rendered = md.render(value) } - // prevent XSS + // prevent XSS rendered = preventXSS(rendered) var result = postProcess(rendered).children().toArray() partialUpdate(result, lastResult, ui.area.markdown.children().toArray()) @@ -2799,7 +2790,7 @@ function updateViewInner () { smoothHashScroll() isDirty = false clearMap() - // buildMap(); + // buildMap(); updateTitleReminder() if (postUpdateEvent && typeof postUpdateEvent === 'function') { postUpdateEvent() } } @@ -2813,7 +2804,7 @@ function updateHistoryInner () { } function updateDataAttrs (src, des) { - // sync data attr startline and endline + // sync data attr startline and endline for (var i = 0; i < src.length; i++) { copyAttribute(src[i], des[i], 'data-startline') copyAttribute(src[i], des[i], 'data-endline') @@ -2832,8 +2823,8 @@ function partialUpdate (src, tar, des) { var rawSrc = cloneAndRemoveDataAttr(src[i]) var rawTar = cloneAndRemoveDataAttr(tar[i]) if (rawSrc.outerHTML !== rawTar.outerHTML) { - // console.log(rawSrc); - // console.log(rawTar); + // console.debug(rawSrc); + // console.debug(rawTar); $(des[i]).replaceWith(src[i]) } } @@ -2841,8 +2832,8 @@ function partialUpdate (src, tar, des) { var start = 0 // find diff start position for (let i = 0; i < tar.length; i++) { - // copyAttribute(src[i], des[i], 'data-startline'); - // copyAttribute(src[i], des[i], 'data-endline'); + // copyAttribute(src[i], des[i], 'data-startline'); + // copyAttribute(src[i], des[i], 'data-endline'); let rawSrc = cloneAndRemoveDataAttr(src[i]) let rawTar = cloneAndRemoveDataAttr(tar[i]) if (!rawSrc || !rawTar || rawSrc.outerHTML !== rawTar.outerHTML) { @@ -2850,12 +2841,12 @@ function partialUpdate (src, tar, des) { break } } - // find diff end position + // find diff end position var srcEnd = 0 var tarEnd = 0 for (let i = 0; i < src.length; i++) { - // copyAttribute(src[i], des[i], 'data-startline'); - // copyAttribute(src[i], des[i], 'data-endline'); + // copyAttribute(src[i], des[i], 'data-startline'); + // copyAttribute(src[i], des[i], 'data-endline'); let rawSrc = cloneAndRemoveDataAttr(src[i]) let rawTar = cloneAndRemoveDataAttr(tar[i]) if (!rawSrc || !rawTar || rawSrc.outerHTML !== rawTar.outerHTML) { @@ -2863,12 +2854,12 @@ function partialUpdate (src, tar, des) { break } } - // tar end + // tar end for (let i = 1; i <= tar.length + 1; i++) { let srcLength = src.length let tarLength = tar.length - // copyAttribute(src[srcLength - i], des[srcLength - i], 'data-startline'); - // copyAttribute(src[srcLength - i], des[srcLength - i], 'data-endline'); + // copyAttribute(src[srcLength - i], des[srcLength - i], 'data-startline'); + // copyAttribute(src[srcLength - i], des[srcLength - i], 'data-endline'); let rawSrc = cloneAndRemoveDataAttr(src[srcLength - i]) let rawTar = cloneAndRemoveDataAttr(tar[tarLength - i]) if (!rawSrc || !rawTar || rawSrc.outerHTML !== rawTar.outerHTML) { @@ -2876,12 +2867,12 @@ function partialUpdate (src, tar, des) { break } } - // src end + // src end for (let i = 1; i <= src.length + 1; i++) { let srcLength = src.length let tarLength = tar.length - // copyAttribute(src[srcLength - i], des[srcLength - i], 'data-startline'); - // copyAttribute(src[srcLength - i], des[srcLength - i], 'data-endline'); + // copyAttribute(src[srcLength - i], des[srcLength - i], 'data-startline'); + // copyAttribute(src[srcLength - i], des[srcLength - i], 'data-endline'); let rawSrc = cloneAndRemoveDataAttr(src[srcLength - i]) let rawTar = cloneAndRemoveDataAttr(tar[tarLength - i]) if (!rawSrc || !rawTar || rawSrc.outerHTML !== rawTar.outerHTML) { @@ -2889,25 +2880,25 @@ function partialUpdate (src, tar, des) { break } } - // check if tar end overlap tar start + // check if tar end overlap tar start var overlap = 0 for (var i = start; i >= 0; i--) { var rawTarStart = cloneAndRemoveDataAttr(tar[i - 1]) var rawTarEnd = cloneAndRemoveDataAttr(tar[tarEnd + 1 + start - i]) if (rawTarStart && rawTarEnd && rawTarStart.outerHTML === rawTarEnd.outerHTML) { overlap++ } else { break } } - if (debug) { console.log('overlap:' + overlap) } - // show diff content + if (debug) { console.debug('overlap:' + overlap) } + // show diff content if (debug) { - console.log('start:' + start) - console.log('tarEnd:' + tarEnd) - console.log('srcEnd:' + srcEnd) + console.debug('start:' + start) + console.debug('tarEnd:' + tarEnd) + console.debug('srcEnd:' + srcEnd) } tarEnd += overlap srcEnd += overlap var repeatAdd = (start - srcEnd) < (start - tarEnd) var repeatDiff = Math.abs(srcEnd - tarEnd) - 1 - // push new elements + // push new elements var newElements = [] if (srcEnd >= start) { for (let j = start; j <= srcEnd; j++) { @@ -2920,7 +2911,7 @@ function partialUpdate (src, tar, des) { newElements.push(des[j].outerHTML) } } - // push remove elements + // push remove elements var removeElements = [] if (tarEnd >= start) { for (let j = start; j <= tarEnd; j++) { @@ -2933,17 +2924,17 @@ function partialUpdate (src, tar, des) { removeElements.push(des[j]) } } - // add elements + // add elements if (debug) { - console.log('ADD ELEMENTS') - console.log(newElements.join('\n')) + console.debug('ADD ELEMENTS') + console.debug(newElements.join('\n')) } if (des[start]) { $(newElements.join('')).insertBefore(des[start]) } else { $(newElements.join('')).insertAfter(des[start - 1]) } - // remove elements - if (debug) { console.log('REMOVE ELEMENTS') } + // remove elements + if (debug) { console.debug('REMOVE ELEMENTS') } for (let j = 0; j < removeElements.length; j++) { if (debug) { - console.log(removeElements[j].outerHTML) + console.debug(removeElements[j].outerHTML) } if (removeElements[j]) { $(removeElements[j]).remove() } } @@ -2977,50 +2968,50 @@ function reverseSortCursorMenu (dropdown) { var checkCursorMenu = _.throttle(checkCursorMenuInner, cursorMenuThrottle) function checkCursorMenuInner () { - // get element + // get element var dropdown = $('.cursor-menu > .dropdown-menu') - // return if not exists + // return if not exists if (dropdown.length <= 0) return - // set margin + // set margin var menuRightMargin = 10 var menuBottomMargin = 4 - // use sizer to get the real doc size (won't count status bar and gutters) + // use sizer to get the real doc size (won't count status bar and gutters) var docWidth = ui.area.codemirrorSizer.width() - // get editor size (status bar not count in) + // get editor size (status bar not count in) var editorHeight = ui.area.codemirror.height() - // get element size + // get element size var width = dropdown.outerWidth() var height = dropdown.outerHeight() - // get cursor + // get cursor var cursor = editor.getCursor() - // set element cursor data + // set element cursor data if (!dropdown.hasClass('CodeMirror-other-cursor')) { dropdown.addClass('CodeMirror-other-cursor') } dropdown.attr('data-line', cursor.line) dropdown.attr('data-ch', cursor.ch) - // get coord position + // get coord position var coord = editor.charCoords({ line: cursor.line, ch: cursor.ch }, 'windows') var left = coord.left var top = coord.top - // get doc top offset (to workaround with viewport) + // get doc top offset (to workaround with viewport) var docTopOffset = ui.area.codemirrorSizerInner.position().top - // set offset + // set offset var offsetLeft = 0 var offsetTop = defaultTextHeight - // set up side down + // set up side down window.upSideDown = false var lastUpSideDown = window.upSideDown = false - // only do when have width and height + // only do when have width and height if (width > 0 && height > 0) { - // make element right bound not larger than doc width + // make element right bound not larger than doc width if (left + width + offsetLeft + menuRightMargin > docWidth) { offsetLeft = -(left + width - docWidth + menuRightMargin) } - // flip y when element bottom bound larger than doc height - // and element top position is larger than element height + // flip y when element bottom bound larger than doc height + // and element top position is larger than element height if (top + docTopOffset + height + offsetTop + menuBottomMargin > Math.max(editor.doc.height, editorHeight) && top + docTopOffset > height + menuBottomMargin) { offsetTop = -(height + menuBottomMargin) - // reverse sort menu because upSideDown + // reverse sort menu because upSideDown dropdown.html(reverseSortCursorMenu(dropdown)) window.upSideDown = true } @@ -3028,18 +3019,18 @@ function checkCursorMenuInner () { lastUpSideDown = textCompleteDropdown.upSideDown textCompleteDropdown.upSideDown = window.upSideDown } - // make menu scroll top only if upSideDown changed + // make menu scroll top only if upSideDown changed if (window.upSideDown !== lastUpSideDown) { dropdown.scrollTop(dropdown[0].scrollHeight) } - // set element offset data + // set element offset data dropdown.attr('data-offset-left', offsetLeft) dropdown.attr('data-offset-top', offsetTop) - // set position + // set position dropdown[0].style.left = left + offsetLeft + 'px' dropdown[0].style.top = top + offsetTop + 'px' } function checkInIndentCode () { - // if line starts with tab or four spaces is a code block + // if line starts with tab or four spaces is a code block var line = editor.getLine(editor.getCursor().line) var isIndentCode = ((line.substr(0, 4) === ' ') || (line.substr(0, 1) === '\t')) return isIndentCode @@ -3058,7 +3049,7 @@ function checkAbove (method) { text.push(editor.getLine(i)) } text = text.join('\n') + '\n' + editor.getLine(cursor.line).slice(0, cursor.ch) - // console.log(text); + // console.debug(text); return method(text) } @@ -3070,7 +3061,7 @@ function checkBelow (method) { text.push(editor.getLine(i)) } text = editor.getLine(cursor.line).slice(cursor.ch) + '\n' + text.join('\n') - // console.log(text); + // console.debug(text); return method(text) } @@ -3097,7 +3088,7 @@ function checkInContainer () { } function checkInContainerSyntax () { - // if line starts with :::, it's in container syntax + // if line starts with :::, it's in container syntax var line = editor.getLine(editor.getCursor().line) isInContainerSyntax = (line.substr(0, 3) === ':::') } @@ -3113,229 +3104,229 @@ function matchInContainer (text) { } $(editor.getInputField()) - .textcomplete([ - { // emoji strategy - match: /(^|\n|\s)\B:([-+\w]*)$/, - search: function (term, callback) { - var line = editor.getLine(editor.getCursor().line) - term = line.match(this.match)[2] - var list = [] - $.map(window.emojify.emojiNames, function (emoji) { - if (emoji.indexOf(term) === 0) { // match at first character - list.push(emoji) - } - }) - $.map(window.emojify.emojiNames, function (emoji) { - if (emoji.indexOf(term) !== -1) { // match inside the word - list.push(emoji) - } - }) - callback(list) - }, - template: function (value) { - return '<img class="emoji" src="' + serverurl + '/build/emojify.js/dist/images/basic/' + value + '.png"></img> ' + value - }, - replace: function (value) { - return '$1:' + value + ': ' - }, - index: 1, - context: function (text) { - checkInCode() - checkInContainer() - checkInContainerSyntax() - return !isInCode && !isInContainerSyntax - } - }, - { // Code block language strategy - langs: supportCodeModes, - charts: supportCharts, - match: /(^|\n)```(\w+)$/, - search: function (term, callback) { - var line = editor.getLine(editor.getCursor().line) - term = line.match(this.match)[2] - var list = [] - $.map(this.langs, function (lang) { - if (lang.indexOf(term) === 0 && lang !== term) { list.push(lang) } - }) - $.map(this.charts, function (chart) { - if (chart.indexOf(term) === 0 && chart !== term) { list.push(chart) } - }) - callback(list) - }, - replace: function (lang) { - var ending = '' - if (!checkBelow(matchInCode)) { - ending = '\n\n```' + .textcomplete([ + { // emoji strategy + match: /(^|\n|\s)\B:([-+\w]*)$/, + search: function (term, callback) { + var line = editor.getLine(editor.getCursor().line) + term = line.match(this.match)[2] + var list = [] + $.map(window.emojify.emojiNames, function (emoji) { + if (emoji.indexOf(term) === 0) { // match at first character + list.push(emoji) } - if (this.langs.indexOf(lang) !== -1) { return '$1```' + lang + '=' + ending } else if (this.charts.indexOf(lang) !== -1) { return '$1```' + lang + ending } - }, - done: function () { - var cursor = editor.getCursor() - var text = [] - text.push(editor.getLine(cursor.line - 1)) - text.push(editor.getLine(cursor.line)) - text = text.join('\n') - // console.log(text); - if (text === '\n```') { editor.doc.cm.execCommand('goLineUp') } - }, - context: function (text) { - return isInCode - } - }, - { // Container strategy - containers: supportContainers, - match: /(^|\n):::(\s*)(\w*)$/, - search: function (term, callback) { - var line = editor.getLine(editor.getCursor().line) - term = line.match(this.match)[3].trim() - var list = [] - $.map(this.containers, function (container) { - if (container.indexOf(term) === 0 && container !== term) { list.push(container) } - }) - callback(list) - }, - replace: function (lang) { - var ending = '' - if (!checkBelow(matchInContainer)) { - ending = '\n\n:::' + }) + $.map(window.emojify.emojiNames, function (emoji) { + if (emoji.indexOf(term) !== -1) { // match inside the word + list.push(emoji) } - if (this.containers.indexOf(lang) !== -1) { return '$1:::$2' + lang + ending } - }, - done: function () { - var cursor = editor.getCursor() - var text = [] - text.push(editor.getLine(cursor.line - 1)) - text.push(editor.getLine(cursor.line)) - text = text.join('\n') - // console.log(text); - if (text === '\n:::') { editor.doc.cm.execCommand('goLineUp') } - }, - context: function (text) { - return !isInCode && isInContainer + }) + callback(list) + }, + template: function (value) { + return '<img class="emoji" src="' + serverurl + '/build/emojify.js/dist/images/basic/' + value + '.png"></img> ' + value + }, + replace: function (value) { + return '$1:' + value + ': ' + }, + index: 1, + context: function (text) { + checkInCode() + checkInContainer() + checkInContainerSyntax() + return !isInCode && !isInContainerSyntax + } + }, + { // Code block language strategy + langs: supportCodeModes, + charts: supportCharts, + match: /(^|\n)```(\w+)$/, + search: function (term, callback) { + var line = editor.getLine(editor.getCursor().line) + term = line.match(this.match)[2] + var list = [] + $.map(this.langs, function (lang) { + if (lang.indexOf(term) === 0 && lang !== term) { list.push(lang) } + }) + $.map(this.charts, function (chart) { + if (chart.indexOf(term) === 0 && chart !== term) { list.push(chart) } + }) + callback(list) + }, + replace: function (lang) { + var ending = '' + if (!checkBelow(matchInCode)) { + ending = '\n\n```' } + if (this.langs.indexOf(lang) !== -1) { return '$1```' + lang + '=' + ending } else if (this.charts.indexOf(lang) !== -1) { return '$1```' + lang + ending } }, - { // header - match: /(?:^|\n)(\s{0,3})(#{1,6}\w*)$/, - search: function (term, callback) { - callback($.map(supportHeaders, function (header) { - return header.search.indexOf(term) === 0 ? header.text : null - })) - }, - replace: function (value) { - return '$1' + value - }, - context: function (text) { - return !isInCode + done: function () { + var cursor = editor.getCursor() + var text = [] + text.push(editor.getLine(cursor.line - 1)) + text.push(editor.getLine(cursor.line)) + text = text.join('\n') + // console.debug(text); + if (text === '\n```') { editor.doc.cm.execCommand('goLineUp') } + }, + context: function (text) { + return isInCode + } + }, + { // Container strategy + containers: supportContainers, + match: /(^|\n):::(\s*)(\w*)$/, + search: function (term, callback) { + var line = editor.getLine(editor.getCursor().line) + term = line.match(this.match)[3].trim() + var list = [] + $.map(this.containers, function (container) { + if (container.indexOf(term) === 0 && container !== term) { list.push(container) } + }) + callback(list) + }, + replace: function (lang) { + var ending = '' + if (!checkBelow(matchInContainer)) { + ending = '\n\n:::' } + if (this.containers.indexOf(lang) !== -1) { return '$1:::$2' + lang + ending } + }, + done: function () { + var cursor = editor.getCursor() + var text = [] + text.push(editor.getLine(cursor.line - 1)) + text.push(editor.getLine(cursor.line)) + text = text.join('\n') + // console.debug(text); + if (text === '\n:::') { editor.doc.cm.execCommand('goLineUp') } + }, + context: function (text) { + return !isInCode && isInContainer + } + }, + { // header + match: /(?:^|\n)(\s{0,3})(#{1,6}\w*)$/, + search: function (term, callback) { + callback($.map(supportHeaders, function (header) { + return header.search.indexOf(term) === 0 ? header.text : null + })) + }, + replace: function (value) { + return '$1' + value + }, + context: function (text) { + return !isInCode + } + }, + { // extra tags for list + match: /(^[>\s]*[-+*]\s(?:\[[x ]\]|.*))(\[\])(\w*)$/, + search: function (term, callback) { + var list = [] + $.map(supportExtraTags, function (extratag) { + if (extratag.search.indexOf(term) === 0) { list.push(extratag.command()) } + }) + $.map(supportReferrals, function (referral) { + if (referral.search.indexOf(term) === 0) { list.push(referral.text) } + }) + callback(list) + }, + replace: function (value) { + return '$1' + value }, - { // extra tags for list - match: /(^[>\s]*[-+*]\s(?:\[[x ]\]|.*))(\[\])(\w*)$/, - search: function (term, callback) { - var list = [] + context: function (text) { + return !isInCode + } + }, + { // extra tags for blockquote + match: /(?:^|\n|\s)(>.*|\s|)((\^|)\[(\^|)\](\[\]|\(\)|:|)\s*\w*)$/, + search: function (term, callback) { + var line = editor.getLine(editor.getCursor().line) + var quote = line.match(this.match)[1].trim() + var list = [] + if (quote.indexOf('>') === 0) { $.map(supportExtraTags, function (extratag) { if (extratag.search.indexOf(term) === 0) { list.push(extratag.command()) } }) - $.map(supportReferrals, function (referral) { - if (referral.search.indexOf(term) === 0) { list.push(referral.text) } - }) - callback(list) - }, - replace: function (value) { - return '$1' + value - }, - context: function (text) { - return !isInCode } + $.map(supportReferrals, function (referral) { + if (referral.search.indexOf(term) === 0) { list.push(referral.text) } + }) + callback(list) }, - { // extra tags for blockquote - match: /(?:^|\n|\s)(>.*|\s|)((\^|)\[(\^|)\](\[\]|\(\)|:|)\s*\w*)$/, - search: function (term, callback) { - var line = editor.getLine(editor.getCursor().line) - var quote = line.match(this.match)[1].trim() - var list = [] - if (quote.indexOf('>') === 0) { - $.map(supportExtraTags, function (extratag) { - if (extratag.search.indexOf(term) === 0) { list.push(extratag.command()) } - }) - } - $.map(supportReferrals, function (referral) { - if (referral.search.indexOf(term) === 0) { list.push(referral.text) } - }) - callback(list) - }, - replace: function (value) { - return '$1' + value - }, - context: function (text) { - return !isInCode - } + replace: function (value) { + return '$1' + value }, - { // referral - match: /(^\s*|\n|\s{2})((\[\]|\[\]\[\]|\[\]\(\)|!|!\[\]|!\[\]\[\]|!\[\]\(\))\s*\w*)$/, - search: function (term, callback) { - callback($.map(supportReferrals, function (referral) { - return referral.search.indexOf(term) === 0 ? referral.text : null - })) - }, - replace: function (value) { - return '$1' + value - }, - context: function (text) { - return !isInCode - } - }, - { // externals - match: /(^|\n|\s)\{\}(\w*)$/, - search: function (term, callback) { - callback($.map(supportExternals, function (external) { - return external.search.indexOf(term) === 0 ? external.text : null - })) - }, - replace: function (value) { - return '$1' + value - }, - context: function (text) { - return !isInCode - } + context: function (text) { + return !isInCode } - ], { - appendTo: $('.cursor-menu') - }) - .on({ - 'textComplete:beforeSearch': function (e) { - // NA + }, + { // referral + match: /(^\s*|\n|\s{2})((\[\]|\[\]\[\]|\[\]\(\)|!|!\[\]|!\[\]\[\]|!\[\]\(\))\s*\w*)$/, + search: function (term, callback) { + callback($.map(supportReferrals, function (referral) { + return referral.search.indexOf(term) === 0 ? referral.text : null + })) }, - 'textComplete:afterSearch': function (e) { - checkCursorMenu() + replace: function (value) { + return '$1' + value }, - 'textComplete:select': function (e, value, strategy) { - // NA + context: function (text) { + return !isInCode + } + }, + { // externals + match: /(^|\n|\s)\{\}(\w*)$/, + search: function (term, callback) { + callback($.map(supportExternals, function (external) { + return external.search.indexOf(term) === 0 ? external.text : null + })) }, - 'textComplete:show': function (e) { - $(this).data('autocompleting', true) - editor.setOption('extraKeys', { - 'Up': function () { - return false - }, - 'Right': function () { - editor.doc.cm.execCommand('goCharRight') - }, - 'Down': function () { - return false - }, - 'Left': function () { - editor.doc.cm.execCommand('goCharLeft') - }, - 'Enter': function () { - return false - }, - 'Backspace': function () { - editor.doc.cm.execCommand('delCharBefore') - } - }) + replace: function (value) { + return '$1' + value }, - 'textComplete:hide': function (e) { - $(this).data('autocompleting', false) - editor.setOption('extraKeys', editorInstance.defaultExtraKeys) + context: function (text) { + return !isInCode } - }) + } + ], { + appendTo: $('.cursor-menu') + }) + .on({ + 'textComplete:beforeSearch': function (e) { + // NA + }, + 'textComplete:afterSearch': function (e) { + checkCursorMenu() + }, + 'textComplete:select': function (e, value, strategy) { + // NA + }, + 'textComplete:show': function (e) { + $(this).data('autocompleting', true) + editor.setOption('extraKeys', { + 'Up': function () { + return false + }, + 'Right': function () { + editor.doc.cm.execCommand('goCharRight') + }, + 'Down': function () { + return false + }, + 'Left': function () { + editor.doc.cm.execCommand('goCharLeft') + }, + 'Enter': function () { + return false + }, + 'Backspace': function () { + editor.doc.cm.execCommand('delCharBefore') + } + }) + }, + 'textComplete:hide': function (e) { + $(this).data('autocompleting', false) + editor.setOption('extraKeys', editorInstance.defaultExtraKeys) + } + }) diff --git a/public/js/lib/common/login.js b/public/js/lib/common/login.js index 18cd377d..28e5b470 100644 --- a/public/js/lib/common/login.js +++ b/public/js/lib/common/login.js @@ -60,22 +60,22 @@ export function checkIfAuth (yesCallback, noCallback) { if (checkLoginStateChanged()) checkAuth = false if (!checkAuth || typeof cookieLoginState === 'undefined') { $.get(`${serverurl}/me`) - .done(data => { - if (data && data.status === 'ok') { - profile = data - yesCallback(profile) - setLoginState(true, data.id) - } else { - noCallback() - setLoginState(false) - } - }) - .fail(() => { - noCallback() - }) - .always(() => { - checkAuth = true - }) + .done(data => { + if (data && data.status === 'ok') { + profile = data + yesCallback(profile) + setLoginState(true, data.id) + } else { + noCallback() + setLoginState(false) + } + }) + .fail(() => { + noCallback() + }) + .always(() => { + checkAuth = true + }) } else if (cookieLoginState) { yesCallback(profile) } else { diff --git a/public/js/lib/editor/index.js b/public/js/lib/editor/index.js index f05d01b8..8553caa9 100644 --- a/public/js/lib/editor/index.js +++ b/public/js/lib/editor/index.js @@ -3,6 +3,8 @@ import config from './config' import statusBarTemplate from './statusbar.html' import toolBarTemplate from './toolbar.html' +import '../../../css/ui/toolbar.css' + /* config section */ const isMac = CodeMirror.keyMap.default === CodeMirror.keyMap.macDefault const defaultEditorMode = 'gfm' @@ -138,6 +140,7 @@ export default class Editor { } addToolBar () { + var inlineAttach = inlineAttachment.editors.codemirror4.attach(this.editor) this.toolBar = $(toolBarTemplate) this.toolbarPanel = this.editor.addPanel(this.toolBar[0], { position: 'top' @@ -157,6 +160,7 @@ export default class Editor { var makeTable = $('#makeTable') var makeLine = $('#makeLine') var makeComment = $('#makeComment') + var uploadImage = $('#uploadImage') makeBold.click(() => { utils.wrapTextWith(this.editor, this.editor, '**') @@ -217,6 +221,13 @@ export default class Editor { makeComment.click(() => { utils.insertText(this.editor, '> []') }) + + uploadImage.bind('change', function (e) { + var files = e.target.files || e.dataTransfer.files + e.dataTransfer = {} + e.dataTransfer.files = files + inlineAttach.onDrop(e) + }) } addStatusBar () { diff --git a/public/js/lib/editor/toolbar.html b/public/js/lib/editor/toolbar.html index a2ac476f..dc5b8ad4 100644 --- a/public/js/lib/editor/toolbar.html +++ b/public/js/lib/editor/toolbar.html @@ -1,46 +1,49 @@ <div class="toolbar"> <div class="btn-toolbar" role="toolbar" aria-label="Editor toolbar"> <div class="btn-group" role="group"> - <a id="makeBold" class="btn btn-sm btn-dark text-uppercase" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false" title="Bold"> + <a id="makeBold" class="btn btn-sm text-uppercase" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false" title="Bold"> <i class="fa fa-bold fa-fw"></i> </a> - <a id="makeItalic" class="btn btn-sm btn-dark text-uppercase" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false" title="Italic"> + <a id="makeItalic" class="btn btn-sm text-uppercase" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false" title="Italic"> <i class="fa fa-italic fa-fw"></i> </a> - <a id="makeStrike" class="btn btn-sm btn-dark text-uppercase" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false" title="Strikethrough"> + <a id="makeStrike" class="btn btn-sm text-uppercase" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false" title="Strikethrough"> <i class="fa fa-strikethrough fa-fw"></i> </a> - <a id="makeHeader" class="btn btn-sm btn-dark text-uppercase" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false" title="Heading"> + <a id="makeHeader" class="btn btn-sm text-uppercase" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false" title="Heading"> <i class="fa fa-h1 fa-fw">H</i> </a> - <a id="makeCode" class="btn btn-sm btn-dark text-uppercase" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false" title="Code"> + <a id="makeCode" class="btn btn-sm text-uppercase" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false" title="Code"> <i class="fa fa-code fa-fw"></i> </a> - <a id="makeQuote" class="btn btn-sm btn-dark text-uppercase" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false" title="Quote"> + <a id="makeQuote" class="btn btn-sm text-uppercase" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false" title="Quote"> <i class="fa fa-quote-right fa-fw"></i> </a> - <a id="makeGenericList" class="btn btn-sm btn-dark text-uppercase" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false" title="List"> + <a id="makeGenericList" class="btn btn-sm text-uppercase" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false" title="List"> <i class="fa fa-list fa-fw"></i> </a> - <a id="makeOrderedList" class="btn btn-sm btn-dark text-uppercase" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false" title="Numbered List"> + <a id="makeOrderedList" class="btn btn-sm text-uppercase" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false" title="Numbered List"> <i class="fa fa-list-ol fa-fw"></i> </a> - <a id="makeCheckList" class="btn btn-sm btn-dark text-uppercase" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false" title="Check List"> + <a id="makeCheckList" class="btn btn-sm text-uppercase" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false" title="Check List"> <i class="fa fa-check-square fa-fw"></i> </a> - <a id="makeLink" class="btn btn-sm btn-dark text-uppercase" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false" title="Link"> + <a id="makeLink" class="btn btn-sm text-uppercase" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false" title="Link"> <i class="fa fa-link fa-fw"></i> </a> - <a id="makeImage" class="btn btn-sm btn-dark text-uppercase" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false" title="Image"> + <a id="makeImage" class="btn btn-sm text-uppercase" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false" title="Image"> <i class="fa fa-image fa-fw"></i> </a> - <a id="makeTable" class="btn btn-sm btn-dark text-uppercase" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false" title="Table"> + <a id="uploadImage" class="btn btn-sm btn-file ui-upload-image" title="Upload Image"> + <i class="fa fa-upload fa-fw"></i><input type="file" accept="image/*" name="upload" multiple title="Upload Image"> + </a> + <a id="makeTable" class="btn btn-sm text-uppercase" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false" title="Table"> <i class="fa fa-table fa-fw"></i> </a> - <a id="makeLine" class="btn btn-sm btn-dark text-uppercase" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false" title="Line"> + <a id="makeLine" class="btn btn-sm text-uppercase" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false" title="Line"> <i class="fa fa-minus fa-fw"></i> </a> - <a id="makeComment" class="btn btn-sm btn-dark text-uppercase" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false" title="Comment"> + <a id="makeComment" class="btn btn-sm text-uppercase" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false" title="Comment"> <i class="fa fa-comment fa-fw"></i> </a> </div> diff --git a/public/js/lib/editor/ui-elements.js b/public/js/lib/editor/ui-elements.js index 29a37782..ce19436b 100644 --- a/public/js/lib/editor/ui-elements.js +++ b/public/js/lib/editor/ui-elements.js @@ -35,8 +35,7 @@ export const getUIElements = () => ({ edit: $('.ui-edit'), view: $('.ui-view'), both: $('.ui-both'), - night: $('.ui-night'), - uploadImage: $('.ui-upload-image') + night: $('.ui-night') }, infobar: { lastchange: $('.ui-lastchange'), diff --git a/public/js/lib/editor/utils.js b/public/js/lib/editor/utils.js index 36e5c121..d87c7e41 100644 --- a/public/js/lib/editor/utils.js +++ b/public/js/lib/editor/utils.js @@ -51,7 +51,7 @@ export function insertText (cm, text, cursorEnd = 0) { let cursor = cm.getCursor() cm.replaceSelection(text, cursor, cursor) cm.focus() - cm.setCursor({line: cursor.line, ch: cursor.ch + cursorEnd}) + cm.setCursor({ line: cursor.line, ch: cursor.ch + cursorEnd }) } export function insertLink (cm, isImage) { @@ -80,7 +80,7 @@ export function insertLink (cm, isImage) { cm.setSelections(ranges) } else { cm.replaceRange(symbol + linkEnd, cursor, cursor) - cm.setCursor({line: cursor.line, ch: cursor.ch + symbol.length + linkEnd.length}) + cm.setCursor({ line: cursor.line, ch: cursor.ch + symbol.length + linkEnd.length }) } } cm.focus() @@ -88,8 +88,8 @@ export function insertLink (cm, isImage) { export function insertHeader (cm) { let cursor = cm.getCursor() - let startOfLine = {line: cursor.line, ch: 0} - let startOfLineText = cm.getRange(startOfLine, {line: cursor.line, ch: 1}) + let startOfLine = { line: cursor.line, ch: 0 } + let startOfLineText = cm.getRange(startOfLine, { line: cursor.line, ch: 1 }) // See if it is already a header if (startOfLineText === '#') { cm.replaceRange('#', startOfLine, startOfLine) @@ -108,14 +108,14 @@ export function insertOnStartOfLines (cm, symbol) { if (!range.empty()) { const from = range.from() const to = range.to() - let selection = cm.getRange({line: from.line, ch: 0}, to) + let selection = cm.getRange({ line: from.line, ch: 0 }, to) selection = selection.replace(/\n/g, '\n' + symbol) selection = symbol + selection cm.replaceRange(selection, from, to) } else { - cm.replaceRange(symbol, {line: cursor.line, ch: 0}, {line: cursor.line, ch: 0}) + cm.replaceRange(symbol, { line: cursor.line, ch: 0 }, { line: cursor.line, ch: 0 }) } } - cm.setCursor({line: cursor.line, ch: cursor.ch + symbol.length}) + cm.setCursor({ line: cursor.line, ch: cursor.ch + symbol.length }) cm.focus() } diff --git a/public/js/lib/syncscroll.js b/public/js/lib/syncscroll.js index cee317ea..d492fbc9 100644 --- a/public/js/lib/syncscroll.js +++ b/public/js/lib/syncscroll.js @@ -188,7 +188,7 @@ function buildMapInner (callback) { } nonEmptyList.push(0) - // make the first line go top + // make the first line go top _scrollMap[0] = viewTop const parts = markdownArea.find('.part').toArray() @@ -336,7 +336,7 @@ export function syncScrollToView (event, preventAnimate) { const scrollInfo = editor.getScrollInfo() const textHeight = editor.defaultTextHeight() lineNo = Math.floor(scrollInfo.top / textHeight) - // if reach the last line, will start lerp to the bottom + // 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 diff --git a/public/js/pretty.js b/public/js/pretty.js index ff6f9dfd..a5df0a47 100644 --- a/public/js/pretty.js +++ b/public/js/pretty.js @@ -1,29 +1,29 @@ /* eslint-env browser, jquery */ /* global refreshView */ +import { + autoLinkify, + deduplicatedHeaderId, + removeDOMEvents, + finishView, + generateToc, + md, + parseMeta, + postProcess, + renderTOC, + scrollToHash, + smoothHashScroll, + updateLastChange +} from './extra' + +import { preventXSS } from './render' + require('../css/extra.css') require('../css/slide-preview.css') require('../css/site.css') require('highlight.js/styles/github-gist.css') -import { - autoLinkify, - deduplicatedHeaderId, - removeDOMEvents, - finishView, - generateToc, - md, - parseMeta, - postProcess, - renderTOC, - scrollToHash, - smoothHashScroll, - updateLastChange -} from './extra' - -import { preventXSS } from './render' - const markdown = $('#doc.markdown-body') const text = markdown.text() const lastMeta = md.meta @@ -38,7 +38,7 @@ if (md.meta.type && md.meta.type === 'slide') { const slides = window.RevealMarkdown.slidify(text, slideOptions) markdown.html(slides) window.RevealMarkdown.initialize() - // prevent XSS + // prevent XSS markdown.html(preventXSS(markdown.html())) markdown.addClass('slides') } else { @@ -46,12 +46,12 @@ if (md.meta.type && md.meta.type === 'slide') { refreshView() markdown.removeClass('slides') } - // only render again when meta changed + // only render again when meta changed if (JSON.stringify(md.meta) !== JSON.stringify(lastMeta)) { parseMeta(md, null, markdown, $('#ui-toc'), $('#ui-toc-affix')) rendered = md.render(text) } - // prevent XSS + // prevent XSS rendered = preventXSS(rendered) const result = postProcess(rendered) markdown.html(result.html()) @@ -98,14 +98,14 @@ function generateScrollspy () { } function windowResize () { - // toc right + // toc right const paddingRight = parseFloat(markdown.css('padding-right')) const right = ($(window).width() - (markdown.offset().left + markdown.outerWidth() - paddingRight)) toc.css('right', `${right}px`) - // affix toc left + // affix toc left let newbool const rightMargin = (markdown.parent().outerWidth() - markdown.outerWidth()) / 2 - // for ipad or wider device + // for ipad or wider device if (rightMargin >= 133) { newbool = true const affixLeftMargin = (tocAffix.outerWidth() - tocAffix.width()) / 2 @@ -126,7 +126,7 @@ $(document).ready(() => { windowResize() generateScrollspy() setTimeout(scrollToHash, 0) - // tooltip + // tooltip $('[data-toggle="tooltip"]').tooltip() }) diff --git a/public/js/render.js b/public/js/render.js index 87e5cfdf..d37f38ef 100644 --- a/public/js/render.js +++ b/public/js/render.js @@ -44,7 +44,7 @@ var filterXSSOptions = { onIgnoreTag: function (tag, html, options) { // allow comment tag if (tag === '!--') { - // do not filter its attributes + // do not filter its attributes return html.replace(/<(?!!--)/g, '<').replace(/-->/g, '__HTML_COMMENT_END__').replace(/>/g, '>').replace(/__HTML_COMMENT_END__/g, '-->') } }, diff --git a/public/js/slide.js b/public/js/slide.js index 1eb8dfd4..3a47ac43 100644 --- a/public/js/slide.js +++ b/public/js/slide.js @@ -1,12 +1,12 @@ /* eslint-env browser, jquery */ /* global serverurl, Reveal, RevealMarkdown */ -require('../css/extra.css') -require('../css/site.css') - import { preventXSS } from './render' import { md, updateLastChange, removeDOMEvents, finishView } from './extra' +require('../css/extra.css') +require('../css/site.css') + const body = preventXSS($('.slides').text()) window.createtime = window.lastchangeui.time.attr('data-createtime') @@ -17,7 +17,7 @@ $('.ui-edit').attr('href', `${url}/edit`) $('.ui-print').attr('href', `${url}?print-pdf`) $(document).ready(() => { - // tooltip + // tooltip $('[data-toggle="tooltip"]').tooltip() }) @@ -127,7 +127,7 @@ function renderSlide (event) { Reveal.addEventListener('ready', event => { renderSlide(event) const markdown = $(event.currentSlide) - // force browser redraw + // force browser redraw setTimeout(() => { markdown.hide().show(0) }, 0) |