Added undo/redo buttons. Restore file when imported from Google Drive and Dropbox. Lint server
This commit is contained in:
parent
e15b9fae16
commit
83a94dbdff
@ -12,7 +12,7 @@
|
|||||||
"postinstall": "gulp build-prism",
|
"postinstall": "gulp build-prism",
|
||||||
"start": "node build/dev-server.js",
|
"start": "node build/dev-server.js",
|
||||||
"build": "node build/build.js",
|
"build": "node build/build.js",
|
||||||
"lint": "eslint --ext .js,.vue src",
|
"lint": "eslint --ext .js,.vue src server",
|
||||||
"preversion": "npm run lint",
|
"preversion": "npm run lint",
|
||||||
"postversion": "git push origin master --tags && npm publish",
|
"postversion": "git push origin master --tags && npm publish",
|
||||||
"patch": "npm version patch -m \"Tag v%s\"",
|
"patch": "npm version patch -m \"Tag v%s\"",
|
||||||
|
@ -1,31 +1,35 @@
|
|||||||
var compression = require('compression');
|
const compression = require('compression');
|
||||||
var serveStatic = require('serve-static');
|
const serveStatic = require('serve-static');
|
||||||
var path = require('path');
|
const path = require('path');
|
||||||
|
const github = require('./github');
|
||||||
|
|
||||||
module.exports = function (app, serveV4) {
|
module.exports = (app, serveV4) => {
|
||||||
// Use gzip compression
|
// Use gzip compression
|
||||||
if (process.env.NODE_ENV === 'production') {
|
if (process.env.NODE_ENV === 'production') {
|
||||||
app.all('*', function(req, res, next) {
|
app.all('*', (req, res, next) => {
|
||||||
// Force HTTPS on stackedit.io
|
// Force HTTPS on stackedit.io
|
||||||
if (req.headers.host === 'stackedit.io' && !req.secure && req.headers['x-forwarded-proto'] !== 'https') {
|
if (req.headers.host === 'stackedit.io' && !req.secure && req.headers['x-forwarded-proto'] !== 'https') {
|
||||||
return res.redirect('https://stackedit.io' + req.url);
|
res.redirect(`https://stackedit.io${req.url}`);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
// Enable CORS for fonts
|
// Enable CORS for fonts
|
||||||
if (/\.(eot|ttf|woff|svg)$/.test(req.url)) {
|
if (/\.(eot|ttf|woff|svg)$/.test(req.url)) {
|
||||||
res.header('Access-Control-Allow-Origin', '*');
|
res.header('Access-Control-Allow-Origin', '*');
|
||||||
}
|
};
|
||||||
next();
|
next();
|
||||||
});
|
});
|
||||||
|
|
||||||
app.use(compression());
|
app.use(compression());
|
||||||
}
|
}
|
||||||
|
|
||||||
app.get('/oauth2/githubToken', require('./github').githubToken);
|
app.get('/oauth2/githubToken', github.githubToken);
|
||||||
if (serveV4) {
|
if (serveV4) {
|
||||||
|
/* eslint-disable global-require, import/no-unresolved */
|
||||||
app.post('/pdfExport', require('../stackedit_v4/app/pdf').export);
|
app.post('/pdfExport', require('../stackedit_v4/app/pdf').export);
|
||||||
app.post('/sshPublish', require('../stackedit_v4/app/ssh').publish);
|
app.post('/sshPublish', require('../stackedit_v4/app/ssh').publish);
|
||||||
app.post('/picasaImportImg', require('../stackedit_v4/app/picasa').importImg);
|
app.post('/picasaImportImg', require('../stackedit_v4/app/picasa').importImg);
|
||||||
app.get('/downloadImport', require('../stackedit_v4/app/download').importPublic);
|
app.get('/downloadImport', require('../stackedit_v4/app/download').importPublic);
|
||||||
|
/* eslint-enable global-require, import/no-unresolved */
|
||||||
}
|
}
|
||||||
|
|
||||||
// Serve callback.html in /app
|
// Serve callback.html in /app
|
||||||
@ -37,23 +41,15 @@ module.exports = function (app, serveV4) {
|
|||||||
if (process.env.NODE_ENV === 'production') {
|
if (process.env.NODE_ENV === 'production') {
|
||||||
if (serveV4) {
|
if (serveV4) {
|
||||||
// Serve landing.html in /
|
// Serve landing.html in /
|
||||||
app.get('/', function(req, res) {
|
app.get('/', (req, res) => res.sendFile(require.resolve('../stackedit_v4/views/landing.html')));
|
||||||
res.sendFile(require.resolve('../stackedit_v4/views/landing.html'));
|
|
||||||
});
|
|
||||||
// Serve editor.html in /viewer
|
// Serve editor.html in /viewer
|
||||||
app.get('/editor', function(req, res) {
|
app.get('/editor', (req, res) => res.sendFile(require.resolve('../stackedit_v4/views/editor.html')));
|
||||||
res.sendFile(require.resolve('../stackedit_v4/views/editor.html'));
|
|
||||||
});
|
|
||||||
// Serve viewer.html in /viewer
|
// Serve viewer.html in /viewer
|
||||||
app.get('/viewer', function(req, res) {
|
app.get('/viewer', (req, res) => res.sendFile(require.resolve('../stackedit_v4/views/viewer.html')));
|
||||||
res.sendFile(require.resolve('../stackedit_v4/views/viewer.html'));
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Serve index.html in /app
|
// Serve index.html in /app
|
||||||
app.get('/app', function(req, res) {
|
app.get('/app', (req, res) => res.sendFile(path.join(__dirname, '../dist/index.html')));
|
||||||
res.sendFile(path.join(__dirname, '../dist/index.html'));
|
|
||||||
});
|
|
||||||
|
|
||||||
app.use(serveStatic(path.join(__dirname, '../dist')));
|
app.use(serveStatic(path.join(__dirname, '../dist')));
|
||||||
|
|
||||||
@ -61,9 +57,7 @@ module.exports = function (app, serveV4) {
|
|||||||
app.use(serveStatic(path.dirname(require.resolve('../stackedit_v4/public/cache.manifest'))));
|
app.use(serveStatic(path.dirname(require.resolve('../stackedit_v4/public/cache.manifest'))));
|
||||||
|
|
||||||
// Error 404
|
// Error 404
|
||||||
app.use(function(req, res) {
|
app.use((req, res) => res.status(404).sendFile(require.resolve('../stackedit_v4/views/error_404.html')));
|
||||||
res.status(404).sendFile(require.resolve('../stackedit_v4/views/error_404.html'));
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
145
server/pdf.js
145
server/pdf.js
@ -1,145 +0,0 @@
|
|||||||
/* global window,MathJax */
|
|
||||||
var spawn = require('child_process').spawn;
|
|
||||||
var fs = require('fs');
|
|
||||||
var path = require('path');
|
|
||||||
var os = require('os');
|
|
||||||
var request = require('request');
|
|
||||||
|
|
||||||
function waitForJavaScript() {
|
|
||||||
if(window.MathJax) {
|
|
||||||
// Amazon EC2: fix TeX font detection
|
|
||||||
MathJax.Hub.Register.StartupHook("HTML-CSS Jax Startup",function () {
|
|
||||||
var HTMLCSS = MathJax.OutputJax["HTML-CSS"];
|
|
||||||
HTMLCSS.Font.checkWebFont = function (check,font,callback) {
|
|
||||||
if (check.time(callback)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (check.total === 0) {
|
|
||||||
HTMLCSS.Font.testFont(font);
|
|
||||||
setTimeout(check,200);
|
|
||||||
} else {
|
|
||||||
callback(check.STATUS.OK);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
});
|
|
||||||
MathJax.Hub.Queue(function () {
|
|
||||||
window.status = 'done';
|
|
||||||
});
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
setTimeout(function() {
|
|
||||||
window.status = 'done';
|
|
||||||
}, 2000);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var authorizedPageSizes = [
|
|
||||||
'A3',
|
|
||||||
'A4',
|
|
||||||
'Legal',
|
|
||||||
'Letter'
|
|
||||||
];
|
|
||||||
|
|
||||||
exports.export = function(req, res, next) {
|
|
||||||
function onError(err) {
|
|
||||||
next(err);
|
|
||||||
}
|
|
||||||
function onUnknownError() {
|
|
||||||
res.statusCode = 400;
|
|
||||||
res.end('Unknown error');
|
|
||||||
}
|
|
||||||
function onUnauthorizedError() {
|
|
||||||
res.statusCode = 401;
|
|
||||||
res.end('Unauthorized');
|
|
||||||
}
|
|
||||||
function onTimeout() {
|
|
||||||
res.statusCode = 408;
|
|
||||||
res.end('Request timeout');
|
|
||||||
}
|
|
||||||
request({
|
|
||||||
uri: 'https://monetizejs.com/api/payments',
|
|
||||||
qs: {
|
|
||||||
access_token: req.query.token
|
|
||||||
},
|
|
||||||
json: true
|
|
||||||
}, function (err, paymentsRes, payments) {
|
|
||||||
var authorized = payments && payments.app == 'ESTHdCYOi18iLhhO' && (
|
|
||||||
(payments.chargeOption && payments.chargeOption.alias == 'once') ||
|
|
||||||
(payments.subscriptionOption && payments.subscriptionOption.alias == 'yearly'));
|
|
||||||
if(err || paymentsRes.statusCode != 200 || !authorized) {
|
|
||||||
return onUnauthorizedError();
|
|
||||||
}
|
|
||||||
var options, params = [];
|
|
||||||
try {
|
|
||||||
options = JSON.parse(req.query.options);
|
|
||||||
}
|
|
||||||
catch(e) {
|
|
||||||
options = {};
|
|
||||||
}
|
|
||||||
|
|
||||||
// Margins
|
|
||||||
var marginTop = parseInt(options.marginTop);
|
|
||||||
params.push('-T', isNaN(marginTop) ? 25 : marginTop);
|
|
||||||
var marginRight = parseInt(options.marginRight);
|
|
||||||
params.push('-R', isNaN(marginRight) ? 25 : marginRight);
|
|
||||||
var marginBottom = parseInt(options.marginBottom);
|
|
||||||
params.push('-B', isNaN(marginBottom) ? 25 : marginBottom);
|
|
||||||
var marginLeft = parseInt(options.marginLeft);
|
|
||||||
params.push('-L', isNaN(marginLeft) ? 25 : marginLeft);
|
|
||||||
|
|
||||||
// Header
|
|
||||||
options.headerCenter && params.push('--header-center', options.headerCenter);
|
|
||||||
options.headerLeft && params.push('--header-left', options.headerLeft);
|
|
||||||
options.headerRight && params.push('--header-right', options.headerRight);
|
|
||||||
options.headerFontName && params.push('--header-font-name', options.headerFontName);
|
|
||||||
options.headerFontSize && params.push('--header-font-size', options.headerFontSize);
|
|
||||||
|
|
||||||
// Footer
|
|
||||||
options.footerCenter && params.push('--footer-center', options.footerCenter);
|
|
||||||
options.footerLeft && params.push('--footer-left', options.footerLeft);
|
|
||||||
options.footerRight && params.push('--footer-right', options.footerRight);
|
|
||||||
options.footerFontName && params.push('--footer-font-name', options.footerFontName);
|
|
||||||
options.footerFontSize && params.push('--footer-font-size', options.footerFontSize);
|
|
||||||
|
|
||||||
// Page size
|
|
||||||
params.push('--page-size', authorizedPageSizes.indexOf(options.pageSize) === -1 ? 'A4' : options.pageSize);
|
|
||||||
|
|
||||||
// Use a temp file as wkhtmltopdf can't access /dev/stdout on Amazon EC2 for some reason
|
|
||||||
var filePath = path.join(os.tmpDir(), Date.now() + '.pdf');
|
|
||||||
var binPath = process.env.WKHTMLTOPDF_PATH || 'wkhtmltopdf';
|
|
||||||
params.push('--run-script', waitForJavaScript.toString() + 'waitForJavaScript()');
|
|
||||||
params.push('--window-status', 'done');
|
|
||||||
var wkhtmltopdf = spawn(binPath, params.concat('-', filePath), {
|
|
||||||
stdio: [
|
|
||||||
'pipe',
|
|
||||||
'ignore',
|
|
||||||
'ignore'
|
|
||||||
]
|
|
||||||
});
|
|
||||||
var timeoutId = setTimeout(function() {
|
|
||||||
timeoutId = undefined;
|
|
||||||
wkhtmltopdf.kill();
|
|
||||||
}, 30000);
|
|
||||||
wkhtmltopdf.on('error', onError);
|
|
||||||
wkhtmltopdf.stdin.on('error', onError);
|
|
||||||
wkhtmltopdf.on('close', function(code) {
|
|
||||||
if(!timeoutId) {
|
|
||||||
return onTimeout();
|
|
||||||
}
|
|
||||||
clearTimeout(timeoutId);
|
|
||||||
if(code) {
|
|
||||||
return onUnknownError();
|
|
||||||
}
|
|
||||||
var readStream = fs.createReadStream(filePath);
|
|
||||||
readStream.on('open', function() {
|
|
||||||
readStream.pipe(res);
|
|
||||||
});
|
|
||||||
readStream.on('close', function() {
|
|
||||||
fs.unlink(filePath, function() {
|
|
||||||
});
|
|
||||||
});
|
|
||||||
readStream.on('error', onUnknownError);
|
|
||||||
});
|
|
||||||
req.pipe(wkhtmltopdf.stdin);
|
|
||||||
});
|
|
||||||
};
|
|
@ -160,7 +160,7 @@ export default {
|
|||||||
position: absolute;
|
position: absolute;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
background-color: rgba(180, 180, 180, 0.75);
|
background-color: rgba(128, 128, 128, 0.5);
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -34,6 +34,13 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="navigation-bar__inner navigation-bar__inner--edit-buttons">
|
<div class="navigation-bar__inner navigation-bar__inner--edit-buttons">
|
||||||
|
<button class="navigation-bar__button button" @click="undo" v-title="'Undo'" :disabled="!canUndo">
|
||||||
|
<icon-undo></icon-undo>
|
||||||
|
</button>
|
||||||
|
<button class="navigation-bar__button button" @click="redo" v-title="'Redo'" :disabled="!canRedo">
|
||||||
|
<icon-redo></icon-redo>
|
||||||
|
</button>
|
||||||
|
<div class="navigation-bar__spacer"></div>
|
||||||
<button class="navigation-bar__button button" @click="pagedownClick('bold')" v-title="'Bold'">
|
<button class="navigation-bar__button button" @click="pagedownClick('bold')" v-title="'Bold'">
|
||||||
<icon-format-bold></icon-format-bold>
|
<icon-format-bold></icon-format-bold>
|
||||||
</button>
|
</button>
|
||||||
@ -77,6 +84,7 @@
|
|||||||
<script>
|
<script>
|
||||||
import { mapState, mapGetters, mapActions } from 'vuex';
|
import { mapState, mapGetters, mapActions } from 'vuex';
|
||||||
import editorSvc from '../services/editorSvc';
|
import editorSvc from '../services/editorSvc';
|
||||||
|
import editorEngineSvc from '../services/editorEngineSvc';
|
||||||
import syncSvc from '../services/syncSvc';
|
import syncSvc from '../services/syncSvc';
|
||||||
import publishSvc from '../services/publishSvc';
|
import publishSvc from '../services/publishSvc';
|
||||||
import animationSvc from '../services/animationSvc';
|
import animationSvc from '../services/animationSvc';
|
||||||
@ -98,6 +106,10 @@ export default {
|
|||||||
'isPublishRequested',
|
'isPublishRequested',
|
||||||
'currentLocation',
|
'currentLocation',
|
||||||
]),
|
]),
|
||||||
|
...mapState('layout', [
|
||||||
|
'canUndo',
|
||||||
|
'canRedo',
|
||||||
|
]),
|
||||||
...mapGetters('layout', [
|
...mapGetters('layout', [
|
||||||
'styles',
|
'styles',
|
||||||
]),
|
]),
|
||||||
@ -148,6 +160,12 @@ export default {
|
|||||||
'toggleExplorer',
|
'toggleExplorer',
|
||||||
'toggleSideBar',
|
'toggleSideBar',
|
||||||
]),
|
]),
|
||||||
|
undo() {
|
||||||
|
return editorEngineSvc.clEditor.undoMgr.undo();
|
||||||
|
},
|
||||||
|
redo() {
|
||||||
|
return editorEngineSvc.clEditor.undoMgr.redo();
|
||||||
|
},
|
||||||
requestSync() {
|
requestSync() {
|
||||||
if (this.isSyncPossible && !this.isSyncRequested) {
|
if (this.isSyncPossible && !this.isSyncRequested) {
|
||||||
syncSvc.requestSync();
|
syncSvc.requestSync();
|
||||||
@ -234,7 +252,8 @@ export default {
|
|||||||
.navigation-bar__inner--edit-buttons {
|
.navigation-bar__inner--edit-buttons {
|
||||||
margin-left: 15px;
|
margin-left: 15px;
|
||||||
|
|
||||||
.navigation-bar__button {
|
.navigation-bar__button,
|
||||||
|
.navigation-bar__spacer {
|
||||||
float: left;
|
float: left;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -243,13 +262,20 @@ export default {
|
|||||||
flex: none;
|
flex: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.navigation-bar__button {
|
$button-size: 36px;
|
||||||
width: 36px;
|
|
||||||
height: 36px;
|
.navigation-bar__button,
|
||||||
padding: 0 8px;
|
.navigation-bar__spacer {
|
||||||
|
height: $button-size;
|
||||||
|
padding: 0 4px;
|
||||||
|
|
||||||
/* prevent from seeing wrapped buttons */
|
/* prevent from seeing wrapped buttons */
|
||||||
margin-bottom: 20px;
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.navigation-bar__button {
|
||||||
|
width: $button-size;
|
||||||
|
padding: 0 8px;
|
||||||
|
|
||||||
.navigation-bar__inner--button & {
|
.navigation-bar__inner--button & {
|
||||||
padding: 0 4px;
|
padding: 0 4px;
|
||||||
|
@ -4,7 +4,7 @@ $line-height-base: 1.67;
|
|||||||
$line-height-title: 1.33;
|
$line-height-title: 1.33;
|
||||||
$font-size-monospace: 0.85em;
|
$font-size-monospace: 0.85em;
|
||||||
$code-bg: rgba(0, 0, 0, 0.05);
|
$code-bg: rgba(0, 0, 0, 0.05);
|
||||||
$info-bg: transparentize(#ffa000, 0.85);
|
$info-bg: transparentize(#f90, 0.85);
|
||||||
$code-border-radius: 2px;
|
$code-border-radius: 2px;
|
||||||
$link-color: #0c93e4;
|
$link-color: #0c93e4;
|
||||||
$error-color: #f20;
|
$error-color: #f20;
|
||||||
|
@ -44,14 +44,14 @@
|
|||||||
<input class="hidden-file" id="import-disk-file-input" type="file" @change="onImportFile">
|
<input class="hidden-file" id="import-disk-file-input" type="file" @change="onImportFile">
|
||||||
<label class="menu-entry button flex flex--row flex--align-center" for="import-disk-file-input">
|
<label class="menu-entry button flex flex--row flex--align-center" for="import-disk-file-input">
|
||||||
<div class="menu-entry__icon flex flex--column flex--center">
|
<div class="menu-entry__icon flex flex--column flex--center">
|
||||||
<icon-hard-disk></icon-hard-disk>
|
<icon-content-save></icon-content-save>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex flex--column">
|
<div class="flex flex--column">
|
||||||
Import from disk
|
Import from disk
|
||||||
</div>
|
</div>
|
||||||
</label>
|
</label>
|
||||||
<menu-entry @click.native="setPanel('export')">
|
<menu-entry @click.native="setPanel('export')">
|
||||||
<icon-hard-disk slot="icon"></icon-hard-disk>
|
<icon-content-save slot="icon"></icon-content-save>
|
||||||
Export to disk
|
Export to disk
|
||||||
</menu-entry>
|
</menu-entry>
|
||||||
<hr>
|
<hr>
|
||||||
|
@ -19,14 +19,14 @@
|
|||||||
<input class="hidden-file" id="import-backup-file-input" type="file" @change="onImportBackup">
|
<input class="hidden-file" id="import-backup-file-input" type="file" @change="onImportBackup">
|
||||||
<label class="menu-entry button flex flex--row flex--align-center" for="import-backup-file-input">
|
<label class="menu-entry button flex flex--row flex--align-center" for="import-backup-file-input">
|
||||||
<div class="menu-entry__icon flex flex--column flex--center">
|
<div class="menu-entry__icon flex flex--column flex--center">
|
||||||
<icon-hard-disk></icon-hard-disk>
|
<icon-content-save></icon-content-save>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex flex--column">
|
<div class="flex flex--column">
|
||||||
Import backup
|
Import backup
|
||||||
</div>
|
</div>
|
||||||
</label>
|
</label>
|
||||||
<menu-entry href="#exportBackup=true" target="_blank">
|
<menu-entry href="#exportBackup=true" target="_blank">
|
||||||
<icon-hard-disk slot="icon"></icon-hard-disk>
|
<icon-content-save slot="icon"></icon-content-save>
|
||||||
Export backup
|
Export backup
|
||||||
</menu-entry>
|
</menu-entry>
|
||||||
<hr>
|
<hr>
|
||||||
|
@ -21,6 +21,9 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="modal__info" v-if="publishLocations.length">
|
||||||
|
<b>Note:</b> Removing a synchronized location won't delete any file.
|
||||||
|
</div>
|
||||||
<div class="modal__button-bar">
|
<div class="modal__button-bar">
|
||||||
<button class="button" @click="config.reject()">Cancel</button>
|
<button class="button" @click="config.reject()">Cancel</button>
|
||||||
<button class="button" @click="config.resolve()">Ok</button>
|
<button class="button" @click="config.resolve()">Ok</button>
|
||||||
|
@ -21,6 +21,9 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="modal__info" v-if="syncLocations.length">
|
||||||
|
<b>Note:</b> Removing a synchronized location won't delete any file.
|
||||||
|
</div>
|
||||||
<div class="modal__button-bar">
|
<div class="modal__button-bar">
|
||||||
<button class="button" @click="config.reject()">Cancel</button>
|
<button class="button" @click="config.reject()">Cancel</button>
|
||||||
<button class="button" @click="config.resolve()">Ok</button>
|
<button class="button" @click="config.resolve()">Ok</button>
|
||||||
|
5
src/icons/ContentSave.vue
Normal file
5
src/icons/ContentSave.vue
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
<template>
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" class="icon" viewBox="0 0 24 24">
|
||||||
|
<path d="M15,9H5V5H15M12,19C10.34,19 9,17.66 9,16C9,14.34 10.34,13 12,13C13.66,13 15,14.34 15,16C15,17.66 13.66,19 12,19M17,3H5C3.89,3 3,3.9 3,5V19C3,20.1 3.9,21 5,21H19C20.1,21 21,20.1 21,19V7L17,3Z" />
|
||||||
|
</svg>
|
||||||
|
</template>
|
@ -1,5 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" class="icon" viewBox="-1 -1 25 25">
|
<svg xmlns="http://www.w3.org/2000/svg" class="icon" viewBox="-2 -2 26 26">
|
||||||
<path d="M15,7H20.5L15,1.5V7M8,0H16L22,6V18C22,19.1 21.1,20 20,20H8C6.89,20 6,19.1 6,18V2C6,0.9 6.9,0 8,0M4,4V22H20V24H4C2.9,24 2,23.1 2,22V4H4Z" />
|
<path d="M15,7H20.5L15,1.5V7M8,0H16L22,6V18C22,19.1 21.1,20 20,20H8C6.89,20 6,19.1 6,18V2C6,0.9 6.9,0 8,0M4,4V22H20V24H4C2.9,24 2,23.1 2,22V4H4Z" />
|
||||||
</svg>
|
</svg>
|
||||||
</template>
|
</template>
|
||||||
|
@ -1,5 +0,0 @@
|
|||||||
<template>
|
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" class="icon" viewBox="0 0 24 24">
|
|
||||||
<path d="M 6,2L 18,2C 19.1046,2 20,2.89543 20,4L 20,20C 20,21.1046 19.1046,22 18,22L 6,22C 4.89543,22 4,21.1046 4,20L 4,4C 4,2.89543 4.89543,2 6,2 Z M 12,4.00001C 8.68629,4.00001 5.99999,6.6863 5.99999,10C 5.99999,13.3137 8.68629,16 12.1022,15.9992L 11.2221,13.7674C 10.946,13.2891 11.1099,12.6775 11.5882,12.4013L 12.4542,11.9013C 12.9325,11.6252 13.5441,11.7891 13.8202,12.2674L 15.7446,14.6884C 17.1194,13.5889 18,11.8973 18,10C 18,6.6863 15.3137,4.00001 12,4.00001 Z M 12,9.00001C 12.5523,9.00001 13,9.44773 13,10C 13,10.5523 12.5523,11 12,11C 11.4477,11 11,10.5523 11,10C 11,9.44773 11.4477,9.00001 12,9.00001 Z M 7,18C 6.44771,18 6,18.4477 6,19C 6,19.5523 6.44771,20 7,20C 7.55228,20 8,19.5523 8,19C 8,18.4477 7.55228,18 7,18 Z M 12.0882,13.2674L 14.5757,19.5759L 17.1738,18.0759L 12.9542,12.7674L 12.0882,13.2674 Z "/>
|
|
||||||
</svg>
|
|
||||||
</template>
|
|
@ -1,5 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" class="icon" viewBox="0 0 24 24">
|
<svg xmlns="http://www.w3.org/2000/svg" class="icon" viewBox="0 0 24 24">
|
||||||
<path fill="#000000" fill-opacity="1" stroke-linejoin="round" d="M 10.5858,13.4142C 10.9763,13.8047 10.9763,14.4379 10.5858,14.8284C 10.1952,15.2189 9.56207,15.2189 9.17154,14.8284C 7.21892,12.8758 7.21892,9.70995 9.17154,7.75733L 9.17157,7.75736L 12.707,4.2219C 14.6596,2.26928 17.8255,2.26929 19.7781,4.2219C 21.7307,6.17452 21.7307,9.34034 19.7781,11.293L 18.2925,12.7785C 18.3008,11.9583 18.1659,11.1368 17.8876,10.355L 18.3639,9.87865C 19.5355,8.70708 19.5355,6.80759 18.3639,5.63602C 17.1923,4.46445 15.2929,4.46445 14.1213,5.63602L 10.5858,9.17155C 9.41419,10.3431 9.41419,12.2426 10.5858,13.4142 Z M 13.4142,9.17155C 13.8047,8.78103 14.4379,8.78103 14.8284,9.17155C 16.781,11.1242 16.781,14.29 14.8284,16.2426L 14.8284,16.2426L 11.2929,19.7782C 9.34026,21.7308 6.17444,21.7308 4.22182,19.7782C 2.26921,17.8255 2.2692,14.6597 4.22182,12.7071L 5.70744,11.2215C 5.69913,12.0417 5.8341,12.8631 6.11234,13.645L 5.63601,14.1213C 4.46444,15.2929 4.46444,17.1924 5.63601,18.3639C 6.80758,19.5355 8.70708,19.5355 9.87865,18.3639L 13.4142,14.8284C 14.5858,13.6568 14.5858,11.7573 13.4142,10.5858C 13.0237,10.1952 13.0237,9.56207 13.4142,9.17155 Z " />
|
<path d="M 10.5858,13.4142C 10.9763,13.8047 10.9763,14.4379 10.5858,14.8284C 10.1952,15.2189 9.56207,15.2189 9.17154,14.8284C 7.21892,12.8758 7.21892,9.70995 9.17154,7.75733L 9.17157,7.75736L 12.707,4.2219C 14.6596,2.26928 17.8255,2.26929 19.7781,4.2219C 21.7307,6.17452 21.7307,9.34034 19.7781,11.293L 18.2925,12.7785C 18.3008,11.9583 18.1659,11.1368 17.8876,10.355L 18.3639,9.87865C 19.5355,8.70708 19.5355,6.80759 18.3639,5.63602C 17.1923,4.46445 15.2929,4.46445 14.1213,5.63602L 10.5858,9.17155C 9.41419,10.3431 9.41419,12.2426 10.5858,13.4142 Z M 13.4142,9.17155C 13.8047,8.78103 14.4379,8.78103 14.8284,9.17155C 16.781,11.1242 16.781,14.29 14.8284,16.2426L 14.8284,16.2426L 11.2929,19.7782C 9.34026,21.7308 6.17444,21.7308 4.22182,19.7782C 2.26921,17.8255 2.2692,14.6597 4.22182,12.7071L 5.70744,11.2215C 5.69913,12.0417 5.8341,12.8631 6.11234,13.645L 5.63601,14.1213C 4.46444,15.2929 4.46444,17.1924 5.63601,18.3639C 6.80758,19.5355 8.70708,19.5355 9.87865,18.3639L 13.4142,14.8284C 14.5858,13.6568 14.5858,11.7573 13.4142,10.5858C 13.0237,10.1952 13.0237,9.56207 13.4142,9.17155 Z " />
|
||||||
</svg>
|
</svg>
|
||||||
</template>
|
</template>
|
||||||
|
5
src/icons/Redo.vue
Normal file
5
src/icons/Redo.vue
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
<template>
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" class="icon" viewBox="0 0 24 24">
|
||||||
|
<path d="M18.4,10.6C16.55,9 14.15,8 11.5,8C6.85,8 2.92,11.03 1.54,15.22L3.9,16C4.95,12.81 7.95,10.5 11.5,10.5C13.45,10.5 15.23,11.22 16.62,12.38L13,16H22V7L18.4,10.6Z" />
|
||||||
|
</svg>
|
||||||
|
</template>
|
5
src/icons/Undo.vue
Normal file
5
src/icons/Undo.vue
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
<template>
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" class="icon" viewBox="0 0 24 24">
|
||||||
|
<path d="M12.5,8C9.85,8 7.45,9 5.6,10.6L2,7V16H11L7.38,12.38C8.77,11.22 10.54,10.5 12.5,10.5C16.04,10.5 19.05,12.81 20.1,16L22.47,15.22C21.08,11.03 17.15,8 12.5,8Z" />
|
||||||
|
</svg>
|
||||||
|
</template>
|
@ -32,7 +32,6 @@ import Sync from './Sync';
|
|||||||
import SyncOff from './SyncOff';
|
import SyncOff from './SyncOff';
|
||||||
import Upload from './Upload';
|
import Upload from './Upload';
|
||||||
import ViewList from './ViewList';
|
import ViewList from './ViewList';
|
||||||
import HardDisk from './HardDisk';
|
|
||||||
import Download from './Download';
|
import Download from './Download';
|
||||||
import CodeTags from './CodeTags';
|
import CodeTags from './CodeTags';
|
||||||
import CodeBraces from './CodeBraces';
|
import CodeBraces from './CodeBraces';
|
||||||
@ -43,6 +42,9 @@ import SignalOff from './SignalOff';
|
|||||||
import Folder from './Folder';
|
import Folder from './Folder';
|
||||||
import ScrollSync from './ScrollSync';
|
import ScrollSync from './ScrollSync';
|
||||||
import Printer from './Printer';
|
import Printer from './Printer';
|
||||||
|
import Undo from './Undo';
|
||||||
|
import Redo from './Redo';
|
||||||
|
import ContentSave from './ContentSave';
|
||||||
|
|
||||||
Vue.component('iconProvider', Provider);
|
Vue.component('iconProvider', Provider);
|
||||||
Vue.component('iconFormatBold', FormatBold);
|
Vue.component('iconFormatBold', FormatBold);
|
||||||
@ -77,7 +79,6 @@ Vue.component('iconSync', Sync);
|
|||||||
Vue.component('iconSyncOff', SyncOff);
|
Vue.component('iconSyncOff', SyncOff);
|
||||||
Vue.component('iconUpload', Upload);
|
Vue.component('iconUpload', Upload);
|
||||||
Vue.component('iconViewList', ViewList);
|
Vue.component('iconViewList', ViewList);
|
||||||
Vue.component('iconHardDisk', HardDisk);
|
|
||||||
Vue.component('iconDownload', Download);
|
Vue.component('iconDownload', Download);
|
||||||
Vue.component('iconCodeTags', CodeTags);
|
Vue.component('iconCodeTags', CodeTags);
|
||||||
Vue.component('iconCodeBraces', CodeBraces);
|
Vue.component('iconCodeBraces', CodeBraces);
|
||||||
@ -88,3 +89,6 @@ Vue.component('iconSignalOff', SignalOff);
|
|||||||
Vue.component('iconFolder', Folder);
|
Vue.component('iconFolder', Folder);
|
||||||
Vue.component('iconScrollSync', ScrollSync);
|
Vue.component('iconScrollSync', ScrollSync);
|
||||||
Vue.component('iconPrinter', Printer);
|
Vue.component('iconPrinter', Printer);
|
||||||
|
Vue.component('iconUndo', Undo);
|
||||||
|
Vue.component('iconRedo', Redo);
|
||||||
|
Vue.component('iconContentSave', ContentSave);
|
||||||
|
@ -448,6 +448,16 @@ const editorSvc = Object.assign(new Vue(), { // Use a vue instance as an event b
|
|||||||
};
|
};
|
||||||
this.parsingCtx = parsingCtx;
|
this.parsingCtx = parsingCtx;
|
||||||
});
|
});
|
||||||
|
editorEngineSvc.clEditor.undoMgr.on('undoStateChange', () => {
|
||||||
|
const canUndo = editorEngineSvc.clEditor.undoMgr.canUndo();
|
||||||
|
if (canUndo !== store.state.layout.canUndo) {
|
||||||
|
store.commit('layout/setCanUndo', canUndo);
|
||||||
|
}
|
||||||
|
const canRedo = editorEngineSvc.clEditor.undoMgr.canRedo();
|
||||||
|
if (canRedo !== store.state.layout.canRedo) {
|
||||||
|
store.commit('layout/setCanRedo', canRedo);
|
||||||
|
}
|
||||||
|
});
|
||||||
this.pagedownEditor = pagedown({
|
this.pagedownEditor = pagedown({
|
||||||
input: Object.create(editorEngineSvc.clEditor),
|
input: Object.create(editorEngineSvc.clEditor),
|
||||||
});
|
});
|
||||||
|
@ -74,23 +74,15 @@ export default providerRegistry.register({
|
|||||||
if (!path) {
|
if (!path) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
let syncLocation;
|
if (providerUtils.openFileWithLocation(store.getters['syncLocation/items'], {
|
||||||
// Try to find an existing sync location
|
providerId: this.id,
|
||||||
store.getters['syncLocation/items'].some((existingSyncLocation) => {
|
path,
|
||||||
if (existingSyncLocation.providerId === this.id &&
|
})) {
|
||||||
existingSyncLocation.path === path
|
// File exists and has just been opened. Next...
|
||||||
) {
|
|
||||||
syncLocation = existingSyncLocation;
|
|
||||||
}
|
|
||||||
return syncLocation;
|
|
||||||
});
|
|
||||||
if (syncLocation) {
|
|
||||||
// Sync location already exists, just open the file
|
|
||||||
store.commit('file/setCurrentId', syncLocation.fileId);
|
|
||||||
return openOneFile();
|
return openOneFile();
|
||||||
}
|
}
|
||||||
// Sync location does not exist, download content from Dropbox and create the file
|
// Download content from Dropbox and create the file
|
||||||
syncLocation = {
|
const syncLocation = {
|
||||||
path,
|
path,
|
||||||
providerId: this.id,
|
providerId: this.id,
|
||||||
sub: token.sub,
|
sub: token.sub,
|
||||||
|
@ -56,30 +56,22 @@ export default providerRegistry.register({
|
|||||||
driveFileId: driveFile.id,
|
driveFileId: driveFile.id,
|
||||||
}));
|
}));
|
||||||
},
|
},
|
||||||
openFiles(token, files) {
|
openFiles(token, driveFiles) {
|
||||||
const openOneFile = () => {
|
const openOneFile = () => {
|
||||||
const file = files.pop();
|
const driveFile = driveFiles.pop();
|
||||||
if (!file) {
|
if (!driveFile) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
let syncLocation;
|
if (providerUtils.openFileWithLocation(store.getters['syncLocation/items'], {
|
||||||
// Try to find an existing sync location
|
providerId: this.id,
|
||||||
store.getters['syncLocation/items'].some((existingSyncLocation) => {
|
driveFileId: driveFile.id,
|
||||||
if (existingSyncLocation.providerId === this.id &&
|
})) {
|
||||||
existingSyncLocation.driveFileId === file.id
|
// File exists and has just been opened. Next...
|
||||||
) {
|
|
||||||
syncLocation = existingSyncLocation;
|
|
||||||
}
|
|
||||||
return syncLocation;
|
|
||||||
});
|
|
||||||
if (syncLocation) {
|
|
||||||
// Sync location already exists, just open the file
|
|
||||||
store.commit('file/setCurrentId', syncLocation.fileId);
|
|
||||||
return openOneFile();
|
return openOneFile();
|
||||||
}
|
}
|
||||||
// Sync location does not exist, download content from Google Drive and create the file
|
// Download content from Google Drive and create the file
|
||||||
syncLocation = {
|
const syncLocation = {
|
||||||
driveFileId: file.id,
|
driveFileId: driveFile.id,
|
||||||
providerId: this.id,
|
providerId: this.id,
|
||||||
sub: token.sub,
|
sub: token.sub,
|
||||||
};
|
};
|
||||||
@ -93,7 +85,7 @@ export default providerRegistry.register({
|
|||||||
});
|
});
|
||||||
store.commit('file/setItem', {
|
store.commit('file/setItem', {
|
||||||
id,
|
id,
|
||||||
name: utils.sanitizeName(file.name),
|
name: utils.sanitizeName(driveFile.name),
|
||||||
parentId: store.getters['file/current'].parentId,
|
parentId: store.getters['file/current'].parentId,
|
||||||
});
|
});
|
||||||
store.commit('syncLocation/setItem', {
|
store.commit('syncLocation/setItem', {
|
||||||
@ -104,7 +96,7 @@ export default providerRegistry.register({
|
|||||||
store.commit('file/setCurrentId', id);
|
store.commit('file/setCurrentId', id);
|
||||||
store.dispatch('notification/info', `${store.getters['file/current'].name} was imported from Google Drive.`);
|
store.dispatch('notification/info', `${store.getters['file/current'].name} was imported from Google Drive.`);
|
||||||
}, () => {
|
}, () => {
|
||||||
store.dispatch('notification/error', `Could not open file ${file.id}.`);
|
store.dispatch('notification/error', `Could not open file ${driveFile.id}.`);
|
||||||
})
|
})
|
||||||
.then(() => openOneFile());
|
.then(() => openOneFile());
|
||||||
};
|
};
|
||||||
|
@ -57,4 +57,28 @@ export default {
|
|||||||
}));
|
}));
|
||||||
return result;
|
return result;
|
||||||
},
|
},
|
||||||
|
/**
|
||||||
|
* Find and open a file location that fits the criteria
|
||||||
|
*/
|
||||||
|
openFileWithLocation(allLocations, criteria) {
|
||||||
|
return allLocations.some((location) => {
|
||||||
|
// If every field fits the criteria
|
||||||
|
if (Object.keys(criteria).every(key => criteria[key] === location[key])) {
|
||||||
|
// Found one location that fits, open it if it exists
|
||||||
|
const file = store.state.file.itemMap[location.fileId];
|
||||||
|
if (file) {
|
||||||
|
store.commit('file/setCurrentId', file.id);
|
||||||
|
// If file is in the trash, restore it
|
||||||
|
if (file.parentId === 'trash') {
|
||||||
|
store.commit('file/patchItem', {
|
||||||
|
...file,
|
||||||
|
parentId: null,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
@ -2,7 +2,7 @@ const editorMinWidth = 320;
|
|||||||
const minPadding = 20;
|
const minPadding = 20;
|
||||||
const previewButtonWidth = 55;
|
const previewButtonWidth = 55;
|
||||||
const editorTopPadding = 10;
|
const editorTopPadding = 10;
|
||||||
const navigationBarEditButtonsWidth = 36 * 12; // 12 buttons
|
const navigationBarEditButtonsWidth = (36 * 14) + 8; // 14 buttons + 1 spacer
|
||||||
const navigationBarLeftButtonWidth = 38 + 4 + 15;
|
const navigationBarLeftButtonWidth = 38 + 4 + 15;
|
||||||
const navigationBarRightButtonWidth = 38 + 8;
|
const navigationBarRightButtonWidth = 38 + 8;
|
||||||
const navigationBarSpinnerWidth = 24 + 8 + 5; // 5 for left margin
|
const navigationBarSpinnerWidth = 24 + 8 + 5; // 5 for left margin
|
||||||
@ -118,10 +118,18 @@ function computeStyles(state, localSettings, getters, styles = {
|
|||||||
export default {
|
export default {
|
||||||
namespaced: true,
|
namespaced: true,
|
||||||
state: {
|
state: {
|
||||||
|
canUndo: false,
|
||||||
|
canRedo: false,
|
||||||
bodyWidth: 0,
|
bodyWidth: 0,
|
||||||
bodyHeight: 0,
|
bodyHeight: 0,
|
||||||
},
|
},
|
||||||
mutations: {
|
mutations: {
|
||||||
|
setCanUndo: (state, value) => {
|
||||||
|
state.canUndo = value;
|
||||||
|
},
|
||||||
|
setCanRedo: (state, value) => {
|
||||||
|
state.canRedo = value;
|
||||||
|
},
|
||||||
updateBodySize: (state) => {
|
updateBodySize: (state) => {
|
||||||
state.bodyWidth = document.body.clientWidth;
|
state.bodyWidth = document.body.clientWidth;
|
||||||
state.bodyHeight = document.body.clientHeight;
|
state.bodyHeight = document.body.clientHeight;
|
||||||
|
Loading…
Reference in New Issue
Block a user