diff --git a/README.md b/README.md index 6cd384e67..08d66b493 100644 --- a/README.md +++ b/README.md @@ -58,9 +58,9 @@ More info about that can be found in the configuration docs above. To use HedgeDoc, your browser should match or exceed these versions: - ![Chrome](https://raw.githubusercontent.com/alrra/browser-logos/HEAD/src/chrome/chrome_24x24.png) Chrome >= 47, ![Chrome](https://raw.githubusercontent.com/alrra/browser-logos/HEAD/src/chrome/chrome_24x24.png) Chrome for Android >= 47 -- ![Safari](https://raw.githubusercontent.com/alrra/browser-logos/HEAD/src/safari/safari_24x24.png) Safari >= 9, ![iOS Safarai](https://raw.githubusercontent.com/alrra/browser-logos/HEAD/src/safari-ios/safari-ios_24x24.png) iOS Safari >= 8.4 +- ![Safari](https://raw.githubusercontent.com/alrra/browser-logos/HEAD/src/safari/safari_24x24.png) Safari >= 10.1, ![iOS Safari](https://raw.githubusercontent.com/alrra/browser-logos/HEAD/src/safari-ios/safari-ios_24x24.png) iOS Safari >= 10.3 - ![Firefox](https://raw.githubusercontent.com/alrra/browser-logos/HEAD/src/firefox/firefox_24x24.png) Firefox >= 44 -- ![Edge](https://raw.githubusercontent.com/alrra/browser-logos/HEAD/src/edge/edge_24x24.png) Edge >= 12 +- ![Edge](https://raw.githubusercontent.com/alrra/browser-logos/HEAD/src/edge/edge_24x24.png) Edge >= 14 - ![Opera](https://raw.githubusercontent.com/alrra/browser-logos/HEAD/src/opera/opera_24x24.png) Opera >= 34, ![Opera Mini](https://raw.githubusercontent.com/alrra/browser-logos/HEAD/src/opera-mini/opera-mini_24x24.png) Opera Mini not supported diff --git a/app.js b/app.js index 45e70c8b5..878c85729 100644 --- a/app.js +++ b/app.js @@ -191,7 +191,6 @@ app.engine('ejs', ejs.renderFile) // set view engine app.set('view engine', 'ejs') // set generally available variables for all views -app.locals.useCDN = config.useCDN app.locals.serverURL = config.serverURL app.locals.sourceURL = config.sourceURL app.locals.allowAnonymous = config.allowAnonymous diff --git a/docs/content/configuration.md b/docs/content/configuration.md index 51f82581a..36f253148 100644 --- a/docs/content/configuration.md +++ b/docs/content/configuration.md @@ -100,7 +100,6 @@ these are rarely used for various reasons. | config file | environment | **default** and example value | description | | --------------- | -------------------- | ----------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | `allowGravatar` | `CMD_ALLOW_GRAVATAR` | **`true`** or `false` | set to `false` to disable [Libravatar](https://www.libravatar.org/) as profile picture source on your instance. Libravatar is a federated open-source alternative to Gravatar. | -| `useCDN` | `CMD_USECDN` | **`false`** or `true` | set to use CDN resources or not (default is `false`) | ## Users and Privileges diff --git a/lib/config/default.js b/lib/config/default.js index c687e4841..89de46727 100644 --- a/lib/config/default.js +++ b/lib/config/default.js @@ -31,7 +31,6 @@ module.exports = { }, cookiePolicy: 'lax', protocolUseSSL: false, - useCDN: false, allowAnonymous: true, allowAnonymousEdits: false, allowFreeURL: false, diff --git a/lib/config/environment.js b/lib/config/environment.js index cd83dc12f..8c161341e 100644 --- a/lib/config/environment.js +++ b/lib/config/environment.js @@ -29,7 +29,6 @@ module.exports = { cookiePolicy: process.env.CMD_COOKIE_POLICY, protocolUseSSL: toBooleanConfig(process.env.CMD_PROTOCOL_USESSL), allowOrigin: toArrayConfig(process.env.CMD_ALLOW_ORIGIN), - useCDN: toBooleanConfig(process.env.CMD_USECDN), allowAnonymous: toBooleanConfig(process.env.CMD_ALLOW_ANONYMOUS), allowAnonymousEdits: toBooleanConfig(process.env.CMD_ALLOW_ANONYMOUS_EDITS), allowFreeURL: toBooleanConfig(process.env.CMD_ALLOW_FREEURL), diff --git a/lib/config/hackmdEnvironment.js b/lib/config/hackmdEnvironment.js index 76e413612..c40ffc961 100644 --- a/lib/config/hackmdEnvironment.js +++ b/lib/config/hackmdEnvironment.js @@ -20,7 +20,6 @@ module.exports = { }, protocolUseSSL: toBooleanConfig(process.env.HMD_PROTOCOL_USESSL), allowOrigin: toArrayConfig(process.env.HMD_ALLOW_ORIGIN), - useCDN: toBooleanConfig(process.env.HMD_USECDN), allowAnonymous: toBooleanConfig(process.env.HMD_ALLOW_ANONYMOUS), allowAnonymousEdits: toBooleanConfig(process.env.HMD_ALLOW_ANONYMOUS_EDITS), allowFreeURL: toBooleanConfig(process.env.HMD_ALLOW_FREEURL), diff --git a/lib/config/oldDefault.js b/lib/config/oldDefault.js index 738ad9f7d..c9af50988 100644 --- a/lib/config/oldDefault.js +++ b/lib/config/oldDefault.js @@ -6,7 +6,6 @@ module.exports = { alloworigin: undefined, usessl: undefined, protocolusessl: undefined, - usecdn: undefined, allowanonymous: undefined, allowanonymousedits: undefined, allowfreeurl: undefined, diff --git a/lib/csp.js b/lib/csp.js index 52a8d4b8a..00a9a5a7f 100644 --- a/lib/csp.js +++ b/lib/csp.js @@ -26,12 +26,6 @@ const defaultDirectives = { mediaSrc: ['*'] } -const cdnDirectives = { - scriptSrc: ['https://cdnjs.cloudflare.com', 'https://cdn.mathjax.org'], - styleSrc: ['https://cdnjs.cloudflare.com', 'https://fonts.googleapis.com'], - fontSrc: ['https://cdnjs.cloudflare.com', 'https://fonts.gstatic.com'] -} - const disqusDirectives = { scriptSrc: ['https://disqus.com', 'https://*.disqus.com', 'https://*.disquscdn.com'], styleSrc: ['https://*.disquscdn.com'], @@ -59,7 +53,6 @@ CspStrategy.computeDirectives = function () { const directives = {} mergeDirectives(directives, config.csp.directives) mergeDirectivesIf(config.csp.addDefaults, directives, defaultDirectives) - mergeDirectivesIf(config.useCDN, directives, cdnDirectives) mergeDirectivesIf(config.csp.addDisqus, directives, disqusDirectives) mergeDirectivesIf(config.csp.addGoogleAnalytics, directives, googleAnalyticsDirectives) mergeDirectivesIf(config.dropbox.appKey, directives, dropboxDirectives) diff --git a/public/css/google-font.css b/public/css/google-font.css deleted file mode 100644 index 02c1b103e..000000000 --- a/public/css/google-font.css +++ /dev/null @@ -1 +0,0 @@ -@import url(https://fonts.googleapis.com/css?family=Source+Sans+Pro:400,400italic,600,600italic,300italic,300|Source+Serif+Pro|Source+Code+Pro:400,300,500&subset=latin,latin-ext); diff --git a/public/js/extra.js b/public/js/extra.js index 616d43a32..52879e1a8 100644 --- a/public/js/extra.js +++ b/public/js/extra.js @@ -620,6 +620,18 @@ export function removeDOMEvents (view) { } window.removeDOMEvents = removeDOMEvents +function toDataURL (url, callback) { + fetch(url).then(response => { + const fr = new FileReader() + fr.onload = function () { + callback(this.result) + } + response.blob().then(blob => { + fr.readAsDataURL(blob) + }) + }) +} + function generateCleanHTML (view) { const src = view.clone() const eles = src.find('*') @@ -634,10 +646,9 @@ function generateCleanHTML (view) { src.find('input.task-list-item-checkbox').attr('disabled', '') // replace emoji image path src.find('img.emoji').each((key, value) => { - let name = $(value).attr('alt') - name = name.substr(1) - name = name.slice(0, name.length - 1) - $(value).attr('src', `https://cdnjs.cloudflare.com/ajax/libs/emojify.js/1.1.0/images/basic/${name}.png`) + toDataURL($(value).attr('src'), dataURL => { + $(value).attr('src', dataURL) + }) }) // replace video to iframe src.find('div[data-videoid]').each((key, value) => { @@ -681,21 +692,18 @@ export function exportToHTML (view) { const tocAffix = $('#ui-toc-affix').clone() tocAffix.find('*').removeClass('active').find("a[href^='#'][smoothhashscroll]").removeAttr('smoothhashscroll') // generate html via template - $.get(`${serverurl}/build/html.min.css`, css => { - $.get(`${serverurl}/views/html.hbs`, template => { - let html = template.replace('{{{url}}}', serverurl) - html = html.replace('{{title}}', title) - html = html.replace('{{{css}}}', css) - html = html.replace('{{{html}}}', src[0].outerHTML) - html = html.replace('{{{ui-toc}}}', toc.html()) - html = html.replace('{{{ui-toc-affix}}}', tocAffix.html()) - html = html.replace('{{{lang}}}', (md && md.meta && md.meta.lang) ? `lang="${md.meta.lang}"` : '') - html = html.replace('{{{dir}}}', (md && md.meta && md.meta.dir) ? `dir="${md.meta.dir}"` : '') - const blob = new Blob([html], { - type: 'text/html;charset=utf-8' - }) - saveAs(blob, filename, true) + $.get(`${serverurl}/build/htmlexport.html`, template => { + let html = template.replace('{{{url}}}', serverurl) + html = html.replace('{{title}}', title) + html = html.replace('{{{html}}}', src[0].outerHTML) + html = html.replace('{{{ui-toc}}}', toc.html()) + html = html.replace('{{{ui-toc-affix}}}', tocAffix.html()) + html = html.replace('{{{lang}}}', (md && md.meta && md.meta.lang) ? `lang="${md.meta.lang}"` : '') + html = html.replace('{{{dir}}}', (md && md.meta && md.meta.dir) ? `dir="${md.meta.dir}"` : '') + const blob = new Blob([html], { + type: 'text/html;charset=utf-8' }) + saveAs(blob, filename, true) }) } diff --git a/public/js/htmlExport.js b/public/js/htmlExport.js index 1a873aca0..676e2b1bc 100644 --- a/public/js/htmlExport.js +++ b/public/js/htmlExport.js @@ -1,6 +1,25 @@ +require('bootstrap/dist/css/bootstrap.min.css') +require('fork-awesome/css/fork-awesome.min.css') +require('ionicons/css/ionicons.min.css') +require('prismjs/prism') +require('prismjs/themes/prism.css') +require('prismjs/components/prism-wiki') +require('prismjs/components/prism-haskell') +require('prismjs/components/prism-go') +require('prismjs/components/prism-typescript') +require('prismjs/components/prism-jsx') +require('prismjs/components/prism-makefile') +require('prismjs/components/prism-gherkin') +require('highlight.js/styles/github-gist.css') +require('emojify.js/dist/css/basic/emojify.min.css') require('../css/github-extract.css') require('../css/markdown.css') require('../css/extra.css') require('../css/slide-preview.css') -require('../css/google-font.css') +require('../css/font.css') require('../css/site.css') +const $ = require('jquery') +window.jQuery = $ +window.$ = $ +require('bootstrap') +require('gist-embed/gist-embed.min') diff --git a/public/views/hedgedoc/footer.ejs b/public/views/hedgedoc/footer.ejs index 86572091e..c3927db77 100644 --- a/public/views/hedgedoc/footer.ejs +++ b/public/views/hedgedoc/footer.ejs @@ -1,28 +1,5 @@ -<% if(useCDN) { %> - - - - - - - - - - - - - - - - - - - -<%- include('../build/index-scripts') %> -<% } else { %> <%- include('../build/index-pack-scripts') %> -<% } %> diff --git a/public/views/hedgedoc/head.ejs b/public/views/hedgedoc/head.ejs index 419d5dcc7..c2321fccf 100644 --- a/public/views/hedgedoc/head.ejs +++ b/public/views/hedgedoc/head.ejs @@ -15,15 +15,5 @@ <% } %> <%= title %> -<% if(useCDN) { %> - - - - - -<%- include('../build/index-header') %> -<%- include('../shared/polyfill') %> -<% } else { %> <%- include('../build/index-pack-header') %> -<% } %> diff --git a/public/views/html.hbs b/public/views/htmlexport.ejs similarity index 71% rename from public/views/html.hbs rename to public/views/htmlexport.ejs index 7e8268ba3..ee83d8505 100644 --- a/public/views/html.hbs +++ b/public/views/htmlexport.ejs @@ -19,22 +19,7 @@ - - - - - - - - - - + <% _.forEach(htmlWebpackPlugin.files.css, function(cssFile) { %><% }); %> @@ -52,9 +37,7 @@ - - - + <% _.forEach(htmlWebpackPlugin.files.js, function(jsFile) { %><% }); %> - - - - - - -<%- include('../build/cover-scripts') %> -<% } else { %> <%- include('../build/cover-pack-scripts') %> -<% } %> diff --git a/public/views/index/head.ejs b/public/views/index/head.ejs index a6e79e35c..a6751bbf4 100644 --- a/public/views/index/head.ejs +++ b/public/views/index/head.ejs @@ -16,14 +16,4 @@ HedgeDoc - <%= __('Collaborative markdown notes') %> -<% if(useCDN) { %> - - - - - -<%- include('../build/cover-header') %> -<%- include('../shared/polyfill') %> -<% } else { %> <%- include('../build/cover-pack-header') %> -<% } %> diff --git a/public/views/pretty.ejs b/public/views/pretty.ejs index fc222cb8c..97274ba15 100644 --- a/public/views/pretty.ejs +++ b/public/views/pretty.ejs @@ -25,17 +25,8 @@ <%= title %> <%- include('includes/favicon.ejs') %> - <% if(useCDN) { %> - - - - - <%- include('build/pretty-header') %> - <%- include('shared/polyfill') %> - <% } else { %> <%- include('build/pretty-pack-header') %> - <% } %> @@ -80,27 +71,8 @@ -<% if(useCDN) { %> - - - - - - - - - - - - - - - -<%- include('build/pretty-scripts') %> -<% } else { %> <%- include('build/pretty-pack-scripts') %> -<% } %> <%- include('shared/ga') %> diff --git a/public/views/shared/polyfill.ejs b/public/views/shared/polyfill.ejs deleted file mode 100644 index 82b2f0253..000000000 --- a/public/views/shared/polyfill.ejs +++ /dev/null @@ -1,7 +0,0 @@ - - - diff --git a/public/views/slide.ejs b/public/views/slide.ejs index e08ca0579..5837737ce 100644 --- a/public/views/slide.ejs +++ b/public/views/slide.ejs @@ -14,19 +14,9 @@ <%= title %> <%- include('includes/favicon.ejs') %> - - <% if(useCDN) { %> - - - - - <%- include('build/slide-header') %> - <%- include('shared/polyfill') %> - <% } else { %> <%- include('build/slide-pack-header') %> - <% } %> <% if(typeof theme !== 'undefined' && theme) { %> @@ -87,29 +77,10 @@ - <% if(useCDN) { %> - - - - - - - - - - - - - - - - <%- include('build/slide-scripts') %> - <% } else { %> <%- include('build/slide-pack-scripts') %> - <% } %> diff --git a/test/csp.js b/test/csp.js index 9257eed96..a1edc9e95 100644 --- a/test/csp.js +++ b/test/csp.js @@ -27,7 +27,6 @@ describe('Content security policies', function () { upgradeInsecureRequests: 'auto', reportURI: undefined }, - useCDN: true, dropbox: { appKey: undefined } @@ -44,21 +43,6 @@ describe('Content security policies', function () { csp = mock.reRequire('../lib/csp') }) - // beginnging Tests - it('Disable CDN', function () { - const testconfig = defaultConfig - testconfig.useCDN = false - mock('../lib/config', testconfig) - csp = mock.reRequire('../lib/csp') - - assert(!csp.computeDirectives().scriptSrc.includes('https://cdnjs.cloudflare.com')) - assert(!csp.computeDirectives().scriptSrc.includes('https://cdn.mathjax.org')) - assert(!csp.computeDirectives().styleSrc.includes('https://cdnjs.cloudflare.com')) - assert(!csp.computeDirectives().styleSrc.includes('https://fonts.googleapis.com')) - assert(!csp.computeDirectives().fontSrc.includes('https://cdnjs.cloudflare.com')) - assert(!csp.computeDirectives().fontSrc.includes('https://fonts.gstatic.com')) - }) - it('Disable Google Analytics', function () { const testconfig = defaultConfig testconfig.csp.addGoogleAnalytics = false diff --git a/webpack.common.js b/webpack.common.js index 48b5bf147..98a2bfc16 100644 --- a/webpack.common.js +++ b/webpack.common.js @@ -22,13 +22,6 @@ module.exports = { 'moment': 'moment', CodeMirror: 'codemirror/lib/codemirror.js' }), - new HtmlWebpackPlugin({ - template: 'public/views/includes/header.ejs', - chunks: ['font', 'index-styles', 'index'], - filename: path.join(__dirname, 'public/views/build/index-header.ejs'), - inject: false, - chunksSortMode: 'manual' - }), new HtmlWebpackPlugin({ template: 'public/views/includes/header.ejs', chunks: ['font-pack', 'index-styles-pack', 'index-styles', 'index'], @@ -36,12 +29,6 @@ module.exports = { inject: false, chunksSortMode: 'manual' }), - new HtmlWebpackPlugin({ - template: 'public/views/includes/scripts.ejs', - chunks: ['index'], - filename: path.join(__dirname, 'public/views/build/index-scripts.ejs'), - inject: false - }), new HtmlWebpackPlugin({ template: 'public/views/includes/scripts.ejs', chunks: ['common', 'index-pack'], @@ -49,13 +36,6 @@ module.exports = { inject: false, chunksSortMode: 'manual' }), - new HtmlWebpackPlugin({ - template: 'public/views/includes/header.ejs', - chunks: ['font', 'cover'], - filename: path.join(__dirname, 'public/views/build/cover-header.ejs'), - inject: false, - chunksSortMode: 'manual' - }), new HtmlWebpackPlugin({ template: 'public/views/includes/header.ejs', chunks: ['font-pack', 'cover-styles-pack', 'cover'], @@ -63,12 +43,6 @@ module.exports = { inject: false, chunksSortMode: 'manual' }), - new HtmlWebpackPlugin({ - template: 'public/views/includes/scripts.ejs', - chunks: ['cover'], - filename: path.join(__dirname, 'public/views/build/cover-scripts.ejs'), - inject: false - }), new HtmlWebpackPlugin({ template: 'public/views/includes/scripts.ejs', chunks: ['common', 'cover-pack'], @@ -76,13 +50,6 @@ module.exports = { inject: false, chunksSortMode: 'manual' }), - new HtmlWebpackPlugin({ - template: 'public/views/includes/header.ejs', - chunks: ['font', 'pretty-styles', 'pretty'], - filename: path.join(__dirname, 'public/views/build/pretty-header.ejs'), - inject: false, - chunksSortMode: 'manual' - }), new HtmlWebpackPlugin({ template: 'public/views/includes/header.ejs', chunks: ['font-pack', 'pretty-styles-pack', 'pretty-styles', 'pretty'], @@ -90,12 +57,6 @@ module.exports = { inject: false, chunksSortMode: 'manual' }), - new HtmlWebpackPlugin({ - template: 'public/views/includes/scripts.ejs', - chunks: ['pretty'], - filename: path.join(__dirname, 'public/views/build/pretty-scripts.ejs'), - inject: false - }), new HtmlWebpackPlugin({ template: 'public/views/includes/scripts.ejs', chunks: ['common', 'pretty-pack'], @@ -103,13 +64,6 @@ module.exports = { inject: false, chunksSortMode: 'manual' }), - new HtmlWebpackPlugin({ - template: 'public/views/includes/header.ejs', - chunks: ['font', 'slide-styles', 'slide'], - filename: path.join(__dirname, 'public/views/build/slide-header.ejs'), - inject: false, - chunksSortMode: 'manual' - }), new HtmlWebpackPlugin({ template: 'public/views/includes/header.ejs', chunks: ['font-pack', 'slide-styles-pack', 'slide-styles', 'slide'], @@ -117,12 +71,6 @@ module.exports = { inject: false, chunksSortMode: 'manual' }), - new HtmlWebpackPlugin({ - template: 'public/views/includes/scripts.ejs', - chunks: ['slide'], - filename: path.join(__dirname, 'public/views/build/slide-scripts.ejs'), - inject: false - }), new HtmlWebpackPlugin({ template: 'public/views/includes/scripts.ejs', chunks: ['slide-pack'], @@ -184,7 +132,6 @@ module.exports = { ], entry: { - font: path.join(__dirname, 'public/css/google-font.css'), 'font-pack': path.join(__dirname, 'public/css/font.css'), common: [ 'expose-loader?exposes[]=$&exposes[]=jQuery!jquery', diff --git a/webpack.htmlexport.js b/webpack.htmlexport.js index 2aa2bc4fc..458e1dd13 100644 --- a/webpack.htmlexport.js +++ b/webpack.htmlexport.js @@ -1,5 +1,6 @@ const MiniCssExtractPlugin = require('mini-css-extract-plugin') const path = require('path') +const HtmlWebpackPlugin = require('html-webpack-plugin') module.exports = { name: 'save-as-html', @@ -10,6 +11,14 @@ module.exports = { rules: [{ test: /\.css$/, use: [MiniCssExtractPlugin.loader, 'css-loader'] + }, + { + test: /\.(woff(2)?|ttf|eot|svg)(\?v=\d+\.\d+\.\d+)?$/, + use: [ + { + loader: 'url-loader' + } + ] }] }, output: { @@ -18,8 +27,13 @@ module.exports = { filename: '[name].js' }, plugins: [ - new MiniCssExtractPlugin({ - filename: 'html.min.css' - }) + new HtmlWebpackPlugin({ + // Load a custom template (uses lodash templating) + template: 'public/views/htmlexport.ejs', + filename: 'htmlexport.html', + inject: false, + cache: false + }), + new MiniCssExtractPlugin({ filename: 'htmlexport.css' }) ] } diff --git a/webpack.prod.js b/webpack.prod.js index 69d77fb10..17585aa25 100644 --- a/webpack.prod.js +++ b/webpack.prod.js @@ -30,6 +30,9 @@ module.exports = [ mode: 'production', optimization: { minimizer: [ + new ESBuildMinifyPlugin({ + target: 'es2015' + }), new OptimizeCSSAssetsPlugin({}) ] }