-
{setToolsVisible(!toolsVisible)}}>
+
{setToolsVisible(!toolsVisible);}}>
{/*v=====----------------------< Zoom Controls >---------------------=====v*/}
{},
onMetaChange : ()=>{},
reportError : ()=>{},
-
+
onCursorPageChange : ()=>{},
onViewPageChange : ()=>{},
editorTheme : 'default',
renderer : 'legacy',
- currentEditorCursorPageNum : 0,
- currentEditorViewPageNum : 0,
- currentBrewRendererPageNum : 0,
+ currentEditorCursorPageNum : 1,
+ currentEditorViewPageNum : 1,
+ currentBrewRendererPageNum : 1,
};
},
getInitialState : function() {
@@ -63,14 +63,15 @@ const Editor = createClass({
isMeta : function() {return this.state.view == 'meta';},
componentDidMount : function() {
+
this.updateEditorSize();
this.highlightCustomMarkdown();
window.addEventListener('resize', this.updateEditorSize);
document.getElementById('BrewRenderer').addEventListener('keydown', this.handleControlKeys);
document.addEventListener('keydown', this.handleControlKeys);
- this.codeEditor.current.codeMirror.on('cursorActivity', (cm)=>{this.updateCurrentCursorPage(cm.getCursor())});
- this.codeEditor.current.codeMirror.on('scroll', _.throttle(()=>{this.updateCurrentViewPage(this.codeEditor.current.getTopVisibleLine())}, 200));
+ this.codeEditor.current.codeMirror.on('cursorActivity', (cm)=>{this.updateCurrentCursorPage(cm.getCursor());});
+ this.codeEditor.current.codeMirror.on('scroll', _.throttle(()=>{this.updateCurrentViewPage(this.codeEditor.current.getTopVisibleLine());}, 200));
const editorTheme = window.localStorage.getItem(EDITOR_THEME_KEY);
if(editorTheme) {
@@ -85,22 +86,32 @@ const Editor = createClass({
},
componentDidUpdate : function(prevProps, prevState, snapshot) {
+
this.highlightCustomMarkdown();
- if(prevProps.moveBrew !== this.props.moveBrew) {
+ if(prevProps.moveBrew !== this.props.moveBrew)
this.brewJump();
- };
- if(prevProps.moveSource !== this.props.moveSource) {
+
+ if(prevProps.moveSource !== this.props.moveSource)
this.sourceJump();
- };
+
+ if(this.props.liveScroll) {
+ if(prevProps.currentBrewRendererPageNum !== this.props.currentBrewRendererPageNum) {
+ this.sourceJump(this.props.currentBrewRendererPageNum, false);
+ } else if(prevProps.currentEditorViewPageNum !== this.props.currentEditorViewPageNum) {
+ this.brewJump(this.props.currentEditorViewPageNum, false);
+ } else if(prevProps.currentEditorCursorPageNum !== this.props.currentEditorCursorPageNum) {
+ this.brewJump(this.props.currentEditorCursorPageNum, false);
+ }
+ }
},
handleControlKeys : function(e){
- if(!(e.ctrlKey && e.metaKey)) return;
+ if(!(e.ctrlKey && e.metaKey && e.shiftKey)) return;
const LEFTARROW_KEY = 37;
const RIGHTARROW_KEY = 39;
- if (e.shiftKey && (e.keyCode == RIGHTARROW_KEY)) this.brewJump();
- if (e.shiftKey && (e.keyCode == LEFTARROW_KEY)) this.sourceJump();
- if ((e.keyCode == LEFTARROW_KEY) || (e.keyCode == RIGHTARROW_KEY)) {
+ if(e.keyCode == RIGHTARROW_KEY) this.brewJump();
+ if(e.keyCode == LEFTARROW_KEY) this.sourceJump();
+ if(e.keyCode == LEFTARROW_KEY || e.keyCode == RIGHTARROW_KEY) {
e.stopPropagation();
e.preventDefault();
}
@@ -117,14 +128,14 @@ const Editor = createClass({
updateCurrentCursorPage : function(cursor) {
const lines = this.props.brew.text.split('\n').slice(0, cursor.line + 1);
const pageRegex = this.props.brew.renderer == 'V3' ? /^\\page$/ : /\\page/;
- const currentPage = lines.reduce((count, line) => count + (pageRegex.test(line) ? 1 : 0), 1);
+ const currentPage = lines.reduce((count, line)=>count + (pageRegex.test(line) ? 1 : 0), 1);
this.props.onCursorPageChange(currentPage);
},
updateCurrentViewPage : function(topScrollLine) {
const lines = this.props.brew.text.split('\n').slice(0, topScrollLine + 1);
const pageRegex = this.props.brew.renderer == 'V3' ? /^\\page$/ : /\\page/;
- const currentPage = lines.reduce((count, line) => count + (pageRegex.test(line) ? 1 : 0), 1);
+ const currentPage = lines.reduce((count, line)=>count + (pageRegex.test(line) ? 1 : 0), 1);
this.props.onViewPageChange(currentPage);
},
@@ -156,7 +167,7 @@ const Editor = createClass({
// Record details of folded sections
if(mark.__isFold) {
const fold = mark.find();
- foldLines.push({from: fold.from?.line, to: fold.to?.line});
+ foldLines.push({ from: fold.from?.line, to: fold.to?.line });
}
return !mark.__isFold;
}); //Don't undo code folding
@@ -174,7 +185,7 @@ const Editor = createClass({
// Don't process lines inside folded text
// If the current lineNumber is inside any folded marks, skip line styling
- if (foldLines.some(fold => lineNumber >= fold.from && lineNumber <= fold.to))
+ if(foldLines.some((fold)=>lineNumber >= fold.from && lineNumber <= fold.to))
return;
// Styling for \page breaks
@@ -200,7 +211,7 @@ const Editor = createClass({
// definition lists
if(line.includes('::')){
- if(/^:*$/.test(line) == true){ return };
+ if(/^:*$/.test(line) == true){ return; };
const regex = /^([^\n]*?:?\s?)(::[^\n]*)(?:\n|$)/ymd; // the `d` flag, for match indices, throws an ESLint error.
let match;
while ((match = regex.exec(line)) != null){
@@ -208,10 +219,10 @@ const Editor = createClass({
codeMirror.markText({ line: lineNumber, ch: match.indices[1][0] }, { line: lineNumber, ch: match.indices[1][1] }, { className: 'dt-highlight' });
codeMirror.markText({ line: lineNumber, ch: match.indices[2][0] }, { line: lineNumber, ch: match.indices[2][1] }, { className: 'dd-highlight' });
const ddIndex = match.indices[2][0];
- let colons = /::/g;
- let colonMatches = colons.exec(match[2]);
+ const colons = /::/g;
+ const colonMatches = colons.exec(match[2]);
if(colonMatches !== null){
- codeMirror.markText({ line: lineNumber, ch: colonMatches.index + ddIndex }, { line: lineNumber, ch: colonMatches.index + colonMatches[0].length + ddIndex }, { className: 'dl-colon-highlight'} )
+ codeMirror.markText({ line: lineNumber, ch: colonMatches.index + ddIndex }, { line: lineNumber, ch: colonMatches.index + colonMatches[0].length + ddIndex }, { className: 'dl-colon-highlight' });
}
}
}
@@ -221,12 +232,12 @@ const Editor = createClass({
let startIndex = line.indexOf('^');
const superRegex = /\^(?!\s)(?=([^\n\^]*[^\s\^]))\1\^/gy;
const subRegex = /\^\^(?!\s)(?=([^\n\^]*[^\s\^]))\1\^\^/gy;
-
+
while (startIndex >= 0) {
superRegex.lastIndex = subRegex.lastIndex = startIndex;
let isSuper = false;
- let match = subRegex.exec(line) || superRegex.exec(line);
- if (match) {
+ const match = subRegex.exec(line) || superRegex.exec(line);
+ if(match) {
isSuper = !subRegex.lastIndex;
codeMirror.markText({ line: lineNumber, ch: match.index }, { line: lineNumber, ch: match.index + match[0].length }, { className: isSuper ? 'superscript' : 'subscript' });
}
@@ -276,18 +287,18 @@ const Editor = createClass({
while (startIndex >= 0) {
emojiRegex.lastIndex = startIndex;
- let match = emojiRegex.exec(line);
- if (match) {
+ const match = emojiRegex.exec(line);
+ if(match) {
let tokens = Markdown.marked.lexer(match[0]);
- tokens = tokens[0].tokens.filter(t => t.type == 'emoji')
- if (!tokens.length)
+ tokens = tokens[0].tokens.filter((t)=>t.type == 'emoji');
+ if(!tokens.length)
return;
- let startPos = { line: lineNumber, ch: match.index };
- let endPos = { line: lineNumber, ch: match.index + match[0].length };
+ const startPos = { line: lineNumber, ch: match.index };
+ const endPos = { line: lineNumber, ch: match.index + match[0].length };
// Iterate over conflicting marks and clear them
- var marks = codeMirror.findMarks(startPos, endPos);
+ const marks = codeMirror.findMarks(startPos, endPos);
marks.forEach(function(marker) {
if(!marker.__isFold) marker.clear();
});
@@ -302,64 +313,93 @@ const Editor = createClass({
}
},
- brewJump : function(targetPage=this.props.currentEditorCursorPageNum){
- if(!window) return;
+ brewJump : function(targetPage=this.props.currentEditorCursorPageNum, smooth=true){
+ if(!window || isJumping)
+ return;
+
// Get current brewRenderer scroll position and calculate target position
const brewRenderer = window.frames['BrewRenderer'].contentDocument.getElementsByClassName('brewRenderer')[0];
const currentPos = brewRenderer.scrollTop;
const targetPos = window.frames['BrewRenderer'].contentDocument.getElementById(`p${targetPage}`).getBoundingClientRect().top;
- const interimPos = targetPos >= 0 ? -30 : 30;
- const bounceDelay = 100;
- const scrollDelay = 500;
-
- if(!this.throttleBrewMove) {
- this.throttleBrewMove = _.throttle((currentPos, interimPos, targetPos)=>{
- brewRenderer.scrollTo({ top: currentPos + interimPos, behavior: 'smooth' });
- setTimeout(()=>{
- brewRenderer.scrollTo({ top: currentPos + targetPos, behavior: 'smooth', block: 'start' });
- }, bounceDelay);
- }, scrollDelay, { leading: true, trailing: false });
+ const checkIfScrollComplete = ()=>{
+ let scrollingTimeout;
+ clearTimeout(scrollingTimeout); // Reset the timer every time a scroll event occurs
+ scrollingTimeout = setTimeout(()=>{
+ isJumping = false;
+ brewRenderer.removeEventListener('scroll', checkIfScrollComplete);
+ }, 150); // If 150 ms pass without a brewRenderer scroll event, assume scrolling is done
};
- this.throttleBrewMove(currentPos, interimPos, targetPos);
- // const hashPage = (page != 1) ? `p${page}` : '';
- // window.location.hash = hashPage;
+ isJumping = true;
+ checkIfScrollComplete();
+ brewRenderer.addEventListener('scroll', checkIfScrollComplete);
+
+ if(smooth) {
+ const bouncePos = targetPos >= 0 ? -30 : 30; //Do a little bounce before scrolling
+ const bounceDelay = 100;
+ const scrollDelay = 500;
+
+ if(!this.throttleBrewMove) {
+ this.throttleBrewMove = _.throttle((currentPos, bouncePos, targetPos)=>{
+ brewRenderer.scrollTo({ top: currentPos + bouncePos, behavior: 'smooth' });
+ setTimeout(()=>{
+ brewRenderer.scrollTo({ top: currentPos + targetPos, behavior: 'smooth', block: 'start' });
+ }, bounceDelay);
+ }, scrollDelay, { leading: true, trailing: false });
+ };
+ this.throttleBrewMove(currentPos, bouncePos, targetPos);
+ } else {
+ brewRenderer.scrollTo({ top: currentPos + targetPos, behavior: 'instant', block: 'start' });
+ }
},
- sourceJump : function(targetLine=null){
- if(this.isText()) {
- if(targetLine == null) {
- targetLine = 0;
+ sourceJump : function(targetPage=this.props.currentBrewRendererPageNum, smooth=true){
+ if(!this.isText || isJumping)
+ return;
- const textSplit = this.props.renderer == 'V3' ? /^\\page$/gm : /\\page/;
- const textString = this.props.brew.text.split(textSplit).slice(0, this.props.currentBrewRendererPageNum-1).join(textSplit);
- const textPosition = textString.length;
- const lineCount = textString.match('\n') ? textString.slice(0, textPosition).split('\n').length : 0;
+ const textSplit = this.props.renderer == 'V3' ? /^\\page$/gm : /\\page/;
+ const textString = this.props.brew.text.split(textSplit).slice(0, targetPage-1).join(textSplit);
+ const targetLine = textString.match('\n') ? textString.split('\n').length - 1 : -1;
- targetLine = lineCount - 1; //Scroll to `\page`, which is one line back.
+ let currentY = this.codeEditor.current.codeMirror.getScrollInfo().top;
+ let targetY = this.codeEditor.current.codeMirror.heightAtLine(targetLine, 'local', true);
- let currentY = this.codeEditor.current.codeMirror.getScrollInfo().top;
- let targetY = this.codeEditor.current.codeMirror.heightAtLine(targetLine, 'local', true);
+ const checkIfScrollComplete = ()=>{
+ let scrollingTimeout;
+ clearTimeout(scrollingTimeout); // Reset the timer every time a scroll event occurs
+ scrollingTimeout = setTimeout(()=>{
+ isJumping = false;
+ this.codeEditor.current.codeMirror.off('scroll', checkIfScrollComplete);
+ }, 150); // If 150 ms pass without a scroll event, assume scrolling is done
+ };
- //Scroll 1/10 of the way every 10ms until 1px off.
- const incrementalScroll = setInterval(()=>{
- currentY += (targetY - currentY) / 10;
- this.codeEditor.current.codeMirror.scrollTo(null, currentY);
+ isJumping = true;
+ checkIfScrollComplete();
+ this.codeEditor.current.codeMirror.on('scroll', checkIfScrollComplete);
- // Update target: target height is not accurate until within +-10 lines of the visible window
- if(Math.abs(targetY - currentY > 100))
- targetY = this.codeEditor.current.codeMirror.heightAtLine(targetLine, 'local', true);
+ if(smooth) {
+ //Scroll 1/10 of the way every 10ms until 1px off.
+ const incrementalScroll = setInterval(()=>{
+ currentY += (targetY - currentY) / 10;
+ this.codeEditor.current.codeMirror.scrollTo(null, currentY);
- // End when close enough
- if(Math.abs(targetY - currentY) < 1) {
- this.codeEditor.current.codeMirror.scrollTo(null, targetY); // Scroll any remaining difference
- this.codeEditor.current.setCursorPosition({ line: targetLine + 1, ch: 0 });
- this.codeEditor.current.codeMirror.addLineClass(targetLine + 1, 'wrap', 'sourceMoveFlash');
- clearInterval(incrementalScroll);
- }
- }, 10);
- }
+ // Update target: target height is not accurate until within +-10 lines of the visible window
+ if(Math.abs(targetY - currentY > 100))
+ targetY = this.codeEditor.current.codeMirror.heightAtLine(targetLine, 'local', true);
+
+ // End when close enough
+ if(Math.abs(targetY - currentY) < 1) {
+ this.codeEditor.current.codeMirror.scrollTo(null, targetY); // Scroll any remaining difference
+ this.codeEditor.current.setCursorPosition({ line: targetLine + 1, ch: 0 });
+ this.codeEditor.current.codeMirror.addLineClass(targetLine + 1, 'wrap', 'sourceMoveFlash');
+ clearInterval(incrementalScroll);
+ }
+ }, 10);
+ } else {
+ this.codeEditor.current.codeMirror.scrollTo(null, targetY); // Scroll any remaining difference
+ this.codeEditor.current.setCursorPosition({ line: targetLine + 1, ch: 0 });
+ this.codeEditor.current.codeMirror.addLineClass(targetLine + 1, 'wrap', 'sourceMoveFlash');
}
},
@@ -389,8 +429,6 @@ const Editor = createClass({
view={this.state.view}
value={this.props.brew.text}
onChange={this.props.onTextChange}
- onCursorActivity={this.props.onCursorActivity}
- onScroll={this.props.onPageChange}
editorTheme={this.state.editorTheme}
rerenderParent={this.rerenderParent} />
>;
diff --git a/client/homebrew/editor/snippetbar/snippetbar.jsx b/client/homebrew/editor/snippetbar/snippetbar.jsx
index f2680079e..0707f74ba 100644
--- a/client/homebrew/editor/snippetbar/snippetbar.jsx
+++ b/client/homebrew/editor/snippetbar/snippetbar.jsx
@@ -70,7 +70,7 @@ const Snippetbar = createClass({
mergeCustomizer : function(oldValue, newValue, key) {
if(key == 'snippets') {
const result = _.reverse(_.unionBy(_.reverse(newValue), _.reverse(oldValue), 'name')); // Join snippets together, with preference for the child theme over the parent theme
- return result.filter(snip => snip.gen || snip.subsnippets);
+ return result.filter((snip)=>snip.gen || snip.subsnippets);
}
},
diff --git a/client/homebrew/navbar/error-navitem.jsx b/client/homebrew/navbar/error-navitem.jsx
index 5dd5c1eb9..f6788e6d5 100644
--- a/client/homebrew/navbar/error-navitem.jsx
+++ b/client/homebrew/navbar/error-navitem.jsx
@@ -111,7 +111,7 @@ const ErrorNavItem = createClass({
Looks like there was a problem retreiving
the theme, or a theme that it inherits,
for this brew. Verify that brew
- {response.body.brewId} still exists!
+ {response.body.brewId} still exists!
;
}
diff --git a/client/homebrew/pages/editPage/editPage.jsx b/client/homebrew/pages/editPage/editPage.jsx
index e6a67e414..1ff841024 100644
--- a/client/homebrew/pages/editPage/editPage.jsx
+++ b/client/homebrew/pages/editPage/editPage.jsx
@@ -55,9 +55,9 @@ const EditPage = createClass({
autoSave : true,
autoSaveWarning : false,
unsavedTime : new Date(),
- currentEditorViewPageNum : 0,
- currentEditorCursorPageNum : 0,
- currentBrewRendererPageNum : 0,
+ currentEditorViewPageNum : 1,
+ currentEditorCursorPageNum : 1,
+ currentBrewRendererPageNum : 1,
displayLockMessage : this.props.brew.lock || false,
themeBundle : {}
};
@@ -117,23 +117,19 @@ const EditPage = createClass({
},
handleEditorViewPageChange : function(pageNumber){
- console.log(`editor view : ${pageNumber}`);
this.setState({ currentEditorViewPageNum: pageNumber });
},
handleEditorCursorPageChange : function(pageNumber){
- console.log(`editor cursor : ${pageNumber}`);
this.setState({ currentEditorCursorPageNum: pageNumber });
},
handleBrewRendererPageChange : function(pageNumber){
- console.log(`brewRenderer view : ${pageNumber}`);
this.setState({ currentBrewRendererPageNum: pageNumber });
},
handleTextChange : function(text){
//If there are errors, run the validator on every change to give quick feedback
- console.log('text change');
let htmlErrors = this.state.htmlErrors;
if(htmlErrors.length) htmlErrors = Markdown.validate(text);
diff --git a/client/homebrew/pages/homePage/homePage.jsx b/client/homebrew/pages/homePage/homePage.jsx
index 6e11806bd..ac3be81df 100644
--- a/client/homebrew/pages/homePage/homePage.jsx
+++ b/client/homebrew/pages/homePage/homePage.jsx
@@ -34,9 +34,9 @@ const HomePage = createClass({
brew : this.props.brew,
welcomeText : this.props.brew.text,
error : undefined,
- currentEditorViewPageNum : 0,
- currentEditorCursorPageNum : 0,
- currentBrewRendererPageNum : 0,
+ currentEditorViewPageNum : 1,
+ currentEditorCursorPageNum : 1,
+ currentBrewRendererPageNum : 1,
themeBundle : {}
};
},
@@ -64,17 +64,14 @@ const HomePage = createClass({
},
handleEditorViewPageChange : function(pageNumber){
- console.log(`editor view : ${pageNumber}`);
this.setState({ currentEditorViewPageNum: pageNumber });
},
handleEditorCursorPageChange : function(pageNumber){
- console.log(`editor cursor : ${pageNumber}`);
this.setState({ currentEditorCursorPageNum: pageNumber });
},
handleBrewRendererPageChange : function(pageNumber){
- console.log(`brewRenderer view : ${pageNumber}`);
this.setState({ currentBrewRendererPageNum: pageNumber });
},
diff --git a/client/homebrew/pages/newPage/newPage.jsx b/client/homebrew/pages/newPage/newPage.jsx
index 115f4ea88..c147cd474 100644
--- a/client/homebrew/pages/newPage/newPage.jsx
+++ b/client/homebrew/pages/newPage/newPage.jsx
@@ -44,9 +44,9 @@ const NewPage = createClass({
saveGoogle : (global.account && global.account.googleId ? true : false),
error : null,
htmlErrors : Markdown.validate(brew.text),
- currentEditorViewPageNum : 0,
- currentEditorCursorPageNum : 0,
- currentBrewRendererPageNum : 0,
+ currentEditorViewPageNum : 1,
+ currentEditorCursorPageNum : 1,
+ currentBrewRendererPageNum : 1,
themeBundle : {}
};
},
@@ -111,17 +111,14 @@ const NewPage = createClass({
},
handleEditorViewPageChange : function(pageNumber){
- console.log(`editor view : ${pageNumber}`);
this.setState({ currentEditorViewPageNum: pageNumber });
},
handleEditorCursorPageChange : function(pageNumber){
- console.log(`editor cursor : ${pageNumber}`);
this.setState({ currentEditorCursorPageNum: pageNumber });
},
handleBrewRendererPageChange : function(pageNumber){
- console.log(`brewRenderer view : ${pageNumber}`);
this.setState({ currentBrewRendererPageNum: pageNumber });
},
diff --git a/client/homebrew/pages/vaultPage/vaultPage.jsx b/client/homebrew/pages/vaultPage/vaultPage.jsx
index a550ec578..bad1fbd57 100644
--- a/client/homebrew/pages/vaultPage/vaultPage.jsx
+++ b/client/homebrew/pages/vaultPage/vaultPage.jsx
@@ -330,7 +330,7 @@ const VaultPage = (props)=>{
if(error) {
const errorText = ErrorIndex()[error.HBErrorCode.toString()] || '';
-
+
return (
Error: {errorText}
diff --git a/package-lock.json b/package-lock.json
index 06c4350ca..16d308bf5 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -25,7 +25,7 @@
"expr-eval": "^2.0.2",
"express": "^4.21.0",
"express-async-handler": "^1.2.0",
- "express-static-gzip": "2.1.7",
+ "express-static-gzip": "2.1.8",
"fs-extra": "11.2.0",
"js-yaml": "^4.1.0",
"jwt-simple": "^0.5.6",
@@ -6362,12 +6362,11 @@
"license": "MIT"
},
"node_modules/express-static-gzip": {
- "version": "2.1.7",
- "resolved": "https://registry.npmjs.org/express-static-gzip/-/express-static-gzip-2.1.7.tgz",
- "integrity": "sha512-QOCZUC+lhPPCjIJKpQGu1Oa61Axg9Mq09Qvit8Of7kzpMuwDeMSqjjQteQS3OVw/GkENBoSBheuQDWPlngImvw==",
- "license": "MIT",
+ "version": "2.1.8",
+ "resolved": "https://registry.npmjs.org/express-static-gzip/-/express-static-gzip-2.1.8.tgz",
+ "integrity": "sha512-g8tiJuI9Y9Ffy59ehVXvqb0hhP83JwZiLxzanobPaMbkB5qBWA8nuVgd+rcd5qzH3GkgogTALlc0BaADYwnMbQ==",
"dependencies": {
- "serve-static": "^1.14.1"
+ "serve-static": "^1.16.2"
}
},
"node_modules/express/node_modules/cookie": {
diff --git a/package.json b/package.json
index f760cc096..ef39fbf6f 100644
--- a/package.json
+++ b/package.json
@@ -100,7 +100,7 @@
"expr-eval": "^2.0.2",
"express": "^4.21.0",
"express-async-handler": "^1.2.0",
- "express-static-gzip": "2.1.7",
+ "express-static-gzip": "2.1.8",
"fs-extra": "11.2.0",
"js-yaml": "^4.1.0",
"jwt-simple": "^0.5.6",
diff --git a/server/app.js b/server/app.js
index c03fb2dc2..c97fc302e 100644
--- a/server/app.js
+++ b/server/app.js
@@ -203,22 +203,22 @@ app.get('/download/:id', asyncHandler(getBrew('share')), (req, res)=>{
});
//Serve brew metadata
-app.get('/metadata/:id', asyncHandler(getBrew('share')), (req, res) => {
+app.get('/metadata/:id', asyncHandler(getBrew('share')), (req, res)=>{
const { brew } = req;
sanitizeBrew(brew, 'share');
-
- const fields = [ 'title', 'pageCount', 'description', 'authors', 'lang',
- 'published', 'views', 'shareId', 'createdAt', 'updatedAt',
+
+ const fields = ['title', 'pageCount', 'description', 'authors', 'lang',
+ 'published', 'views', 'shareId', 'createdAt', 'updatedAt',
'lastViewed', 'thumbnail', 'tags'
];
-
- const metadata = fields.reduce((acc, field) => {
- if (brew[field] !== undefined) acc[field] = brew[field];
+
+ const metadata = fields.reduce((acc, field)=>{
+ if(brew[field] !== undefined) acc[field] = brew[field];
return acc;
}, {});
res.status(200).json(metadata);
});
-
+
//Serve brew styling
app.get('/css/:id', asyncHandler(getBrew('share')), (req, res)=>{getCSS(req, res);});
@@ -378,7 +378,7 @@ app.get('/share/:id', asyncHandler(getBrew('share')), asyncHandler(async (req, r
app.get('/account', asyncHandler(async (req, res, next)=>{
const data = {};
data.title = 'Account Information Page';
-
+
if(!req.account) {
res.set('WWW-Authenticate', 'Bearer realm="Authorization Required"');
const error = new Error('No valid account');
@@ -462,8 +462,8 @@ app.get('/vault', asyncHandler(async(req, res, next)=>{
//Send rendered page
app.use(asyncHandler(async (req, res, next)=>{
- if (!req.route) return res.redirect('/'); // Catch-all for invalid routes
-
+ if(!req.route) return res.redirect('/'); // Catch-all for invalid routes
+
const page = await renderPage(req, res);
if(!page) return;
res.send(page);
diff --git a/server/homebrew.api.spec.js b/server/homebrew.api.spec.js
index 6e7c36641..dd4641c09 100644
--- a/server/homebrew.api.spec.js
+++ b/server/homebrew.api.spec.js
@@ -934,7 +934,7 @@ brew`);
expect(req.brew).toEqual(testBrew);
expect(req.brew).toHaveProperty('style', '\nI Have a style!\n');
expect(res.status).toHaveBeenCalledWith(200);
- expect(res.send).toHaveBeenCalledWith("\nI Have a style!\n");
+ expect(res.send).toHaveBeenCalledWith('\nI Have a style!\n');
expect(res.set).toHaveBeenCalledWith({
'Cache-Control' : 'no-cache',
'Content-Type' : 'text/css'
diff --git a/shared/naturalcrit/codeEditor/codeEditor.jsx b/shared/naturalcrit/codeEditor/codeEditor.jsx
index 06f6ba0c2..fb69b6dcf 100644
--- a/shared/naturalcrit/codeEditor/codeEditor.jsx
+++ b/shared/naturalcrit/codeEditor/codeEditor.jsx
@@ -49,12 +49,12 @@ const CodeEditor = createClass({
displayName : 'CodeEditor',
getDefaultProps : function() {
return {
- language : '',
- value : '',
- wrap : true,
- onChange : ()=>{},
- enableFolding : true,
- editorTheme : 'default'
+ language : '',
+ value : '',
+ wrap : true,
+ onChange : ()=>{},
+ enableFolding : true,
+ editorTheme : 'default'
};
},
@@ -189,7 +189,7 @@ const CodeEditor = createClass({
autoCompleteEmoji.showAutocompleteEmoji(CodeMirror, this.codeMirror);
// Note: codeMirror passes a copy of itself in this callback. cm === this.codeMirror. Either one works.
- this.codeMirror.on('change', (cm)=>{this.props.onChange(cm.getValue())});
+ this.codeMirror.on('change', (cm)=>{this.props.onChange(cm.getValue());});
this.updateSize();
},
@@ -399,7 +399,7 @@ const CodeEditor = createClass({
},
getTopVisibleLine : function(){
const rect = this.codeMirror.getWrapperElement().getBoundingClientRect();
- const topVisibleLine = this.codeMirror.lineAtHeight(rect.top, "window");
+ const topVisibleLine = this.codeMirror.lineAtHeight(rect.top, 'window');
return topVisibleLine;
},
updateSize : function(){
diff --git a/shared/naturalcrit/markdown.js b/shared/naturalcrit/markdown.js
index 205063641..ef789bdd6 100644
--- a/shared/naturalcrit/markdown.js
+++ b/shared/naturalcrit/markdown.js
@@ -105,16 +105,16 @@ renderer.link = function (href, title, text) {
// Expose `src` attribute as `--HB_src` to make the URL accessible via CSS
renderer.image = function (href, title, text) {
href = cleanUrl(href);
- if (href === null)
+ if(href === null)
return text;
let out = `
';
return out;
-}
+};
// Disable default reflink behavior, as it steps on our variables extension
tokenizer.def = function () {
@@ -745,7 +745,7 @@ const tableTerminators = [
`:+\\n`, // hardBreak
` *{[^\n]+}`, // blockInjector
` *{{[^{\n]*\n.*?\n}}` // mustacheDiv
-]
+];
Marked.use(MarkedVariables());
Marked.use({ extensions : [definitionListsMultiLine, definitionListsSingleLine, forcedParagraphBreaks, superSubScripts,
@@ -755,12 +755,12 @@ Marked.use({ renderer: renderer, tokenizer: tokenizer, mangle: false });
Marked.use(MarkedExtendedTables(tableTerminators), MarkedGFMHeadingId({ globalSlugs: true }), MarkedSmartypantsLite(), MarkedEmojis(MarkedEmojiOptions));
function cleanUrl(href) {
- try {
- href = encodeURI(href).replace(/%25/g, '%');
- } catch {
- return null;
- }
- return href;
+ try {
+ href = encodeURI(href).replace(/%25/g, '%');
+ } catch {
+ return null;
+ }
+ return href;
}
const escapeTest = /[&<>"']/;
diff --git a/shared/naturalcrit/splitPane/splitPane.jsx b/shared/naturalcrit/splitPane/splitPane.jsx
index 606b22d40..23ae5d321 100644
--- a/shared/naturalcrit/splitPane/splitPane.jsx
+++ b/shared/naturalcrit/splitPane/splitPane.jsx
@@ -42,6 +42,10 @@ const SplitPane = createClass({
});
}
window.addEventListener('resize', this.handleWindowResize);
+
+ // This lives here instead of in the initial render because you cannot touch localStorage until the componant mounts.
+ const loadLiveScroll = window.localStorage.getItem('liveScroll') === 'true';
+ this.setState({ liveScroll: loadLiveScroll });
},
componentWillUnmount : function() {
@@ -89,6 +93,11 @@ const SplitPane = createClass({
userSetDividerPos : newSize
});
},
+
+ liveScrollToggle : function() {
+ window.localStorage.setItem('liveScroll', String(!this.state.liveScroll));
+ this.setState({ liveScroll: !this.state.liveScroll });
+ },
/*
unFocus : function() {
if(document.selection){
@@ -120,6 +129,11 @@ const SplitPane = createClass({
onClick={()=>this.setState({ moveBrew: !this.state.moveBrew })} >
+
+
+
>;
}
},
@@ -144,9 +158,10 @@ const SplitPane = createClass({
>
{React.cloneElement(this.props.children[0], {
...(this.props.showDividerButtons && {
- moveBrew: this.state.moveBrew,
- moveSource: this.state.moveSource,
- setMoveArrows: this.setMoveArrows,
+ moveBrew : this.state.moveBrew,
+ moveSource : this.state.moveSource,
+ liveScroll : this.state.liveScroll,
+ setMoveArrows : this.setMoveArrows,
}),
})}
diff --git a/shared/naturalcrit/splitPane/splitPane.less b/shared/naturalcrit/splitPane/splitPane.less
index 831b5ce47..e5b3dd7f8 100644
--- a/shared/naturalcrit/splitPane/splitPane.less
+++ b/shared/naturalcrit/splitPane/splitPane.less
@@ -53,6 +53,15 @@
.tooltipRight('Jump to location in Preview');
top : 60px;
}
+ &.lock{
+ .tooltipRight('De-sync Editor and Preview locations.');
+ top : 90px;
+ background: #666;
+ }
+ &.unlock{
+ .tooltipRight('Sync Editor and Preview locations');
+ top : 90px;
+ }
&:hover{
background-color: #666;
}
diff --git a/themes/V3/5ePHB/snippets/tableOfContents.gen.js b/themes/V3/5ePHB/snippets/tableOfContents.gen.js
index 49a8c168a..44c400762 100644
--- a/themes/V3/5ePHB/snippets/tableOfContents.gen.js
+++ b/themes/V3/5ePHB/snippets/tableOfContents.gen.js
@@ -4,9 +4,9 @@ const dedent = require('dedent-tabs').default;
const mapPages = (pages)=>{
let actualPage = 0;
let mappedPage = 0; // Number displayed in footer
- let pageMap = [];
+ const pageMap = [];
- pages.forEach(page => {
+ pages.forEach((page)=>{
actualPage++;
const doSkip = page.querySelector('.skipCounting');
const doReset = page.querySelector('.resetCounting');
@@ -24,13 +24,13 @@ const mapPages = (pages)=>{
return pageMap;
};
-const getMarkdown = (headings, pageMap) => {
+const getMarkdown = (headings, pageMap)=>{
const levelPad = ['- ###', ' - ####', ' -', ' -', ' -', ' -'];
-
- let allMarkdown = [];
- let depthChain = [0];
- headings.forEach(heading => {
+ const allMarkdown = [];
+ const depthChain = [0];
+
+ headings.forEach((heading)=>{
const page = parseInt(heading.closest('.page').id?.replace(/^p/, ''));
const mappedPage = pageMap[page].mappedPage;
const showPage = pageMap[page].showPage;
@@ -42,14 +42,14 @@ const getMarkdown = (headings, pageMap) => {
return;
//If different header depth than last, remove indents until nearest higher-level header, then indent once
- if (depth !== depthChain[depthChain.length -1]) {
+ if(depth !== depthChain[depthChain.length -1]) {
while (depth <= depthChain[depthChain.length - 1]) {
depthChain.pop();
}
depthChain.push(depth);
}
- let markdown = `${levelPad[depthChain.length - 2]} [{{ ${title}}}{{ ${mappedPage}}}](#p${page})`;
+ const markdown = `${levelPad[depthChain.length - 2]} [{{ ${title}}}{{ ${mappedPage}}}](#p${page})`;
allMarkdown.push(markdown);
});
return allMarkdown.join('\n');