From 121d84863a055dbe259cdcfb98583a376bd8e2fa Mon Sep 17 00:00:00 2001 From: Yukai Huang Date: Tue, 7 Mar 2017 21:59:18 +0800 Subject: [PATCH 1/8] Extract UI jquery node --- public/js/lib/editor/ui-elements.js | 84 +++++++++++++++++++++++++++++ 1 file changed, 84 insertions(+) create mode 100644 public/js/lib/editor/ui-elements.js diff --git a/public/js/lib/editor/ui-elements.js b/public/js/lib/editor/ui-elements.js new file mode 100644 index 00000000..d06e6402 --- /dev/null +++ b/public/js/lib/editor/ui-elements.js @@ -0,0 +1,84 @@ +/* + * Global UI elements references + */ + +export const getUIElements = () => ({ + spinner: $(".ui-spinner"), + content: $(".ui-content"), + toolbar: { + shortStatus: $(".ui-short-status"), + status: $(".ui-status"), + new: $(".ui-new"), + publish: $(".ui-publish"), + extra: { + revision: $(".ui-extra-revision"), + slide: $(".ui-extra-slide") + }, + download: { + markdown: $(".ui-download-markdown"), + html: $(".ui-download-html"), + rawhtml: $(".ui-download-raw-html"), + pdf: $(".ui-download-pdf-beta"), + }, + export: { + dropbox: $(".ui-save-dropbox"), + googleDrive: $(".ui-save-google-drive"), + gist: $(".ui-save-gist"), + snippet: $(".ui-save-snippet") + }, + import: { + dropbox: $(".ui-import-dropbox"), + googleDrive: $(".ui-import-google-drive"), + gist: $(".ui-import-gist"), + snippet: $(".ui-import-snippet"), + clipboard: $(".ui-import-clipboard") + }, + mode: $(".ui-mode"), + edit: $(".ui-edit"), + view: $(".ui-view"), + both: $(".ui-both"), + uploadImage: $(".ui-upload-image") + }, + infobar: { + lastchange: $(".ui-lastchange"), + lastchangeuser: $(".ui-lastchangeuser"), + nolastchangeuser: $(".ui-no-lastchangeuser"), + permission: { + permission: $(".ui-permission"), + label: $(".ui-permission-label"), + freely: $(".ui-permission-freely"), + editable: $(".ui-permission-editable"), + locked: $(".ui-permission-locked"), + private: $(".ui-permission-private"), + limited: $(".ui-permission-limited"), + protected: $(".ui-permission-protected") + }, + delete: $(".ui-delete-note") + }, + toc: { + toc: $('.ui-toc'), + affix: $('.ui-affix-toc'), + label: $('.ui-toc-label'), + dropdown: $('.ui-toc-dropdown') + }, + area: { + edit: $(".ui-edit-area"), + view: $(".ui-view-area"), + codemirror: $(".ui-edit-area .CodeMirror"), + codemirrorScroll: $(".ui-edit-area .CodeMirror .CodeMirror-scroll"), + codemirrorSizer: $(".ui-edit-area .CodeMirror .CodeMirror-sizer"), + codemirrorSizerInner: $(".ui-edit-area .CodeMirror .CodeMirror-sizer > div"), + markdown: $(".ui-view-area .markdown-body"), + resize: { + handle: $('.ui-resizable-handle'), + syncToggle: $('.ui-sync-toggle') + } + }, + modal: { + snippetImportProjects: $("#snippetImportModalProjects"), + snippetImportSnippets: $("#snippetImportModalSnippets"), + revision: $("#revisionModal") + } +}) + +export default getUIElements; From 6556c284e504790bd9ed0facac3322320e0c347b Mon Sep 17 00:00:00 2001 From: Yukai Huang Date: Tue, 7 Mar 2017 22:35:54 +0800 Subject: [PATCH 2/8] Extract editor related code - in public/js/lib/editor/index.js --- public/js/index.js | 600 ++-------------------------------- public/js/lib/editor/index.js | 459 ++++++++++++++++++++++++++ public/js/lib/editor/utils.js | 46 +++ 3 files changed, 536 insertions(+), 569 deletions(-) create mode 100644 public/js/lib/editor/index.js create mode 100644 public/js/lib/editor/utils.js diff --git a/public/js/index.js b/public/js/index.js index f0c476ef..0d4da4d0 100644 --- a/public/js/index.js +++ b/public/js/index.js @@ -73,145 +73,12 @@ import { var renderer = require('./render'); var preventXSS = renderer.preventXSS; +import Editor from './lib/editor'; + +import getUIElements from './lib/editor/ui-elements'; + var defaultTextHeight = 20; var viewportMargin = 20; -var mac = CodeMirror.keyMap["default"] == CodeMirror.keyMap.macDefault; -var defaultEditorMode = 'gfm'; -var defaultExtraKeys = { - "F10": function (cm) { - cm.setOption("fullScreen", !cm.getOption("fullScreen")); - }, - "Esc": function (cm) { - if (cm.getOption('keyMap').substr(0, 3) === 'vim') return CodeMirror.Pass; - else if (cm.getOption("fullScreen")) cm.setOption("fullScreen", false); - }, - "Cmd-S": function () { - return false; - }, - "Ctrl-S": function () { - return false; - }, - "Enter": "newlineAndIndentContinueMarkdownList", - "Tab": function (cm) { - var tab = '\t'; - var spaces = Array(parseInt(cm.getOption("indentUnit")) + 1).join(" "); - //auto indent whole line when in list or blockquote - var cursor = cm.getCursor(); - var line = cm.getLine(cursor.line); - var regex = /^(\s*)(>[> ]*|[*+-]\s|(\d+)([.)]))/; - var match; - var multiple = cm.getSelection().split('\n').length > 1 || cm.getSelections().length > 1; - if (multiple) { - cm.execCommand('defaultTab'); - } else if ((match = regex.exec(line)) !== null) { - var ch = match[1].length; - var pos = { - line: cursor.line, - ch: ch - }; - if (cm.getOption('indentWithTabs')) - cm.replaceRange(tab, pos, pos, '+input'); - else - cm.replaceRange(spaces, pos, pos, '+input'); - } else { - if (cm.getOption('indentWithTabs')) - cm.execCommand('defaultTab'); - else { - cm.replaceSelection(spaces); - } - } - }, - "Cmd-Left": "goLineLeftSmart", - "Cmd-Right": "goLineRight", - "Ctrl-C": function (cm) { - if (!mac && cm.getOption('keyMap').substr(0, 3) === 'vim') document.execCommand("copy"); - else return CodeMirror.Pass; - }, - "Ctrl-*": function (cm) { - wrapTextWith(cm, '*'); - }, - "Shift-Ctrl-8": function (cm) { - wrapTextWith(cm, '*'); - }, - "Ctrl-_": function (cm) { - wrapTextWith(cm, '_'); - }, - "Shift-Ctrl--": function (cm) { - wrapTextWith(cm, '_'); - }, - "Ctrl-~": function (cm) { - wrapTextWith(cm, '~'); - }, - "Shift-Ctrl-`": function (cm) { - wrapTextWith(cm, '~'); - }, - "Ctrl-^": function (cm) { - wrapTextWith(cm, '^'); - }, - "Shift-Ctrl-6": function (cm) { - wrapTextWith(cm, '^'); - }, - "Ctrl-+": function (cm) { - wrapTextWith(cm, '+'); - }, - "Shift-Ctrl-=": function (cm) { - wrapTextWith(cm, '+'); - }, - "Ctrl-=": function (cm) { - wrapTextWith(cm, '='); - }, - "Shift-Ctrl-Backspace": function (cm) { - wrapTextWith(cm, 'Backspace'); - } -}; - -var wrapSymbols = ['*', '_', '~', '^', '+', '=']; - -function wrapTextWith(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]; - if (!range.empty()) { - var from = range.from(), 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); - if (anchorIndex > headIndex) { - _ranges[i].anchor.ch--; - } else { - _ranges[i].head.ch--; - } - cm.setSelections(_ranges); - } else { - var preEndPos = { - line: to.line, - ch: to.ch + 1 - }; - var preText = cm.getRange(to, preEndPos); - var preIndex = wrapSymbols.indexOf(preText); - var postEndPos = { - line: from.line, - ch: from.ch - 1 - }; - var postText = cm.getRange(postEndPos, from); - var 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'); - cm.replaceRange("", postEndPos, from, '+delete'); - } - } - } - } - } -} var idleTime = 300000; //5 mins var updateViewDebounce = 100; @@ -432,353 +299,25 @@ window.fileTypes = { "py": "python" }; -//editor settings +// editor settings var textit = document.getElementById("textit"); -if (!textit) throw new Error("There was no textit area!"); -window.editor = CodeMirror.fromTextArea(textit, { - mode: defaultEditorMode, - backdrop: defaultEditorMode, - keyMap: "sublime", - viewportMargin: viewportMargin, - styleActiveLine: true, - lineNumbers: true, - lineWrapping: true, - showCursorWhenSelecting: true, - highlightSelectionMatches: true, - indentUnit: 4, - continueComments: "Enter", - theme: "one-dark", - inputStyle: "textarea", - matchBrackets: true, - autoCloseBrackets: true, - matchTags: { - bothTags: true - }, - autoCloseTags: true, - foldGutter: true, - gutters: ["CodeMirror-linenumbers", "authorship-gutters", "CodeMirror-foldgutter"], - extraKeys: defaultExtraKeys, - flattenSpans: true, - addModeClass: true, - readOnly: true, - autoRefresh: true, - otherCursors: true, - placeholder: "← Start by entering a title here\n===\nVisit /features if you don't know what to do.\nHappy hacking :)" -}); +if (!textit) { + throw new Error("There was no textit area!"); +} + +const editorInstance = new Editor(); +var editor = editorInstance.init(textit); + +// TODO: global referncing in jquery-textcomplete patch +window.editor = editor; + var inlineAttach = inlineAttachment.editors.codemirror4.attach(editor); defaultTextHeight = parseInt($(".CodeMirror").css('line-height')); -var statusBarTemplate = null; -var statusBar = null; -var statusPanel = null; -var statusCursor = null; -var statusFile = null; -var statusIndicators = null; -var statusLength = null; -var statusKeymap = null; -var statusIndent = null; -var statusTheme = null; -var statusSpellcheck = null; -var statusPreferences = null; - -function getStatusBarTemplate(callback) { - $.get(serverurl + '/views/statusbar.html', function (template) { - statusBarTemplate = template; - if (callback) callback(); - }); -} -getStatusBarTemplate(); - -function addStatusBar() { - if (!statusBarTemplate) { - getStatusBarTemplate(addStatusBar); - return; - } - statusBar = $(statusBarTemplate); - statusCursor = statusBar.find('.status-cursor'); - statusFile = statusBar.find('.status-file'); - statusIndicators = statusBar.find('.status-indicators'); - statusIndent = statusBar.find('.status-indent'); - statusKeymap = statusBar.find('.status-keymap'); - statusLength = statusBar.find('.status-length'); - statusTheme = statusBar.find('.status-theme'); - statusSpellcheck = statusBar.find('.status-spellcheck'); - statusPreferences = statusBar.find('.status-preferences'); - statusPanel = editor.addPanel(statusBar[0], { - position: "bottom" - }); - - setIndent(); - setKeymap(); - setTheme(); - setSpellcheck(); - setPreferences(); -} - -function setIndent() { - var cookieIndentType = Cookies.get('indent_type'); - var cookieTabSize = parseInt(Cookies.get('tab_size')); - var cookieSpaceUnits = parseInt(Cookies.get('space_units')); - if (cookieIndentType) { - if (cookieIndentType == 'tab') { - editor.setOption('indentWithTabs', true); - if (cookieTabSize) - editor.setOption('indentUnit', cookieTabSize); - } else if (cookieIndentType == 'space') { - editor.setOption('indentWithTabs', false); - if (cookieSpaceUnits) - editor.setOption('indentUnit', cookieSpaceUnits); - } - } - if (cookieTabSize) - editor.setOption('tabSize', cookieTabSize); - - var type = statusIndicators.find('.indent-type'); - var widthLabel = statusIndicators.find('.indent-width-label'); - var widthInput = statusIndicators.find('.indent-width-input'); - - function setType() { - if (editor.getOption('indentWithTabs')) { - Cookies.set('indent_type', 'tab', { - expires: 365 - }); - type.text('Tab Size:'); - } else { - Cookies.set('indent_type', 'space', { - expires: 365 - }); - type.text('Spaces:'); - } - } - setType(); - - function setUnit() { - var unit = editor.getOption('indentUnit'); - if (editor.getOption('indentWithTabs')) { - Cookies.set('tab_size', unit, { - expires: 365 - }); - } else { - Cookies.set('space_units', unit, { - expires: 365 - }); - } - widthLabel.text(unit); - } - setUnit(); - - type.click(function () { - if (editor.getOption('indentWithTabs')) { - editor.setOption('indentWithTabs', false); - cookieSpaceUnits = parseInt(Cookies.get('space_units')); - if (cookieSpaceUnits) - editor.setOption('indentUnit', cookieSpaceUnits) - } else { - editor.setOption('indentWithTabs', true); - cookieTabSize = parseInt(Cookies.get('tab_size')); - if (cookieTabSize) { - editor.setOption('indentUnit', cookieTabSize); - editor.setOption('tabSize', cookieTabSize); - } - } - setType(); - setUnit(); - }); - widthLabel.click(function () { - if (widthLabel.is(':visible')) { - widthLabel.addClass('hidden'); - widthInput.removeClass('hidden'); - widthInput.val(editor.getOption('indentUnit')); - widthInput.select(); - } else { - widthLabel.removeClass('hidden'); - widthInput.addClass('hidden'); - } - }); - widthInput.on('change', function () { - var val = parseInt(widthInput.val()); - if (!val) val = editor.getOption('indentUnit'); - if (val < 1) val = 1; - else if (val > 10) val = 10; - - if (editor.getOption('indentWithTabs')) { - editor.setOption('tabSize', val); - } - editor.setOption('indentUnit', val); - setUnit(); - }); - widthInput.on('blur', function () { - widthLabel.removeClass('hidden'); - widthInput.addClass('hidden'); - }); -} - -function setKeymap() { - var cookieKeymap = Cookies.get('keymap'); - if (cookieKeymap) - editor.setOption('keyMap', cookieKeymap); - - var label = statusIndicators.find('.ui-keymap-label'); - var sublime = statusIndicators.find('.ui-keymap-sublime'); - var emacs = statusIndicators.find('.ui-keymap-emacs'); - var vim = statusIndicators.find('.ui-keymap-vim'); - - function setKeymapLabel() { - var keymap = editor.getOption('keyMap'); - Cookies.set('keymap', keymap, { - expires: 365 - }); - label.text(keymap); - restoreOverrideEditorKeymap(); - setOverrideBrowserKeymap(); - } - setKeymapLabel(); - - sublime.click(function () { - editor.setOption('keyMap', 'sublime'); - setKeymapLabel(); - }); - emacs.click(function () { - editor.setOption('keyMap', 'emacs'); - setKeymapLabel(); - }); - vim.click(function () { - editor.setOption('keyMap', 'vim'); - setKeymapLabel(); - }); -} - -function setTheme() { - var cookieTheme = Cookies.get('theme'); - if (cookieTheme) { - editor.setOption('theme', cookieTheme); - } - - var themeToggle = statusTheme.find('.ui-theme-toggle'); - themeToggle.click(function () { - var theme = editor.getOption('theme'); - if (theme == "one-dark") { - theme = "default"; - } else { - theme = "one-dark"; - } - editor.setOption('theme', theme); - Cookies.set('theme', theme, { - expires: 365 - }); - checkTheme(); - }); - function checkTheme() { - var theme = editor.getOption('theme'); - if (theme == "one-dark") { - themeToggle.removeClass('active'); - } else { - themeToggle.addClass('active'); - } - } - checkTheme(); -} - -function setSpellcheck() { - var cookieSpellcheck = Cookies.get('spellcheck'); - if (cookieSpellcheck) { - var mode = null; - if (cookieSpellcheck === 'true' || cookieSpellcheck === true) { - mode = 'spell-checker'; - } else { - mode = defaultEditorMode; - } - if (mode && mode !== editor.getOption('mode')) { - editor.setOption('mode', mode); - } - } - - var spellcheckToggle = statusSpellcheck.find('.ui-spellcheck-toggle'); - spellcheckToggle.click(function () { - var mode = editor.getOption('mode'); - if (mode == defaultEditorMode) { - mode = "spell-checker"; - } else { - mode = defaultEditorMode; - } - if (mode && mode !== editor.getOption('mode')) { - editor.setOption('mode', mode); - } - Cookies.set('spellcheck', (mode == "spell-checker"), { - expires: 365 - }); - checkSpellcheck(); - }); - function checkSpellcheck() { - var mode = editor.getOption('mode'); - if (mode == defaultEditorMode) { - spellcheckToggle.removeClass('active'); - } else { - spellcheckToggle.addClass('active'); - } - } - checkSpellcheck(); - - //workaround spellcheck might not activate beacuse the ajax loading - if (num_loaded < 2) { - var spellcheckTimer = setInterval(function () { - if (num_loaded >= 2) { - if (editor.getOption('mode') == "spell-checker") - editor.setOption('mode', "spell-checker"); - clearInterval(spellcheckTimer); - } - }, 100); - } -} - -var jumpToAddressBarKeymapName = mac ? "Cmd-L" : "Ctrl-L"; -var jumpToAddressBarKeymapValue = null; -function resetEditorKeymapToBrowserKeymap() { - var keymap = editor.getOption('keyMap'); - if (!jumpToAddressBarKeymapValue) { - jumpToAddressBarKeymapValue = CodeMirror.keyMap[keymap][jumpToAddressBarKeymapName]; - delete CodeMirror.keyMap[keymap][jumpToAddressBarKeymapName]; - } -} -function restoreOverrideEditorKeymap() { - var keymap = editor.getOption('keyMap'); - if (jumpToAddressBarKeymapValue) { - CodeMirror.keyMap[keymap][jumpToAddressBarKeymapName] = jumpToAddressBarKeymapValue; - jumpToAddressBarKeymapValue = null; - } -} -function setOverrideBrowserKeymap() { - var overrideBrowserKeymap = $('.ui-preferences-override-browser-keymap label > input[type="checkbox"]'); - if(overrideBrowserKeymap.is(":checked")) { - Cookies.set('preferences-override-browser-keymap', true, { - expires: 365 - }); - restoreOverrideEditorKeymap(); - } else { - Cookies.remove('preferences-override-browser-keymap'); - resetEditorKeymapToBrowserKeymap(); - } -} - -function setPreferences() { - var overrideBrowserKeymap = $('.ui-preferences-override-browser-keymap label > input[type="checkbox"]'); - var cookieOverrideBrowserKeymap = Cookies.get('preferences-override-browser-keymap'); - if (cookieOverrideBrowserKeymap && cookieOverrideBrowserKeymap === "true") { - overrideBrowserKeymap.prop('checked', true); - } else { - overrideBrowserKeymap.prop('checked', false); - } - setOverrideBrowserKeymap(); - - overrideBrowserKeymap.change(function() { - setOverrideBrowserKeymap(); - }); -} - var selection = null; function updateStatusBar() { - if (!statusBar) return; + if (!editorInstance.statusBar) return; var cursor = editor.getCursor(); var cursorText = 'Line ' + (cursor.line + 1) + ', Columns ' + (cursor.ch + 1); if (selection) { @@ -800,102 +339,25 @@ function updateStatusBar() { if (start.line !== end.line || selectionCharCount > 0) cursorText += selectionText; } - statusCursor.text(cursorText); + editorInstance.statusCursor.text(cursorText); var fileText = ' — ' + editor.lineCount() + ' Lines'; - statusFile.text(fileText); + editorInstance.statusFile.text(fileText); var docLength = editor.getValue().length; - statusLength.text('Length ' + docLength); + editorInstance.statusLength.text('Length ' + docLength); if (docLength > (docmaxlength * 0.95)) { - statusLength.css('color', 'red'); - statusLength.attr('title', 'Your almost reach note max length limit.'); + editorInstance.statusLength.css('color', 'red'); + editorInstance.statusLength.attr('title', 'Your almost reach note max length limit.'); } else if (docLength > (docmaxlength * 0.8)) { - statusLength.css('color', 'orange'); - statusLength.attr('title', 'You nearly fill the note, consider to make more pieces.'); + editorInstance.statusLength.css('color', 'orange'); + editorInstance.statusLength.attr('title', 'You nearly fill the note, consider to make more pieces.'); } else { - statusLength.css('color', 'white'); - statusLength.attr('title', 'You could write up to ' + docmaxlength + ' characters in this note.'); + editorInstance.statusLength.css('color', 'white'); + editorInstance.statusLength.attr('title', 'You could write up to ' + docmaxlength + ' characters in this note.'); } } -//ui vars -window.ui = { - spinner: $(".ui-spinner"), - content: $(".ui-content"), - toolbar: { - shortStatus: $(".ui-short-status"), - status: $(".ui-status"), - new: $(".ui-new"), - publish: $(".ui-publish"), - extra: { - revision: $(".ui-extra-revision"), - slide: $(".ui-extra-slide") - }, - download: { - markdown: $(".ui-download-markdown"), - html: $(".ui-download-html"), - rawhtml: $(".ui-download-raw-html"), - pdf: $(".ui-download-pdf-beta"), - }, - export: { - dropbox: $(".ui-save-dropbox"), - googleDrive: $(".ui-save-google-drive"), - gist: $(".ui-save-gist"), - snippet: $(".ui-save-snippet") - }, - import: { - dropbox: $(".ui-import-dropbox"), - googleDrive: $(".ui-import-google-drive"), - gist: $(".ui-import-gist"), - snippet: $(".ui-import-snippet"), - clipboard: $(".ui-import-clipboard") - }, - mode: $(".ui-mode"), - edit: $(".ui-edit"), - view: $(".ui-view"), - both: $(".ui-both"), - uploadImage: $(".ui-upload-image") - }, - infobar: { - lastchange: $(".ui-lastchange"), - lastchangeuser: $(".ui-lastchangeuser"), - nolastchangeuser: $(".ui-no-lastchangeuser"), - permission: { - permission: $(".ui-permission"), - label: $(".ui-permission-label"), - freely: $(".ui-permission-freely"), - editable: $(".ui-permission-editable"), - locked: $(".ui-permission-locked"), - private: $(".ui-permission-private"), - limited: $(".ui-permission-limited"), - protected: $(".ui-permission-protected") - }, - delete: $(".ui-delete-note") - }, - toc: { - toc: $('.ui-toc'), - affix: $('.ui-affix-toc'), - label: $('.ui-toc-label'), - dropdown: $('.ui-toc-dropdown') - }, - area: { - edit: $(".ui-edit-area"), - view: $(".ui-view-area"), - codemirror: $(".ui-edit-area .CodeMirror"), - codemirrorScroll: $(".ui-edit-area .CodeMirror .CodeMirror-scroll"), - codemirrorSizer: $(".ui-edit-area .CodeMirror .CodeMirror-sizer"), - codemirrorSizerInner: $(".ui-edit-area .CodeMirror .CodeMirror-sizer > div"), - markdown: $(".ui-view-area .markdown-body"), - resize: { - handle: $('.ui-resizable-handle'), - syncToggle: $('.ui-sync-toggle') - } - }, - modal: { - snippetImportProjects: $("#snippetImportModalProjects"), - snippetImportSnippets: $("#snippetImportModalSnippets"), - revision: $("#revisionModal") - } -}; +// initalize ui reference +const ui = getUIElements(); //page actions var opts = { @@ -1146,7 +608,7 @@ var lastEditorWidth = 0; var previousFocusOnEditor = null; function checkEditorStyle() { - var desireHeight = statusBar ? (ui.area.edit.height() - statusBar.outerHeight()) : ui.area.edit.height(); + var desireHeight = editorInstance.statusBar ? (ui.area.edit.height() - editorInstance.statusBar.outerHeight()) : ui.area.edit.height(); // set editor height and min height based on scrollbar style and mode var scrollbarStyle = editor.getOption('scrollbarStyle'); if (scrollbarStyle == 'overlay' || currentMode == modeType.both) { @@ -1381,8 +843,8 @@ function changeMode(type) { if (currentMode == modeType.edit || currentMode == modeType.both) { ui.toolbar.uploadImage.fadeIn(); //add and update status bar - if (!statusBar) { - addStatusBar(); + if (!editorInstance.statusBar) { + editorInstance.addStatusBar(); updateStatusBar(); } //work around foldGutter might not init properly @@ -4069,6 +3531,6 @@ $(editor.getInputField()) }, 'textComplete:hide': function (e) { $(this).data('autocompleting', false); - editor.setOption("extraKeys", defaultExtraKeys); + editor.setOption("extraKeys", editorInstance.defaultExtraKeys); } }); diff --git a/public/js/lib/editor/index.js b/public/js/lib/editor/index.js new file mode 100644 index 00000000..ec22ac92 --- /dev/null +++ b/public/js/lib/editor/index.js @@ -0,0 +1,459 @@ +import * as utils from './utils'; + +/* config section */ +const isMac = CodeMirror.keyMap.default === CodeMirror.keyMap.macDefault; +const defaultEditorMode = 'gfm'; +const viewportMargin = 20; + +const jumpToAddressBarKeymapName = isMac ? "Cmd-L" : "Ctrl-L"; + +export default class Editor { + constructor() { + this.editor = null; + + this.defaultExtraKeys = { + "F10": function (cm) { + cm.setOption("fullScreen", !cm.getOption("fullScreen")); + }, + "Esc": function (cm) { + if (cm.getOption('keyMap').substr(0, 3) === 'vim') return CodeMirror.Pass; + else if (cm.getOption("fullScreen")) cm.setOption("fullScreen", false); + }, + "Cmd-S": function () { + return false; + }, + "Ctrl-S": function () { + return false; + }, + "Enter": "newlineAndIndentContinueMarkdownList", + "Tab": function (cm) { + var tab = '\t'; + + // contruct x length spaces + var spaces = Array(parseInt(cm.getOption("indentUnit")) + 1).join(" "); + + //auto indent whole line when in list or blockquote + var cursor = cm.getCursor(); + var line = cm.getLine(cursor.line); + + // this regex match the following patterns + // 1. blockquote starts with "> " or ">>" + // 2. unorder list starts with *+- + // 3. order list starts with "1." or "1)" + var regex = /^(\s*)(>[> ]*|[*+-]\s|(\d+)([.)]))/; + + var match; + var multiple = cm.getSelection().split('\n').length > 1 || cm.getSelections().length > 1; + + if (multiple) { + cm.execCommand('defaultTab'); + } else if ((match = regex.exec(line)) !== null) { + var ch = match[1].length; + var pos = { + line: cursor.line, + ch: ch + }; + if (cm.getOption('indentWithTabs')) + cm.replaceRange(tab, pos, pos, '+input'); + else + cm.replaceRange(spaces, pos, pos, '+input'); + } else { + if (cm.getOption('indentWithTabs')) + cm.execCommand('defaultTab'); + else { + cm.replaceSelection(spaces); + } + } + }, + "Cmd-Left": "goLineLeftSmart", + "Cmd-Right": "goLineRight", + "Ctrl-C": function (cm) { + if (!isMac && cm.getOption('keyMap').substr(0, 3) === 'vim') { + document.execCommand("copy"); + } else { + return CodeMirror.Pass; + } + }, + "Ctrl-*": (cm) => { + utils.wrapTextWith(this.editor, cm, '*'); + }, + "Shift-Ctrl-8": (cm) => { + utils.wrapTextWith(this.editor, cm, '*'); + }, + "Ctrl-_": (cm) => { + utils.wrapTextWith(this.editor, cm, '_'); + }, + "Shift-Ctrl--": (cm) => { + utils.wrapTextWith(this.editor, cm, '_'); + }, + "Ctrl-~": (cm) => { + utils.wrapTextWith(this.editor, cm, '~'); + }, + "Shift-Ctrl-`": (cm) => { + utils.wrapTextWith(this.editor, cm, '~'); + }, + "Ctrl-^": (cm) => { + utils.wrapTextWith(this.editor, cm, '^'); + }, + "Shift-Ctrl-6": (cm) => { + utils.wrapTextWith(this.editor, cm, '^'); + }, + "Ctrl-+": (cm) => { + utils.wrapTextWith(this.editor, cm, '+'); + }, + "Shift-Ctrl-=": (cm) => { + utils.wrapTextWith(this.editor, cm, '+'); + }, + "Ctrl-=": (cm) => { + utils.wrapTextWith(this.editor, cm, '='); + }, + "Shift-Ctrl-Backspace": (cm) => { + utils.wrapTextWith(this.editor, cm, 'Backspace'); + } + }; + + this.jumpToAddressBarKeymapValue = null; + } + + getStatusBarTemplate(callback) { + $.get(window.serverurl + '/views/statusbar.html', (template) => { + this.statusBarTemplate = template; + if (callback) callback(); + }); + } + + addStatusBar() { + if (!this.statusBarTemplate) { + this.getStatusBarTemplate(this.addStatusBar); + return; + } + this.statusBar = $(this.statusBarTemplate); + this.statusCursor = this.statusBar.find('.status-cursor'); + this.statusFile = this.statusBar.find('.status-file'); + this.statusIndicators = this.statusBar.find('.status-indicators'); + this.statusIndent = this.statusBar.find('.status-indent'); + this.statusKeymap = this.statusBar.find('.status-keymap'); + this.statusLength = this.statusBar.find('.status-length'); + this.statusTheme = this.statusBar.find('.status-theme'); + this.statusSpellcheck = this.statusBar.find('.status-spellcheck'); + this.statusPreferences = this.statusBar.find('.status-preferences'); + this.statusPanel = this.editor.addPanel(this.statusBar[0], { + position: "bottom" + }); + + this.setIndent(); + this.setKeymap(); + this.setTheme(); + this.setSpellcheck(); + this.setPreferences(); + } + + setIndent() { + var cookieIndentType = Cookies.get('indent_type'); + var cookieTabSize = parseInt(Cookies.get('tab_size')); + var cookieSpaceUnits = parseInt(Cookies.get('space_units')); + if (cookieIndentType) { + if (cookieIndentType == 'tab') { + this.editor.setOption('indentWithTabs', true); + if (cookieTabSize) + this.editor.setOption('indentUnit', cookieTabSize); + } else if (cookieIndentType == 'space') { + this.editor.setOption('indentWithTabs', false); + if (cookieSpaceUnits) + this.editor.setOption('indentUnit', cookieSpaceUnits); + } + } + if (cookieTabSize) + this.editor.setOption('tabSize', cookieTabSize); + + var type = this.statusIndicators.find('.indent-type'); + var widthLabel = this.statusIndicators.find('.indent-width-label'); + var widthInput = this.statusIndicators.find('.indent-width-input'); + + const setType = () => { + if (this.editor.getOption('indentWithTabs')) { + Cookies.set('indent_type', 'tab', { + expires: 365 + }); + type.text('Tab Size:'); + } else { + Cookies.set('indent_type', 'space', { + expires: 365 + }); + type.text('Spaces:'); + } + } + setType(); + + const setUnit = () => { + var unit = this.editor.getOption('indentUnit'); + if (this.editor.getOption('indentWithTabs')) { + Cookies.set('tab_size', unit, { + expires: 365 + }); + } else { + Cookies.set('space_units', unit, { + expires: 365 + }); + } + widthLabel.text(unit); + } + setUnit(); + + type.click(() => { + if (this.editor.getOption('indentWithTabs')) { + this.editor.setOption('indentWithTabs', false); + cookieSpaceUnits = parseInt(Cookies.get('space_units')); + if (cookieSpaceUnits) + this.editor.setOption('indentUnit', cookieSpaceUnits) + } else { + this.editor.setOption('indentWithTabs', true); + cookieTabSize = parseInt(Cookies.get('tab_size')); + if (cookieTabSize) { + this.editor.setOption('indentUnit', cookieTabSize); + this.editor.setOption('tabSize', cookieTabSize); + } + } + setType(); + setUnit(); + }); + widthLabel.click(() => { + if (widthLabel.is(':visible')) { + widthLabel.addClass('hidden'); + widthInput.removeClass('hidden'); + widthInput.val(this.editor.getOption('indentUnit')); + widthInput.select(); + } else { + widthLabel.removeClass('hidden'); + widthInput.addClass('hidden'); + } + }); + widthInput.on('change', () => { + var val = parseInt(widthInput.val()); + if (!val) val = this.editor.getOption('indentUnit'); + if (val < 1) val = 1; + else if (val > 10) val = 10; + + if (this.editor.getOption('indentWithTabs')) { + this.editor.setOption('tabSize', val); + } + this.editor.setOption('indentUnit', val); + setUnit(); + }); + widthInput.on('blur', function () { + widthLabel.removeClass('hidden'); + widthInput.addClass('hidden'); + }); + } + + setKeymap() { + var cookieKeymap = Cookies.get('keymap'); + if (cookieKeymap) + this.editor.setOption('keyMap', cookieKeymap); + + var label = this.statusIndicators.find('.ui-keymap-label'); + var sublime = this.statusIndicators.find('.ui-keymap-sublime'); + var emacs = this.statusIndicators.find('.ui-keymap-emacs'); + var vim = this.statusIndicators.find('.ui-keymap-vim'); + + const setKeymapLabel = () => { + var keymap = this.editor.getOption('keyMap'); + Cookies.set('keymap', keymap, { + expires: 365 + }); + label.text(keymap); + this.restoreOverrideEditorKeymap(); + this.setOverrideBrowserKeymap(); + } + setKeymapLabel(); + + sublime.click(() => { + this.editor.setOption('keyMap', 'sublime'); + setKeymapLabel(); + }); + emacs.click(() => { + this.editor.setOption('keyMap', 'emacs'); + setKeymapLabel(); + }); + vim.click(() => { + this.editor.setOption('keyMap', 'vim'); + setKeymapLabel(); + }); + } + + setTheme() { + var cookieTheme = Cookies.get('theme'); + if (cookieTheme) { + this.editor.setOption('theme', cookieTheme); + } + + var themeToggle = this.statusTheme.find('.ui-theme-toggle'); + + const checkTheme = () => { + var theme = this.editor.getOption('theme'); + if (theme == "one-dark") { + themeToggle.removeClass('active'); + } else { + themeToggle.addClass('active'); + } + } + + themeToggle.click(() => { + var theme = this.editor.getOption('theme'); + if (theme == "one-dark") { + theme = "default"; + } else { + theme = "one-dark"; + } + this.editor.setOption('theme', theme); + Cookies.set('theme', theme, { + expires: 365 + }); + + checkTheme(); + }); + + checkTheme(); + } + + setSpellcheck() { + var cookieSpellcheck = Cookies.get('spellcheck'); + if (cookieSpellcheck) { + var mode = null; + if (cookieSpellcheck === 'true' || cookieSpellcheck === true) { + mode = 'spell-checker'; + } else { + mode = defaultEditorMode; + } + if (mode && mode !== this.editor.getOption('mode')) { + this.editor.setOption('mode', mode); + } + } + + var spellcheckToggle = this.statusSpellcheck.find('.ui-spellcheck-toggle'); + + const checkSpellcheck = () => { + var mode = this.editor.getOption('mode'); + if (mode == defaultEditorMode) { + spellcheckToggle.removeClass('active'); + } else { + spellcheckToggle.addClass('active'); + } + } + + spellcheckToggle.click(() => { + var mode = this.editor.getOption('mode'); + if (mode == defaultEditorMode) { + mode = "spell-checker"; + } else { + mode = defaultEditorMode; + } + if (mode && mode !== this.editor.getOption('mode')) { + this.editor.setOption('mode', mode); + } + Cookies.set('spellcheck', (mode == "spell-checker"), { + expires: 365 + }); + + checkSpellcheck(); + }); + + checkSpellcheck(); + + //workaround spellcheck might not activate beacuse the ajax loading + if (window.num_loaded < 2) { + var spellcheckTimer = setInterval(() => { + if (window.num_loaded >= 2) { + if (this.editor.getOption('mode') == "spell-checker") { + this.editor.setOption('mode', "spell-checker"); + } + clearInterval(spellcheckTimer); + } + }, 100); + } + } + + resetEditorKeymapToBrowserKeymap() { + var keymap = this.editor.getOption('keyMap'); + if (!this.jumpToAddressBarKeymapValue) { + this.jumpToAddressBarKeymapValue = CodeMirror.keyMap[keymap][jumpToAddressBarKeymapName]; + delete CodeMirror.keyMap[keymap][jumpToAddressBarKeymapName]; + } + } + + restoreOverrideEditorKeymap() { + var keymap = this.editor.getOption('keyMap'); + if (this.jumpToAddressBarKeymapValue) { + CodeMirror.keyMap[keymap][jumpToAddressBarKeymapName] = this.jumpToAddressBarKeymapValue; + this.jumpToAddressBarKeymapValue = null; + } + } + setOverrideBrowserKeymap() { + var overrideBrowserKeymap = $('.ui-preferences-override-browser-keymap label > input[type="checkbox"]'); + if (overrideBrowserKeymap.is(":checked")) { + Cookies.set('preferences-override-browser-keymap', true, { + expires: 365 + }); + this.restoreOverrideEditorKeymap(); + } else { + Cookies.remove('preferences-override-browser-keymap'); + this.resetEditorKeymapToBrowserKeymap(); + } + } + + setPreferences() { + var overrideBrowserKeymap = $('.ui-preferences-override-browser-keymap label > input[type="checkbox"]'); + var cookieOverrideBrowserKeymap = Cookies.get('preferences-override-browser-keymap'); + if (cookieOverrideBrowserKeymap && cookieOverrideBrowserKeymap === "true") { + overrideBrowserKeymap.prop('checked', true); + } else { + overrideBrowserKeymap.prop('checked', false); + } + this.setOverrideBrowserKeymap(); + + overrideBrowserKeymap.change(() => { + this.setOverrideBrowserKeymap(); + }); + } + + init(textit) { + this.editor = CodeMirror.fromTextArea(textit, { + mode: defaultEditorMode, + backdrop: defaultEditorMode, + keyMap: "sublime", + viewportMargin: viewportMargin, + styleActiveLine: true, + lineNumbers: true, + lineWrapping: true, + showCursorWhenSelecting: true, + highlightSelectionMatches: true, + indentUnit: 4, + continueComments: "Enter", + theme: "one-dark", + inputStyle: "textarea", + matchBrackets: true, + autoCloseBrackets: true, + matchTags: { + bothTags: true + }, + autoCloseTags: true, + foldGutter: true, + gutters: ["CodeMirror-linenumbers", "authorship-gutters", "CodeMirror-foldgutter"], + extraKeys: this.defaultExtraKeys, + flattenSpans: true, + addModeClass: true, + readOnly: true, + autoRefresh: true, + otherCursors: true, + placeholder: "← Start by entering a title here\n===\nVisit /features if you don't know what to do.\nHappy hacking :)" + }); + + this.getStatusBarTemplate(); + + return this.editor; + } + + getEditor() { + return this.editor; + } +} diff --git a/public/js/lib/editor/utils.js b/public/js/lib/editor/utils.js new file mode 100644 index 00000000..120d3646 --- /dev/null +++ b/public/js/lib/editor/utils.js @@ -0,0 +1,46 @@ +const wrapSymbols = ['*', '_', '~', '^', '+', '=']; +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]; + if (!range.empty()) { + var from = range.from(), 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); + if (anchorIndex > headIndex) { + _ranges[i].anchor.ch--; + } else { + _ranges[i].head.ch--; + } + cm.setSelections(_ranges); + } else { + var preEndPos = { + line: to.line, + ch: to.ch + 1 + }; + var preText = cm.getRange(to, preEndPos); + var preIndex = wrapSymbols.indexOf(preText); + var postEndPos = { + line: from.line, + ch: from.ch - 1 + }; + var postText = cm.getRange(postEndPos, from); + var 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'); + cm.replaceRange("", postEndPos, from, '+delete'); + } + } + } + } + } +} From 9b513f619fe74a579fadd807f86bd38f1049c56b Mon Sep 17 00:00:00 2001 From: Yukai Huang Date: Thu, 9 Mar 2017 15:39:42 +0800 Subject: [PATCH 3/8] Use JavaScript standard style --- public/js/lib/editor/index.js | 893 ++++++++++++++-------------- public/js/lib/editor/ui-elements.js | 146 ++--- public/js/lib/editor/utils.js | 88 +-- 3 files changed, 577 insertions(+), 550 deletions(-) diff --git a/public/js/lib/editor/index.js b/public/js/lib/editor/index.js index ec22ac92..a97fd976 100644 --- a/public/js/lib/editor/index.js +++ b/public/js/lib/editor/index.js @@ -1,459 +1,482 @@ -import * as utils from './utils'; +import * as utils from './utils' /* config section */ -const isMac = CodeMirror.keyMap.default === CodeMirror.keyMap.macDefault; -const defaultEditorMode = 'gfm'; -const viewportMargin = 20; +const isMac = CodeMirror.keyMap.default === CodeMirror.keyMap.macDefault +const defaultEditorMode = 'gfm' +const viewportMargin = 20 -const jumpToAddressBarKeymapName = isMac ? "Cmd-L" : "Ctrl-L"; +const jumpToAddressBarKeymapName = isMac ? 'Cmd-L' : 'Ctrl-L' export default class Editor { - constructor() { - this.editor = null; + constructor () { + this.editor = null - this.defaultExtraKeys = { - "F10": function (cm) { - cm.setOption("fullScreen", !cm.getOption("fullScreen")); - }, - "Esc": function (cm) { - if (cm.getOption('keyMap').substr(0, 3) === 'vim') return CodeMirror.Pass; - else if (cm.getOption("fullScreen")) cm.setOption("fullScreen", false); - }, - "Cmd-S": function () { - return false; - }, - "Ctrl-S": function () { - return false; - }, - "Enter": "newlineAndIndentContinueMarkdownList", - "Tab": function (cm) { - var tab = '\t'; - - // contruct x length spaces - var spaces = Array(parseInt(cm.getOption("indentUnit")) + 1).join(" "); - - //auto indent whole line when in list or blockquote - var cursor = cm.getCursor(); - var line = cm.getLine(cursor.line); - - // this regex match the following patterns - // 1. blockquote starts with "> " or ">>" - // 2. unorder list starts with *+- - // 3. order list starts with "1." or "1)" - var regex = /^(\s*)(>[> ]*|[*+-]\s|(\d+)([.)]))/; - - var match; - var multiple = cm.getSelection().split('\n').length > 1 || cm.getSelections().length > 1; - - if (multiple) { - cm.execCommand('defaultTab'); - } else if ((match = regex.exec(line)) !== null) { - var ch = match[1].length; - var pos = { - line: cursor.line, - ch: ch - }; - if (cm.getOption('indentWithTabs')) - cm.replaceRange(tab, pos, pos, '+input'); - else - cm.replaceRange(spaces, pos, pos, '+input'); - } else { - if (cm.getOption('indentWithTabs')) - cm.execCommand('defaultTab'); - else { - cm.replaceSelection(spaces); - } - } - }, - "Cmd-Left": "goLineLeftSmart", - "Cmd-Right": "goLineRight", - "Ctrl-C": function (cm) { - if (!isMac && cm.getOption('keyMap').substr(0, 3) === 'vim') { - document.execCommand("copy"); - } else { - return CodeMirror.Pass; - } - }, - "Ctrl-*": (cm) => { - utils.wrapTextWith(this.editor, cm, '*'); - }, - "Shift-Ctrl-8": (cm) => { - utils.wrapTextWith(this.editor, cm, '*'); - }, - "Ctrl-_": (cm) => { - utils.wrapTextWith(this.editor, cm, '_'); - }, - "Shift-Ctrl--": (cm) => { - utils.wrapTextWith(this.editor, cm, '_'); - }, - "Ctrl-~": (cm) => { - utils.wrapTextWith(this.editor, cm, '~'); - }, - "Shift-Ctrl-`": (cm) => { - utils.wrapTextWith(this.editor, cm, '~'); - }, - "Ctrl-^": (cm) => { - utils.wrapTextWith(this.editor, cm, '^'); - }, - "Shift-Ctrl-6": (cm) => { - utils.wrapTextWith(this.editor, cm, '^'); - }, - "Ctrl-+": (cm) => { - utils.wrapTextWith(this.editor, cm, '+'); - }, - "Shift-Ctrl-=": (cm) => { - utils.wrapTextWith(this.editor, cm, '+'); - }, - "Ctrl-=": (cm) => { - utils.wrapTextWith(this.editor, cm, '='); - }, - "Shift-Ctrl-Backspace": (cm) => { - utils.wrapTextWith(this.editor, cm, 'Backspace'); - } - }; - - this.jumpToAddressBarKeymapValue = null; - } - - getStatusBarTemplate(callback) { - $.get(window.serverurl + '/views/statusbar.html', (template) => { - this.statusBarTemplate = template; - if (callback) callback(); - }); - } - - addStatusBar() { - if (!this.statusBarTemplate) { - this.getStatusBarTemplate(this.addStatusBar); - return; + this.defaultExtraKeys = { + F10: function (cm) { + cm.setOption('fullScreen', !cm.getOption('fullScreen')) + }, + Esc: function (cm) { + if (cm.getOption('keyMap').substr(0, 3) === 'vim') { + return CodeMirror.Pass + } else if (cm.getOption('fullScreen')) { + cm.setOption('fullScreen', false) } - this.statusBar = $(this.statusBarTemplate); - this.statusCursor = this.statusBar.find('.status-cursor'); - this.statusFile = this.statusBar.find('.status-file'); - this.statusIndicators = this.statusBar.find('.status-indicators'); - this.statusIndent = this.statusBar.find('.status-indent'); - this.statusKeymap = this.statusBar.find('.status-keymap'); - this.statusLength = this.statusBar.find('.status-length'); - this.statusTheme = this.statusBar.find('.status-theme'); - this.statusSpellcheck = this.statusBar.find('.status-spellcheck'); - this.statusPreferences = this.statusBar.find('.status-preferences'); - this.statusPanel = this.editor.addPanel(this.statusBar[0], { - position: "bottom" - }); + }, + 'Cmd-S': function () { + return false + }, + 'Ctrl-S': function () { + return false + }, + Enter: 'newlineAndIndentContinueMarkdownList', + Tab: function (cm) { + var tab = '\t' - this.setIndent(); - this.setKeymap(); - this.setTheme(); - this.setSpellcheck(); - this.setPreferences(); - } + // contruct x length spaces + var spaces = Array(parseInt(cm.getOption('indentUnit')) + 1).join(' ') - setIndent() { - var cookieIndentType = Cookies.get('indent_type'); - var cookieTabSize = parseInt(Cookies.get('tab_size')); - var cookieSpaceUnits = parseInt(Cookies.get('space_units')); - if (cookieIndentType) { - if (cookieIndentType == 'tab') { - this.editor.setOption('indentWithTabs', true); - if (cookieTabSize) - this.editor.setOption('indentUnit', cookieTabSize); - } else if (cookieIndentType == 'space') { - this.editor.setOption('indentWithTabs', false); - if (cookieSpaceUnits) - this.editor.setOption('indentUnit', cookieSpaceUnits); - } - } - if (cookieTabSize) - this.editor.setOption('tabSize', cookieTabSize); + // auto indent whole line when in list or blockquote + var cursor = cm.getCursor() + var line = cm.getLine(cursor.line) - var type = this.statusIndicators.find('.indent-type'); - var widthLabel = this.statusIndicators.find('.indent-width-label'); - var widthInput = this.statusIndicators.find('.indent-width-input'); + // this regex match the following patterns + // 1. blockquote starts with "> " or ">>" + // 2. unorder list starts with *+- + // 3. order list starts with "1." or "1)" + var regex = /^(\s*)(>[> ]*|[*+-]\s|(\d+)([.)]))/ - const setType = () => { - if (this.editor.getOption('indentWithTabs')) { - Cookies.set('indent_type', 'tab', { - expires: 365 - }); - type.text('Tab Size:'); - } else { - Cookies.set('indent_type', 'space', { - expires: 365 - }); - type.text('Spaces:'); - } - } - setType(); + var match + var multiple = cm.getSelection().split('\n').length > 1 || + cm.getSelections().length > 1 - const setUnit = () => { - var unit = this.editor.getOption('indentUnit'); - if (this.editor.getOption('indentWithTabs')) { - Cookies.set('tab_size', unit, { - expires: 365 - }); - } else { - Cookies.set('space_units', unit, { - expires: 365 - }); - } - widthLabel.text(unit); - } - setUnit(); - - type.click(() => { - if (this.editor.getOption('indentWithTabs')) { - this.editor.setOption('indentWithTabs', false); - cookieSpaceUnits = parseInt(Cookies.get('space_units')); - if (cookieSpaceUnits) - this.editor.setOption('indentUnit', cookieSpaceUnits) - } else { - this.editor.setOption('indentWithTabs', true); - cookieTabSize = parseInt(Cookies.get('tab_size')); - if (cookieTabSize) { - this.editor.setOption('indentUnit', cookieTabSize); - this.editor.setOption('tabSize', cookieTabSize); - } - } - setType(); - setUnit(); - }); - widthLabel.click(() => { - if (widthLabel.is(':visible')) { - widthLabel.addClass('hidden'); - widthInput.removeClass('hidden'); - widthInput.val(this.editor.getOption('indentUnit')); - widthInput.select(); - } else { - widthLabel.removeClass('hidden'); - widthInput.addClass('hidden'); - } - }); - widthInput.on('change', () => { - var val = parseInt(widthInput.val()); - if (!val) val = this.editor.getOption('indentUnit'); - if (val < 1) val = 1; - else if (val > 10) val = 10; - - if (this.editor.getOption('indentWithTabs')) { - this.editor.setOption('tabSize', val); - } - this.editor.setOption('indentUnit', val); - setUnit(); - }); - widthInput.on('blur', function () { - widthLabel.removeClass('hidden'); - widthInput.addClass('hidden'); - }); - } - - setKeymap() { - var cookieKeymap = Cookies.get('keymap'); - if (cookieKeymap) - this.editor.setOption('keyMap', cookieKeymap); - - var label = this.statusIndicators.find('.ui-keymap-label'); - var sublime = this.statusIndicators.find('.ui-keymap-sublime'); - var emacs = this.statusIndicators.find('.ui-keymap-emacs'); - var vim = this.statusIndicators.find('.ui-keymap-vim'); - - const setKeymapLabel = () => { - var keymap = this.editor.getOption('keyMap'); - Cookies.set('keymap', keymap, { - expires: 365 - }); - label.text(keymap); - this.restoreOverrideEditorKeymap(); - this.setOverrideBrowserKeymap(); - } - setKeymapLabel(); - - sublime.click(() => { - this.editor.setOption('keyMap', 'sublime'); - setKeymapLabel(); - }); - emacs.click(() => { - this.editor.setOption('keyMap', 'emacs'); - setKeymapLabel(); - }); - vim.click(() => { - this.editor.setOption('keyMap', 'vim'); - setKeymapLabel(); - }); - } - - setTheme() { - var cookieTheme = Cookies.get('theme'); - if (cookieTheme) { - this.editor.setOption('theme', cookieTheme); - } - - var themeToggle = this.statusTheme.find('.ui-theme-toggle'); - - const checkTheme = () => { - var theme = this.editor.getOption('theme'); - if (theme == "one-dark") { - themeToggle.removeClass('active'); - } else { - themeToggle.addClass('active'); - } - } - - themeToggle.click(() => { - var theme = this.editor.getOption('theme'); - if (theme == "one-dark") { - theme = "default"; - } else { - theme = "one-dark"; - } - this.editor.setOption('theme', theme); - Cookies.set('theme', theme, { - expires: 365 - }); - - checkTheme(); - }); - - checkTheme(); - } - - setSpellcheck() { - var cookieSpellcheck = Cookies.get('spellcheck'); - if (cookieSpellcheck) { - var mode = null; - if (cookieSpellcheck === 'true' || cookieSpellcheck === true) { - mode = 'spell-checker'; - } else { - mode = defaultEditorMode; - } - if (mode && mode !== this.editor.getOption('mode')) { - this.editor.setOption('mode', mode); - } - } - - var spellcheckToggle = this.statusSpellcheck.find('.ui-spellcheck-toggle'); - - const checkSpellcheck = () => { - var mode = this.editor.getOption('mode'); - if (mode == defaultEditorMode) { - spellcheckToggle.removeClass('active'); - } else { - spellcheckToggle.addClass('active'); - } - } - - spellcheckToggle.click(() => { - var mode = this.editor.getOption('mode'); - if (mode == defaultEditorMode) { - mode = "spell-checker"; - } else { - mode = defaultEditorMode; - } - if (mode && mode !== this.editor.getOption('mode')) { - this.editor.setOption('mode', mode); - } - Cookies.set('spellcheck', (mode == "spell-checker"), { - expires: 365 - }); - - checkSpellcheck(); - }); - - checkSpellcheck(); - - //workaround spellcheck might not activate beacuse the ajax loading - if (window.num_loaded < 2) { - var spellcheckTimer = setInterval(() => { - if (window.num_loaded >= 2) { - if (this.editor.getOption('mode') == "spell-checker") { - this.editor.setOption('mode', "spell-checker"); - } - clearInterval(spellcheckTimer); - } - }, 100); - } - } - - resetEditorKeymapToBrowserKeymap() { - var keymap = this.editor.getOption('keyMap'); - if (!this.jumpToAddressBarKeymapValue) { - this.jumpToAddressBarKeymapValue = CodeMirror.keyMap[keymap][jumpToAddressBarKeymapName]; - delete CodeMirror.keyMap[keymap][jumpToAddressBarKeymapName]; - } - } - - restoreOverrideEditorKeymap() { - var keymap = this.editor.getOption('keyMap'); - if (this.jumpToAddressBarKeymapValue) { - CodeMirror.keyMap[keymap][jumpToAddressBarKeymapName] = this.jumpToAddressBarKeymapValue; - this.jumpToAddressBarKeymapValue = null; - } - } - setOverrideBrowserKeymap() { - var overrideBrowserKeymap = $('.ui-preferences-override-browser-keymap label > input[type="checkbox"]'); - if (overrideBrowserKeymap.is(":checked")) { - Cookies.set('preferences-override-browser-keymap', true, { - expires: 365 - }); - this.restoreOverrideEditorKeymap(); + if (multiple) { + cm.execCommand('defaultTab') + } else if ((match = regex.exec(line)) !== null) { + var ch = match[1].length + var pos = { + line: cursor.line, + ch: ch + } + if (cm.getOption('indentWithTabs')) { + cm.replaceRange(tab, pos, pos, '+input') + } else { + cm.replaceRange(spaces, pos, pos, '+input') + } } else { - Cookies.remove('preferences-override-browser-keymap'); - this.resetEditorKeymapToBrowserKeymap(); + if (cm.getOption('indentWithTabs')) { + cm.execCommand('defaultTab') + } else { + cm.replaceSelection(spaces) + } } - } - - setPreferences() { - var overrideBrowserKeymap = $('.ui-preferences-override-browser-keymap label > input[type="checkbox"]'); - var cookieOverrideBrowserKeymap = Cookies.get('preferences-override-browser-keymap'); - if (cookieOverrideBrowserKeymap && cookieOverrideBrowserKeymap === "true") { - overrideBrowserKeymap.prop('checked', true); + }, + 'Cmd-Left': 'goLineLeftSmart', + 'Cmd-Right': 'goLineRight', + 'Ctrl-C': function (cm) { + if (!isMac && cm.getOption('keyMap').substr(0, 3) === 'vim') { + document.execCommand('copy') } else { - overrideBrowserKeymap.prop('checked', false); + return CodeMirror.Pass } - this.setOverrideBrowserKeymap(); - - overrideBrowserKeymap.change(() => { - this.setOverrideBrowserKeymap(); - }); + }, + 'Ctrl-*': cm => { + utils.wrapTextWith(this.editor, cm, '*') + }, + 'Shift-Ctrl-8': cm => { + utils.wrapTextWith(this.editor, cm, '*') + }, + 'Ctrl-_': cm => { + utils.wrapTextWith(this.editor, cm, '_') + }, + 'Shift-Ctrl--': cm => { + utils.wrapTextWith(this.editor, cm, '_') + }, + 'Ctrl-~': cm => { + utils.wrapTextWith(this.editor, cm, '~') + }, + 'Shift-Ctrl-`': cm => { + utils.wrapTextWith(this.editor, cm, '~') + }, + 'Ctrl-^': cm => { + utils.wrapTextWith(this.editor, cm, '^') + }, + 'Shift-Ctrl-6': cm => { + utils.wrapTextWith(this.editor, cm, '^') + }, + 'Ctrl-+': cm => { + utils.wrapTextWith(this.editor, cm, '+') + }, + 'Shift-Ctrl-=': cm => { + utils.wrapTextWith(this.editor, cm, '+') + }, + 'Ctrl-=': cm => { + utils.wrapTextWith(this.editor, cm, '=') + }, + 'Shift-Ctrl-Backspace': cm => { + utils.wrapTextWith(this.editor, cm, 'Backspace') + } } - init(textit) { - this.editor = CodeMirror.fromTextArea(textit, { - mode: defaultEditorMode, - backdrop: defaultEditorMode, - keyMap: "sublime", - viewportMargin: viewportMargin, - styleActiveLine: true, - lineNumbers: true, - lineWrapping: true, - showCursorWhenSelecting: true, - highlightSelectionMatches: true, - indentUnit: 4, - continueComments: "Enter", - theme: "one-dark", - inputStyle: "textarea", - matchBrackets: true, - autoCloseBrackets: true, - matchTags: { - bothTags: true - }, - autoCloseTags: true, - foldGutter: true, - gutters: ["CodeMirror-linenumbers", "authorship-gutters", "CodeMirror-foldgutter"], - extraKeys: this.defaultExtraKeys, - flattenSpans: true, - addModeClass: true, - readOnly: true, - autoRefresh: true, - otherCursors: true, - placeholder: "← Start by entering a title here\n===\nVisit /features if you don't know what to do.\nHappy hacking :)" - }); + this.jumpToAddressBarKeymapValue = null + } - this.getStatusBarTemplate(); + getStatusBarTemplate (callback) { + $.get(window.serverurl + '/views/statusbar.html', template => { + this.statusBarTemplate = template + if (callback) callback() + }) + } - return this.editor; + addStatusBar () { + if (!this.statusBarTemplate) { + this.getStatusBarTemplate(this.addStatusBar) + return + } + this.statusBar = $(this.statusBarTemplate) + this.statusCursor = this.statusBar.find('.status-cursor') + this.statusFile = this.statusBar.find('.status-file') + this.statusIndicators = this.statusBar.find('.status-indicators') + this.statusIndent = this.statusBar.find('.status-indent') + this.statusKeymap = this.statusBar.find('.status-keymap') + this.statusLength = this.statusBar.find('.status-length') + this.statusTheme = this.statusBar.find('.status-theme') + this.statusSpellcheck = this.statusBar.find('.status-spellcheck') + this.statusPreferences = this.statusBar.find('.status-preferences') + this.statusPanel = this.editor.addPanel(this.statusBar[0], { + position: 'bottom' + }) + + this.setIndent() + this.setKeymap() + this.setTheme() + this.setSpellcheck() + this.setPreferences() + } + + setIndent () { + var cookieIndentType = Cookies.get('indent_type') + var cookieTabSize = parseInt(Cookies.get('tab_size')) + var cookieSpaceUnits = parseInt(Cookies.get('space_units')) + if (cookieIndentType) { + if (cookieIndentType === 'tab') { + this.editor.setOption('indentWithTabs', true) + if (cookieTabSize) { + this.editor.setOption('indentUnit', cookieTabSize) + } + } else if (cookieIndentType === 'space') { + this.editor.setOption('indentWithTabs', false) + if (cookieSpaceUnits) { + this.editor.setOption('indentUnit', cookieSpaceUnits) + } + } + } + if (cookieTabSize) { + this.editor.setOption('tabSize', cookieTabSize) } - getEditor() { - return this.editor; + var type = this.statusIndicators.find('.indent-type') + var widthLabel = this.statusIndicators.find('.indent-width-label') + var widthInput = this.statusIndicators.find('.indent-width-input') + + const setType = () => { + if (this.editor.getOption('indentWithTabs')) { + Cookies.set('indent_type', 'tab', { + expires: 365 + }) + type.text('Tab Size:') + } else { + Cookies.set('indent_type', 'space', { + expires: 365 + }) + type.text('Spaces:') + } } + setType() + + const setUnit = () => { + var unit = this.editor.getOption('indentUnit') + if (this.editor.getOption('indentWithTabs')) { + Cookies.set('tab_size', unit, { + expires: 365 + }) + } else { + Cookies.set('space_units', unit, { + expires: 365 + }) + } + widthLabel.text(unit) + } + setUnit() + + type.click(() => { + if (this.editor.getOption('indentWithTabs')) { + this.editor.setOption('indentWithTabs', false) + cookieSpaceUnits = parseInt(Cookies.get('space_units')) + if (cookieSpaceUnits) { + this.editor.setOption('indentUnit', cookieSpaceUnits) + } + } else { + this.editor.setOption('indentWithTabs', true) + cookieTabSize = parseInt(Cookies.get('tab_size')) + if (cookieTabSize) { + this.editor.setOption('indentUnit', cookieTabSize) + this.editor.setOption('tabSize', cookieTabSize) + } + } + setType() + setUnit() + }) + widthLabel.click(() => { + if (widthLabel.is(':visible')) { + widthLabel.addClass('hidden') + widthInput.removeClass('hidden') + widthInput.val(this.editor.getOption('indentUnit')) + widthInput.select() + } else { + widthLabel.removeClass('hidden') + widthInput.addClass('hidden') + } + }) + widthInput.on('change', () => { + var val = parseInt(widthInput.val()) + if (!val) val = this.editor.getOption('indentUnit') + if (val < 1) val = 1 + else if (val > 10) val = 10 + + if (this.editor.getOption('indentWithTabs')) { + this.editor.setOption('tabSize', val) + } + this.editor.setOption('indentUnit', val) + setUnit() + }) + widthInput.on('blur', function () { + widthLabel.removeClass('hidden') + widthInput.addClass('hidden') + }) + } + + setKeymap () { + var cookieKeymap = Cookies.get('keymap') + if (cookieKeymap) { + this.editor.setOption('keyMap', cookieKeymap) + } + + var label = this.statusIndicators.find('.ui-keymap-label') + var sublime = this.statusIndicators.find('.ui-keymap-sublime') + var emacs = this.statusIndicators.find('.ui-keymap-emacs') + var vim = this.statusIndicators.find('.ui-keymap-vim') + + const setKeymapLabel = () => { + var keymap = this.editor.getOption('keyMap') + Cookies.set('keymap', keymap, { + expires: 365 + }) + label.text(keymap) + this.restoreOverrideEditorKeymap() + this.setOverrideBrowserKeymap() + } + setKeymapLabel() + + sublime.click(() => { + this.editor.setOption('keyMap', 'sublime') + setKeymapLabel() + }) + emacs.click(() => { + this.editor.setOption('keyMap', 'emacs') + setKeymapLabel() + }) + vim.click(() => { + this.editor.setOption('keyMap', 'vim') + setKeymapLabel() + }) + } + + setTheme () { + var cookieTheme = Cookies.get('theme') + if (cookieTheme) { + this.editor.setOption('theme', cookieTheme) + } + + var themeToggle = this.statusTheme.find('.ui-theme-toggle') + + const checkTheme = () => { + var theme = this.editor.getOption('theme') + if (theme === 'one-dark') { + themeToggle.removeClass('active') + } else { + themeToggle.addClass('active') + } + } + + themeToggle.click(() => { + var theme = this.editor.getOption('theme') + if (theme === 'one-dark') { + theme = 'default' + } else { + theme = 'one-dark' + } + this.editor.setOption('theme', theme) + Cookies.set('theme', theme, { + expires: 365 + }) + + checkTheme() + }) + + checkTheme() + } + + setSpellcheck () { + var cookieSpellcheck = Cookies.get('spellcheck') + if (cookieSpellcheck) { + var mode = null + if (cookieSpellcheck === 'true' || cookieSpellcheck === true) { + mode = 'spell-checker' + } else { + mode = defaultEditorMode + } + if (mode && mode !== this.editor.getOption('mode')) { + this.editor.setOption('mode', mode) + } + } + + var spellcheckToggle = this.statusSpellcheck.find('.ui-spellcheck-toggle') + + const checkSpellcheck = () => { + var mode = this.editor.getOption('mode') + if (mode === defaultEditorMode) { + spellcheckToggle.removeClass('active') + } else { + spellcheckToggle.addClass('active') + } + } + + spellcheckToggle.click(() => { + var mode = this.editor.getOption('mode') + if (mode === defaultEditorMode) { + mode = 'spell-checker' + } else { + mode = defaultEditorMode + } + if (mode && mode !== this.editor.getOption('mode')) { + this.editor.setOption('mode', mode) + } + Cookies.set('spellcheck', mode === 'spell-checker', { + expires: 365 + }) + + checkSpellcheck() + }) + + checkSpellcheck() + + // workaround spellcheck might not activate beacuse the ajax loading + if (window.num_loaded < 2) { + var spellcheckTimer = setInterval( + () => { + if (window.num_loaded >= 2) { + if (this.editor.getOption('mode') === 'spell-checker') { + this.editor.setOption('mode', 'spell-checker') + } + clearInterval(spellcheckTimer) + } + }, + 100, + ) + } + } + + resetEditorKeymapToBrowserKeymap () { + var keymap = this.editor.getOption('keyMap') + if (!this.jumpToAddressBarKeymapValue) { + this.jumpToAddressBarKeymapValue = CodeMirror.keyMap[keymap][jumpToAddressBarKeymapName] + delete CodeMirror.keyMap[keymap][jumpToAddressBarKeymapName] + } + } + + restoreOverrideEditorKeymap () { + var keymap = this.editor.getOption('keyMap') + if (this.jumpToAddressBarKeymapValue) { + CodeMirror.keyMap[keymap][jumpToAddressBarKeymapName] = this.jumpToAddressBarKeymapValue + this.jumpToAddressBarKeymapValue = null + } + } + setOverrideBrowserKeymap () { + var overrideBrowserKeymap = $( + '.ui-preferences-override-browser-keymap label > input[type="checkbox"]', + ) + if (overrideBrowserKeymap.is(':checked')) { + Cookies.set('preferences-override-browser-keymap', true, { + expires: 365 + }) + this.restoreOverrideEditorKeymap() + } else { + Cookies.remove('preferences-override-browser-keymap') + this.resetEditorKeymapToBrowserKeymap() + } + } + + setPreferences () { + var overrideBrowserKeymap = $( + '.ui-preferences-override-browser-keymap label > input[type="checkbox"]', + ) + var cookieOverrideBrowserKeymap = Cookies.get( + 'preferences-override-browser-keymap', + ) + if (cookieOverrideBrowserKeymap && cookieOverrideBrowserKeymap === 'true') { + overrideBrowserKeymap.prop('checked', true) + } else { + overrideBrowserKeymap.prop('checked', false) + } + this.setOverrideBrowserKeymap() + + overrideBrowserKeymap.change(() => { + this.setOverrideBrowserKeymap() + }) + } + + init (textit) { + this.editor = CodeMirror.fromTextArea(textit, { + mode: defaultEditorMode, + backdrop: defaultEditorMode, + keyMap: 'sublime', + viewportMargin: viewportMargin, + styleActiveLine: true, + lineNumbers: true, + lineWrapping: true, + showCursorWhenSelecting: true, + highlightSelectionMatches: true, + indentUnit: 4, + continueComments: 'Enter', + theme: 'one-dark', + inputStyle: 'textarea', + matchBrackets: true, + autoCloseBrackets: true, + matchTags: { + bothTags: true + }, + autoCloseTags: true, + foldGutter: true, + gutters: [ + 'CodeMirror-linenumbers', + 'authorship-gutters', + 'CodeMirror-foldgutter' + ], + extraKeys: this.defaultExtraKeys, + flattenSpans: true, + addModeClass: true, + readOnly: true, + autoRefresh: true, + otherCursors: true, + placeholder: "← Start by entering a title here\n===\nVisit /features if you don't know what to do.\nHappy hacking :)" + }) + + this.getStatusBarTemplate() + + return this.editor + } + + getEditor () { + return this.editor + } } diff --git a/public/js/lib/editor/ui-elements.js b/public/js/lib/editor/ui-elements.js index d06e6402..0d330d77 100644 --- a/public/js/lib/editor/ui-elements.js +++ b/public/js/lib/editor/ui-elements.js @@ -3,82 +3,84 @@ */ export const getUIElements = () => ({ - spinner: $(".ui-spinner"), - content: $(".ui-content"), - toolbar: { - shortStatus: $(".ui-short-status"), - status: $(".ui-status"), - new: $(".ui-new"), - publish: $(".ui-publish"), - extra: { - revision: $(".ui-extra-revision"), - slide: $(".ui-extra-slide") - }, - download: { - markdown: $(".ui-download-markdown"), - html: $(".ui-download-html"), - rawhtml: $(".ui-download-raw-html"), - pdf: $(".ui-download-pdf-beta"), - }, - export: { - dropbox: $(".ui-save-dropbox"), - googleDrive: $(".ui-save-google-drive"), - gist: $(".ui-save-gist"), - snippet: $(".ui-save-snippet") - }, - import: { - dropbox: $(".ui-import-dropbox"), - googleDrive: $(".ui-import-google-drive"), - gist: $(".ui-import-gist"), - snippet: $(".ui-import-snippet"), - clipboard: $(".ui-import-clipboard") - }, - mode: $(".ui-mode"), - edit: $(".ui-edit"), - view: $(".ui-view"), - both: $(".ui-both"), - uploadImage: $(".ui-upload-image") + spinner: $('.ui-spinner'), + content: $('.ui-content'), + toolbar: { + shortStatus: $('.ui-short-status'), + status: $('.ui-status'), + new: $('.ui-new'), + publish: $('.ui-publish'), + extra: { + revision: $('.ui-extra-revision'), + slide: $('.ui-extra-slide') }, - infobar: { - lastchange: $(".ui-lastchange"), - lastchangeuser: $(".ui-lastchangeuser"), - nolastchangeuser: $(".ui-no-lastchangeuser"), - permission: { - permission: $(".ui-permission"), - label: $(".ui-permission-label"), - freely: $(".ui-permission-freely"), - editable: $(".ui-permission-editable"), - locked: $(".ui-permission-locked"), - private: $(".ui-permission-private"), - limited: $(".ui-permission-limited"), - protected: $(".ui-permission-protected") - }, - delete: $(".ui-delete-note") + download: { + markdown: $('.ui-download-markdown'), + html: $('.ui-download-html'), + rawhtml: $('.ui-download-raw-html'), + pdf: $('.ui-download-pdf-beta') }, - toc: { - toc: $('.ui-toc'), - affix: $('.ui-affix-toc'), - label: $('.ui-toc-label'), - dropdown: $('.ui-toc-dropdown') + export: { + dropbox: $('.ui-save-dropbox'), + googleDrive: $('.ui-save-google-drive'), + gist: $('.ui-save-gist'), + snippet: $('.ui-save-snippet') }, - area: { - edit: $(".ui-edit-area"), - view: $(".ui-view-area"), - codemirror: $(".ui-edit-area .CodeMirror"), - codemirrorScroll: $(".ui-edit-area .CodeMirror .CodeMirror-scroll"), - codemirrorSizer: $(".ui-edit-area .CodeMirror .CodeMirror-sizer"), - codemirrorSizerInner: $(".ui-edit-area .CodeMirror .CodeMirror-sizer > div"), - markdown: $(".ui-view-area .markdown-body"), - resize: { - handle: $('.ui-resizable-handle'), - syncToggle: $('.ui-sync-toggle') - } + import: { + dropbox: $('.ui-import-dropbox'), + googleDrive: $('.ui-import-google-drive'), + gist: $('.ui-import-gist'), + snippet: $('.ui-import-snippet'), + clipboard: $('.ui-import-clipboard') }, - modal: { - snippetImportProjects: $("#snippetImportModalProjects"), - snippetImportSnippets: $("#snippetImportModalSnippets"), - revision: $("#revisionModal") + mode: $('.ui-mode'), + edit: $('.ui-edit'), + view: $('.ui-view'), + both: $('.ui-both'), + uploadImage: $('.ui-upload-image') + }, + infobar: { + lastchange: $('.ui-lastchange'), + lastchangeuser: $('.ui-lastchangeuser'), + nolastchangeuser: $('.ui-no-lastchangeuser'), + permission: { + permission: $('.ui-permission'), + label: $('.ui-permission-label'), + freely: $('.ui-permission-freely'), + editable: $('.ui-permission-editable'), + locked: $('.ui-permission-locked'), + private: $('.ui-permission-private'), + limited: $('.ui-permission-limited'), + protected: $('.ui-permission-protected') + }, + delete: $('.ui-delete-note') + }, + toc: { + toc: $('.ui-toc'), + affix: $('.ui-affix-toc'), + label: $('.ui-toc-label'), + dropdown: $('.ui-toc-dropdown') + }, + area: { + edit: $('.ui-edit-area'), + view: $('.ui-view-area'), + codemirror: $('.ui-edit-area .CodeMirror'), + codemirrorScroll: $('.ui-edit-area .CodeMirror .CodeMirror-scroll'), + codemirrorSizer: $('.ui-edit-area .CodeMirror .CodeMirror-sizer'), + codemirrorSizerInner: $( + '.ui-edit-area .CodeMirror .CodeMirror-sizer > div', + ), + markdown: $('.ui-view-area .markdown-body'), + resize: { + handle: $('.ui-resizable-handle'), + syncToggle: $('.ui-sync-toggle') } + }, + modal: { + snippetImportProjects: $('#snippetImportModalProjects'), + snippetImportSnippets: $('#snippetImportModalSnippets'), + revision: $('#revisionModal') + } }) -export default getUIElements; +export default getUIElements diff --git a/public/js/lib/editor/utils.js b/public/js/lib/editor/utils.js index 120d3646..3702a166 100644 --- a/public/js/lib/editor/utils.js +++ b/public/js/lib/editor/utils.js @@ -1,46 +1,48 @@ -const wrapSymbols = ['*', '_', '~', '^', '+', '=']; -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]; - if (!range.empty()) { - var from = range.from(), 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); - if (anchorIndex > headIndex) { - _ranges[i].anchor.ch--; - } else { - _ranges[i].head.ch--; - } - cm.setSelections(_ranges); - } else { - var preEndPos = { - line: to.line, - ch: to.ch + 1 - }; - var preText = cm.getRange(to, preEndPos); - var preIndex = wrapSymbols.indexOf(preText); - var postEndPos = { - line: from.line, - ch: from.ch - 1 - }; - var postText = cm.getRange(postEndPos, from); - var 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'); - cm.replaceRange("", postEndPos, from, '+delete'); - } - } - } +const wrapSymbols = ['*', '_', '~', '^', '+', '='] +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] + 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) + if (anchorIndex > headIndex) { + _ranges[i].anchor.ch-- + } else { + _ranges[i].head.ch-- + } + cm.setSelections(_ranges) + } else { + var preEndPos = { + line: to.line, + ch: to.ch + 1 + } + var preText = cm.getRange(to, preEndPos) + var preIndex = wrapSymbols.indexOf(preText) + var postEndPos = { + line: from.line, + ch: from.ch - 1 + } + var postText = cm.getRange(postEndPos, from) + var 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') + cm.replaceRange('', postEndPos, from, '+delete') + } } + } } + } } From b4424419c0eb2e07c6783d6853346853957e78a8 Mon Sep 17 00:00:00 2001 From: Yukai Huang Date: Mon, 13 Mar 2017 21:42:09 +0800 Subject: [PATCH 4/8] Add standarjs globals --- package.json | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/package.json b/package.json index 05789321..01c867df 100644 --- a/package.json +++ b/package.json @@ -170,6 +170,26 @@ "webpack-parallel-uglify-plugin": "^0.2.0" }, "standard": { + "globals": [ + "$", + "CodeMirror", + "Cookies", + "moment", "editor", + "ui", + "Spinner", + "modeType", + "Idle", + "serverurl", + "key", + "gapi", + "Dropbox", + "FilePicker", + "ot", + "MediaUploader", + "hex2rgb", + "num_loaded", + "Visibility" + ], "ignore": [ "lib/ot", "public/vendor" From e97b609d91adab015148184935369216a199038e Mon Sep 17 00:00:00 2001 From: Yukai Huang Date: Mon, 13 Mar 2017 21:42:33 +0800 Subject: [PATCH 5/8] Update new editorconfig indent favor --- .editorconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.editorconfig b/.editorconfig index 619c178d..e271be73 100644 --- a/.editorconfig +++ b/.editorconfig @@ -3,7 +3,7 @@ root = true # Tab indentation [*] indent_style = space -indent_size = 4 +indent_size = 2 trim_trailing_whitespace = true insert_final_newline = true From cc30d370f7cf662eb4ea21fb93148a30bd8764d2 Mon Sep 17 00:00:00 2001 From: Yukai Huang Date: Mon, 13 Mar 2017 22:00:20 +0800 Subject: [PATCH 6/8] Fix variable exporting error --- public/js/index.js | 3 +++ public/js/lib/editor/index.js | 4 +--- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/public/js/index.js b/public/js/index.js index 7764fb58..53dd647c 100644 --- a/public/js/index.js +++ b/public/js/index.js @@ -83,6 +83,7 @@ import getUIElements from './lib/editor/ui-elements' var defaultTextHeight = 20 var viewportMargin = 20 +var defaultEditorMode = 'gfm' var idleTime = 300000 // 5 mins var updateViewDebounce = 100 @@ -363,7 +364,9 @@ function updateStatusBar () { } // initalize ui reference +// TODO: fix ui exporting const ui = getUIElements() +window.ui = ui // page actions var opts = { diff --git a/public/js/lib/editor/index.js b/public/js/lib/editor/index.js index a97fd976..6ae40d82 100644 --- a/public/js/lib/editor/index.js +++ b/public/js/lib/editor/index.js @@ -10,7 +10,7 @@ const jumpToAddressBarKeymapName = isMac ? 'Cmd-L' : 'Ctrl-L' export default class Editor { constructor () { this.editor = null - + this.jumpToAddressBarKeymapValue = null this.defaultExtraKeys = { F10: function (cm) { cm.setOption('fullScreen', !cm.getOption('fullScreen')) @@ -116,8 +116,6 @@ export default class Editor { utils.wrapTextWith(this.editor, cm, 'Backspace') } } - - this.jumpToAddressBarKeymapValue = null } getStatusBarTemplate (callback) { From 24f1413654947a00ed81c5480164eca25b531e51 Mon Sep 17 00:00:00 2001 From: Yukai Huang Date: Mon, 13 Mar 2017 22:00:29 +0800 Subject: [PATCH 7/8] Add inlineAttachment to global --- package.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index 01c867df..d1b4c032 100644 --- a/package.json +++ b/package.json @@ -188,7 +188,8 @@ "MediaUploader", "hex2rgb", "num_loaded", - "Visibility" + "Visibility", + "inlineAttachment" ], "ignore": [ "lib/ot", From 16d80edc653dc02407cd570adfc485300ec9a66b Mon Sep 17 00:00:00 2001 From: Max Wu Date: Tue, 14 Mar 2017 23:30:35 +0800 Subject: [PATCH 8/8] Fix badges and links in README.md --- README.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index a93c5732..ad3c2afd 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ HackMD === -[![Standard - JavaScript Style Guide](https://cdn.rawgit.com/feross/standard/master/badge.svg)](https://github.com/feross/standard) +[![Standard - JavaScript Style Guide][standardjs-image]][standardjs-url] [![Join the chat at https://gitter.im/hackmdio/hackmd][gitter-image]][gitter-url] [![build status][travis-image]][travis-url] @@ -227,7 +227,10 @@ Additionally, now can show other clients' selections. See more at [http://operational-transformation.github.io/](http://operational-transformation.github.io/) **License under MIT.** + [gitter-image]: https://badges.gitter.im/Join%20Chat.svg [gitter-url]: https://gitter.im/hackmdio/hackmd?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge [travis-image]: https://travis-ci.org/hackmdio/hackmd.svg?branch=master [travis-url]: https://travis-ci.org/hackmdio/hackmd +[standardjs-image]: https://cdn.rawgit.com/feross/standard/master/badge.svg +[standardjs-url]: https://github.com/feross/standard