/* jquery and jquery plugins */
require('../vendor/showup/showup');
require('../css/index.css');
require('../css/extra.css');
require('../css/slide-preview.css');
require('../css/site.css');
require('highlight.js/styles/github-gist.css');
var toMarkdown = require('to-markdown');
var saveAs = require('file-saver').saveAs;
var randomColor = require('randomcolor');
var _ = require("lodash");
var List = require('list.js');
var common = require('./common.js');
var urlpath = common.urlpath;
var noteid = common.noteid;
var debug = common.debug;
var version = common.version;
var GOOGLE_API_KEY = common.GOOGLE_API_KEY;
var GOOGLE_CLIENT_ID = common.GOOGLE_CLIENT_ID;
var DROPBOX_APP_KEY = common.DROPBOX_APP_KEY;
var noteurl = common.noteurl;
var checkLoginStateChanged = common.checkLoginStateChanged;
var loginStateChangeEvent = common.loginStateChangeEvent;
var extra = require('./extra');
var md = extra.md;
var updateLastChange = extra.updateLastChange;
var postProcess = extra.postProcess;
var finishView = extra.finishView;
var autoLinkify = extra.autoLinkify;
var generateToc = extra.generateToc;
var smoothHashScroll = extra.smoothHashScroll;
var deduplicatedHeaderId = extra.deduplicatedHeaderId;
var renderTOC = extra.renderTOC;
var renderTitle = extra.renderTitle;
var renderFilename = extra.renderFilename;
var renderTags = extra.renderTags;
var isValidURL = extra.isValidURL;
var scrollToHash = extra.scrollToHash;
var updateLastChangeUser = extra.updateLastChangeUser;
var updateOwner = extra.updateOwner;
var parseMeta = extra.parseMeta;
var exportToHTML = extra.exportToHTML;
var exportToRawHTML = extra.exportToRawHTML;
import {
clearMap,
setupSyncAreas,
syncScrollToEdit,
syncScrollToView
} from './syncscroll';
var historyModule = require('./history');
var writeHistory = historyModule.writeHistory;
var deleteServerHistory = historyModule.deleteServerHistory;
var getHistory = historyModule.getHistory;
var saveHistory = historyModule.saveHistory;
var removeHistory = historyModule.removeHistory;
var renderer = require('./render');
var preventXSS = renderer.preventXSS;
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;
var cursorMenuThrottle = 50;
var cursorActivityDebounce = 50;
var cursorAnimatePeriod = 100;
var supportContainers = ['success', 'info', 'warning', 'danger'];
var supportCodeModes = ['javascript', 'typescript', 'jsx', 'htmlmixed', 'htmlembedded', 'css', 'xml', 'clike', 'clojure', 'ruby', 'python', 'shell', 'php', 'sql', 'haskell', 'coffeescript', 'yaml', 'pug', 'lua', 'cmake', 'nginx', 'perl', 'sass', 'r', 'dockerfile', 'tiddlywiki', 'mediawiki', 'go'];
var supportCharts = ['sequence', 'flow', 'graphviz', 'mermaid'];
var supportHeaders = [
{
text: '# h1',
search: '#'
},
{
text: '## h2',
search: '##'
},
{
text: '### h3',
search: '###'
},
{
text: '#### h4',
search: '####'
},
{
text: '##### h5',
search: '#####'
},
{
text: '###### h6',
search: '######'
},
{
text: '###### tags: `example`',
search: '###### tags:'
}
];
var supportReferrals = [
{
text: '[reference link]',
search: '[]'
},
{
text: '[reference]: https:// "title"',
search: '[]:'
},
{
text: '[^footnote link]',
search: '[^]'
},
{
text: '[^footnote reference]: https:// "title"',
search: '[^]:'
},
{
text: '^[inline footnote]',
search: '^[]'
},
{
text: '[link text][reference]',
search: '[][]'
},
{
text: '[link text](https:// "title")',
search: '[]()'
},
{
text: '![image alt][reference]',
search: '![][]'
},
{
text: '![image alt](https:// "title")',
search: '![]()'
},
{
text: '![image alt](https:// "title" =WidthxHeight)',
search: '![]()'
},
{
text: '[TOC]',
search: '[]'
}
];
var supportExternals = [
{
text: '{%youtube youtubeid %}',
search: 'youtube'
},
{
text: '{%vimeo vimeoid %}',
search: 'vimeo'
},
{
text: '{%gist gistid %}',
search: 'gist'
},
{
text: '{%slideshare slideshareid %}',
search: 'slideshare'
},
{
text: '{%speakerdeck speakerdeckid %}',
search: 'speakerdeck'
},
{
text: '{%pdf pdfurl %}',
search: 'pdf'
}
];
var supportExtraTags = [
{
text: '[name tag]',
search: '[]',
command: function () {
return '[name=' + personalInfo.name + ']';
},
},
{
text: '[time tag]',
search: '[]',
command: function () {
return '[time=' + moment().format('llll') + ']';
},
},
{
text: '[my color tag]',
search: '[]',
command: function () {
return '[color=' + personalInfo.color + ']';
}
},
{
text: '[random color tag]',
search: '[]',
command: function () {
var color = randomColor();
return '[color=' + color + ']';
}
}
];
window.modeType = {
edit: {
name: "edit"
},
view: {
name: "view"
},
both: {
name: "both"
}
};
var statusType = {
connected: {
msg: "CONNECTED",
label: "label-warning",
fa: "fa-wifi"
},
online: {
msg: "ONLINE",
label: "label-primary",
fa: "fa-users"
},
offline: {
msg: "OFFLINE",
label: "label-danger",
fa: "fa-plug"
}
};
var defaultMode = modeType.view;
//global vars
window.loaded = false;
window.needRefresh = false;
window.isDirty = false;
window.editShown = false;
window.visibleXS = false;
window.visibleSM = false;
window.visibleMD = false;
window.visibleLG = false;
window.isTouchDevice = 'ontouchstart' in document.documentElement;
window.currentMode = defaultMode;
window.currentStatus = statusType.offline;
window.lastInfo = {
needRestore: false,
cursor: null,
scroll: null,
edit: {
scroll: {
left: null,
top: null
},
cursor: {
line: null,
ch: null
}
},
view: {
scroll: {
left: null,
top: null
}
},
history: null
};
window.personalInfo = {};
window.onlineUsers = [];
window.fileTypes = {
"pl": "perl",
"cgi": "perl",
"js": "javascript",
"php": "php",
"sh": "bash",
"rb": "ruby",
"html": "html",
"py": "python"
};
//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 :)"
});
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;
var cursor = editor.getCursor();
var cursorText = 'Line ' + (cursor.line + 1) + ', Columns ' + (cursor.ch + 1);
if (selection) {
var anchor = selection.anchor;
var head = selection.head;
var start = head.line <= anchor.line ? head : anchor;
var end = head.line >= anchor.line ? head : anchor;
var selectionText = ' — Selected ';
var selectionCharCount = Math.abs(head.ch - anchor.ch);
// borrow from brackets EditorStatusBar.js
if (start.line !== end.line) {
var lines = end.line - start.line + 1;
if (end.ch === 0) {
lines--;
}
selectionText += lines + ' lines';
} else if (selectionCharCount > 0)
selectionText += selectionCharCount + ' columns';
if (start.line !== end.line || selectionCharCount > 0)
cursorText += selectionText;
}
statusCursor.text(cursorText);
var fileText = ' — ' + editor.lineCount() + ' Lines';
statusFile.text(fileText);
var docLength = editor.getValue().length;
statusLength.text('Length ' + docLength);
if (docLength > (docmaxlength * 0.95)) {
statusLength.css('color', 'red');
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.');
} else {
statusLength.css('color', 'white');
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")
},
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")
}
};
//page actions
var opts = {
lines: 11, // The number of lines to draw
length: 20, // The length of each line
width: 2, // The line thickness
radius: 30, // The radius of the inner circle
corners: 0, // Corner roundness (0..1)
rotate: 0, // The rotation offset
direction: 1, // 1: clockwise, -1: counterclockwise
color: '#000', // #rgb or #rrggbb or array of colors
speed: 1.1, // Rounds per second
trail: 60, // Afterglow percentage
shadow: false, // Whether to render a shadow
hwaccel: true, // Whether to use hardware acceleration
className: 'spinner', // The CSS class to assign to the spinner
zIndex: 2e9, // The z-index (defaults to 2000000000)
top: '50%', // Top position relative to parent
left: '50%' // Left position relative to parent
};
var spinner = new Spinner(opts).spin(ui.spinner[0]);
//idle
var idle = new Idle({
onAway: function () {
idle.isAway = true;
emitUserStatus();
updateOnlineStatus();
},
onAwayBack: function () {
idle.isAway = false;
emitUserStatus();
updateOnlineStatus();
setHaveUnreadChanges(false);
updateTitleReminder();
},
awayTimeout: idleTime
});
ui.area.codemirror.on('touchstart', function () {
idle.onActive();
});
var haveUnreadChanges = false;
function setHaveUnreadChanges(bool) {
if (!loaded) return;
if (bool && (idle.isAway || Visibility.hidden())) {
haveUnreadChanges = true;
} else if (!bool && !idle.isAway && !Visibility.hidden()) {
haveUnreadChanges = false;
}
}
function updateTitleReminder() {
if (!loaded) return;
if (haveUnreadChanges) {
document.title = '• ' + renderTitle(ui.area.markdown);
} else {
document.title = renderTitle(ui.area.markdown);
}
}
function setRefreshModal(status) {
$('#refreshModal').modal('show');
$('#refreshModal').find('.modal-body > div').hide();
$('#refreshModal').find('.' + status).show();
}
function setNeedRefresh() {
needRefresh = true;
editor.setOption('readOnly', true);
socket.disconnect();
showStatus(statusType.offline);
}
loginStateChangeEvent = function () {
setRefreshModal('user-state-changed');
setNeedRefresh();
};
//visibility
var wasFocus = false;
Visibility.change(function (e, state) {
var hidden = Visibility.hidden();
if (hidden) {
if (editorHasFocus()) {
wasFocus = true;
editor.getInputField().blur();
}
} else {
if (wasFocus) {
if (!visibleXS) {
editor.focus();
editor.refresh();
}
wasFocus = false;
}
setHaveUnreadChanges(false);
}
updateTitleReminder();
});
//when page ready
$(document).ready(function () {
idle.checkAway();
checkResponsive();
//if in smaller screen, we don't need advanced scrollbar
var scrollbarStyle;
if (visibleXS) {
scrollbarStyle = 'native';
} else {
scrollbarStyle = 'overlay';
}
if (scrollbarStyle != editor.getOption('scrollbarStyle')) {
editor.setOption('scrollbarStyle', scrollbarStyle);
clearMap();
}
checkEditorStyle();
/* we need this only on touch devices */
if (isTouchDevice) {
/* cache dom references */
var $body = jQuery('body');
/* bind events */
$(document)
.on('focus', 'textarea, input', function () {
$body.addClass('fixfixed');
})
.on('blur', 'textarea, input', function () {
$body.removeClass('fixfixed');
});
}
//showup
$().showUp('.navbar', {
upClass: 'navbar-hide',
downClass: 'navbar-show'
});
//tooltip
$('[data-toggle="tooltip"]').tooltip();
// shortcuts
// allow on all tags
key.filter = function (e) { return true; };
key('ctrl+alt+e', function (e) {
changeMode(modeType.edit);
});
key('ctrl+alt+v', function (e) {
changeMode(modeType.view);
});
key('ctrl+alt+b', function (e) {
changeMode(modeType.both);
});
// toggle-dropdown
$(document).on('click', '.toggle-dropdown .dropdown-menu', function (e) {
e.stopPropagation();
});
});
//when page resize
$(window).resize(function () {
checkLayout();
checkEditorStyle();
checkTocStyle();
checkCursorMenu();
windowResize();
});
//when page unload
$(window).on('unload', function () {
//updateHistoryInner();
});
$(window).on('error', function () {
//setNeedRefresh();
});
setupSyncAreas(ui.area.codemirrorScroll, ui.area.view, ui.area.markdown);
function autoSyncscroll() {
if (editorHasFocus()) {
syncScrollToView();
} else {
syncScrollToEdit();
}
}
var windowResizeDebounce = 200;
var windowResize = _.debounce(windowResizeInner, windowResizeDebounce);
function windowResizeInner(callback) {
checkLayout();
checkResponsive();
checkEditorStyle();
checkTocStyle();
checkCursorMenu();
//refresh editor
if (loaded) {
if (editor.getOption('scrollbarStyle') === 'native') {
setTimeout(function () {
clearMap();
autoSyncscroll();
updateScrollspy();
if (callback && typeof callback === 'function')
callback();
}, 1);
} else {
// force it load all docs at once to prevent scroll knob blink
editor.setOption('viewportMargin', Infinity);
setTimeout(function () {
clearMap();
autoSyncscroll();
editor.setOption('viewportMargin', viewportMargin);
//add or update user cursors
for (var i = 0; i < onlineUsers.length; i++) {
if (onlineUsers[i].id != personalInfo.id)
buildCursor(onlineUsers[i]);
}
updateScrollspy();
if (callback && typeof callback === 'function')
callback();
}, 1);
}
}
}
function checkLayout() {
var navbarHieght = $('.navbar').outerHeight();
$('body').css('padding-top', navbarHieght + 'px');
}
function editorHasFocus() {
return $(editor.getInputField()).is(":focus");
}
//768-792px have a gap
function checkResponsive() {
visibleXS = $(".visible-xs").is(":visible");
visibleSM = $(".visible-sm").is(":visible");
visibleMD = $(".visible-md").is(":visible");
visibleLG = $(".visible-lg").is(":visible");
if (visibleXS && currentMode == modeType.both)
if (editorHasFocus())
changeMode(modeType.edit);
else
changeMode(modeType.view);
emitUserStatus();
}
var lastEditorWidth = 0;
var previousFocusOnEditor = null;
function checkEditorStyle() {
var desireHeight = statusBar ? (ui.area.edit.height() - 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) {
ui.area.codemirrorScroll.css('height', desireHeight + 'px');
ui.area.codemirrorScroll.css('min-height', '');
checkEditorScrollbar();
} else if (scrollbarStyle == 'native') {
ui.area.codemirrorScroll.css('height', '');
ui.area.codemirrorScroll.css('min-height', desireHeight + 'px');
}
// workaround editor will have wrong doc height when editor height changed
editor.setSize(null, ui.area.edit.height());
//make editor resizable
if (!ui.area.resize.handle.length) {
ui.area.edit.resizable({
handles: 'e',
maxWidth: $(window).width() * 0.7,
minWidth: $(window).width() * 0.2,
create: function (e, ui) {
$(this).parent().on('resize', function (e) {
e.stopPropagation();
});
},
start: function (e) {
editor.setOption('viewportMargin', Infinity);
},
resize: function (e) {
ui.area.resize.syncToggle.stop(true, true).show();
checkTocStyle();
},
stop: function (e) {
lastEditorWidth = ui.area.edit.width();
// workaround that scroll event bindings
preventSyncScrollToView = 2;
preventSyncScrollToEdit = true;
editor.setOption('viewportMargin', viewportMargin);
if (editorHasFocus()) {
windowResizeInner(function () {
ui.area.codemirrorScroll.scroll();
});
} else {
windowResizeInner(function () {
ui.area.view.scroll();
});
}
checkEditorScrollbar();
}
});
ui.area.resize.handle = $('.ui-resizable-handle');
}
if (!ui.area.resize.syncToggle.length) {
ui.area.resize.syncToggle = $('');
ui.area.resize.syncToggle.hover(function () {
previousFocusOnEditor = editorHasFocus();
}, function () {
previousFocusOnEditor = null;
});
ui.area.resize.syncToggle.click(function () {
syncscroll = !syncscroll;
checkSyncToggle();
});
ui.area.resize.handle.append(ui.area.resize.syncToggle);
ui.area.resize.syncToggle.hide();
ui.area.resize.handle.hover(function () {
ui.area.resize.syncToggle.stop(true, true).delay(200).fadeIn(100);
}, function () {
ui.area.resize.syncToggle.stop(true, true).delay(300).fadeOut(300);
});
}
}
function checkSyncToggle() {
if (syncscroll) {
if (previousFocusOnEditor) {
preventSyncScrollToView = false;
syncScrollToView();
} else {
preventSyncScrollToEdit = false;
syncScrollToEdit();
}
ui.area.resize.syncToggle.find('i').removeClass('fa-unlink').addClass('fa-link');
} else {
ui.area.resize.syncToggle.find('i').removeClass('fa-link').addClass('fa-unlink');
}
}
var checkEditorScrollbar = _.debounce(function () {
editor.operation(checkEditorScrollbarInner);
}, 50);
function checkEditorScrollbarInner() {
// workaround simple scroll bar knob
// will get wrong position when editor height changed
var scrollInfo = editor.getScrollInfo();
editor.scrollTo(null, scrollInfo.top - 1);
editor.scrollTo(null, scrollInfo.top);
}
function checkTocStyle() {
//toc right
var paddingRight = parseFloat(ui.area.markdown.css('padding-right'));
var right = ($(window).width() - (ui.area.markdown.offset().left + ui.area.markdown.outerWidth() - paddingRight));
ui.toc.toc.css('right', right + 'px');
//affix toc left
var newbool;
var rightMargin = (ui.area.markdown.parent().outerWidth() - ui.area.markdown.outerWidth()) / 2;
//for ipad or wider device
if (rightMargin >= 133) {
newbool = true;
var affixLeftMargin = (ui.toc.affix.outerWidth() - ui.toc.affix.width()) / 2;
var left = ui.area.markdown.offset().left + ui.area.markdown.outerWidth() - affixLeftMargin;
ui.toc.affix.css('left', left + 'px');
ui.toc.affix.css('width', rightMargin + 'px');
} else {
newbool = false;
}
//toc scrollspy
ui.toc.toc.removeClass('scrollspy-body, scrollspy-view');
ui.toc.affix.removeClass('scrollspy-body, scrollspy-view');
if (currentMode == modeType.both) {
ui.toc.toc.addClass('scrollspy-view');
ui.toc.affix.addClass('scrollspy-view');
} else if (currentMode != modeType.both && !newbool) {
ui.toc.toc.addClass('scrollspy-body');
ui.toc.affix.addClass('scrollspy-body');
} else {
ui.toc.toc.addClass('scrollspy-view');
ui.toc.affix.addClass('scrollspy-body');
}
if (newbool != enoughForAffixToc) {
enoughForAffixToc = newbool;
generateScrollspy();
}
}
function showStatus(type, num) {
currentStatus = type;
var shortStatus = ui.toolbar.shortStatus;
var status = ui.toolbar.status;
var label = $('');
var fa = $('');
var msg = "";
var shortMsg = "";
shortStatus.html("");
status.html("");
switch (currentStatus) {
case statusType.connected:
label.addClass(statusType.connected.label);
fa.addClass(statusType.connected.fa);
msg = statusType.connected.msg;
break;
case statusType.online:
label.addClass(statusType.online.label);
fa.addClass(statusType.online.fa);
shortMsg = num;
msg = num + " " + statusType.online.msg;
break;
case statusType.offline:
label.addClass(statusType.offline.label);
fa.addClass(statusType.offline.fa);
msg = statusType.offline.msg;
break;
}
label.append(fa);
var shortLabel = label.clone();
shortLabel.append(" " + shortMsg);
shortStatus.append(shortLabel);
label.append(" " + msg);
status.append(label);
}
function toggleMode() {
switch (currentMode) {
case modeType.edit:
changeMode(modeType.view);
break;
case modeType.view:
changeMode(modeType.edit);
break;
case modeType.both:
changeMode(modeType.view);
break;
}
}
var lastMode = null;
function changeMode(type) {
// lock navbar to prevent it hide after changeMode
lockNavbar();
saveInfo();
if (type) {
lastMode = currentMode;
currentMode = type;
}
var responsiveClass = "col-lg-6 col-md-6 col-sm-6";
var scrollClass = "ui-scrollable";
ui.area.codemirror.removeClass(scrollClass);
ui.area.edit.removeClass(responsiveClass);
ui.area.view.removeClass(scrollClass);
ui.area.view.removeClass(responsiveClass);
switch (currentMode) {
case modeType.edit:
ui.area.edit.show();
ui.area.view.hide();
if (!editShown) {
editor.refresh();
editShown = true;
}
break;
case modeType.view:
ui.area.edit.hide();
ui.area.view.show();
break;
case modeType.both:
ui.area.codemirror.addClass(scrollClass);
ui.area.edit.addClass(responsiveClass).show();
ui.area.view.addClass(scrollClass);
ui.area.view.show();
break;
}
// save mode to url
if (history.replaceState && loaded) history.replaceState(null, "", serverurl + '/' + noteid + '?' + currentMode.name);
if (currentMode == modeType.view) {
editor.getInputField().blur();
}
if (currentMode == modeType.edit || currentMode == modeType.both) {
ui.toolbar.uploadImage.fadeIn();
//add and update status bar
if (!statusBar) {
addStatusBar();
updateStatusBar();
}
//work around foldGutter might not init properly
editor.setOption('foldGutter', false);
editor.setOption('foldGutter', true);
} else {
ui.toolbar.uploadImage.fadeOut();
}
if (currentMode != modeType.edit) {
$(document.body).css('background-color', 'white');
updateView();
} else {
$(document.body).css('background-color', ui.area.codemirror.css('background-color'));
}
//check resizable editor style
if (currentMode == modeType.both) {
if (lastEditorWidth > 0)
ui.area.edit.css('width', lastEditorWidth + 'px');
else
ui.area.edit.css('width', '');
ui.area.resize.handle.show();
} else {
ui.area.edit.css('width', '');
ui.area.resize.handle.hide();
}
windowResizeInner();
restoreInfo();
if (lastMode == modeType.view && currentMode == modeType.both) {
preventSyncScrollToView = 2;
syncScrollToEdit(null, true);
}
if (lastMode == modeType.edit && currentMode == modeType.both) {
preventSyncScrollToEdit = 2;
syncScrollToView(null, true);
}
if (lastMode == modeType.both && currentMode != modeType.both) {
preventSyncScrollToView = false;
preventSyncScrollToEdit = false;
}
if (lastMode != modeType.edit && currentMode == modeType.edit) {
editor.refresh();
}
$(document.body).scrollspy('refresh');
ui.area.view.scrollspy('refresh');
ui.toolbar.both.removeClass("active");
ui.toolbar.edit.removeClass("active");
ui.toolbar.view.removeClass("active");
var modeIcon = ui.toolbar.mode.find('i');
modeIcon.removeClass('fa-pencil').removeClass('fa-eye');
if (ui.area.edit.is(":visible") && ui.area.view.is(":visible")) { //both
ui.toolbar.both.addClass("active");
modeIcon.addClass('fa-eye');
} else if (ui.area.edit.is(":visible")) { //edit
ui.toolbar.edit.addClass("active");
modeIcon.addClass('fa-eye');
} else if (ui.area.view.is(":visible")) { //view
ui.toolbar.view.addClass("active");
modeIcon.addClass('fa-pencil');
}
unlockNavbar();
}
function lockNavbar() {
$('.navbar').addClass('locked');
}
var unlockNavbar = _.debounce(function () {
$('.navbar').removeClass('locked');
}, 200);
function closestIndex(arr, closestTo) {
var closest = Math.max.apply(null, arr); //Get the highest number in arr in case it match nothing.
var index = 0;
for (var i = 0; i < arr.length; i++) { //Loop the array
if (arr[i] >= closestTo && arr[i] < closest) {
closest = arr[i]; //Check if it's higher than your number, but lower than your closest value
index = i;
}
}
return index; // return the value
}
function showMessageModal(title, header, href, text, success) {
var modal = $('.message-modal');
modal.find('.modal-title').html(title);
modal.find('.modal-body h5').html(header);
if (href)
modal.find('.modal-body a').attr('href', href).text(text);
else
modal.find('.modal-body a').removeAttr('href').text(text);
modal.find('.modal-footer button').removeClass('btn-default btn-success btn-danger')
if (success)
modal.find('.modal-footer button').addClass('btn-success');
else
modal.find('.modal-footer button').addClass('btn-danger');
modal.modal('show');
}
// check if dropbox app key is set and load scripts
if (DROPBOX_APP_KEY) {
$('