From a8b664fdb5b425625928d0ce6ae9cdbd78fb3833 Mon Sep 17 00:00:00 2001
From: Edgar Zanella Alvarenga
Date: Tue, 19 Jun 2018 16:03:32 +0200
Subject: Add a toolbar to Codemirror editor

Signed-off-by: Edgar Zanella Alvarenga <e@vaz.io>
---
 public/js/index.js                |  9 ++++
 public/js/lib/editor/index.js     | 87 +++++++++++++++++++++++++++++++++++++++
 public/js/lib/editor/toolbar.html | 48 +++++++++++++++++++++
 public/js/lib/editor/utils.js     | 40 ++++++++++++++++++
 4 files changed, 184 insertions(+)
 create mode 100644 public/js/lib/editor/toolbar.html

(limited to 'public/js')

diff --git a/public/js/index.js b/public/js/index.js
index c6a4f770..5e0aded6 100644
--- a/public/js/index.js
+++ b/public/js/index.js
@@ -30,6 +30,8 @@ import {
 import {
     debug,
     DROPBOX_APP_KEY,
+    GOOGLE_API_KEY,
+    GOOGLE_CLIENT_ID,
     noteid,
     noteurl,
     urlpath,
@@ -566,6 +568,9 @@ var previousFocusOnEditor = null
 
 function checkEditorStyle () {
   var desireHeight = editorInstance.statusBar ? (ui.area.edit.height() - editorInstance.statusBar.outerHeight()) : ui.area.edit.height()
+  if (editorInstance.toolBar) {
+     desireHeight = desireHeight - editorInstance.toolBar.outerHeight()
+  }
     // set editor height and min height based on scrollbar style and mode
   var scrollbarStyle = editor.getOption('scrollbarStyle')
   if (scrollbarStyle === 'overlay' || appState.currentMode === modeType.both) {
@@ -804,6 +809,10 @@ function changeMode (type) {
       editorInstance.addStatusBar()
       editorInstance.updateStatusBar()
     }
+    // add and update tool bar
+    if (!editorInstance.toolBar) {
+      editorInstance.addToolBar()
+    }
     // work around foldGutter might not init properly
     editor.setOption('foldGutter', false)
     editor.setOption('foldGutter', true)
diff --git a/public/js/lib/editor/index.js b/public/js/lib/editor/index.js
index bc228b7b..cb9192bc 100644
--- a/public/js/lib/editor/index.js
+++ b/public/js/lib/editor/index.js
@@ -1,6 +1,7 @@
 import * as utils from './utils'
 import config from './config'
 import statusBarTemplate from './statusbar.html'
+import toolBarTemplate from './toolbar.html'
 
 /* config section */
 const isMac = CodeMirror.keyMap.default === CodeMirror.keyMap.macDefault
@@ -136,6 +137,92 @@ export default class Editor {
     })
   }
 
+  addToolBar () {
+    this.toolBar = $(toolBarTemplate)
+    //console.log('PLACE', $('#toolbarPlace'))
+    //$('#toolbarPlace').html(this.toolBar)
+    this.toolbarPanel = this.editor.addPanel(this.toolBar[0], {
+       position: 'top'
+    })
+
+    var insertDemo = $('#insertDemo')
+    var makeBold = $('#makeBold')
+    var makeItalic = $('#makeItalic')
+    var makeStrike = $('#makeStrike')
+    var makeHeader = $('#makeHeader')
+    var makeCode = $('#makeCode')
+    var makeQuote = $('#makeQuote')
+    var makeGenericList = $('#makeGenericList')
+    var makeOrderedList = $('#makeOrderedList')
+    var makeCheckList = $('#makeCheckList')
+    var makeLink = $('#makeLink')
+    var makeImage = $('#makeImage')
+    var makeTable = $('#makeTable')
+    var makeLine = $('#makeLine')
+    var makeComment = $('#makeComment')
+
+    makeBold.click(() => {
+        utils.wrapTextWith(this.editor, this.editor, '**')
+        this.editor.focus()
+    })
+
+    makeItalic.click(() => {
+        utils.wrapTextWith(this.editor, this.editor, '*')
+        this.editor.focus()
+    })
+
+    makeStrike.click(() => {
+        utils.wrapTextWith(this.editor, this.editor, '~~')
+        this.editor.focus()
+    })
+
+    makeHeader.click(() => {
+      utils.insertHeader(this.editor)
+    })
+
+    makeCode.click(() => {
+      utils.wrapTextWith(this.editor, this.editor, '```')
+        this.editor.focus()
+    })
+
+    makeQuote.click(() => {
+      utils.insertOnStartOfLines(this.editor, '> ')
+    })
+
+    makeGenericList.click(() => {
+      utils.insertOnStartOfLines(this.editor, '* ')
+    })
+
+    makeOrderedList.click(() => {
+      utils.insertOnStartOfLines(this.editor, '1. ')
+    })
+
+    makeCheckList.click(() => {
+      utils.insertOnStartOfLines(this.editor, '- [ ] ')
+    })
+
+    makeLink.click(() => {
+      utils.insertText(this.editor, '[](https://)', 1)
+    })
+
+    makeImage.click(() => {
+      utils.insertText(this.editor, '![](https://)', 4)
+    })
+
+    makeTable.click(() => {
+      utils.insertText(this.editor, '\n\n| Column 1 | Column 2 | Column 3 |\n| -------- | -------- | -------- |\n| Text     | Text     | Text     |\n')
+    })
+
+    makeLine.click(() => {
+      utils.insertText(this.editor, '\n----\n')
+    })
+
+    makeComment.click(() => {
+      utils.insertText(this.editor, '> []', 4)
+    })
+
+  }
+
   addStatusBar () {
     this.statusBar = $(statusBarTemplate)
     this.statusCursor = this.statusBar.find('.status-cursor > .status-line-column')
diff --git a/public/js/lib/editor/toolbar.html b/public/js/lib/editor/toolbar.html
new file mode 100644
index 00000000..2894eef9
--- /dev/null
+++ b/public/js/lib/editor/toolbar.html
@@ -0,0 +1,48 @@
+<div class="toolbar">
+  <div class="btn-toolbar" role="toolbar" aria-label="Editor toolbar">
+    <div class="btn-group" role="group">
+      <a id="makeBold" class="btn btn-sm btn-dark text-uppercase" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false" title="Bold">
+          <i class="fa fa-bold fa-fw"></i>
+      </a>
+      <a id="makeItalic" class="btn btn-sm btn-dark text-uppercase" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false" title="Italic">
+          <i class="fa fa-italic fa-fw"></i>
+      </a>
+      <a id="makeStrike" class="btn btn-sm btn-dark text-uppercase" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false" title="Strikethrough">
+          <i class="fa fa-strikethrough fa-fw"></i>
+      </a>
+      <a id="makeHeader" class="btn btn-sm btn-dark text-uppercase" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false" title="Heading">
+          <i class="fa fa-h1 fa-fw">H</i>
+      </a>
+      <a id="makeCode" class="btn btn-sm btn-dark text-uppercase" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false" title="Quote">
+          <i class="fa fa-code fa-fw"></i>
+      </a>
+      <a id="makeQuote" class="btn btn-sm btn-dark text-uppercase" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false" title="Quote">
+          <i class="fa fa-quote-right fa-fw"></i>
+      </a>
+      <a id="makeGenericList" class="btn btn-sm btn-dark text-uppercase" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false" title="List">
+          <i class="fa fa-list fa-fw"></i>
+      </a>
+      <a id="makeOrderedList" class="btn btn-sm btn-dark text-uppercase" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false" title="Numbered List">
+          <i class="fa fa-list-ol fa-fw"></i>
+      </a>
+      <a id="makeCheckList" class="btn btn-sm btn-dark text-uppercase" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false" title="Check List">
+          <i class="fa fa-check-square fa-fw"></i>
+      </a>
+      <a id="makeLink" class="btn btn-sm btn-dark text-uppercase" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false" title="Link">
+          <i class="fa fa-link fa-fw"></i>
+      </a>
+      <a id="makeImage" class="btn btn-sm btn-dark text-uppercase" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false" title="Image">
+          <i class="fa fa-image fa-fw"></i>
+      </a>
+      <a id="makeTable" class="btn btn-sm btn-dark text-uppercase" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false" title="Table">
+          <i class="fa fa-table fa-fw"></i>
+      </a>
+      <a id="makeLine" class="btn btn-sm btn-dark text-uppercase" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false" title="Line">
+          <i class="fa fa-minus fa-fw"></i>
+      </a>
+      <a id="makeComment" class="btn btn-sm btn-dark text-uppercase" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false" title="Line">
+          <i class="fa fa-comment fa-fw"></i>
+      </a>
+    </div>
+  </div>
+</div>
diff --git a/public/js/lib/editor/utils.js b/public/js/lib/editor/utils.js
index 3702a166..f1053c4c 100644
--- a/public/js/lib/editor/utils.js
+++ b/public/js/lib/editor/utils.js
@@ -46,3 +46,43 @@ export function wrapTextWith (editor, cm, symbol) {
     }
   }
 }
+
+export function insertText (cm, text, cursorEnd = 0) {
+  var cursor = cm.getCursor()
+  cm.replaceSelection(text, cursor, cursor)
+  cm.focus()
+  cm.setCursor({line: cursor.line, ch: cursor.ch + cursorEnd})
+}
+
+export function insertHeader (cm) {
+  let cursor = cm.getCursor()
+  let startOfLine = {line: cursor.line, ch: 0}
+  let startOfLineText = cm.getRange(startOfLine, {line: cursor.line, ch: 1})
+  // See if it is already a header
+  if (startOfLineText === '#') {
+    cm.replaceRange('#', startOfLine, startOfLine)
+  } else {
+    cm.replaceRange('# ', startOfLine, startOfLine)
+  }
+  cm.focus()
+}
+
+export function insertOnStartOfLines (cm, symbol, cursorEnd) {
+  let cursor = cm.getCursor()
+  var ranges = cm.listSelections()
+
+  for (let i = 0; i < ranges.length; i++) {
+    var range = ranges[i]
+    if (!range.empty()) {
+      const from = range.from()
+      const to = range.to()
+      for (let j = from.line; j <= to.line; ++j) {
+        cm.replaceRange(symbol, {line: j, ch: 0}, {line: j, ch: 0})
+      }
+    } else {
+      cm.replaceRange(symbol, {line: cursor.line, ch: 0}, {line: cursor.line, ch: 0})
+    }
+  }
+  cm.setCursor({line: cursor.line, ch: (cursorEnd)? cursorEnd : cursor.ch})
+  cm.focus()
+}
-- 
cgit v1.2.3


From f65d96c57b02c98616ffe3d8d7cc93f3e3942897 Mon Sep 17 00:00:00 2001
From: Sheogorath
Date: Sat, 23 Jun 2018 20:55:32 +0200
Subject: Fix liniting and optimize some functions

First fixed some linting issues. Also optimized some functions to be
undoable with one ctrl+z.

This should also speedup some operations

Signed-off-by: Sheogorath <sheogorath@shivering-isles.com>
---
 public/js/index.js            |  6 +--
 public/js/lib/editor/index.js | 26 ++++++-------
 public/js/lib/editor/utils.js | 89 +++++++++++++++++++++++++++++--------------
 3 files changed, 74 insertions(+), 47 deletions(-)

(limited to 'public/js')

diff --git a/public/js/index.js b/public/js/index.js
index 5e0aded6..6e13fe9c 100644
--- a/public/js/index.js
+++ b/public/js/index.js
@@ -30,8 +30,6 @@ import {
 import {
     debug,
     DROPBOX_APP_KEY,
-    GOOGLE_API_KEY,
-    GOOGLE_CLIENT_ID,
     noteid,
     noteurl,
     urlpath,
@@ -569,9 +567,9 @@ var previousFocusOnEditor = null
 function checkEditorStyle () {
   var desireHeight = editorInstance.statusBar ? (ui.area.edit.height() - editorInstance.statusBar.outerHeight()) : ui.area.edit.height()
   if (editorInstance.toolBar) {
-     desireHeight = desireHeight - editorInstance.toolBar.outerHeight()
+    desireHeight = desireHeight - editorInstance.toolBar.outerHeight()
   }
-    // set editor height and min height based on scrollbar style and mode
+  // set editor height and min height based on scrollbar style and mode
   var scrollbarStyle = editor.getOption('scrollbarStyle')
   if (scrollbarStyle === 'overlay' || appState.currentMode === modeType.both) {
     ui.area.codemirrorScroll.css('height', desireHeight + 'px')
diff --git a/public/js/lib/editor/index.js b/public/js/lib/editor/index.js
index cb9192bc..0537e927 100644
--- a/public/js/lib/editor/index.js
+++ b/public/js/lib/editor/index.js
@@ -139,13 +139,10 @@ export default class Editor {
 
   addToolBar () {
     this.toolBar = $(toolBarTemplate)
-    //console.log('PLACE', $('#toolbarPlace'))
-    //$('#toolbarPlace').html(this.toolBar)
     this.toolbarPanel = this.editor.addPanel(this.toolBar[0], {
-       position: 'top'
+      position: 'top'
     })
 
-    var insertDemo = $('#insertDemo')
     var makeBold = $('#makeBold')
     var makeItalic = $('#makeItalic')
     var makeStrike = $('#makeStrike')
@@ -162,18 +159,18 @@ export default class Editor {
     var makeComment = $('#makeComment')
 
     makeBold.click(() => {
-        utils.wrapTextWith(this.editor, this.editor, '**')
-        this.editor.focus()
+      utils.wrapTextWith(this.editor, this.editor, '**')
+      this.editor.focus()
     })
 
     makeItalic.click(() => {
-        utils.wrapTextWith(this.editor, this.editor, '*')
-        this.editor.focus()
+      utils.wrapTextWith(this.editor, this.editor, '*')
+      this.editor.focus()
     })
 
     makeStrike.click(() => {
-        utils.wrapTextWith(this.editor, this.editor, '~~')
-        this.editor.focus()
+      utils.wrapTextWith(this.editor, this.editor, '~~')
+      this.editor.focus()
     })
 
     makeHeader.click(() => {
@@ -182,7 +179,7 @@ export default class Editor {
 
     makeCode.click(() => {
       utils.wrapTextWith(this.editor, this.editor, '```')
-        this.editor.focus()
+      this.editor.focus()
     })
 
     makeQuote.click(() => {
@@ -202,11 +199,11 @@ export default class Editor {
     })
 
     makeLink.click(() => {
-      utils.insertText(this.editor, '[](https://)', 1)
+      utils.insertLink(this.editor, false)
     })
 
     makeImage.click(() => {
-      utils.insertText(this.editor, '![](https://)', 4)
+      utils.insertLink(this.editor, true)
     })
 
     makeTable.click(() => {
@@ -218,9 +215,8 @@ export default class Editor {
     })
 
     makeComment.click(() => {
-      utils.insertText(this.editor, '> []', 4)
+      utils.insertText(this.editor, '> []')
     })
-
   }
 
   addStatusBar () {
diff --git a/public/js/lib/editor/utils.js b/public/js/lib/editor/utils.js
index f1053c4c..33670884 100644
--- a/public/js/lib/editor/utils.js
+++ b/public/js/lib/editor/utils.js
@@ -3,39 +3,39 @@ export function wrapTextWith (editor, cm, symbol) {
   if (!cm.getSelection()) {
     return CodeMirror.Pass
   } else {
-    var ranges = cm.listSelections()
-    for (var i = 0; i < ranges.length; i++) {
-      var range = ranges[i]
+    let ranges = cm.listSelections()
+    for (let i = 0; i < ranges.length; i++) {
+      let range = ranges[i]
       if (!range.empty()) {
         const from = range.from()
         const to = range.to()
 
         if (symbol !== 'Backspace') {
-          cm.replaceRange(symbol, to, to, '+input')
-          cm.replaceRange(symbol, from, from, '+input')
-          // workaround selection range not correct after add symbol
-          var _ranges = cm.listSelections()
-          var anchorIndex = editor.indexFromPos(_ranges[i].anchor)
-          var headIndex = editor.indexFromPos(_ranges[i].head)
+          let selection = cm.getRange(from, to)
+          let anchorIndex = editor.indexFromPos(ranges[i].anchor)
+          let headIndex = editor.indexFromPos(ranges[i].head)
+          cm.replaceRange(symbol + selection + symbol, from, to, '+input')
           if (anchorIndex > headIndex) {
-            _ranges[i].anchor.ch--
+            ranges[i].anchor.ch+= symbol.length
+            ranges[i].head.ch+= symbol.length
           } else {
-            _ranges[i].head.ch--
+            ranges[i].head.ch+= symbol.length
+            ranges[i].anchor.ch+= symbol.length
           }
-          cm.setSelections(_ranges)
+          cm.setSelections(ranges)
         } else {
-          var preEndPos = {
+          let preEndPos = {
             line: to.line,
-            ch: to.ch + 1
+            ch: to.ch + symbol.length
           }
-          var preText = cm.getRange(to, preEndPos)
-          var preIndex = wrapSymbols.indexOf(preText)
-          var postEndPos = {
+          let preText = cm.getRange(to, preEndPos)
+          let preIndex = wrapSymbols.indexOf(preText)
+          let postEndPos = {
             line: from.line,
-            ch: from.ch - 1
+            ch: from.ch - symbol.length
           }
-          var postText = cm.getRange(postEndPos, from)
-          var postIndex = wrapSymbols.indexOf(postText)
+          let postText = cm.getRange(postEndPos, from)
+          let postIndex = wrapSymbols.indexOf(postText)
           // check if surround symbol are list in array and matched
           if (preIndex > -1 && postIndex > -1 && preIndex === postIndex) {
             cm.replaceRange('', to, preEndPos, '+delete')
@@ -48,12 +48,44 @@ export function wrapTextWith (editor, cm, symbol) {
 }
 
 export function insertText (cm, text, cursorEnd = 0) {
-  var cursor = cm.getCursor()
+  let cursor = cm.getCursor()
   cm.replaceSelection(text, cursor, cursor)
   cm.focus()
   cm.setCursor({line: cursor.line, ch: cursor.ch + cursorEnd})
 }
 
+export function insertLink(cm, isImage) {
+  let cursor = cm.getCursor()
+  let ranges = cm.listSelections()
+  const linkEnd = '](https://)'
+  const symbol = (isImage) ? '![' : '['
+
+  for (let i = 0; i < ranges.length; i++) {
+    let range = ranges[i]
+    if (!range.empty()) {
+      const from = range.from()
+      const to = range.to()
+      let anchorIndex = editor.indexFromPos(ranges[i].anchor)
+      let headIndex = editor.indexFromPos(ranges[i].head)
+      let selection = cm.getRange(from, to)
+      selection = symbol + selection + linkEnd
+      cm.replaceRange(selection, from, to)
+      if (anchorIndex > headIndex) {
+        ranges[i].anchor.ch+= symbol.length
+        ranges[i].head.ch+= symbol.length
+      } else {
+        ranges[i].head.ch+= symbol.length
+        ranges[i].anchor.ch+= symbol.length
+      }
+      cm.setSelections(ranges)
+    } else {
+      cm.replaceRange(symbol + linkEnd, cursor, cursor)
+      cm.setCursor({line: cursor.line, ch: cursor.ch + symbol.length + linkend.length})
+    }
+  }
+  cm.focus()
+}
+
 export function insertHeader (cm) {
   let cursor = cm.getCursor()
   let startOfLine = {line: cursor.line, ch: 0}
@@ -67,22 +99,23 @@ export function insertHeader (cm) {
   cm.focus()
 }
 
-export function insertOnStartOfLines (cm, symbol, cursorEnd) {
+export function insertOnStartOfLines (cm, symbol) {
   let cursor = cm.getCursor()
-  var ranges = cm.listSelections()
+  let ranges = cm.listSelections()
 
   for (let i = 0; i < ranges.length; i++) {
-    var range = ranges[i]
+    let range = ranges[i]
     if (!range.empty()) {
       const from = range.from()
       const to = range.to()
-      for (let j = from.line; j <= to.line; ++j) {
-        cm.replaceRange(symbol, {line: j, ch: 0}, {line: j, ch: 0})
-      }
+      let selection = cm.getRange({line: from.line, ch: 0}, to)
+      selection = selection.replace(/\n/g, '\n' + symbol)
+      selection = symbol + selection
+      cm.replaceRange(selection, from, to)
     } else {
       cm.replaceRange(symbol, {line: cursor.line, ch: 0}, {line: cursor.line, ch: 0})
     }
   }
-  cm.setCursor({line: cursor.line, ch: (cursorEnd)? cursorEnd : cursor.ch})
+  cm.setCursor({line: cursor.line, ch: cursor.ch + symbol.length})
   cm.focus()
 }
-- 
cgit v1.2.3