From 4b0ca55eb79e963523eb6c8197825e9e8ae904e2 Mon Sep 17 00:00:00 2001 From: Wu Cheng-Han Date: Mon, 4 May 2015 15:53:29 +0800 Subject: First commit, version 0.2.7 --- public/js/index.js | 940 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 940 insertions(+) create mode 100644 public/js/index.js (limited to 'public/js/index.js') diff --git a/public/js/index.js b/public/js/index.js new file mode 100644 index 00000000..73b4e594 --- /dev/null +++ b/public/js/index.js @@ -0,0 +1,940 @@ +//constant vars +//settings +var debug = false; +var version = '0.2.7'; +var doneTypingDelay = 400; +var finishChangeDelay = 400; +var cursorActivityDelay = 50; +var syncScrollDelay = 50; +var scrollAnimatePeriod = 100; +var cursorAnimatePeriod = 100; +var modeType = { + edit: {}, + view: {}, + 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.both; + +//global vars +var loaded = false; +var isDirty = false; +var editShown = false; +var visibleXS = false; +var visibleSM = false; +var visibleMD = false; +var visibleLG = false; +var isTouchDevice = 'ontouchstart' in document.documentElement; +var currentMode = defaultMode; +var currentStatus = statusType.offline; +var 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 +}; + +//editor settings +var editor = CodeMirror.fromTextArea(document.getElementById("textit"), { + mode: 'gfm', + viewportMargin: 20, + styleActiveLine: true, + lineNumbers: true, + lineWrapping: true, + theme: "monokai", + autofocus: true, + inputStyle: "textarea", + matchBrackets: true, + autoCloseBrackets: true, + matchTags: { + bothTags: true + }, + autoCloseTags: true, + foldGutter: true, + gutters: ["CodeMirror-linenumbers", "CodeMirror-foldgutter"], + extraKeys: { + "Enter": "newlineAndIndentContinueMarkdownList" + }, + readOnly: true +}); + +//ui vars +var ui = { + spinner: $(".ui-spinner"), + content: $(".ui-content"), + toolbar: { + shortStatus: $(".ui-short-status"), + status: $(".ui-status"), + new: $(".ui-new"), + pretty: $(".ui-pretty"), + download: { + markdown: $(".ui-download-markdown") + }, + save: { + dropbox: $(".ui-save-dropbox") + }, + import: { + dropbox: $(".ui-import-dropbox"), + clipboard: $(".ui-import-clipboard") + }, + mode: $(".ui-mode"), + edit: $(".ui-edit"), + view: $(".ui-view"), + both: $(".ui-both") + }, + area: { + edit: $(".ui-edit-area"), + view: $(".ui-view-area"), + codemirror: $(".ui-edit-area .CodeMirror"), + markdown: $(".ui-view-area .markdown-body") + } +}; + +//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]); +//when page ready +$(document).ready(function () { + checkResponsive(); + changeMode(currentMode); + /* 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'); + }); + } +}); +//when page resize +$(window).resize(function () { + checkResponsive(); +}); +//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 (editor.hasFocus()) + changeMode(modeType.edit); + else + changeMode(modeType.view); +} + +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 = statusType.online.msg + num; + 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; + } +} + +function changeMode(type) { + saveInfo(); + if (type) + 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.addClass(responsiveClass).show(); + break; + } + if (currentMode != modeType.view && visibleLG) { + editor.focus(); + editor.refresh(); + } else { + editor.getInputField().blur(); + } + if (changeMode != modeType.edit) + updateView(); + restoreInfo(); + + 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-toggle-on').removeClass('fa-toggle-off'); + 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-toggle-off'); + } else if (ui.area.view.is(":visible")) { //view + ui.toolbar.view.addClass("active"); + modeIcon.addClass('fa-toggle-on'); + } +} + +//button actions +var noteId = window.location.pathname.split('/')[1]; +var url = window.location.origin + '/' + noteId; +//pretty +ui.toolbar.pretty.attr("href", url + "/pretty"); +//download +//markdown +ui.toolbar.download.markdown.click(function() { + var filename = renderFilename(ui.area.markdown) + '.md'; + var markdown = editor.getValue(); + var blob = new Blob([markdown], {type: "text/markdown;charset=utf-8"}); + saveAs(blob, filename); +}); +//save to dropbox +ui.toolbar.save.dropbox.click(function() { + var filename = renderFilename(ui.area.markdown) + '.md'; + var options = { + files: [ + {'url': url + "/download", 'filename': filename} + ] + }; + Dropbox.save(options); +}); +//import from dropbox +ui.toolbar.import.dropbox.click(function() { + var options = { + success: function(files) { + ui.spinner.show(); + var url = files[0].link; + importFromUrl(url); + }, + linkType: "direct", + multiselect: false, + extensions: ['.md', '.html'] + }; + Dropbox.choose(options); +}); +//import from clipboard +ui.toolbar.import.clipboard.click(function() { + //na +}); +//fix for wrong autofocus +$('#clipboardModal').on('shown.bs.modal', function() { + $('#clipboardModal').blur(); +}); +$("#clipboardModalClear").click(function() { + $("#clipboardModalContent").html(''); +}); +$("#clipboardModalConfirm").click(function() { + var data = $("#clipboardModalContent").html(); + if(data) { + parseToEditor(data); + $('#clipboardModal').modal('hide'); + $("#clipboardModalContent").html(''); + } +}); +function parseToEditor(data) { + var parsed = toMarkdown(data); + if(parsed) + editor.replaceRange(parsed, {line:0, ch:0}, {line:editor.lastLine(), ch:editor.lastLine().length}, '+input'); +} +function importFromUrl(url) { + //console.log(url); + if(url == null) return; + if(!isValidURL(url)) { + alert('Not valid URL :('); + return; + } + $.ajax({ + method: "GET", + url: url, + success: function(data) { + parseToEditor(data); + }, + error: function() { + alert('Import failed :('); + }, + complete: function() { + ui.spinner.hide(); + } + }); +} +function isValidURL(str) { + var pattern = new RegExp('^(https?:\\/\\/)?'+ // protocol + '((([a-z\\d]([a-z\\d-]*[a-z\\d])*)\\.)+[a-z]{2,}|'+ // domain name + '((\\d{1,3}\\.){3}\\d{1,3}))'+ // OR ip (v4) address + '(\\:\\d+)?(\\/[-a-z\\d%_.~+]*)*'+ // port and path + '(\\?[;&a-z\\d%_.~+=-]*)?'+ // query string + '(\\#[-a-z\\d_]*)?$','i'); // fragment locator + if(!pattern.test(str)) { + return false; + } else { + return true; + } +} +//mode +ui.toolbar.mode.click(function () { + toggleMode(); +}); +//edit +ui.toolbar.edit.click(function () { + changeMode(modeType.edit); +}); +//view +ui.toolbar.view.click(function () { + changeMode(modeType.view); +}); +//both +ui.toolbar.both.click(function () { + changeMode(modeType.both); +}); + +//socket.io actions +var socket = io.connect(); +socket.on('info', function (data) { + console.error(data); + location.href = "./404.html"; +}); +socket.on('disconnect', function (data) { + showStatus(statusType.offline); + if (loaded) { + saveInfo(); + lastInfo.history = editor.getHistory(); + } + if (!editor.getOption('readOnly')) + editor.setOption('readOnly', true); +}); +socket.on('connect', function (data) { + showStatus(statusType.connected); + socket.emit('version'); +}); +socket.on('version', function (data) { + if (data != version) + location.reload(true); +}); +socket.on('refresh', function (data) { + saveInfo(); + + var body = data.body; + body = LZString.decompressFromBase64(body); + if (body) + editor.setValue(body); + else + editor.setValue(""); + + if (!loaded) { + editor.clearHistory(); + ui.spinner.hide(); + ui.content.fadeIn(); + changeMode(); + loaded = true; + } else { + if (LZString.compressToBase64(editor.getValue()) !== data.body) + editor.clearHistory(); + else { + if (lastInfo.history) + editor.setHistory(lastInfo.history); + } + lastInfo.history = null; + } + + updateView(); + + if (editor.getOption('readOnly')) + editor.setOption('readOnly', false); + + restoreInfo(); +}); +socket.on('change', function (data) { + data = LZString.decompressFromBase64(data); + data = JSON.parse(data); + editor.replaceRange(data.text, data.from, data.to, "ignoreHistory"); + isDirty = true; + clearTimeout(finishChangeTimer); + finishChangeTimer = setTimeout(finishChange, finishChangeDelay); +}); +socket.on('online users', function (data) { + if (debug) + console.debug(data); + showStatus(statusType.online, data.count); + $('.other-cursors').html(''); + for(var i = 0; i < data.users.length; i++) { + var user = data.users[i]; + if(user.id != socket.id) + buildCursor(user.id, user.color, user.cursor); + } +}); +socket.on('cursor focus', function (data) { + if(debug) + console.debug(data); + var cursor = $('#' + data.id); + if(cursor.length > 0) { + cursor.fadeIn(); + } else { + if(data.id != socket.id) + buildCursor(data.id, data.color, data.cursor); + } +}); +socket.on('cursor activity', function (data) { + if(debug) + console.debug(data); + if(data.id != socket.id) + buildCursor(data.id, data.color, data.cursor); +}); +socket.on('cursor blur', function (data) { + if(debug) + console.debug(data); + var cursor = $('#' + data.id); + if(cursor.length > 0) { + cursor.fadeOut(); + } +}); +function emitUserStatus() { + checkIfAuth( + function (data) { + socket.emit('user status', {login:true}); + }, + function () { + socket.emit('user status', {login:false}); + } + ); +} + +function buildCursor(id, color, pos) { + if(!pos) return; + if ($('.other-cursors').length <= 0) { + $("
';
+ }
+ return '';
+};
+
+md.renderer.rules.heading_open = function (tokens, idx) {
+ var line;
+ if (tokens[idx].lines && tokens[idx].level === 0) {
+ var startline = tokens[idx].lines[0] + 1;
+ var endline = tokens[idx].lines[1];
+ return '