diff --git a/.gitignore b/.gitignore index dd94e6efce..d46e704e74 100644 --- a/.gitignore +++ b/.gitignore @@ -28,3 +28,5 @@ public/uploads/* !public/uploads/.gitkeep /.nyc_output /coverage/ + +.vscode/settings.json \ No newline at end of file diff --git a/.nvmrc b/.nvmrc new file mode 100644 index 0000000000..fc5e4cfe99 --- /dev/null +++ b/.nvmrc @@ -0,0 +1 @@ +v10.20.1 diff --git a/app.js b/app.js index 24e6b62bf7..4980d45d02 100644 --- a/app.js +++ b/app.js @@ -280,12 +280,18 @@ models.sequelize.sync().then(function () { } else { throw new Error('server still not ready after db synced') } +}).catch(err => { + logger.error('Can\'t sync database') + logger.error(err.stack) + logger.error('Process will exit now.') + process.exit(1) }) // log uncaught exception process.on('uncaughtException', function (err) { logger.error('An uncaught exception has occured.') logger.error(err) + console.error(err) logger.error('Process will exit now.') process.exit(1) }) diff --git a/deployments/docker-compose.yml b/deployments/docker-compose.yml index 5fe0ac7572..b5f2a4d20a 100644 --- a/deployments/docker-compose.yml +++ b/deployments/docker-compose.yml @@ -10,8 +10,11 @@ services: - "database-data:/var/lib/postgresql/data" restart: always codimd: - # you can use image or custom build below - image: nabo.codimd.dev/hackmdio/hackmd:2.0.0 + # you can use image or custom build below, + # if you need CJK character with exported PDF files, + # please change the image tag with `cjk` postfix version + image: nabo.codimd.dev/hackmdio/hackmd:2.1.0 + # image: nabo.codimd.dev/hackmdio/hackmd:2.1.0-cjk # build: # context: .. # dockerfile: ./deployments/Dockerfile diff --git a/lib/auth/gitlab/index.js b/lib/auth/gitlab/index.js index 72ab0cde42..ab4f6900a8 100644 --- a/lib/auth/gitlab/index.js +++ b/lib/auth/gitlab/index.js @@ -18,8 +18,8 @@ const gitlabAuthStrategy = new GitlabStrategy({ callbackURL: config.serverURL + '/auth/gitlab/callback' }, passportGeneralCallback) -if (process.env['https_proxy']) { - const httpsProxyAgent = new HttpsProxyAgent(process.env['https_proxy']) +if (process.env.https_proxy) { + const httpsProxyAgent = new HttpsProxyAgent(process.env.https_proxy) gitlabAuthStrategy._oauth2.setAgent(httpsProxyAgent) } diff --git a/lib/history/index.js b/lib/history/index.js index 3826ce98aa..ab8b423228 100644 --- a/lib/history/index.js +++ b/lib/history/index.js @@ -131,7 +131,7 @@ function historyPost (req, res) { if (req.isAuthenticated()) { var noteId = req.params.noteId if (!noteId) { - if (typeof req.body['history'] === 'undefined') return response.errorBadRequest(req, res) + if (typeof req.body.history === 'undefined') return response.errorBadRequest(req, res) if (config.debug) { logger.info('SERVER received history from [' + req.user.id + ']: ' + req.body.history) } try { var history = JSON.parse(req.body.history) @@ -147,7 +147,7 @@ function historyPost (req, res) { return response.errorBadRequest(req, res) } } else { - if (typeof req.body['pinned'] === 'undefined') return response.errorBadRequest(req, res) + if (typeof req.body.pinned === 'undefined') return response.errorBadRequest(req, res) getHistory(req.user.id, function (err, history) { if (err) return response.errorInternalError(req, res) if (!history) return response.errorNotFound(req, res) diff --git a/lib/imageRouter/filesystem.js b/lib/imageRouter/filesystem.js index 53ecc9d424..49a811ef49 100644 --- a/lib/imageRouter/filesystem.js +++ b/lib/imageRouter/filesystem.js @@ -1,10 +1,39 @@ 'use strict' + +const crypto = require('crypto') +const fs = require('fs') const URL = require('url').URL const path = require('path') const config = require('../config') const logger = require('../logger') +/** + * generate a random filename for uploaded image + */ +function randomFilename () { + const buf = crypto.randomBytes(16) + return `upload_${buf.toString('hex')}` +} + +/** + * pick a filename not exist in filesystem + * maximum attempt 5 times + */ +function pickFilename (defaultFilename) { + let retryCounter = 5 + let filename = defaultFilename + const extname = path.extname(defaultFilename) + while (retryCounter-- > 0) { + if (fs.existsSync(path.join(config.uploadsPath, filename))) { + filename = `${randomFilename()}${extname}` + continue + } + return filename + } + throw new Error('file exists.') +} + exports.uploadImage = function (imagePath, callback) { if (!imagePath || typeof imagePath !== 'string') { callback(new Error('Image path is missing or wrong'), null) @@ -16,11 +45,24 @@ exports.uploadImage = function (imagePath, callback) { return } + let filename = path.basename(imagePath) + try { + filename = pickFilename(path.basename(imagePath)) + } catch (e) { + return callback(e, null) + } + + try { + fs.copyFileSync(imagePath, path.join(config.uploadsPath, filename)) + } catch (e) { + return callback(e, null) + } + let url try { - url = (new URL(path.basename(imagePath), config.serverURL + '/uploads/')).href + url = (new URL(filename, config.serverURL + '/uploads/')).href } catch (e) { - url = config.serverURL + '/uploads/' + path.basename(imagePath) + url = config.serverURL + '/uploads/' + filename } callback(null, url) diff --git a/lib/imageRouter/index.js b/lib/imageRouter/index.js index 9b0c40289c..5af005bc15 100644 --- a/lib/imageRouter/index.js +++ b/lib/imageRouter/index.js @@ -1,5 +1,6 @@ 'use strict' +const fs = require('fs') const Router = require('express').Router const formidable = require('formidable') @@ -15,10 +16,6 @@ imageRouter.post('/uploadimage', function (req, res) { form.keepExtensions = true - if (config.imageUploadType === 'filesystem') { - form.uploadDir = config.uploadsPath - } - form.parse(req, function (err, fields, files) { if (err || !files.image || !files.image.path) { response.errorForbidden(req, res) @@ -29,6 +26,8 @@ imageRouter.post('/uploadimage', function (req, res) { const uploadProvider = require('./' + config.imageUploadType) uploadProvider.uploadImage(files.image.path, function (err, url) { + // remove temporary upload file, and ignore any error + fs.unlink(files.image.path, () => {}) if (err !== null) { logger.error(err) return res.status(500).end('upload image error') diff --git a/lib/models/note.js b/lib/models/note.js index 8030be0059..d7109d4449 100644 --- a/lib/models/note.js +++ b/lib/models/note.js @@ -367,8 +367,13 @@ module.exports = function (sequelize, DataTypes) { Note.extractNoteTags = function (meta, $) { var tags = [] var rawtags = [] + var metaTags if (meta.tags && (typeof meta.tags === 'string' || typeof meta.tags === 'number')) { - var metaTags = ('' + meta.tags).split(',') + metaTags = ('' + meta.tags).split(',') + } else if (meta.tags && (Array.isArray(meta.tags))) { + metaTags = meta.tags + } + if (metaTags) { for (let i = 0; i < metaTags.length; i++) { var text = metaTags[i].trim() if (text) rawtags.push(text) diff --git a/lib/note/index.js b/lib/note/index.js index b9b7c9e351..a387b59dba 100644 --- a/lib/note/index.js +++ b/lib/note/index.js @@ -2,10 +2,9 @@ const config = require('../config') const logger = require('../logger') - const { Note, User } = require('../models') -const { newCheckViewPermission, errorForbidden, responseCodiMD, errorNotFound } = require('../response') +const { newCheckViewPermission, errorForbidden, responseCodiMD, errorNotFound, errorInternalError } = require('../response') const { updateHistory } = require('../history') const { actionPublish, actionSlide, actionInfo, actionDownload, actionPDF, actionGist, actionRevision, actionPandoc } = require('./noteActions') @@ -121,6 +120,7 @@ async function showPublishNote (req, res) { const data = { title: title, description: meta.description || (markdown ? Note.generateDescription(markdown) : null), + image: meta.image, viewcount: note.viewcount, createtime: createTime, updatetime: updateTime, @@ -190,6 +190,49 @@ async function noteActions (req, res) { } } +async function getMyNoteList (userId, callback) { + const myNotes = await Note.findAll({ + where: { + ownerId: userId + } + }) + if (!myNotes) { + return callback(null, null) + } + try { + const myNoteList = myNotes.map(note => ({ + id: Note.encodeNoteId(note.id), + text: note.title, + tags: Note.parseNoteInfo(note.content).tags, + createdAt: note.createdAt, + lastchangeAt: note.lastchangeAt, + shortId: note.shortid + })) + if (config.debug) { + logger.info('Parse myNoteList success: ' + userId) + } + return callback(null, myNoteList) + } catch (err) { + logger.error('Parse myNoteList failed') + return callback(err, null) + } +} + +function listMyNotes (req, res) { + if (req.isAuthenticated()) { + getMyNoteList(req.user.id, (err, myNoteList) => { + if (err) return errorInternalError(req, res) + if (!myNoteList) return errorNotFound(req, res) + res.send({ + myNotes: myNoteList + }) + }) + } else { + return errorForbidden(req, res) + } +} + exports.showNote = showNote exports.showPublishNote = showPublishNote exports.noteActions = noteActions +exports.listMyNotes = listMyNotes diff --git a/lib/routes.js b/lib/routes.js index f4345039d7..48ac61f3dc 100644 --- a/lib/routes.js +++ b/lib/routes.js @@ -70,6 +70,8 @@ appRouter.get('/s/:shortid/:action', response.publishNoteActions) appRouter.get('/p/:shortid', response.showPublishSlide) // publish slide actions appRouter.get('/p/:shortid/:action', response.publishSlideActions) +// gey my note list +appRouter.get('/api/notes/myNotes', noteController.listMyNotes) // get note by id appRouter.get('/:noteId', wrap(noteController.showNote)) // note actions diff --git a/locales/zh-CN.json b/locales/zh-CN.json index 93e1c86f82..667f457fe2 100644 --- a/locales/zh-CN.json +++ b/locales/zh-CN.json @@ -1,117 +1,121 @@ { "Collaborative markdown notes": "Markdown 协作笔记", - "Realtime collaborative markdown notes on all platforms.": "使用 Markdown 的跨平台即时协作笔记", - "Best way to write and share your knowledge in markdown.": "您使用 Markdown 写作与分享知识的最佳方式", + "Realtime collaborative markdown notes on all platforms.": "使用 Markdown 的跨平台即时协作笔记。", + "Best way to write and share your knowledge in markdown.": "写作与分享 Markdown 的最佳平台。", "Intro": "简介", "History": "历史", - "New guest note": "建立访客笔记", - "Collaborate with URL": "使用网址协作", + "New guest note": "新建访客笔记", + "Collaborate with URL": "实时协作", "Support charts and MathJax": "支持图表与 MathJax", - "Support slide mode": "支持简报模式", + "Support slide mode": "支持幻灯模式", "Sign In": "登录", "Below is the history from browser": "以下为来自浏览器的历史", "Welcome!": "欢迎!", - "New note": "建立笔记", + "New note": "新建笔记", "or": "或", "Sign Out": "登出", "Explore all features": "探索所有功能", "Select tags...": "选择标签...", "Search keyword...": "搜索关键字...", - "Sort by title": "用标题排序", + "Sort by title": "按标题排序", "Title": "标题", - "Sort by time": "用时间排序", + "Sort by time": "按时间排序", "Time": "时间", "Export history": "导出历史", "Import history": "导入历史", "Clear history": "清空历史", "Refresh history": "刷新历史", - "No history": "没有历史", + "No history": "无历史记录", "Import from browser": "从浏览器导入", "Releases": "版本", - "Are you sure?": "你确定吗?", - "Do you really want to delete this note?": "确定要删除这个文件吗?", - "All users will lose their connection.": "所有用户将失去连接", + "Are you sure?": "您确定吗?", + "Do you really want to delete this note?": "您确定要删除这篇笔记吗?", + "All users will lose their connection.": "所有用户将失去连接。", "Cancel": "取消", - "Yes, do it!": "没错,就这样办!", + "Yes, do it!": "是的,就这样做!", "Choose method": "选择方式", "Sign in via %s": "通过 %s 登录", - "New": "新增", + "New": "新建", "Publish": "发表", - "Extra": "增益", + "Extra": "附加功能", "Revision": "修订版本", - "Slide Mode": "简报模式", + "Slide Mode": "幻灯模式", "Export": "导出", "Import": "导入", "Clipboard": "剪贴板", "Download": "下载", - "Raw HTML": "纯 HTML", + "Raw HTML": "原始 HTML", "Edit": "编辑", - "View": "检视", + "View": "预览", "Both": "双栏", "Help": "帮助", "Upload Image": "上传图片", "Menu": "菜单", - "This page need refresh": "此页面需要重新整理", - "You have an incompatible client version.": "您使用的是不相容的客户端", - "Refresh to update.": "请重新整理来更新", - "New version available!": "新版本来了!", - "See releases notes here": "请由此查阅更新纪录", - "Refresh to enjoy new features.": "请重新整理来享受最新功能", - "Your user state has changed.": "您的使用者状态已变更", - "Refresh to load new user state.": "请重新整理来载入新的使用者状态", - "Refresh": "重新整理", - "Contacts": "联络方式", + "This page need refresh": "此页面需要刷新", + "You have an incompatible client version.": "您的客户端版本不兼容。", + "Refresh to update.": "刷新页面以更新。", + "New version available!": "新版本可用!", + "See releases notes here": "在此查看更新记录", + "Refresh to enjoy new features.": "刷新页面以体验新功能。", + "Your user state has changed.": "您的用户状态已变更。", + "Refresh to load new user state.": "刷新页面以加载新的用户状态。", + "Refresh": "刷新", + "Contacts": "联系我们", "Report an issue": "报告问题", "Meet us on %s": "在 %s 上联系我们", - "Send us email": "寄信给我们", - "Documents": "文件", - "Features": "功能简介", - "YAML Metadata": "YAML Metadata", - "Slide Example": "简报范例", - "Cheatsheet": "快速简表", + "Send us email": "给我们发送电子邮件", + "Documents": "文档", + "Features": "功能", + "YAML Metadata": "YAML 元数据", + "Slide Example": "幻灯范例", + "Cheatsheet": "速查表", "Example": "范例", "Syntax": "语法", "Header": "标题", - "Unordered List": "无序清单", - "Ordered List": "有序清单", - "Todo List": "待办事项", + "Unordered List": "无序列表", + "Ordered List": "有序列表", + "Todo List": "清单", "Blockquote": "引用", "Bold font": "粗体", "Italics font": "斜体", "Strikethrough": "删除线", - "Inserted text": "插入文字", - "Marked text": "标记文字", + "Inserted text": "下划线文字", + "Marked text": "高亮文字", "Link": "链接", "Image": "图片", "Code": "代码", - "Externals": "外部", - "This is a alert area.": "这是警告区块", + "Externals": "外部扩展", + "This is a alert area.": "这是一个警告区块。", "Revert": "还原", "Import from clipboard": "从剪贴板导入", - "Paste your markdown or webpage here...": "在这里贴上 Markdown 或是网页内容...", + "Paste your markdown or webpage here...": "在这里粘贴 Markdown 或网页内容...", "Clear": "清除", - "This note is locked": "此份笔记已被锁定", - "Sorry, only owner can edit this note.": "抱歉,只有拥有者可以编辑此笔记", + "This note is locked": "这篇笔记已被锁定", + "Sorry, only owner can edit this note.": "抱歉,只有所有者可以编辑这篇笔记。", "OK": "好的", - "Reach the limit": "到达上限", - "Sorry, you've reached the max length this note can be.": "抱歉,您已使用到此份笔记可用的最大长度", - "Please reduce the content or divide it to more notes, thank you!": "请减少内容或是将内容切成更多笔记,谢谢!", + "Reach the limit": "达到上限", + "Sorry, you've reached the max length this note can be.": "抱歉,您的这篇笔记已达到可用的最大长度。", + "Please reduce the content or divide it to more notes, thank you!": "请减少笔记的内容。", "Import from Gist": "从 Gist 导入", - "Paste your gist url here...": "在这里贴上 gist 网址...", + "Paste your gist url here...": "在这里粘贴 Gist 网址...", "Import from Snippet": "从 Snippet 导入", "Select From Available Projects": "从可用的项目中选择", "Select From Available Snippets": "从可用的 Snippets 中选择", - "OR": "或是", + "OR": "或", "Export to Snippet": "导出到 Snippet", "Select Visibility Level": "选择可见层级", "Night Theme": "夜间主题", - "Follow us on %s and %s.": "在%s和%s上关注我们", - "Privacy": "隐私政策", + "Follow us on %s and %s.": "在 %s 和 %s 上关注我们", + "Privacy": "隐私", "Terms of Use": "使用条款", - "Do you really want to delete your user account?": "你确定真的想要删除帐户?", - "This will delete your account, all notes that are owned by you and remove all references to your account from other notes.": "我们将会删除你的帐户、你所拥有的笔记、以及你在别人笔记里的作者纪录。", + "Do you really want to delete your user account?": "您确定要删除帐户吗?", + "This will delete your account, all notes that are owned by you and remove all references to your account from other notes.": "您的帐户、您所拥有的笔记、他人笔记中对您帐户的引用都将被删除。", "Delete user": "删除帐户", - "Export user data": "汇出使用者资料", - "Help us translating on %s": "来 %s 帮我们翻译", - "Source Code": "源码" -} \ No newline at end of file + "Export user data": "导出用户数据", + "Help us translating on %s": "在 %s 上帮我们翻译", + "Source Code": "源代码", + "Powered by %s": "由 %s 驱动", + "Register": "注册", + "Export with pandoc": "使用 Pandoc 导出", + "Select output format": "选择输出格式" +} diff --git a/package-lock.json b/package-lock.json index 19377a7af6..8bb4481ffe 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "codimd", - "version": "2.1.0", + "version": "2.2.0", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -1085,6 +1085,266 @@ "@types/node": "*" } }, + "@types/d3": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/@types/d3/-/d3-5.7.2.tgz", + "integrity": "sha512-7/wClB8ycneWGy3jdvLfXKTd5SoTg9hji7IdJ0RuO9xTY54YpJ8zlcFADcXhY1J3kCBwxp+/1jeN6a5OMwgYOw==", + "dev": true, + "requires": { + "@types/d3-array": "^1", + "@types/d3-axis": "*", + "@types/d3-brush": "*", + "@types/d3-chord": "*", + "@types/d3-collection": "*", + "@types/d3-color": "*", + "@types/d3-contour": "*", + "@types/d3-dispatch": "*", + "@types/d3-drag": "*", + "@types/d3-dsv": "*", + "@types/d3-ease": "*", + "@types/d3-fetch": "*", + "@types/d3-force": "*", + "@types/d3-format": "*", + "@types/d3-geo": "*", + "@types/d3-hierarchy": "*", + "@types/d3-interpolate": "*", + "@types/d3-path": "*", + "@types/d3-polygon": "*", + "@types/d3-quadtree": "*", + "@types/d3-random": "*", + "@types/d3-scale": "*", + "@types/d3-scale-chromatic": "*", + "@types/d3-selection": "*", + "@types/d3-shape": "*", + "@types/d3-time": "*", + "@types/d3-time-format": "*", + "@types/d3-timer": "*", + "@types/d3-transition": "*", + "@types/d3-voronoi": "*", + "@types/d3-zoom": "*" + } + }, + "@types/d3-array": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/@types/d3-array/-/d3-array-1.2.7.tgz", + "integrity": "sha512-51vHWuUyDOi+8XuwPrTw3cFqyh2Slg9y8COYkRfjCPG9TfYqY0hoNPzv/8BrcAy0FeQBzqEo/D/8Nk2caOQJnA==", + "dev": true + }, + "@types/d3-axis": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/@types/d3-axis/-/d3-axis-1.0.12.tgz", + "integrity": "sha512-BZISgSD5M8TgURyNtcPAmUB9sk490CO1Thb6/gIn0WZTt3Y50IssX+2Z0vTccoqZksUDTep0b+o4ofXslvNbqg==", + "dev": true, + "requires": { + "@types/d3-selection": "*" + } + }, + "@types/d3-brush": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@types/d3-brush/-/d3-brush-1.1.1.tgz", + "integrity": "sha512-Exx14trm/q2cskHyMjCrdDllOQ35r1/pmZXaOIt8bBHwYNk722vWY3VxHvN0jdFFX7p2iL3+gD+cGny/aEmhlw==", + "dev": true, + "requires": { + "@types/d3-selection": "*" + } + }, + "@types/d3-chord": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@types/d3-chord/-/d3-chord-1.0.9.tgz", + "integrity": "sha512-UA6lI9CVW5cT5Ku/RV4hxoFn4mKySHm7HEgodtfRthAj1lt9rKZEPon58vyYfk+HIAm33DtJJgZwMXy2QgyPXw==", + "dev": true + }, + "@types/d3-collection": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/d3-collection/-/d3-collection-1.0.8.tgz", + "integrity": "sha512-y5lGlazdc0HNO0F3UUX2DPE7OmYvd9Kcym4hXwrJcNUkDaypR5pX+apuMikl9LfTxKItJsY9KYvzBulpCKyvuQ==", + "dev": true + }, + "@types/d3-color": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@types/d3-color/-/d3-color-1.2.2.tgz", + "integrity": "sha512-6pBxzJ8ZP3dYEQ4YjQ+NVbQaOflfgXq/JbDiS99oLobM2o72uAST4q6yPxHv6FOTCRC/n35ktuo8pvw/S4M7sw==", + "dev": true + }, + "@types/d3-contour": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@types/d3-contour/-/d3-contour-1.3.0.tgz", + "integrity": "sha512-AUCUIjEnC5lCGBM9hS+MryRaFLIrPls4Rbv6ktqbd+TK/RXZPwOy9rtBWmGpbeXcSOYCJTUDwNJuEnmYPJRxHQ==", + "dev": true, + "requires": { + "@types/d3-array": "*", + "@types/geojson": "*" + } + }, + "@types/d3-dispatch": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/d3-dispatch/-/d3-dispatch-1.0.8.tgz", + "integrity": "sha512-lCDtqoYez0TgFN3FljBXrz2icqeSzD0gufGook6DPBia+NOh2TBfogjHIsmNa/a+ZOewlHtq4cgLY80O1uLymw==", + "dev": true + }, + "@types/d3-drag": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@types/d3-drag/-/d3-drag-1.2.3.tgz", + "integrity": "sha512-rWB5SPvkYVxW3sqUxHOJUZwifD0KqvKwvt1bhNqcLpW6Azsd0BJgRNcyVW8GAferaAk5r8dzeZnf9zKlg9+xMQ==", + "dev": true, + "requires": { + "@types/d3-selection": "*" + } + }, + "@types/d3-dsv": { + "version": "1.0.36", + "resolved": "https://registry.npmjs.org/@types/d3-dsv/-/d3-dsv-1.0.36.tgz", + "integrity": "sha512-jbIWQ27QJcBNMZbQv0NSQMHnBDCmxghAxePxgyiPH1XPCRkOsTBei7jcdi3fDrUCGpCV3lKrSZFSlOkhUQVClA==", + "dev": true + }, + "@types/d3-ease": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@types/d3-ease/-/d3-ease-1.0.9.tgz", + "integrity": "sha512-U5ADevQ+W6fy32FVZZC9EXallcV/Mi12A5Tkd0My5MrC7T8soMQEhlDAg88XUWm0zoCQlB4XV0en/24LvuDB4Q==", + "dev": true + }, + "@types/d3-fetch": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@types/d3-fetch/-/d3-fetch-1.1.5.tgz", + "integrity": "sha512-o9c0ItT5/Gl3wbNuVpzRnYX1t3RghzeWAjHUVLuyZJudiTxC4f/fC0ZPFWLQ2lVY8pAMmxpV8TJ6ETYCgPeI3A==", + "dev": true, + "requires": { + "@types/d3-dsv": "*" + } + }, + "@types/d3-force": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@types/d3-force/-/d3-force-1.2.1.tgz", + "integrity": "sha512-jqK+I36uz4kTBjyk39meed5y31Ab+tXYN/x1dn3nZEus9yOHCLc+VrcIYLc/aSQ0Y7tMPRlIhLetulME76EiiA==", + "dev": true + }, + "@types/d3-format": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@types/d3-format/-/d3-format-1.3.1.tgz", + "integrity": "sha512-KAWvReOKMDreaAwOjdfQMm0HjcUMlQG47GwqdVKgmm20vTd2pucj0a70c3gUSHrnsmo6H2AMrkBsZU2UhJLq8A==", + "dev": true + }, + "@types/d3-geo": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@types/d3-geo/-/d3-geo-1.11.1.tgz", + "integrity": "sha512-Ox8WWOG3igDRoep/dNsGbOiSJYdUG3ew/6z0ETvHyAtXZVBjOE0S96zSSmzgl0gqQ3RdZjn2eeJOj9oRcMZPkQ==", + "dev": true, + "requires": { + "@types/geojson": "*" + } + }, + "@types/d3-hierarchy": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/@types/d3-hierarchy/-/d3-hierarchy-1.1.6.tgz", + "integrity": "sha512-vvSaIDf/Ov0o3KwMT+1M8+WbnnlRiGjlGD5uvk83a1mPCTd/E5x12bUJ/oP55+wUY/4Kb5kc67rVpVGJ2KUHxg==", + "dev": true + }, + "@types/d3-interpolate": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@types/d3-interpolate/-/d3-interpolate-1.3.1.tgz", + "integrity": "sha512-z8Zmi08XVwe8e62vP6wcA+CNuRhpuUU5XPEfqpG0hRypDE5BWNthQHB1UNWWDB7ojCbGaN4qBdsWp5kWxhT1IQ==", + "dev": true, + "requires": { + "@types/d3-color": "*" + } + }, + "@types/d3-path": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/d3-path/-/d3-path-1.0.8.tgz", + "integrity": "sha512-AZGHWslq/oApTAHu9+yH/Bnk63y9oFOMROtqPAtxl5uB6qm1x2lueWdVEjsjjV3Qc2+QfuzKIwIR5MvVBakfzA==", + "dev": true + }, + "@types/d3-polygon": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/@types/d3-polygon/-/d3-polygon-1.0.7.tgz", + "integrity": "sha512-Xuw0eSjQQKs8jTiNbntWH0S+Xp+JyhqxmQ0YAQ3rDu6c3kKMFfgsaGN7Jv5u3zG6yVX/AsLP/Xs/QRjmi9g43Q==", + "dev": true + }, + "@types/d3-quadtree": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/@types/d3-quadtree/-/d3-quadtree-1.0.7.tgz", + "integrity": "sha512-0ajFawWicfjsaCLh6NzxOyVDYhQAmMFbsiI3MPGLInorauHFEh9/Cl6UHNf+kt/J1jfoxKY/ZJaKAoDpbvde5Q==", + "dev": true + }, + "@types/d3-random": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@types/d3-random/-/d3-random-1.1.2.tgz", + "integrity": "sha512-Jui+Zn28pQw/3EayPKaN4c/PqTvqNbIPjHkgIIFnxne1FdwNjfHtAIsZIBMKlquQNrrMjFzCrlF2gPs3xckqaA==", + "dev": true + }, + "@types/d3-scale": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@types/d3-scale/-/d3-scale-2.2.0.tgz", + "integrity": "sha512-oQFanN0/PiR2oySHfj+zAAkK1/p4LD32Nt1TMVmzk+bYHk7vgIg/iTXQWitp1cIkDw4LMdcgvO63wL+mNs47YA==", + "dev": true, + "requires": { + "@types/d3-time": "*" + } + }, + "@types/d3-scale-chromatic": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@types/d3-scale-chromatic/-/d3-scale-chromatic-1.5.0.tgz", + "integrity": "sha512-9/D7cOBKdZdTCPc6re0HeSUFBM0aFzdNdmYggUWT9SRRiYSOa6Ys2xdTwHKgc1WS3gGfwTMatBOdWCS863REsg==", + "dev": true + }, + "@types/d3-selection": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/@types/d3-selection/-/d3-selection-1.4.2.tgz", + "integrity": "sha512-ksY8UxvTXpzD91Dy3D9zZg98yF2ZEPMKJd8ZQJlZt1QH3Xxr08s6fESEdC2l0Kbe6Xd9VhaoJX06cRaMR1lEnA==", + "dev": true + }, + "@types/d3-shape": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@types/d3-shape/-/d3-shape-1.3.2.tgz", + "integrity": "sha512-LtD8EaNYCaBRzHzaAiIPrfcL3DdIysc81dkGlQvv7WQP3+YXV7b0JJTtR1U3bzeRieS603KF4wUo+ZkJVenh8w==", + "dev": true, + "requires": { + "@types/d3-path": "*" + } + }, + "@types/d3-time": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/@types/d3-time/-/d3-time-1.0.10.tgz", + "integrity": "sha512-aKf62rRQafDQmSiv1NylKhIMmznsjRN+MnXRXTqHoqm0U/UZzVpdrtRnSIfdiLS616OuC1soYeX1dBg2n1u8Xw==", + "dev": true + }, + "@types/d3-time-format": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@types/d3-time-format/-/d3-time-format-2.1.1.tgz", + "integrity": "sha512-tJSyXta8ZyJ52wDDHA96JEsvkbL6jl7wowGmuf45+fAkj5Y+SQOnz0N7/H68OWmPshPsAaWMQh+GAws44IzH3g==", + "dev": true + }, + "@types/d3-timer": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@types/d3-timer/-/d3-timer-1.0.9.tgz", + "integrity": "sha512-WvfJ3LFxBbWjqRGz9n7GJt08RrTHPJDVsIwwoCMROlqF+iDacYiAFjf9oqnq0mXpb2juA2N/qjKP+MKdal3YNQ==", + "dev": true + }, + "@types/d3-transition": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/@types/d3-transition/-/d3-transition-1.1.6.tgz", + "integrity": "sha512-/F+O2r4oz4G9ATIH3cuSCMGphAnl7VDx7SbENEK0NlI/FE8Jx2oiIrv0uTrpg7yF/AmuWbqp7AGdEHAPIh24Gg==", + "dev": true, + "requires": { + "@types/d3-selection": "*" + } + }, + "@types/d3-voronoi": { + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/@types/d3-voronoi/-/d3-voronoi-1.1.9.tgz", + "integrity": "sha512-DExNQkaHd1F3dFPvGA/Aw2NGyjMln6E9QzsiqOcBgnE+VInYnFBHBBySbZQts6z6xD+5jTfKCP7M4OqMyVjdwQ==", + "dev": true + }, + "@types/d3-zoom": { + "version": "1.7.4", + "resolved": "https://registry.npmjs.org/@types/d3-zoom/-/d3-zoom-1.7.4.tgz", + "integrity": "sha512-5jnFo/itYhJeB2khO/lKe730kW/h2EbKMOvY0uNp3+7NdPm4w63DwPEMxifQZ7n902xGYK5DdU67FmToSoy4VA==", + "dev": true, + "requires": { + "@types/d3-interpolate": "*", + "@types/d3-selection": "*" + } + }, "@types/estree": { "version": "0.0.39", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.39.tgz", @@ -1116,6 +1376,12 @@ "integrity": "sha512-mky/O83TXmGY39P1H9YbUpjV6l6voRYlufqfFCvel8l1phuy8HRjdWc1rrPuN53ITBJlbyMSV6z3niOySO5pgQ==", "dev": true }, + "@types/geojson": { + "version": "7946.0.7", + "resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.7.tgz", + "integrity": "sha512-wE2v81i4C4Ol09RtsWFAqg3BUitWbHSpSlIo+bNdsCJijO9sjme+zm+73ZMCa/qMC8UEERxzGbvmr1cffo2SiQ==", + "dev": true + }, "@types/http-assert": { "version": "1.5.1", "resolved": "https://registry.npmjs.org/@types/http-assert/-/http-assert-1.5.1.tgz", @@ -4665,6 +4931,15 @@ "d3-dsv": "1" } }, + "d3-flextree": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/d3-flextree/-/d3-flextree-2.1.1.tgz", + "integrity": "sha512-P0SK6bRm0PT5ZZON8Lh/aOcFfr4rO53kaYCXCgwBCvsHJcYjtOb8fNrgmpOmCMCgfrT9EczXzD8wqEO8mlkBrA==", + "dev": true, + "requires": { + "d3-hierarchy": "^1.1.5" + } + }, "d3-force": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/d3-force/-/d3-force-1.2.1.tgz", @@ -7159,28 +7434,28 @@ "dependencies": { "abbrev": { "version": "1.1.1", - "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", + "resolved": false, "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", "dev": true, "optional": true }, "ansi-regex": { "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "resolved": false, "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", "dev": true, "optional": true }, "aproba": { "version": "1.2.0", - "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz", + "resolved": false, "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==", "dev": true, "optional": true }, "are-we-there-yet": { "version": "1.1.5", - "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz", + "resolved": false, "integrity": "sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w==", "dev": true, "optional": true, @@ -7191,14 +7466,14 @@ }, "balanced-match": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", + "resolved": false, "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", "dev": true, "optional": true }, "brace-expansion": { "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "resolved": false, "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", "dev": true, "optional": true, @@ -7209,42 +7484,42 @@ }, "chownr": { "version": "1.1.3", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.3.tgz", + "resolved": false, "integrity": "sha512-i70fVHhmV3DtTl6nqvZOnIjbY0Pe4kAUjwHj8z0zAdgBtYrJyYwLKCCuRBQ5ppkyL0AkN7HKRnETdmdp1zqNXw==", "dev": true, "optional": true }, "code-point-at": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", + "resolved": false, "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=", "dev": true, "optional": true }, "concat-map": { "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "resolved": false, "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", "dev": true, "optional": true }, "console-control-strings": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", + "resolved": false, "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=", "dev": true, "optional": true }, "core-util-is": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "resolved": false, "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", "dev": true, "optional": true }, "debug": { "version": "3.2.6", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", + "resolved": false, "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", "dev": true, "optional": true, @@ -7254,28 +7529,28 @@ }, "deep-extend": { "version": "0.6.0", - "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", + "resolved": false, "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", "dev": true, "optional": true }, "delegates": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", + "resolved": false, "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=", "dev": true, "optional": true }, "detect-libc": { "version": "1.0.3", - "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", + "resolved": false, "integrity": "sha1-+hN8S9aY7fVc1c0CrFWfkaTEups=", "dev": true, "optional": true }, "fs-minipass": { "version": "1.2.7", - "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-1.2.7.tgz", + "resolved": false, "integrity": "sha512-GWSSJGFy4e9GUeCcbIkED+bgAoFyj7XF1mV8rma3QW4NIqX9Kyx79N/PF61H5udOV3aY1IaMLs6pGbH71nlCTA==", "dev": true, "optional": true, @@ -7285,14 +7560,14 @@ }, "fs.realpath": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "resolved": false, "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", "dev": true, "optional": true }, "gauge": { "version": "2.7.4", - "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz", + "resolved": false, "integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=", "dev": true, "optional": true, @@ -7309,7 +7584,7 @@ }, "glob": { "version": "7.1.6", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", + "resolved": false, "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", "dev": true, "optional": true, @@ -7324,14 +7599,14 @@ }, "has-unicode": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", + "resolved": false, "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=", "dev": true, "optional": true }, "iconv-lite": { "version": "0.4.24", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "resolved": false, "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", "dev": true, "optional": true, @@ -7341,7 +7616,7 @@ }, "ignore-walk": { "version": "3.0.3", - "resolved": "https://registry.npmjs.org/ignore-walk/-/ignore-walk-3.0.3.tgz", + "resolved": false, "integrity": "sha512-m7o6xuOaT1aqheYHKf8W6J5pYH85ZI9w077erOzLje3JsB1gkafkAhHHY19dqjulgIZHFm32Cp5uNZgcQqdJKw==", "dev": true, "optional": true, @@ -7351,7 +7626,7 @@ }, "inflight": { "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "resolved": false, "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", "dev": true, "optional": true, @@ -7362,21 +7637,21 @@ }, "inherits": { "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "resolved": false, "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", "dev": true, "optional": true }, "ini": { "version": "1.3.5", - "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz", + "resolved": false, "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==", "dev": true, "optional": true }, "is-fullwidth-code-point": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", + "resolved": false, "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", "dev": true, "optional": true, @@ -7386,14 +7661,14 @@ }, "isarray": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "resolved": false, "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", "dev": true, "optional": true }, "minimatch": { "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "resolved": false, "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", "dev": true, "optional": true, @@ -7403,14 +7678,14 @@ }, "minimist": { "version": "0.0.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", + "resolved": false, "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", "dev": true, "optional": true }, "minipass": { "version": "2.9.0", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-2.9.0.tgz", + "resolved": false, "integrity": "sha512-wxfUjg9WebH+CUDX/CdbRlh5SmfZiy/hpkxaRI16Y9W56Pa75sWgd/rvFilSgrauD9NyFymP/+JFV3KwzIsJeg==", "dev": true, "optional": true, @@ -7421,7 +7696,7 @@ }, "minizlib": { "version": "1.3.3", - "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-1.3.3.tgz", + "resolved": false, "integrity": "sha512-6ZYMOEnmVsdCeTJVE0W9ZD+pVnE8h9Hma/iOwwRDsdQoePpoX56/8B6z3P9VNwppJuBKNRuFDRNRqRWexT9G9Q==", "dev": true, "optional": true, @@ -7431,7 +7706,7 @@ }, "mkdirp": { "version": "0.5.1", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "resolved": false, "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", "dev": true, "optional": true, @@ -7441,14 +7716,14 @@ }, "ms": { "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "resolved": false, "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", "dev": true, "optional": true }, "needle": { "version": "2.4.0", - "resolved": "https://registry.npmjs.org/needle/-/needle-2.4.0.tgz", + "resolved": false, "integrity": "sha512-4Hnwzr3mi5L97hMYeNl8wRW/Onhy4nUKR/lVemJ8gJedxxUyBLm9kkrDColJvoSfwi0jCNhD+xCdOtiGDQiRZg==", "dev": true, "optional": true, @@ -7460,7 +7735,7 @@ }, "node-pre-gyp": { "version": "0.14.0", - "resolved": "https://registry.npmjs.org/node-pre-gyp/-/node-pre-gyp-0.14.0.tgz", + "resolved": false, "integrity": "sha512-+CvDC7ZttU/sSt9rFjix/P05iS43qHCOOGzcr3Ry99bXG7VX953+vFyEuph/tfqoYu8dttBkE86JSKBO2OzcxA==", "dev": true, "optional": true, @@ -7479,7 +7754,7 @@ }, "nopt": { "version": "4.0.1", - "resolved": "https://registry.npmjs.org/nopt/-/nopt-4.0.1.tgz", + "resolved": false, "integrity": "sha1-0NRoWv1UFRk8jHUFYC0NF81kR00=", "dev": true, "optional": true, @@ -7490,7 +7765,7 @@ }, "npm-bundled": { "version": "1.1.1", - "resolved": "https://registry.npmjs.org/npm-bundled/-/npm-bundled-1.1.1.tgz", + "resolved": false, "integrity": "sha512-gqkfgGePhTpAEgUsGEgcq1rqPXA+tv/aVBlgEzfXwA1yiUJF7xtEt3CtVwOjNYQOVknDk0F20w58Fnm3EtG0fA==", "dev": true, "optional": true, @@ -7500,14 +7775,14 @@ }, "npm-normalize-package-bin": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/npm-normalize-package-bin/-/npm-normalize-package-bin-1.0.1.tgz", + "resolved": false, "integrity": "sha512-EPfafl6JL5/rU+ot6P3gRSCpPDW5VmIzX959Ob1+ySFUuuYHWHekXpwdUZcKP5C+DS4GEtdJluwBjnsNDl+fSA==", "dev": true, "optional": true }, "npm-packlist": { "version": "1.4.7", - "resolved": "https://registry.npmjs.org/npm-packlist/-/npm-packlist-1.4.7.tgz", + "resolved": false, "integrity": "sha512-vAj7dIkp5NhieaGZxBJB8fF4R0078rqsmhJcAfXZ6O7JJhjhPK96n5Ry1oZcfLXgfun0GWTZPOxaEyqv8GBykQ==", "dev": true, "optional": true, @@ -7518,7 +7793,7 @@ }, "npmlog": { "version": "4.1.2", - "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz", + "resolved": false, "integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==", "dev": true, "optional": true, @@ -7531,21 +7806,21 @@ }, "number-is-nan": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", + "resolved": false, "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", "dev": true, "optional": true }, "object-assign": { "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "resolved": false, "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", "dev": true, "optional": true }, "once": { "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "resolved": false, "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", "dev": true, "optional": true, @@ -7555,21 +7830,21 @@ }, "os-homedir": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", + "resolved": false, "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=", "dev": true, "optional": true }, "os-tmpdir": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", + "resolved": false, "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", "dev": true, "optional": true }, "osenv": { "version": "0.1.5", - "resolved": "https://registry.npmjs.org/osenv/-/osenv-0.1.5.tgz", + "resolved": false, "integrity": "sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==", "dev": true, "optional": true, @@ -7580,21 +7855,21 @@ }, "path-is-absolute": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "resolved": false, "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", "dev": true, "optional": true }, "process-nextick-args": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "resolved": false, "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", "dev": true, "optional": true }, "rc": { "version": "1.2.8", - "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", + "resolved": false, "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", "dev": true, "optional": true, @@ -7607,7 +7882,7 @@ "dependencies": { "minimist": { "version": "1.2.0", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "resolved": false, "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", "dev": true, "optional": true @@ -7616,7 +7891,7 @@ }, "readable-stream": { "version": "2.3.6", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", + "resolved": false, "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", "dev": true, "optional": true, @@ -7632,7 +7907,7 @@ }, "rimraf": { "version": "2.7.1", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", + "resolved": false, "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", "dev": true, "optional": true, @@ -7642,49 +7917,49 @@ }, "safe-buffer": { "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "resolved": false, "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", "dev": true, "optional": true }, "safer-buffer": { "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "resolved": false, "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", "dev": true, "optional": true }, "sax": { "version": "1.2.4", - "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", + "resolved": false, "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==", "dev": true, "optional": true }, "semver": { "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "resolved": false, "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", "dev": true, "optional": true }, "set-blocking": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "resolved": false, "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", "dev": true, "optional": true }, "signal-exit": { "version": "3.0.2", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", + "resolved": false, "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=", "dev": true, "optional": true }, "string-width": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "resolved": false, "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", "dev": true, "optional": true, @@ -7696,7 +7971,7 @@ }, "string_decoder": { "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "resolved": false, "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "dev": true, "optional": true, @@ -7706,7 +7981,7 @@ }, "strip-ansi": { "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "resolved": false, "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", "dev": true, "optional": true, @@ -7716,14 +7991,14 @@ }, "strip-json-comments": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "resolved": false, "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", "dev": true, "optional": true }, "tar": { "version": "4.4.13", - "resolved": "https://registry.npmjs.org/tar/-/tar-4.4.13.tgz", + "resolved": false, "integrity": "sha512-w2VwSrBoHa5BsSyH+KxEqeQBAllHhccyMFVHtGtdMpF4W7IRWfZjFiQceJPChOeTsSDVUpER2T8FA93pr0L+QA==", "dev": true, "optional": true, @@ -7739,14 +8014,14 @@ }, "util-deprecate": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "resolved": false, "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", "dev": true, "optional": true }, "wide-align": { "version": "1.1.3", - "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz", + "resolved": false, "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==", "dev": true, "optional": true, @@ -7756,14 +8031,14 @@ }, "wrappy": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "resolved": false, "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", "dev": true, "optional": true }, "yallist": { "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "resolved": false, "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", "dev": true, "optional": true @@ -8888,6 +9163,12 @@ "integrity": "sha1-YTObbyR1/Hcv2cnYP1yFddwVSuE=", "dev": true }, + "is-docker": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.0.0.tgz", + "integrity": "sha512-pJEdRugimx4fBMra5z2/5iRdZ63OhYV0vr0Dwm5+xtW4D1FvRkB8hamMIhnWfyJeDdyr/aa7BDyNbtG38VxgoQ==", + "dev": true + }, "is-dotfile": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/is-dotfile/-/is-dotfile-1.0.3.tgz", @@ -10180,6 +10461,63 @@ "resolved": "https://registry.npmjs.org/marked/-/marked-0.6.3.tgz", "integrity": "sha512-Fqa7eq+UaxfMriqzYLayfqAE40WN03jf+zHjT18/uXNuzjq3TY0XTbrAoPeqSJrAmPz11VuUA+kBPYOhHt9oOQ==" }, + "markmap-lib": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/markmap-lib/-/markmap-lib-0.4.2.tgz", + "integrity": "sha512-/1ITvE8HiI+8GLgL//ggH2LNy19Kg3nNcOijK/DcKtH7grJ1TO6k4No/IqCSiM8JjiQbxYDosSHGZKqp6Lyj1A==", + "dev": true, + "requires": { + "@babel/runtime": "^7.9.2", + "@types/d3": "^5.7.2", + "commander": "^5.0.0", + "d3": "^5.15.0", + "d3-flextree": "^2.1.1", + "open": "^7.0.3", + "remarkable": "^2.0.0" + }, + "dependencies": { + "@babel/runtime": { + "version": "7.9.2", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.9.2.tgz", + "integrity": "sha512-NE2DtOdufG7R5vnfQUTehdTfNycfUANEtCa9PssN9O/xmTzP4E08UI797ixaei6hBEVL9BI/PsdJS5x7mWoB9Q==", + "dev": true, + "requires": { + "regenerator-runtime": "^0.13.4" + } + }, + "autolinker": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/autolinker/-/autolinker-3.14.1.tgz", + "integrity": "sha512-yvsRHIaY51EYDml6MGlbqyJGfl4n7zezGYf+R7gvM8c5LNpRGc4SISkvgAswSS8SWxk/OrGCylKV9mJyVstz7w==", + "dev": true, + "requires": { + "tslib": "^1.9.3" + } + }, + "commander": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-5.0.0.tgz", + "integrity": "sha512-JrDGPAKjMGSP1G0DUoaceEJ3DZgAfr/q6X7FVk4+U5KxUSKviYGM2k6zWkfyyBHy5rAtzgYJFa1ro2O9PtoxwQ==", + "dev": true + }, + "regenerator-runtime": { + "version": "0.13.5", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.5.tgz", + "integrity": "sha512-ZS5w8CpKFinUzOwW3c83oPeVXoNsrLsaCoLtJvAClH135j/R77RuymhiSErhm2lKcwSCIpmvIWSbDkIfAqKQlA==", + "dev": true + }, + "remarkable": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/remarkable/-/remarkable-2.0.0.tgz", + "integrity": "sha512-3gvKFAgL4xmmVRKAMNm6UzDo/rO2gPVkZrWagp6AXEA4JvCcMcRx9aapYbb7AJAmLLvi/u06+EhzqoS7ha9qOg==", + "dev": true, + "requires": { + "argparse": "^1.0.10", + "autolinker": "^3.11.0" + } + } + } + }, "math-interval-parser": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/math-interval-parser/-/math-interval-parser-1.1.0.tgz", @@ -11490,6 +11828,24 @@ } } }, + "open": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/open/-/open-7.0.3.tgz", + "integrity": "sha512-sP2ru2v0P290WFfv49Ap8MF6PkzGNnGlAwHweB4WR4mr5d2d0woiCluUeJ218w7/+PmoBy9JmYgD5A4mLcWOFA==", + "dev": true, + "requires": { + "is-docker": "^2.0.0", + "is-wsl": "^2.1.1" + }, + "dependencies": { + "is-wsl": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.1.1.tgz", + "integrity": "sha512-umZHcSrwlDHo2TGMXv0DZ8dIUGunZ2Iv68YZnrmCiBPkZ4aaOhtv7pXJKeki9k3qJ3RJr0cDyitcl5wEH3AYog==", + "dev": true + } + } + }, "openid": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/openid/-/openid-2.0.6.tgz", @@ -11668,6 +12024,12 @@ "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==", "dev": true }, + "papaparse": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/papaparse/-/papaparse-5.2.0.tgz", + "integrity": "sha512-ylq1wgUSnagU+MKQtNeVqrPhZuMYBvOSL00DHycFTCxownF95gpLAk1HiHdUW77N8yxRq1qHXLdlIPyBSG9NSA==", + "dev": true + }, "parallel-transform": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/parallel-transform/-/parallel-transform-1.2.0.tgz", diff --git a/package.json b/package.json index 2fc667ec76..395390497b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "codimd", - "version": "2.1.0", + "version": "2.2.0", "description": "Realtime collaborative markdown notes on all platforms.", "keywords": [ "Collaborative", @@ -161,6 +161,7 @@ "markdown-it-sub": "~1.0.0", "markdown-it-sup": "~1.0.0", "markdownlint": "^0.17.0", + "markmap-lib": "^0.4.2", "mathjax": "~2.7.5", "mermaid": "~8.4.8", "mini-css-extract-plugin": "~0.4.1", @@ -168,6 +169,7 @@ "mock-require": "~3.0.3", "nyc": "~14.0.0", "optimize-css-assets-webpack-plugin": "~5.0.0", + "papaparse": "^5.2.0", "pdfobject": "~2.1.1", "plantuml-encoder": "^1.2.5", "power-assert": "~1.6.1", diff --git a/public/css/cover.css b/public/css/cover.css index 4693c9c3a1..ca9fa866dd 100644 --- a/public/css/cover.css +++ b/public/css/cover.css @@ -397,7 +397,23 @@ select { color: #fff; } -a.btn.btn-social > i.oauth-icon { +/* add btn-login-method replaced of btn-social, to avoid ad block delete the login button */ +.btn-login-method { + padding-left: 61px; + position: relative; + text-align: left; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} +.btn-login-method.btn-lg :first-child { + line-height:45px; + width:45px; + font-size:1.8em; +} +.btn-login-method>:first-child {position:absolute;left:0;top:0;bottom:0;width:32px;line-height:34px;font-size:1.6em;text-align:center;border-right:1px solid rgba(0,0,0,0.2)} + +a.btn.btn-login-method > i.oauth-icon { display: inline-flex; height: 45px; width: 45px; @@ -405,7 +421,7 @@ a.btn.btn-social > i.oauth-icon { padding: 6px; } -a.btn.btn-social > i.oauth-icon > img { +a.btn.btn-login-method > i.oauth-icon > img { width: 100%; height: 100%; line-height: inherit; diff --git a/public/css/extra.css b/public/css/extra.css index 2b23ad2413..e795bc1b01 100644 --- a/public/css/extra.css +++ b/public/css/extra.css @@ -84,6 +84,16 @@ height: 250px; } +/* markmap */ +.markmap-container { + height: 300px; +} + +.markmap-container > svg { + width: 100%; + height: 100%; +} + .MJX_Assistive_MathML { display: none; } diff --git a/public/css/font.css b/public/css/font.css index 4aa2921070..de3ef6f98f 100644 --- a/public/css/font.css +++ b/public/css/font.css @@ -3,7 +3,7 @@ font-family: 'Source Code Pro'; font-style: normal; font-weight: 300; - src: local('Source Code Pro Light'), local('SourceCodePro-Light'), url('/fonts/SourceCodePro-Light.woff') format('woff'); + src: local('Source Code Pro Light'), local('SourceCodePro-Light'), url('../fonts/SourceCodePro-Light.woff') format('woff'); unicode-range: U+0100-024F, U+1E00-1EFF, U+20A0-20AB, U+20AD-20CF, U+2C60-2C7F, U+A720-A7FF; } /* latin */ @@ -11,7 +11,7 @@ font-family: 'Source Code Pro'; font-style: normal; font-weight: 300; - src: local('Source Code Pro Light'), local('SourceCodePro-Light'), url('/fonts/SourceCodePro-Light.woff') format('woff'); + src: local('Source Code Pro Light'), local('SourceCodePro-Light'), url('../fonts/SourceCodePro-Light.woff') format('woff'); unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2212, U+2215, U+E0FF, U+EFFD, U+F000; } /* latin-ext */ @@ -19,7 +19,7 @@ font-family: 'Source Code Pro'; font-style: normal; font-weight: 400; - src: local('Source Code Pro'), local('SourceCodePro-Regular'), url('/fonts/SourceCodePro-Regular.woff') format('woff'); + src: local('Source Code Pro'), local('SourceCodePro-Regular'), url('../fonts/SourceCodePro-Regular.woff') format('woff'); unicode-range: U+0100-024F, U+1E00-1EFF, U+20A0-20AB, U+20AD-20CF, U+2C60-2C7F, U+A720-A7FF; } /* latin */ @@ -27,7 +27,7 @@ font-family: 'Source Code Pro'; font-style: normal; font-weight: 400; - src: local('Source Code Pro'), local('SourceCodePro-Regular'), url('/fonts/SourceCodePro-Regular.woff') format('woff'); + src: local('Source Code Pro'), local('SourceCodePro-Regular'), url('../fonts/SourceCodePro-Regular.woff') format('woff'); unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2212, U+2215, U+E0FF, U+EFFD, U+F000; } /* latin-ext */ @@ -35,7 +35,7 @@ font-family: 'Source Code Pro'; font-style: normal; font-weight: 500; - src: local('Source Code Pro Medium'), local('SourceCodePro-Medium'), url('/fonts/SourceCodePro-Medium.woff') format('woff'); + src: local('Source Code Pro Medium'), local('SourceCodePro-Medium'), url('../fonts/SourceCodePro-Medium.woff') format('woff'); unicode-range: U+0100-024F, U+1E00-1EFF, U+20A0-20AB, U+20AD-20CF, U+2C60-2C7F, U+A720-A7FF; } /* latin */ @@ -43,7 +43,7 @@ font-family: 'Source Code Pro'; font-style: normal; font-weight: 500; - src: local('Source Code Pro Medium'), local('SourceCodePro-Medium'), url('/fonts/SourceCodePro-Medium.woff') format('woff'); + src: local('Source Code Pro Medium'), local('SourceCodePro-Medium'), url('../fonts/SourceCodePro-Medium.woff') format('woff'); unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2212, U+2215, U+E0FF, U+EFFD, U+F000; } /* vietnamese */ @@ -51,7 +51,7 @@ font-family: 'Source Sans Pro'; font-style: normal; font-weight: 300; - src: local('Source Sans Pro Light'), local('SourceSansPro-Light'), url('/fonts/SourceCodePro-Medium.woff') format('woff'); + src: local('Source Sans Pro Light'), local('SourceSansPro-Light'), url('../fonts/SourceCodePro-Medium.woff') format('woff'); unicode-range: U+0102-0103, U+1EA0-1EF9, U+20AB; } /* latin-ext */ @@ -59,7 +59,7 @@ font-family: 'Source Sans Pro'; font-style: normal; font-weight: 300; - src: local('Source Sans Pro Light'), local('SourceSansPro-Light'), url('/fonts/SourceSansPro-Light.woff') format('woff'); + src: local('Source Sans Pro Light'), local('SourceSansPro-Light'), url('../fonts/SourceSansPro-Light.woff') format('woff'); unicode-range: U+0100-024F, U+1E00-1EFF, U+20A0-20AB, U+20AD-20CF, U+2C60-2C7F, U+A720-A7FF; } /* latin */ @@ -67,7 +67,7 @@ font-family: 'Source Sans Pro'; font-style: normal; font-weight: 300; - src: local('Source Sans Pro Light'), local('SourceSansPro-Light'), url('/fonts/SourceSansPro-Light.woff') format('woff'); + src: local('Source Sans Pro Light'), local('SourceSansPro-Light'), url('../fonts/SourceSansPro-Light.woff') format('woff'); unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2212, U+2215, U+E0FF, U+EFFD, U+F000; } /* vietnamese */ @@ -75,7 +75,7 @@ font-family: 'Source Sans Pro'; font-style: normal; font-weight: 400; - src: local('Source Sans Pro'), local('SourceSansPro-Regular'), url('/fonts/SourceSansPro-Regular.woff') format('woff'); + src: local('Source Sans Pro'), local('SourceSansPro-Regular'), url('../fonts/SourceSansPro-Regular.woff') format('woff'); unicode-range: U+0102-0103, U+1EA0-1EF9, U+20AB; } /* latin-ext */ @@ -83,7 +83,7 @@ font-family: 'Source Sans Pro'; font-style: normal; font-weight: 400; - src: local('Source Sans Pro'), local('SourceSansPro-Regular'), url('/fonts/SourceSansPro-Regular.woff') format('woff'); + src: local('Source Sans Pro'), local('SourceSansPro-Regular'), url('../fonts/SourceSansPro-Regular.woff') format('woff'); unicode-range: U+0100-024F, U+1E00-1EFF, U+20A0-20AB, U+20AD-20CF, U+2C60-2C7F, U+A720-A7FF; } /* latin */ @@ -91,7 +91,7 @@ font-family: 'Source Sans Pro'; font-style: normal; font-weight: 400; - src: local('Source Sans Pro'), local('SourceSansPro-Regular'), url('/fonts/SourceSansPro-Regular.woff') format('woff'); + src: local('Source Sans Pro'), local('SourceSansPro-Regular'), url('../fonts/SourceSansPro-Regular.woff') format('woff'); unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2212, U+2215, U+E0FF, U+EFFD, U+F000; } /* vietnamese */ @@ -99,7 +99,7 @@ font-family: 'Source Sans Pro'; font-style: normal; font-weight: 600; - src: local('Source Sans Pro Semibold'), local('SourceSansPro-Semibold'), url('/fonts/SourceSansPro-Semibold.woff') format('woff'); + src: local('Source Sans Pro Semibold'), local('SourceSansPro-Semibold'), url('../fonts/SourceSansPro-Semibold.woff') format('woff'); unicode-range: U+0102-0103, U+1EA0-1EF9, U+20AB; } /* latin-ext */ @@ -107,7 +107,7 @@ font-family: 'Source Sans Pro'; font-style: normal; font-weight: 600; - src: local('Source Sans Pro Semibold'), local('SourceSansPro-Semibold'), url('/fonts/SourceSansPro-Semibold.woff') format('woff'); + src: local('Source Sans Pro Semibold'), local('SourceSansPro-Semibold'), url('../fonts/SourceSansPro-Semibold.woff') format('woff'); unicode-range: U+0100-024F, U+1E00-1EFF, U+20A0-20AB, U+20AD-20CF, U+2C60-2C7F, U+A720-A7FF; } /* latin */ @@ -115,7 +115,7 @@ font-family: 'Source Sans Pro'; font-style: normal; font-weight: 600; - src: local('Source Sans Pro Semibold'), local('SourceSansPro-Semibold'), url('/fonts/SourceSansPro-Semibold.woff') format('woff'); + src: local('Source Sans Pro Semibold'), local('SourceSansPro-Semibold'), url('../fonts/SourceSansPro-Semibold.woff') format('woff'); unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2212, U+2215, U+E0FF, U+EFFD, U+F000; } /* vietnamese */ @@ -123,7 +123,7 @@ font-family: 'Source Sans Pro'; font-style: italic; font-weight: 300; - src: local('Source Sans Pro Light Italic'), local('SourceSansPro-LightIt'), url('/fonts/SourceSansPro-LightItalic.woff') format('woff'); + src: local('Source Sans Pro Light Italic'), local('SourceSansPro-LightIt'), url('../fonts/SourceSansPro-LightItalic.woff') format('woff'); unicode-range: U+0102-0103, U+1EA0-1EF9, U+20AB; } /* latin-ext */ @@ -131,7 +131,7 @@ font-family: 'Source Sans Pro'; font-style: italic; font-weight: 300; - src: local('Source Sans Pro Light Italic'), local('SourceSansPro-LightIt'), url('/fonts/SourceSansPro-LightItalic.woff') format('woff'); + src: local('Source Sans Pro Light Italic'), local('SourceSansPro-LightIt'), url('../fonts/SourceSansPro-LightItalic.woff') format('woff'); unicode-range: U+0100-024F, U+1E00-1EFF, U+20A0-20AB, U+20AD-20CF, U+2C60-2C7F, U+A720-A7FF; } /* latin */ @@ -139,7 +139,7 @@ font-family: 'Source Sans Pro'; font-style: italic; font-weight: 300; - src: local('Source Sans Pro Light Italic'), local('SourceSansPro-LightIt'), url('/fonts/SourceSansPro-LightItalic.woff') format('woff'); + src: local('Source Sans Pro Light Italic'), local('SourceSansPro-LightIt'), url('../fonts/SourceSansPro-LightItalic.woff') format('woff'); unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2212, U+2215, U+E0FF, U+EFFD, U+F000; } /* vietnamese */ @@ -147,7 +147,7 @@ font-family: 'Source Sans Pro'; font-style: italic; font-weight: 400; - src: local('Source Sans Pro Italic'), local('SourceSansPro-It'), url('/fonts/SourceSansPro-Italic.woff') format('woff'); + src: local('Source Sans Pro Italic'), local('SourceSansPro-It'), url('../fonts/SourceSansPro-Italic.woff') format('woff'); unicode-range: U+0102-0103, U+1EA0-1EF9, U+20AB; } /* latin-ext */ @@ -155,7 +155,7 @@ font-family: 'Source Sans Pro'; font-style: italic; font-weight: 400; - src: local('Source Sans Pro Italic'), local('SourceSansPro-It'), url('/fonts/SourceSansPro-Italic.woff') format('woff'); + src: local('Source Sans Pro Italic'), local('SourceSansPro-It'), url('../fonts/SourceSansPro-Italic.woff') format('woff'); unicode-range: U+0100-024F, U+1E00-1EFF, U+20A0-20AB, U+20AD-20CF, U+2C60-2C7F, U+A720-A7FF; } /* latin */ @@ -163,7 +163,7 @@ font-family: 'Source Sans Pro'; font-style: italic; font-weight: 400; - src: local('Source Sans Pro Italic'), local('SourceSansPro-It'), url('/fonts/SourceSansPro-Italic.woff') format('woff'); + src: local('Source Sans Pro Italic'), local('SourceSansPro-It'), url('../fonts/SourceSansPro-Italic.woff') format('woff'); unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2212, U+2215, U+E0FF, U+EFFD, U+F000; } /* vietnamese */ @@ -171,7 +171,7 @@ font-family: 'Source Sans Pro'; font-style: italic; font-weight: 600; - src: local('Source Sans Pro Semibold Italic'), local('SourceSansPro-SemiboldIt'), url('/fonts/SourceSansPro-SemiboldItalic.woff') format('woff'); + src: local('Source Sans Pro Semibold Italic'), local('SourceSansPro-SemiboldIt'), url('../fonts/SourceSansPro-SemiboldItalic.woff') format('woff'); unicode-range: U+0102-0103, U+1EA0-1EF9, U+20AB; } /* latin-ext */ @@ -179,7 +179,7 @@ font-family: 'Source Sans Pro'; font-style: italic; font-weight: 600; - src: local('Source Sans Pro Semibold Italic'), local('SourceSansPro-SemiboldIt'), url('/fonts/SourceSansPro-SemiboldItalic.woff') format('woff'); + src: local('Source Sans Pro Semibold Italic'), local('SourceSansPro-SemiboldIt'), url('../fonts/SourceSansPro-SemiboldItalic.woff') format('woff'); unicode-range: U+0100-024F, U+1E00-1EFF, U+20A0-20AB, U+20AD-20CF, U+2C60-2C7F, U+A720-A7FF; } /* latin */ @@ -187,7 +187,7 @@ font-family: 'Source Sans Pro'; font-style: italic; font-weight: 600; - src: local('Source Sans Pro Semibold Italic'), local('SourceSansPro-SemiboldIt'), url('/fonts/SourceSansPro-SemiboldItalic.woff') format('woff'); + src: local('Source Sans Pro Semibold Italic'), local('SourceSansPro-SemiboldIt'), url('../fonts/SourceSansPro-SemiboldItalic.woff') format('woff'); unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2212, U+2215, U+E0FF, U+EFFD, U+F000; } /* latin-ext */ @@ -195,7 +195,7 @@ font-family: 'Source Serif Pro'; font-style: normal; font-weight: 400; - src: local('Source Serif Pro'), local('SourceSerifPro-Regular'), url('/fonts/SourceSerifPro-Regular.woff') format('woff'); + src: local('Source Serif Pro'), local('SourceSerifPro-Regular'), url('../fonts/SourceSerifPro-Regular.woff') format('woff'); unicode-range: U+0100-024F, U+1E00-1EFF, U+20A0-20AB, U+20AD-20CF, U+2C60-2C7F, U+A720-A7FF; } /* latin */ @@ -203,6 +203,6 @@ font-family: 'Source Serif Pro'; font-style: normal; font-weight: 400; - src: local('Source Serif Pro'), local('SourceSerifPro-Regular'), url('/fonts/SourceSerifPro-Regular.woff') format('woff'); + src: local('Source Serif Pro'), local('SourceSerifPro-Regular'), url('../fonts/SourceSerifPro-Regular.woff') format('woff'); unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2212, U+2215, U+E0FF, U+EFFD, U+F000; } diff --git a/public/css/slide.css b/public/css/slide.css index 72275c6a9f..f30726192a 100644 --- a/public/css/slide.css +++ b/public/css/slide.css @@ -344,3 +344,7 @@ html, body { .print-pdf .footer { display: none; } + +.markmap-container { + background: #f7f7f7; +} diff --git a/public/docs/features.md b/public/docs/features.md index c3f5fac931..c4f081b03d 100644 --- a/public/docs/features.md +++ b/public/docs/features.md @@ -208,6 +208,30 @@ When you’re a carpenter making a beautiful chest of drawers, you’re not goin > > Even support the nest blockquotes! > > [name=ChengHan Wu] [time=Sun, Jun 28, 2015 10:00 PM] [color=red] +### Render CSV as table + +You can use write csv in the codeblock: + +~~~md +```csvpreview {header="true"} +firstName,lastName,email,phoneNumber +John,Doe,john@doe.com,0123456789 +Jane,Doe,jane@doe.com,9876543210 +James,Bond,james.bond@mi6.co.uk,0612345678 +``` +~~~ + +which rendered to: + +```csvpreview {header="true"} +firstName,lastName,email,phoneNumber +John,Doe,john@doe.com,0123456789 +Jane,Doe,jane@doe.com,9876543210 +James,Bond,james.bond@mi6.co.uk,0612345678 +``` + +We use [Papa Parse](https://www.papaparse.com/) for parsing csv. The parsing option is given in braces: `{}`, and multiple options are seperated by a space. e.g. `{header="true" delimiter="."}`. Please read [their documentation](https://www.papaparse.com/docs#config) as reference. + ## Externals ### YouTube @@ -345,6 +369,40 @@ stop } ``` +### Fretboad + +```fretboard {title="horizontal, 5 frets", type="h6 noNut"} +-oO-*- +--o-o- +-o-oo- +-o-oO- +-oo-o- +-*O-o- +``` + +### Mindmap + +```markmap +# markmap-lib + +## Links + +- +- [GitHub](https://github.com/gera2ld/markmap-lib) + +## Related + +- [coc-markmap](https://github.com/gera2ld/coc-markmap) +- [gatsby-remark-markmap](https://github.com/gera2ld/gatsby-remark-markmap) + +## Features + +- links +- **inline** ~~text~~ *styles* +- multiline + text +``` + > More information about **sequence diagrams** syntax [here](http://bramp.github.io/js-sequence-diagrams/). > More information about **flow charts** syntax [here](http://adrai.github.io/flowchart.js/). > More information about **graphviz** syntax [here](http://www.tonyballantyne.com/graphs.html) @@ -352,6 +410,7 @@ stop > More information about **abc** syntax [here](http://abcnotation.com/learn) > More information about **plantuml** syntax [here](http://plantuml.com/index) > More information about **vega** syntax [here](https://vega.github.io/vega-lite/docs) +> More information about **fretboard** syntax [here](https://hackmd.io/c/codimd-documentation/%2F%40codimd%2Ffretboard-syntax) Alert Area --- diff --git a/public/docs/release-notes.md b/public/docs/release-notes.md index a542104810..88de6b2fd6 100644 --- a/public/docs/release-notes.md +++ b/public/docs/release-notes.md @@ -1,6 +1,82 @@ Release Notes === + 2.2.0 Diploderma swinhonis 2020-07-20 +--- + +
+ + Diploderma swinhonis +
+ +> Diploderma swinhonis, also known as the Taiwan japalure, Swinhoe's japalure, and Swinhoe's tree lizard, is a species of lizard in the family Agamidae. The species is endemic to Taiwan. +> \- Wikipedia [Diploderma swinhonis](https://en.wikipedia.org/wiki/Diploderma_swinhonis) + +In this release, we've added some Markdown renderer plugins, including fretboard guitar, Mindmap, and CSV. We believe the simplicity and the extensibility of markdown can bring more possibilities to you and your workflow. So let's find out more about what we can do with markdown. :100: + +We also fixed a long-lasting issue: CodiMD cannot be hosted under URL subpath perfectly. Check PR [#1551](https://github.com/hackmdio/codimd/pull/1551) for details. + +Last but not least, we start standarizing CodiMD API. We drafted [`List my notes`](https://github.com/hackmdio/codimd/pull/1548) API in this release. Stay tuned. :person_in_lotus_position: + +Here are some highlights from this release: + +- [Fretboard Guitar tab renderer](#Fretboard-Guitar-tab-renderer) +- [Mindmap rendrer](#Mindmap) +- [Image Lightbox](#Image-Lightbox-Support) +- [CSV renderer](#Render-csv-codeblock-as-table) + +[Check out the complete release note][v2_2_0]. Thank you CodiMD community and all our contributors. ❤️ + +[v2_2_0]: https://hackmd.io/@codimd/release-notes/%2F%40codimd%2Fv2_2_0 + +### Enhancements + +- Use array for tags when available +- Replace btn-social with btn-login-method +- Set html image meta tag with YAML metadata +- List my note API + +### Fixes + +- Update Simplified Chinese translation and fix typography +- Fix webpack urlpath font loading error + + + + 2.1.0 Zhangixalus prasinatus 2020-05-18 +--- + +
+ + Zhangixalus prasinatus +
+ +> Zhangixalus prasinatus is a species of frog in the family Rhacophoridae endemic to northern Taiwan. It is the largest tree frog in Taiwan; females can reach 7 cm (2.8 in) in snout-vent length. It is known from Taipei, Yilan, and Taoyuan. +> \- Wikipedia [Zhangixalus prasinatus](https://en.wikipedia.org/wiki/Zhangixalus_prasinatus) + +During this hard time of COVID-19, it's a pleasure to help people collaborate better with CodiMD. We hope the world will recover from this situation soon. :sunrise: + +Good news, we have some goodies for CodiMD including: + +- [Support Prometheus metrics](https://hackmd.io/@codimd/v2_1_0#Support-Prometheus-metrics) +- [Cut docker image size by 57%](https://hackmd.io/@codimd/v2_1_0#Cut-docker-image-size-by-57) +- [Drop Node 8 Support](https://hackmd.io/@codimd/v2_1_0#Drop-Node-8-Support) + +[Check out the complete release note][v2_1_0]. Thank you CodiMD community and all our contributors. ❤️ + +[v2_1_0]: https://hackmd.io/@codimd/release-notes/%2F%40codimd%2Fv2_1_0 + +### Enhancements + +- Optimize module size +- Support brace wrapped param in fence lang +- Upgrade Node.JS version to 10.20.1 + +### Fixes + +- Fix getStatus caused "TypeError: Converting circular structure to JSON" + + 2.0.1 Urocissa caerulea 2020-04-09 --- diff --git a/public/docs/yaml-metadata.md b/public/docs/yaml-metadata.md index 839616a8f9..5c1f54d94e 100644 --- a/public/docs/yaml-metadata.md +++ b/public/docs/yaml-metadata.md @@ -40,6 +40,17 @@ This option will set the note description. description: meta description ``` +image +--- +This option will set the html meta tag 'image'. + +> default: not set + +**Example** +```yml +image: https://raw.githubusercontent.com/hackmdio/codimd/develop/public/screenshot.png +``` + tags --- This option will set the tags which prior than content tags. diff --git a/public/js/extra.js b/public/js/extra.js index dfc206b0aa..954a161ce0 100644 --- a/public/js/extra.js +++ b/public/js/extra.js @@ -11,11 +11,21 @@ import unescapeHTML from 'lodash/unescape' import isURL from 'validator/lib/isURL' +import { transform } from 'markmap-lib/dist/transform.common' +import { markmap } from 'markmap-lib/dist/view.common' + import { stripTags } from '../../utils/string' import getUIElements from './lib/editor/ui-elements' import { emojifyImageDir } from './lib/editor/constants' -import { parseFenceCodeParams, serializeParamToAttribute } from './lib/markdown/utils' +import { + parseFenceCodeParams, + serializeParamToAttribute, + deserializeParamAttributeFromElement +} from './lib/markdown/utils' +import { renderFretBoard } from './lib/renderer/fretboard/fretboard' +import './lib/renderer/lightbox' +import { renderCSVPreview } from './lib/renderer/csvpreview' import markdownit from 'markdown-it' import markdownitContainer from 'markdown-it-container' @@ -464,7 +474,7 @@ export function finishView (view) { const { lat, lon } = data[0] position = [lat, lon] } - $elem.html(`
`) + $elem.html('
') const map = L.map($elem.find('.geo-map')[0]).setView(position, zoom || 16) L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', { @@ -483,6 +493,38 @@ export function finishView (view) { console.warn(err) } }) + // fretboard + const fretboard = view.find('div.fretboard_instance.raw').removeClass('raw') + fretboard.each((key, value) => { + const params = deserializeParamAttributeFromElement(value) + const $value = $(value) + + try { + const $ele = $(value).parent().parent() + $ele.html(renderFretBoard($value.text(), params)) + } catch (err) { + $value.unwrap() + $value.parent().append(`
${escapeHTML(err)}
`) + console.warn(err) + } + }) + // markmap + view.find('div.markmap.raw').removeClass('raw').each(async (key, value) => { + const $elem = $(value).parent().parent() + const $value = $(value) + const content = $value.text() + $value.unwrap() + try { + const data = transform(content) + $elem.html(`
`) + markmap($elem.find('svg')[0], data, { + duration: 0 + }) + } catch (err) { + $elem.html(`
${escapeHTML(err)}
`) + console.warn(err) + } + }) // image href new window(emoji not included) const images = view.find('img.raw[src]').removeClass('raw') @@ -975,7 +1017,7 @@ export function deduplicatedHeaderId (view) { if (window.linkifyHeaderStyle === 'gfm') { // consistent with GitHub, GitLab, Pandoc & co. // all headers contained in the document, in order of appearance - const allHeaders = view.find(`:header`).toArray() + const allHeaders = view.find(':header').toArray() // list of finaly assigned header IDs const headerIds = new Set() for (let j = 0; j < allHeaders.length; j++) { @@ -1036,7 +1078,9 @@ const fenceCodeAlias = { mermaid: 'mermaid', abc: 'abc', vega: 'vega', - geo: 'geo' + geo: 'geo', + fretboard: 'fretboard_instance', + markmap: 'markmap' } function highlightRender (code, lang) { @@ -1133,7 +1177,7 @@ md.use(markdownitContainer, 'spoiler', { if (summary) { return `
${md.renderInline(summary)}\n` } else { - return `
\n` + return '
\n' } } else { // closing tag @@ -1145,6 +1189,7 @@ md.use(markdownitContainer, 'spoiler', { const defaultImageRender = md.renderer.rules.image md.renderer.rules.image = function (tokens, idx, options, env, self) { tokens[idx].attrJoin('class', 'raw') + tokens[idx].attrJoin('class', 'md-image') return defaultImageRender(...arguments) } md.renderer.rules.list_item_open = function (tokens, idx, options, env, self) { @@ -1167,6 +1212,12 @@ md.renderer.rules.fence = (tokens, idx, options, env, self) => { if (info) { langName = info.split(/\s+/g)[0] + + if (langName === 'csvpreview') { + const params = parseFenceCodeParams(info) + return renderCSVPreview(token.content, params) + } + if (/!$/.test(info)) token.attrJoin('class', 'wrap') token.attrJoin('class', options.langPrefix + langName.replace(/=$|=\d+$|=\+$|!$|=!$/, '')) token.attrJoin('class', 'hljs') diff --git a/public/js/index.js b/public/js/index.js index b1a0a479a1..c736cb7f2c 100644 --- a/public/js/index.js +++ b/public/js/index.js @@ -102,7 +102,7 @@ var cursorActivityDebounce = 50 var cursorAnimatePeriod = 100 var supportContainers = ['success', 'info', 'warning', 'danger', 'spoiler'] var supportCodeModes = ['javascript', 'typescript', 'jsx', 'htmlmixed', 'htmlembedded', 'css', 'xml', 'clike', 'clojure', 'ruby', 'python', 'shell', 'php', 'sql', 'haskell', 'coffeescript', 'yaml', 'pug', 'lua', 'cmake', 'nginx', 'perl', 'sass', 'r', 'dockerfile', 'tiddlywiki', 'mediawiki', 'go', 'gherkin'].concat(hljs.listLanguages()) -var supportCharts = ['sequence', 'flow', 'graphviz', 'mermaid', 'abc', 'plantuml', 'vega', 'geo'] +var supportCharts = ['sequence', 'flow', 'graphviz', 'mermaid', 'abc', 'plantuml', 'vega', 'geo', 'fretboard', 'markmap'] var supportHeaders = [ { text: '# h1', @@ -588,6 +588,7 @@ function checkEditorStyle () { } // workaround editor will have wrong doc height when editor height changed editor.setSize(null, ui.area.edit.height()) + checkEditorScrollOverLines() // make editor resizable if (!ui.area.resize.handle.length) { ui.area.edit.resizable({ @@ -674,6 +675,15 @@ function checkEditorScrollbarInner () { editor.scrollTo(null, scrollInfo.top) } +function checkEditorScrollOverLines () { + const desireHeight = parseInt(ui.area.codemirrorScroll[0].style.height) || parseInt(ui.area.codemirrorScroll[0].style.minHeight) + // make editor have extra padding in the bottom (except small screen) + const paddingBottom = editor.doc && editor.doc.height > defaultTextHeight ? (desireHeight - defaultTextHeight) : 0 + if (parseInt(ui.area.codemirrorLines.css('padding-bottom')) !== paddingBottom) { + ui.area.codemirrorLines.css('padding-bottom', paddingBottom + 'px') + } +} + function checkTocStyle () { // toc right var paddingRight = parseFloat(ui.area.markdown.css('padding-right')) @@ -1779,7 +1789,7 @@ socket.on('reconnect', function (data) { socket.on('connect', function (data) { clearInterval(retryTimer) retryTimer = null - personalInfo['id'] = socket.id + personalInfo.id = socket.id showStatus(statusType.connected) socket.emit('version') }) @@ -2349,8 +2359,8 @@ function emitUserStatus (force) { var type = null if (visibleXS) { type = 'xs' } else if (visibleSM) { type = 'sm' } else if (visibleMD) { type = 'md' } else if (visibleLG) { type = 'lg' } - personalInfo['idle'] = idle.isAway - personalInfo['type'] = type + personalInfo.idle = idle.isAway + personalInfo.type = type for (var i = 0; i < onlineUsers.length; i++) { if (onlineUsers[i].id === personalInfo.id) { @@ -2554,9 +2564,11 @@ function enforceMaxLength (cm, change) { } return false } +let lastDocHeight var ignoreEmitEvents = ['setValue', 'ignoreHistory'] editorInstance.on('beforeChange', function (cm, change) { if (debug) { console.debug(change) } + lastDocHeight = editor.doc.height removeNullByte(cm, change) if (enforceMaxLength(cm, change)) { $('.limit-modal').modal('show') @@ -2590,6 +2602,7 @@ editorInstance.on('paste', function () { // na }) editorInstance.on('changes', function (editor, changes) { + const docHeightChanged = editor.doc.height !== lastDocHeight updateHistory() var docLength = editor.getValue().length // workaround for big documents @@ -2605,13 +2618,18 @@ editorInstance.on('changes', function (editor, changes) { viewportMargin = newViewportMargin windowResize() } - checkEditorScrollbar() - if (ui.area.codemirrorScroll[0].scrollHeight > ui.area.view[0].scrollHeight && editorHasFocus()) { - postUpdateEvent = function () { - syncScrollToView() - postUpdateEvent = null + if (docHeightChanged) { + checkEditorScrollbar() + checkEditorScrollOverLines() + // always sync edit scrolling to view if user is editing + if (ui.area.codemirrorScroll[0].scrollHeight > ui.area.view[0].scrollHeight && editorHasFocus()) { + postUpdateEvent = function () { + syncScrollToView() + postUpdateEvent = null + } } } + lastDocHeight = editor.doc.height }) editorInstance.on('focus', function (editor) { for (var i = 0; i < onlineUsers.length; i++) { @@ -2619,7 +2637,7 @@ editorInstance.on('focus', function (editor) { onlineUsers[i].cursor = editor.getCursor() } } - personalInfo['cursor'] = editor.getCursor() + personalInfo.cursor = editor.getCursor() socket.emit('cursor focus', editor.getCursor()) }) @@ -2632,7 +2650,7 @@ function cursorActivityInner (editor) { onlineUsers[i].cursor = editor.getCursor() } } - personalInfo['cursor'] = editor.getCursor() + personalInfo.cursor = editor.getCursor() socket.emit('cursor activity', editor.getCursor()) } } @@ -2679,7 +2697,7 @@ editorInstance.on('blur', function (cm) { onlineUsers[i].cursor = null } } - personalInfo['cursor'] = null + personalInfo.cursor = null socket.emit('cursor blur') }) diff --git a/public/js/lib/editor/constants.js b/public/js/lib/editor/constants.js index 697f451414..726c4402bb 100644 --- a/public/js/lib/editor/constants.js +++ b/public/js/lib/editor/constants.js @@ -16,4 +16,4 @@ export const availableThemes = [ { name: 'Tomorror Night Eighties', value: 'tomorrow-night-eighties' } ] -export const emojifyImageDir = window.USE_CDN ? `https://cdn.jsdelivr.net/npm/@hackmd/emojify.js@2.1.0/dist/images/basic` : `${serverurl}/build/emojify.js/dist/images/basic` +export const emojifyImageDir = window.USE_CDN ? 'https://cdn.jsdelivr.net/npm/@hackmd/emojify.js@2.1.0/dist/images/basic' : `${serverurl}/build/emojify.js/dist/images/basic` diff --git a/public/js/lib/editor/index.js b/public/js/lib/editor/index.js index 830670e365..1d8a0dc709 100644 --- a/public/js/lib/editor/index.js +++ b/public/js/lib/editor/index.js @@ -139,9 +139,14 @@ export default class Editor { return null } } + CodeMirror.defineMode('vega', function (config, modeConfig) { return CodeMirror.overlayMode(CodeMirror.getMode(config, 'application/ld+json'), ignoreOverlay) }) + + CodeMirror.defineMode('markmap', function (config, modeConfig) { + return CodeMirror.overlayMode(CodeMirror.getMode(config, 'gfm'), ignoreOverlay) + }) } on (event, cb) { @@ -587,7 +592,7 @@ export default class Editor { if (lang) { this.statusIndicators.find(`.status-spellcheck li[value="${lang}"]`).addClass('active') } else { - this.statusIndicators.find(`.status-spellcheck li[value="disabled"]`).addClass('active') + this.statusIndicators.find('.status-spellcheck li[value="disabled"]').addClass('active') } } @@ -627,7 +632,7 @@ export default class Editor { } const self = this - this.statusIndicators.find(`.status-spellcheck li`).click(function () { + this.statusIndicators.find('.status-spellcheck li').click(function () { const lang = $(this).attr('value') if (lang === 'disabled') { diff --git a/public/js/lib/editor/markdown-lint/index.js b/public/js/lib/editor/markdown-lint/index.js index 1b92ece28e..e3a055e829 100644 --- a/public/js/lib/editor/markdown-lint/index.js +++ b/public/js/lib/editor/markdown-lint/index.js @@ -2,7 +2,6 @@ // load CM lint plugin explicitly import '@hackmd/codemirror/addon/lint/lint' -import './lint.css' window.markdownit = require('markdown-it') // eslint-disable-next-line diff --git a/public/js/lib/editor/spellcheck.js b/public/js/lib/editor/spellcheck.js index 7ae52e617c..c3d619a566 100644 --- a/public/js/lib/editor/spellcheck.js +++ b/public/js/lib/editor/spellcheck.js @@ -23,11 +23,11 @@ export const supportLanguages = [ value: 'de', aff: { url: `${serverurl}/build/dictionary-de/index.aff`, - cdnUrl: `https://cdn.jsdelivr.net/npm/dictionary-de@2.0.3/index.aff` + cdnUrl: 'https://cdn.jsdelivr.net/npm/dictionary-de@2.0.3/index.aff' }, dic: { url: `${serverurl}/build/dictionary-de/index.dic`, - cdnUrl: `https://cdn.jsdelivr.net/npm/dictionary-de@2.0.3/index.dic` + cdnUrl: 'https://cdn.jsdelivr.net/npm/dictionary-de@2.0.3/index.dic' } }, { @@ -35,11 +35,11 @@ export const supportLanguages = [ value: 'de_AT', aff: { url: `${serverurl}/build/dictionary-de-at/index.aff`, - cdnUrl: `https://cdn.jsdelivr.net/npm/dictionary-de-at@2.0.3/index.aff` + cdnUrl: 'https://cdn.jsdelivr.net/npm/dictionary-de-at@2.0.3/index.aff' }, dic: { url: `${serverurl}/build/dictionary-de-at/index.dic`, - cdnUrl: `https://cdn.jsdelivr.net/npm/dictionary-de-at@2.0.3/index.dic` + cdnUrl: 'https://cdn.jsdelivr.net/npm/dictionary-de-at@2.0.3/index.dic' } }, { @@ -47,11 +47,11 @@ export const supportLanguages = [ value: 'de_CH', aff: { url: `${serverurl}/build/dictionary-de-ch/index.aff`, - cdnUrl: `https://cdn.jsdelivr.net/npm/dictionary-de-ch@2.0.3/index.aff` + cdnUrl: 'https://cdn.jsdelivr.net/npm/dictionary-de-ch@2.0.3/index.aff' }, dic: { url: `${serverurl}/build/dictionary-de-ch/index.dic`, - cdnUrl: `https://cdn.jsdelivr.net/npm/dictionary-de-ch@2.0.3/index.dic` + cdnUrl: 'https://cdn.jsdelivr.net/npm/dictionary-de-ch@2.0.3/index.dic' } } ] diff --git a/public/js/lib/editor/ui-elements.js b/public/js/lib/editor/ui-elements.js index 48d8f707fc..d084607fb1 100644 --- a/public/js/lib/editor/ui-elements.js +++ b/public/js/lib/editor/ui-elements.js @@ -70,6 +70,9 @@ export const getUIElements = () => ({ codemirrorSizerInner: $( '.ui-edit-area .CodeMirror .CodeMirror-sizer > div' ), + codemirrorLines: $( + '.ui-edit-area .CodeMirror .CodeMirror-lines' + ), markdown: $('.ui-view-area .markdown-body'), resize: { handle: $('.ui-resizable-handle'), diff --git a/public/js/lib/markdown/utils.js b/public/js/lib/markdown/utils.js index 3aeb646cfd..bc5ee6e9af 100644 --- a/public/js/lib/markdown/utils.js +++ b/public/js/lib/markdown/utils.js @@ -7,10 +7,10 @@ export function parseFenceCodeParams (lang) { paraMatch && paraMatch.forEach(param => { param = param.trim() if (param[0] === '#') { - params['id'] = param.slice(1) + params.id = param.slice(1) } else if (param[0] === '.') { - if (params['class']) params['class'] = [] - params['class'] = params['class'].concat(param.slice(1)) + if (params.class) params.class = [] + params.class = params.class.concat(param.slice(1)) } else { const offset = param.indexOf('=') const id = param.substring(0, offset).trim().toLowerCase() @@ -21,8 +21,8 @@ export function parseFenceCodeParams (lang) { val = val.substring(1, val.length - 1) } if (id === 'class') { - if (params['class']) params['class'] = [] - params['class'] = params['class'].concat(val) + if (params.class) params.class = [] + params.class = params.class.concat(val) } else { params[id] = val } diff --git a/public/js/lib/renderer/csvpreview.js b/public/js/lib/renderer/csvpreview.js new file mode 100644 index 0000000000..a8a0724c2b --- /dev/null +++ b/public/js/lib/renderer/csvpreview.js @@ -0,0 +1,44 @@ +import Papa from 'papaparse' +import escapeHTML from 'lodash/escape' + +const safeParse = d => { + try { + return JSON.parse(d) + } catch (err) { + return d + } +} + +export function renderCSVPreview (csv, options = {}, attr = '') { + const opt = Object.keys(options).reduce((acc, key) => { + return Object.assign(acc, { + [key]: safeParse(options[key]) + }) + }, {}) + + const results = Papa.parse(csv.trim(), opt) + + if (opt.header) { + const fields = results.meta.fields + return ` + + + ${fields.map(f => ``).join('')} + + + + ${results.data.map(d => ` + ${fields.map(f => ``).join('')} + `).join('')} + +
${escapeHTML(f)}
${escapeHTML(d[f])}
` + } else { + return ` + + ${results.data.map(d => ` + ${d.map(f => ``).join('')} + `).join('')} + +
${escapeHTML(f)}
` + } +} diff --git a/public/js/lib/renderer/fretboard/css/i.css b/public/js/lib/renderer/fretboard/css/i.css new file mode 100644 index 0000000000..86e15080c6 --- /dev/null +++ b/public/js/lib/renderer/fretboard/css/i.css @@ -0,0 +1,183 @@ +/* -- GENERAL TYPOGRAPHY -- */ +.fretTitle { + color: #555; + font-family: "Helvetica Neue", sans-serif; + background: #eee; + line-height: 1.4; + font-size: 1.6em; + margin: 10px 0 10px 0; + font-weight: 900; + padding: 5px; +} + +section { + padding: 20px; +} + +.content { + max-width: 960px; + margin: auto; +} + +/* Fretboard Container/Wrapper */ +.fretContainer { + width: 320px; + margin: 0 auto; +} + +.fretContainer_h { + max-width: 400px; + margin: 0 auto; +} + +@media all and (max-width: 400px) { + .fretContainer_h { + max-width: 288px; + } +} +@media all and (max-width: 420px) { + .fretContainer { + max-width: 220px; + } +} +.svg_wrapper { + background-color: #fff; + position: relative; + height: 0; +} + +.svg_wrapper svg.fretboard_bg { + position: absolute; + top: 0; + left: 0; + z-index: 0; +} + +.svg_wrapper .cells { + width: 100%; + height: 100%; + position: absolute; + top: 0; + left: 0; + z-index: 10; +} + +.svg_wrapper.v4 { + padding-top: 91.6666666667%; +} + +.svg_wrapper.v5 { + padding-top: 106.9444444444%; +} + +.svg_wrapper.v7 { + padding-top: 137.5%; +} + +.svg_wrapper.v9 { + padding-top: 168.0555555556%; +} + +.svg_wrapper.v12 { + padding-top: 213.8888888889%; +} + +.svg_wrapper.v15 { + padding-top: 259.7222222222%; +} + +.svg_wrapper.v .cell svg { + width: 12.5%; + float: left; + display: block; +} + +.svg_wrapper.v4 .cell svg { + height: 16.6666666667%; +} + +.svg_wrapper.v5 .cell svg { + height: 14.2857142857%; +} + +.svg_wrapper.v7 .cell svg { + height: 11.1111111111%; +} + +.svg_wrapper.v9 .cell svg { + height: 9.0909090909%; +} + +.svg_wrapper.v12 .cell svg { + height: 7.1428571429%; +} + +.svg_wrapper.v15 .cell svg { + height: 5.8823529412%; +} + +.svg_wrapper.h5 { + padding-top: 85.7142857143%; +} + +.svg_wrapper.h6 { + padding-top: 75%; +} + +.svg_wrapper.h7 { + padding-top: 72.7272727273%; +} + +.svg_wrapper.h .cell svg { + height: 12.5%; + float: left; + display: block; + z-index: 2; +} + +.svg_wrapper.h5 .cell svg { + width: 14.2857142857%; +} + +.svg_wrapper.h6 .cell svg { + width: 12.5%; +} + +.svg_wrapper.h7 .cell svg { + width: 12.1212121212%; +} + +/* Fretboard Element Styles */ +.cell.dot .fretb_dot { + fill: #27a9e1; +} + +.cell.dot.root .fretb_dot { + fill: #F05A28; +} + +.cell.dot.faded .fretb_dot { + opacity: 0.25; +} + +.fretboard_bg .fret_bg rect { + fill: #fff; +} + +.fretboard_bg .frets rect { + fill: #ddd; +} + +.fretboard_bg .nut rect { + fill: #6e6e6e; +} + +.fretboard_bg .strings rect { + fill: #6e6e6e; +} + +.svg_wrapper.noNut .fretboard_bg .nut rect { + fill: none; +} + +/*# sourceMappingURL=i.css.map */ diff --git a/public/js/lib/renderer/fretboard/fretboard.js b/public/js/lib/renderer/fretboard/fretboard.js new file mode 100644 index 0000000000..fdd97fd624 --- /dev/null +++ b/public/js/lib/renderer/fretboard/fretboard.js @@ -0,0 +1,81 @@ +/* global $ */ + +import './css/i.css' +import dotEmpty from './svg/dotEmpty.svg' +import dotEmptyH from './svg/dotEmpty_h.svg' +import dot from './svg/dot.svg' +import dotH from './svg/dot_h.svg' +import dotWideLeft from './svg/dotWideLeft.svg' +import dotWideRight from './svg/dotWideRight.svg' +import dotWideMiddle from './svg/dotWideMiddle.svg' +import stringO from './svg/string_o.svg' +import stringX from './svg/string_x.svg' + +const switchListV = { + o: `
${dot}
`, + '*': `
${dot}
`, + '(': `
${dotWideLeft}
`, + ')': `
${dotWideRight}
`, + '=': `
${dotWideMiddle}
`, + '^': `
${stringO}
`, + x: `
${stringX}
`, + '|': `
${dotEmpty}
`, + ' ': `
${dotEmpty}
`, + '\n': `
${dotEmpty}
` +} +const switchListH = { + o: `
${dotH}
`, + '*': `
${dotH}
`, + O: `
${dotH}
`, + '-': `
${dotEmptyH}
`, + ' ': `
${dotEmptyH}
`, + '\n': `
${dotEmptyH}
${dotEmptyH}
` +} + +export const renderFretBoard = (content, { title: fretTitle, type }) => { + const fretType = type.split(' ') + const containerClass = fretType && fretType[0].startsWith('h') ? 'fretContainer_h' : 'fretContainer' + const fretboardHTML = $(`
`) + + $(fretboardHTML).append($(`
${fretTitle}
`)) + + // create fretboard background HTML + const fretbOrientation = fretType && fretType[0].startsWith('v') ? 'vert' : 'horiz' + const fretbLen = fretType && fretType[0].substring(1) + const fretbClass = fretType && fretType[0][0] + ' ' + fretType[0] + const nut = (fretType[1] && fretType[1] === 'noNut') ? 'noNut' : '' + const svgHTML = $(`
`) + const fretbBg = require(`./svg/fretb_${fretbOrientation}_${fretbLen}.svg`) + $(svgHTML).append($(fretbBg)) + + // create cells HTML + const cellsHTML = $('
') + let switchList = '' + if (fretbOrientation && fretbOrientation === 'vert') { + switchList = switchListV + } else { + // calculate position + const emptyFill = new Array(Number(fretbLen) + 3).fill(' ').join('') + content = `${emptyFill}${content}` + + switchList = switchListH + } + + const contentCell = content && content.split('') + // Go through each ASCII character... + const numArray = [...Array(10).keys()].slice(1) + contentCell && contentCell.forEach(char => { + if (numArray.toString().indexOf(char) !== -1) { + const numType = fretType && fretType[0].startsWith('h') ? '_h' : '' + const numSvg = require(`./svg/number${char}${numType}.svg`) + cellsHTML.append($(`
${numSvg}
`)) + } else if (switchList[char] !== undefined) { + cellsHTML.append($(switchList[char])) + } + }) + + $(svgHTML).append($(cellsHTML)) + $(fretboardHTML).append($(svgHTML)) + + return fretboardHTML[0].outerHTML +} diff --git a/public/js/lib/renderer/fretboard/svg/dot.svg b/public/js/lib/renderer/fretboard/svg/dot.svg new file mode 100644 index 0000000000..96376493f3 --- /dev/null +++ b/public/js/lib/renderer/fretboard/svg/dot.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/public/js/lib/renderer/fretboard/svg/dotEmpty.svg b/public/js/lib/renderer/fretboard/svg/dotEmpty.svg new file mode 100644 index 0000000000..f49702c95c --- /dev/null +++ b/public/js/lib/renderer/fretboard/svg/dotEmpty.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/public/js/lib/renderer/fretboard/svg/dotEmpty_h.svg b/public/js/lib/renderer/fretboard/svg/dotEmpty_h.svg new file mode 100644 index 0000000000..6fc1f306e8 --- /dev/null +++ b/public/js/lib/renderer/fretboard/svg/dotEmpty_h.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/public/js/lib/renderer/fretboard/svg/dotWideLeft.svg b/public/js/lib/renderer/fretboard/svg/dotWideLeft.svg new file mode 100644 index 0000000000..8109a5138f --- /dev/null +++ b/public/js/lib/renderer/fretboard/svg/dotWideLeft.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/public/js/lib/renderer/fretboard/svg/dotWideMiddle.svg b/public/js/lib/renderer/fretboard/svg/dotWideMiddle.svg new file mode 100644 index 0000000000..75804023e8 --- /dev/null +++ b/public/js/lib/renderer/fretboard/svg/dotWideMiddle.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/public/js/lib/renderer/fretboard/svg/dotWideRight.svg b/public/js/lib/renderer/fretboard/svg/dotWideRight.svg new file mode 100644 index 0000000000..185e1567ca --- /dev/null +++ b/public/js/lib/renderer/fretboard/svg/dotWideRight.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/public/js/lib/renderer/fretboard/svg/dot_h.svg b/public/js/lib/renderer/fretboard/svg/dot_h.svg new file mode 100644 index 0000000000..266a1be8cb --- /dev/null +++ b/public/js/lib/renderer/fretboard/svg/dot_h.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/public/js/lib/renderer/fretboard/svg/fretb_horiz_5.svg b/public/js/lib/renderer/fretboard/svg/fretb_horiz_5.svg new file mode 100644 index 0000000000..222c4664fb --- /dev/null +++ b/public/js/lib/renderer/fretboard/svg/fretb_horiz_5.svg @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/public/js/lib/renderer/fretboard/svg/fretb_horiz_6.svg b/public/js/lib/renderer/fretboard/svg/fretb_horiz_6.svg new file mode 100644 index 0000000000..a5f1c86e8f --- /dev/null +++ b/public/js/lib/renderer/fretboard/svg/fretb_horiz_6.svg @@ -0,0 +1,25 @@ + + + + + \ + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/public/js/lib/renderer/fretboard/svg/fretb_horiz_7.svg b/public/js/lib/renderer/fretboard/svg/fretb_horiz_7.svg new file mode 100644 index 0000000000..afac66f101 --- /dev/null +++ b/public/js/lib/renderer/fretboard/svg/fretb_horiz_7.svg @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/public/js/lib/renderer/fretboard/svg/fretb_vert_12.svg b/public/js/lib/renderer/fretboard/svg/fretb_vert_12.svg new file mode 100644 index 0000000000..a394bbef57 --- /dev/null +++ b/public/js/lib/renderer/fretboard/svg/fretb_vert_12.svg @@ -0,0 +1,31 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/public/js/lib/renderer/fretboard/svg/fretb_vert_15.svg b/public/js/lib/renderer/fretboard/svg/fretb_vert_15.svg new file mode 100644 index 0000000000..48c6711ed2 --- /dev/null +++ b/public/js/lib/renderer/fretboard/svg/fretb_vert_15.svg @@ -0,0 +1,34 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/public/js/lib/renderer/fretboard/svg/fretb_vert_4.svg b/public/js/lib/renderer/fretboard/svg/fretb_vert_4.svg new file mode 100644 index 0000000000..b228382ff1 --- /dev/null +++ b/public/js/lib/renderer/fretboard/svg/fretb_vert_4.svg @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/public/js/lib/renderer/fretboard/svg/fretb_vert_5.svg b/public/js/lib/renderer/fretboard/svg/fretb_vert_5.svg new file mode 100644 index 0000000000..6acb3a9543 --- /dev/null +++ b/public/js/lib/renderer/fretboard/svg/fretb_vert_5.svg @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/public/js/lib/renderer/fretboard/svg/fretb_vert_7.svg b/public/js/lib/renderer/fretboard/svg/fretb_vert_7.svg new file mode 100644 index 0000000000..0aa94f272f --- /dev/null +++ b/public/js/lib/renderer/fretboard/svg/fretb_vert_7.svg @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/public/js/lib/renderer/fretboard/svg/fretb_vert_9.svg b/public/js/lib/renderer/fretboard/svg/fretb_vert_9.svg new file mode 100644 index 0000000000..ccb52414a9 --- /dev/null +++ b/public/js/lib/renderer/fretboard/svg/fretb_vert_9.svg @@ -0,0 +1,28 @@ +\ + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/public/js/lib/renderer/fretboard/svg/number1.svg b/public/js/lib/renderer/fretboard/svg/number1.svg new file mode 100644 index 0000000000..fb0cdbbe2a --- /dev/null +++ b/public/js/lib/renderer/fretboard/svg/number1.svg @@ -0,0 +1,10 @@ + + + + Rectangle + Created with Sketch. + + + + + \ No newline at end of file diff --git a/public/js/lib/renderer/fretboard/svg/number1_h.svg b/public/js/lib/renderer/fretboard/svg/number1_h.svg new file mode 100644 index 0000000000..4474e81bf3 --- /dev/null +++ b/public/js/lib/renderer/fretboard/svg/number1_h.svg @@ -0,0 +1,10 @@ + + + + Rectangle + Created with Sketch. + + + + + \ No newline at end of file diff --git a/public/js/lib/renderer/fretboard/svg/number2.svg b/public/js/lib/renderer/fretboard/svg/number2.svg new file mode 100644 index 0000000000..54567ffe03 --- /dev/null +++ b/public/js/lib/renderer/fretboard/svg/number2.svg @@ -0,0 +1,10 @@ + + + + Rectangle + Created with Sketch. + + + + + \ No newline at end of file diff --git a/public/js/lib/renderer/fretboard/svg/number2_h.svg b/public/js/lib/renderer/fretboard/svg/number2_h.svg new file mode 100644 index 0000000000..5e42c860a5 --- /dev/null +++ b/public/js/lib/renderer/fretboard/svg/number2_h.svg @@ -0,0 +1,10 @@ + + + + Rectangle + Created with Sketch. + + + + + \ No newline at end of file diff --git a/public/js/lib/renderer/fretboard/svg/number3.svg b/public/js/lib/renderer/fretboard/svg/number3.svg new file mode 100644 index 0000000000..24cf30ae98 --- /dev/null +++ b/public/js/lib/renderer/fretboard/svg/number3.svg @@ -0,0 +1,10 @@ + + + + Rectangle + Created with Sketch. + + + + + \ No newline at end of file diff --git a/public/js/lib/renderer/fretboard/svg/number3_h.svg b/public/js/lib/renderer/fretboard/svg/number3_h.svg new file mode 100644 index 0000000000..46cb3e89cb --- /dev/null +++ b/public/js/lib/renderer/fretboard/svg/number3_h.svg @@ -0,0 +1,10 @@ + + + + Rectangle + Created with Sketch. + + + + + \ No newline at end of file diff --git a/public/js/lib/renderer/fretboard/svg/number4.svg b/public/js/lib/renderer/fretboard/svg/number4.svg new file mode 100644 index 0000000000..c744dc1bfa --- /dev/null +++ b/public/js/lib/renderer/fretboard/svg/number4.svg @@ -0,0 +1,10 @@ + + + + Rectangle + Created with Sketch. + + + + + \ No newline at end of file diff --git a/public/js/lib/renderer/fretboard/svg/number4_h.svg b/public/js/lib/renderer/fretboard/svg/number4_h.svg new file mode 100644 index 0000000000..488c0ac9ad --- /dev/null +++ b/public/js/lib/renderer/fretboard/svg/number4_h.svg @@ -0,0 +1,10 @@ + + + + Rectangle + Created with Sketch. + + + + + \ No newline at end of file diff --git a/public/js/lib/renderer/fretboard/svg/number5.svg b/public/js/lib/renderer/fretboard/svg/number5.svg new file mode 100644 index 0000000000..c8e95144b4 --- /dev/null +++ b/public/js/lib/renderer/fretboard/svg/number5.svg @@ -0,0 +1,10 @@ + + + + Rectangle + Created with Sketch. + + + + + \ No newline at end of file diff --git a/public/js/lib/renderer/fretboard/svg/number5_h.svg b/public/js/lib/renderer/fretboard/svg/number5_h.svg new file mode 100644 index 0000000000..489d4b34ca --- /dev/null +++ b/public/js/lib/renderer/fretboard/svg/number5_h.svg @@ -0,0 +1,10 @@ + + + + Rectangle + Created with Sketch. + + + + + \ No newline at end of file diff --git a/public/js/lib/renderer/fretboard/svg/number6.svg b/public/js/lib/renderer/fretboard/svg/number6.svg new file mode 100644 index 0000000000..1c88dd0fe0 --- /dev/null +++ b/public/js/lib/renderer/fretboard/svg/number6.svg @@ -0,0 +1,10 @@ + + + + Rectangle + Created with Sketch. + + + + + \ No newline at end of file diff --git a/public/js/lib/renderer/fretboard/svg/number6_h.svg b/public/js/lib/renderer/fretboard/svg/number6_h.svg new file mode 100644 index 0000000000..8af61ec4d2 --- /dev/null +++ b/public/js/lib/renderer/fretboard/svg/number6_h.svg @@ -0,0 +1,10 @@ + + + + Rectangle + Created with Sketch. + + + + + \ No newline at end of file diff --git a/public/js/lib/renderer/fretboard/svg/number7.svg b/public/js/lib/renderer/fretboard/svg/number7.svg new file mode 100644 index 0000000000..f20c30b0c9 --- /dev/null +++ b/public/js/lib/renderer/fretboard/svg/number7.svg @@ -0,0 +1,10 @@ + + + + Rectangle + Created with Sketch. + + + + + \ No newline at end of file diff --git a/public/js/lib/renderer/fretboard/svg/number7_h.svg b/public/js/lib/renderer/fretboard/svg/number7_h.svg new file mode 100644 index 0000000000..419f9f9e3d --- /dev/null +++ b/public/js/lib/renderer/fretboard/svg/number7_h.svg @@ -0,0 +1,10 @@ + + + + Rectangle + Created with Sketch. + + + + + \ No newline at end of file diff --git a/public/js/lib/renderer/fretboard/svg/number8.svg b/public/js/lib/renderer/fretboard/svg/number8.svg new file mode 100644 index 0000000000..5597c26d8a --- /dev/null +++ b/public/js/lib/renderer/fretboard/svg/number8.svg @@ -0,0 +1,10 @@ + + + + Rectangle + Created with Sketch. + + + + + \ No newline at end of file diff --git a/public/js/lib/renderer/fretboard/svg/number8_h.svg b/public/js/lib/renderer/fretboard/svg/number8_h.svg new file mode 100644 index 0000000000..b879c00e1b --- /dev/null +++ b/public/js/lib/renderer/fretboard/svg/number8_h.svg @@ -0,0 +1,10 @@ + + + + Rectangle + Created with Sketch. + + + + + \ No newline at end of file diff --git a/public/js/lib/renderer/fretboard/svg/number9.svg b/public/js/lib/renderer/fretboard/svg/number9.svg new file mode 100644 index 0000000000..a768620cbd --- /dev/null +++ b/public/js/lib/renderer/fretboard/svg/number9.svg @@ -0,0 +1,10 @@ + + + + Rectangle + Created with Sketch. + + + + + \ No newline at end of file diff --git a/public/js/lib/renderer/fretboard/svg/number9_h.svg b/public/js/lib/renderer/fretboard/svg/number9_h.svg new file mode 100644 index 0000000000..c181d570eb --- /dev/null +++ b/public/js/lib/renderer/fretboard/svg/number9_h.svg @@ -0,0 +1,10 @@ + + + + Rectangle + Created with Sketch. + + + + + \ No newline at end of file diff --git a/public/js/lib/renderer/fretboard/svg/string_o.svg b/public/js/lib/renderer/fretboard/svg/string_o.svg new file mode 100644 index 0000000000..4f4aa685ee --- /dev/null +++ b/public/js/lib/renderer/fretboard/svg/string_o.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/public/js/lib/renderer/fretboard/svg/string_x.svg b/public/js/lib/renderer/fretboard/svg/string_x.svg new file mode 100644 index 0000000000..cca096d1fe --- /dev/null +++ b/public/js/lib/renderer/fretboard/svg/string_x.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/public/js/lib/renderer/lightbox/index.js b/public/js/lib/renderer/lightbox/index.js new file mode 100644 index 0000000000..3619e0aeec --- /dev/null +++ b/public/js/lib/renderer/lightbox/index.js @@ -0,0 +1,212 @@ +import './lightbox.css' + +let images = [] +/** @type {HTMLImageElement} */ +let currentImage = null +let currentIndexIndex = 0 + +let hideContainer + +function findOrCreateLightboxContainer () { + const lightboxContainerSelector = '.lightbox-container' + + let lightBoxContainer = document.querySelector(lightboxContainerSelector) + if (!lightBoxContainer) { + lightBoxContainer = document.createElement('div') + lightBoxContainer.className = 'lightbox-container' + + lightBoxContainer.innerHTML = ` + + + + + + ` + + addImageZoomListener(lightBoxContainer) + + hideContainer = () => { + lightBoxContainer.classList.remove('show') + document.body.classList.remove('no-scroll') + currentImage = null + } + + lightBoxContainer.querySelector('.lightbox-control-previous').addEventListener('click', (e) => { + e.stopPropagation() + switchImage(-1) + }) + lightBoxContainer.querySelector('.lightbox-control-next').addEventListener('click', (e) => { + e.stopPropagation() + switchImage(1) + }) + lightBoxContainer.querySelector('.lightbox-control-close').addEventListener('click', (e) => { + e.stopPropagation() + hideContainer() + }) + lightBoxContainer.addEventListener('click', (e) => { + e.stopPropagation() + hideContainer() + }) + + document.body.appendChild(lightBoxContainer) + } + + return lightBoxContainer +} + +function switchImage (dir) { + const lightBoxContainer = findOrCreateLightboxContainer() + + currentIndexIndex += dir + if (currentIndexIndex >= images.length) { + currentIndexIndex = 0 + } else if (currentIndexIndex < 0) { + currentIndexIndex = images.length - 1 + } + + const img = images[currentIndexIndex] + + setImageInner(img, lightBoxContainer) +} + +function setImageInner (img, lightBoxContainer) { + const src = img.getAttribute('src') + const alt = img.getAttribute('alt') + + lightBoxContainer.querySelector('.lightbox-inner').innerHTML = `${alt}` + addImageDragListener(lightBoxContainer.querySelector('.lightbox-inner img')) +} + +function onClickImage (img) { + const lightBoxContainer = findOrCreateLightboxContainer() + + setImageInner(img, lightBoxContainer) + + lightBoxContainer.classList.add('show') + document.body.classList.add('no-scroll') + + currentImage = img + updateLightboxImages() +} + +function updateLightboxImages () { + images = [...document.querySelectorAll('.markdown-body img.md-image')] + + if (currentImage) { + currentIndexIndex = images.findIndex(image => image === currentImage) + } +} + +function addImageZoomListener (container) { + container.addEventListener('wheel', function (e) { + // normalize scroll position as percentage + e.preventDefault() + + /** @type {HTMLImageElement} */ + const image = container.querySelector('img') + + if (!image) { + return + } + + let scale = image.getBoundingClientRect().width / image.offsetWidth + scale += e.deltaY * -0.01 + + // Restrict scale + scale = Math.min(Math.max(0.125, scale), 4) + + var transformValue = `scale(${scale})` + + image.style.WebkitTransform = transformValue + image.style.MozTransform = transformValue + image.style.OTransform = transformValue + image.style.transform = transformValue + }) +} + +/** + * @param {HTMLImageElement} image + */ +function addImageDragListener (image) { + let moved = false + let pos = [] + + const container = findOrCreateLightboxContainer() + const inner = container.querySelector('.lightbox-inner') + + const onMouseDown = (evt) => { + moved = true + + const { left, top } = image.getBoundingClientRect() + + pos = [ + evt.pageX - left, + evt.pageY - top + ] + } + image.addEventListener('mousedown', onMouseDown) + inner.addEventListener('mousedown', onMouseDown) + + const onMouseMove = (evt) => { + if (!moved) { + return + } + + image.style.left = `${evt.pageX - pos[0]}px` + image.style.top = `${evt.pageY - pos[1]}px` + image.style.position = 'absolute' + } + image.addEventListener('mousemove', onMouseMove) + inner.addEventListener('mousemove', onMouseMove) + + const onMouseUp = () => { + moved = false + pos = [] + } + image.addEventListener('mouseup', onMouseUp) + inner.addEventListener('mouseup', onMouseUp) + + inner.addEventListener('click', (e) => { + e.stopPropagation() + }) + image.addEventListener('click', (e) => { + e.stopPropagation() + }) +} + +const init = () => { + const markdownBody = document.querySelector('.markdown-body') + if (!markdownBody) { + return + } + + markdownBody.addEventListener('click', function (e) { + const img = e.target + if (img.nodeName === 'IMG' && img.classList.contains('md-image')) { + onClickImage(img) + e.stopPropagation() + } + }) + + window.addEventListener('keydown', function (e) { + if (!currentImage) { + return + } + + if (e.key === 'ArrowRight') { + switchImage(1) + e.stopPropagation() + } else if (e.key === 'ArrowLeft') { + switchImage(-1) + e.stopPropagation() + } else if (e.key === 'Escape') { + if (hideContainer) { + hideContainer() + e.stopPropagation() + } + } + }) +} + +init() diff --git a/public/js/lib/renderer/lightbox/lightbox.css b/public/js/lib/renderer/lightbox/lightbox.css new file mode 100644 index 0000000000..54ff694814 --- /dev/null +++ b/public/js/lib/renderer/lightbox/lightbox.css @@ -0,0 +1,115 @@ +.lightbox-container.show { + display: flex !important; +} + +.lightbox-container { + display: none; + + position: fixed; + z-index: 99999; + background-color: rgba(255, 255, 255, 0.8); + top: 0; + left: 0; + width: 100%; + height: 100%; + + justify-content: center; + align-items: center; + padding: 0px 40px; +} + +.night .lightbox-container { + background-color: rgba(47, 47, 47, 0.8);; +} + +.lightbox-container .lightbox-control-previous, +.lightbox-container .lightbox-control-next, +.lightbox-container .lightbox-control-close { + position: absolute; + width: 40px; + height: 40px; + color: rgba(65, 65, 65, 0.8); + text-align: center; + cursor: pointer; + user-select: none; + font-size: 25px; + z-index: 1; +} + +.night .lightbox-container .lightbox-control-previous, +.night .lightbox-container .lightbox-control-next, +.night .lightbox-container .lightbox-control-close { + color: rgba(255, 255, 255, 0.5); +} + +.lightbox-container .lightbox-control-previous:hover, +.lightbox-container .lightbox-control-next:hover, +.lightbox-container .lightbox-control-close:hover { + color: rgba(130, 130, 130, 0.78); +} + +.night .lightbox-container .lightbox-control-previous:hover, +.night .lightbox-container .lightbox-control-next:hover, +.night .lightbox-container .lightbox-control-close:hover { + color: rgba(255, 255, 255, 0.8); +} + +.lightbox-container .lightbox-control-next, +.lightbox-container .lightbox-control-previous { + top: calc(50% - 10px); +} + +.lightbox-container .lightbox-control-previous { + left: 0; +} + +.lightbox-container .lightbox-control-next { + right: 0; +} + +.lightbox-container .lightbox-control-close { + top: 10px; + right: 10px; +} + +.lightbox-container .lightbox-inner { + width: 100%; + height: 100%; + cursor: move; + + display: -webkit-box; + display: -webkit-flex; + display: -moz-box; + display: -ms-flexbox; + + display: flex; + -webkit-box-align: center; + -webkit-align-items: center; + -moz-box-align: center; + -ms-flex-align: center; + align-items: center; + -webkit-box-pack: center; + -webkit-justify-content: center; + -moz-box-pack: center; + -ms-flex-pack: center; + justify-content: center; +} + +.lightbox-container .lightbox-inner img { + max-width: 100%; + cursor: move; + + transform-origin: 0 0; + -moz-transform-origin: 0 0; + -webkit-transform-origin: 0 0; + -ms-transform-origin: 0 0; + -o-transform-origin: 0 0; +} + +.markdown-body img.md-image { + cursor: zoom-in; +} + +body.no-scroll { + overflow: hidden; +} diff --git a/public/js/lib/syncscroll.js b/public/js/lib/syncscroll.js index 9daf327c8d..41f0af1f08 100644 --- a/public/js/lib/syncscroll.js +++ b/public/js/lib/syncscroll.js @@ -7,6 +7,8 @@ import markdownitContainer from 'markdown-it-container' import { md } from '../extra' import modeType from './modeType' import appState from './appState' +import { renderCSVPreview } from './renderer/csvpreview' +import { parseFenceCodeParams } from './markdown/utils' function addPart (tokens, idx) { if (tokens[idx].map && tokens[idx].level === 0) { @@ -27,6 +29,11 @@ md.renderer.rules.table_open = function (tokens, idx, options, env, self) { addPart(tokens, idx) return self.renderToken(...arguments) } +const defaultImageRender = md.renderer.rules.image +md.renderer.rules.image = function (tokens, idx, options, env, self) { + tokens[idx].attrJoin('class', 'md-image') + return defaultImageRender(...arguments) +} md.renderer.rules.bullet_list_open = function (tokens, idx, options, env, self) { addPart(tokens, idx) return self.renderToken(...arguments) @@ -66,6 +73,18 @@ md.renderer.rules.fence = (tokens, idx, options, env, self) => { if (info) { langName = info.split(/\s+/g)[0] + + if (langName === 'csvpreview') { + const params = parseFenceCodeParams(info) + let attr = '' + if (tokens[idx].map && tokens[idx].level === 0) { + const startline = tokens[idx].map[0] + 1 + const endline = tokens[idx].map[1] + attr = `class="part" data-startline="${startline}" data-endline="${endline}"` + } + return renderCSVPreview(token.content, params, attr) + } + if (/!$/.test(info)) token.attrJoin('class', 'wrap') token.attrJoin('class', options.langPrefix + langName.replace(/=$|=\d+$|=\+$|!$|=!/, '')) token.attrJoin('class', 'hljs') diff --git a/public/js/render.js b/public/js/render.js index ebda298440..4a9c3b25f7 100644 --- a/public/js/render.js +++ b/public/js/render.js @@ -12,27 +12,27 @@ var dataUriRegex = /^\s*data:([a-z]+\/[a-z0-9-+.]+(;[a-z-]+=[a-z0-9-]+)?)?(;base // custom white list var whiteList = filterXSS.whiteList // allow ol specify start number -whiteList['ol'] = ['start'] +whiteList.ol = ['start'] // allow li specify value number -whiteList['li'] = ['value'] +whiteList.li = ['value'] // allow style tag -whiteList['style'] = [] +whiteList.style = [] // allow kbd tag -whiteList['kbd'] = [] +whiteList.kbd = [] // allow ifram tag with some safe attributes -whiteList['iframe'] = ['allowfullscreen', 'name', 'referrerpolicy', 'src', 'width', 'height'] +whiteList.iframe = ['allowfullscreen', 'name', 'referrerpolicy', 'src', 'width', 'height'] // allow summary tag -whiteList['summary'] = [] +whiteList.summary = [] // allow ruby tag -whiteList['ruby'] = [] +whiteList.ruby = [] // allow rp tag for ruby -whiteList['rp'] = [] +whiteList.rp = [] // allow rt tag for ruby -whiteList['rt'] = [] +whiteList.rt = [] // allow figure tag -whiteList['figure'] = [] +whiteList.figure = [] // allow figcaption tag -whiteList['figcaption'] = [] +whiteList.figcaption = [] var filterXSSOptions = { allowCommentTag: true, diff --git a/public/js/lib/editor/markdown-lint/lint.css b/public/markdown-lint/css/lint.css similarity index 84% rename from public/js/lib/editor/markdown-lint/lint.css rename to public/markdown-lint/css/lint.css index 0244eae251..cad3e236d7 100644 --- a/public/js/lib/editor/markdown-lint/lint.css +++ b/public/markdown-lint/css/lint.css @@ -31,11 +31,11 @@ } .CodeMirror-lint-mark-error { - background-image: url(/images/lint/mark-error.png); + background-image: url(../images/mark-error.png); } .CodeMirror-lint-mark-warning { - background-image: url(/images/lint/mark-warning.png); + background-image: url(../images/mark-warning.png); } .CodeMirror-lint-marker-error, .CodeMirror-lint-marker-warning { @@ -58,15 +58,15 @@ } .CodeMirror-lint-marker-error, .CodeMirror-lint-message-error { - background-image: url(/images/lint/message-error.png); + background-image: url(../images/message-error.png); } .CodeMirror-lint-marker-warning, .CodeMirror-lint-message-warning { - background-image: url(/images/lint/message-warning.png); + background-image: url(../images/message-warning.png); } .CodeMirror-lint-marker-multiple { - background-image: url(/images/lint/mark-multiple.png); + background-image: url(../images/mark-multiple.png); background-repeat: no-repeat; background-position: right bottom; width: 100%; height: 100%; diff --git a/public/images/lint/mark-error.png b/public/markdown-lint/images/mark-error.png similarity index 100% rename from public/images/lint/mark-error.png rename to public/markdown-lint/images/mark-error.png diff --git a/public/images/lint/mark-multiple.png b/public/markdown-lint/images/mark-multiple.png similarity index 100% rename from public/images/lint/mark-multiple.png rename to public/markdown-lint/images/mark-multiple.png diff --git a/public/images/lint/mark-warning.png b/public/markdown-lint/images/mark-warning.png similarity index 100% rename from public/images/lint/mark-warning.png rename to public/markdown-lint/images/mark-warning.png diff --git a/public/images/lint/message-error.png b/public/markdown-lint/images/message-error.png similarity index 100% rename from public/images/lint/message-error.png rename to public/markdown-lint/images/message-error.png diff --git a/public/images/lint/message-warning.png b/public/markdown-lint/images/message-warning.png similarity index 100% rename from public/images/lint/message-warning.png rename to public/markdown-lint/images/message-warning.png diff --git a/public/views/codimd/head.ejs b/public/views/codimd/head.ejs index 451779bb42..810781bbad 100644 --- a/public/views/codimd/head.ejs +++ b/public/views/codimd/head.ejs @@ -18,5 +18,8 @@ <%- include ../shared/polyfill %> <% } else { %> + + <%- include ../build/index-pack-header %> <% } %> + diff --git a/public/views/includes/scripts.ejs b/public/views/includes/scripts.ejs index 08a7d2182f..b63506142e 100644 --- a/public/views/includes/scripts.ejs +++ b/public/views/includes/scripts.ejs @@ -1,4 +1,4 @@ - + <% for (var js in htmlWebpackPlugin.files.js) { %> <% } %> diff --git a/public/views/index/body.ejs b/public/views/index/body.ejs index 7b5200706f..db20b0e3c7 100644 --- a/public/views/index/body.ejs +++ b/public/views/index/body.ejs @@ -151,7 +151,7 @@

- <%- __('Powered by %s', 'CodiMD') %> | <%= __('Releases') %>| <%= __('Source Code') %><% if(privacyStatement) { %> | <%= __('Privacy') %><% } %><% if(termsOfUse) { %> | <%= __('Terms of Use') %><% } %> + <%- __('Powered by %s', 'CodiMD') %> | <%= __('Releases') %> | <%= __('Source Code') %><% if(privacyStatement) { %> | <%= __('Privacy') %><% } %><% if(termsOfUse) { %> | <%= __('Terms of Use') %><% } %>