diff --git a/index.html b/index.html index 067786e3..a28a1458 100644 --- a/index.html +++ b/index.html @@ -115,7 +115,7 @@ - +
diff --git a/js/core.js b/js/core.js index 9fd6cf97..dc207dba 100644 --- a/js/core.js +++ b/js/core.js @@ -363,6 +363,7 @@ define([ if(shownModalId != modalId) { // Hack to avoid conflict with tabs, collapse, tooltips events shownModalId = modalId; + Mousetrap.pause(); _.defer(function(elt) { elt.find("input:enabled:visible:first").focus(); }, $(this)); @@ -372,6 +373,7 @@ define([ var modalId = $(this).attr("id"); if(shownModalId == modalId && $(this).is(":hidden")) { shownModalId = undefined; + Mousetrap.unpause(); _.defer(function() { $("#wmd-input").focus(); }); diff --git a/js/extension-manager.js b/js/extension-manager.js index c499209d..ccecd858 100644 --- a/js/extension-manager.js +++ b/js/extension-manager.js @@ -40,7 +40,7 @@ define([ function createHook(hookName) { var callbackList = getExtensionCallbackList(hookName); return function() { - logger.debug(hookName, arguments); + logger.log(hookName, arguments); var callbackArguments = arguments; _.each(callbackList, function(callback) { callback.apply(null, callbackArguments); @@ -62,7 +62,7 @@ define([ // Load/Save extension config from/to settings extensionMgr["onLoadSettings"] = function() { - logger.debug("onLoadSettings"); + logger.log("onLoadSettings"); _.each(extensionList, function(extension) { utils.setInputChecked("#input-enable-extension-" + extension.extensionId, extension.config.enabled); var onLoadSettingsCallback = extension.onLoadSettings; @@ -70,7 +70,7 @@ define([ }); }; extensionMgr["onSaveSettings"] = function(newExtensionSettings, event) { - logger.debug("onSaveSettings"); + logger.log("onSaveSettings"); _.each(extensionList, function(extension) { var newExtensionConfig = _.extend({}, extension.defaultConfig); newExtensionConfig.enabled = utils.getInputChecked("#input-enable-extension-" + extension.extensionId); @@ -124,7 +124,7 @@ define([ // The number of times we expect tryFinished to be called var nbAsyncPreviewCallback = onAsyncPreviewCallbackList.length + 1; extensionMgr["onAsyncPreview"] = function() { - logger.debug("onAsyncPreview"); + logger.log("onAsyncPreview"); // Call onPreviewFinished callbacks when all async preview are finished var counter = 0; function tryFinished() { @@ -171,7 +171,7 @@ define([ }).each(createSettings); // Create extension buttons - logger.debug("onCreateButton"); + logger.log("onCreateButton"); var onCreateButtonCallbackList = getExtensionCallbackList("onCreateButton"); _.each(onCreateButtonCallbackList, function(callback) { $("#extension-buttons").append($('
').append(callback())); diff --git a/js/extensions/document-selector.js b/js/extensions/document-selector.js index a7da2cd2..bb338520 100644 --- a/js/extensions/document-selector.js +++ b/js/extensions/document-selector.js @@ -1,17 +1,18 @@ define([ "jquery", "underscore", - "file-system" + "file-system", + "libs/mousetrap", ], function($, _, fileSystem) { var documentSelector = { extensionId: "documentSelector", extensionName: "Document selector", - /* defaultConfig: { - keyShortcut: 223 + sortBy: "mru", + keyPrevious: "[", + keyNext: "]" }, - */ settingsBloc: '

Builds the "Open document" dropdown menu.

' }; @@ -21,6 +22,8 @@ define([ }; var liMap = undefined; + var liArray = undefined; + var sortFunction = undefined; var buildSelector = function() { function composeTitle(fileDesc) { @@ -40,9 +43,7 @@ define([ liMap = {}; $("#file-selector li:not(.stick)").empty(); - _.chain(fileSystem).sortBy(function(fileDesc) { - return fileDesc.title.toLowerCase(); - }).each(function(fileDesc) { + _.chain(fileSystem).sortBy(sortFunction).each(function(fileDesc) { var a = $('').html(composeTitle(fileDesc)).click(function() { if(!liMap[fileDesc.fileIndex].is(".disabled")) { fileMgr.selectFile(fileDesc); @@ -52,12 +53,13 @@ define([ liMap[fileDesc.fileIndex] = li; $("#file-selector").append(li); }); + liArray = _.values(liMap); }; + var selectFileDesc = undefined; documentSelector.onFileSelected = function(fileDesc) { - if(liMap === undefined) { - buildSelector(); - } + selectFileDesc = fileDesc; + buildSelector(); $("#file-selector li:not(.stick)").removeClass("disabled"); var li = liMap[fileDesc.fileIndex]; if(li === undefined) { @@ -65,7 +67,7 @@ define([ // selector) return; } - liMap[fileDesc.fileIndex].addClass("disabled"); + li.addClass("disabled"); }; documentSelector.onFileCreated = buildSelector; @@ -94,8 +96,26 @@ define([ } documentSelector.onReady = function() { + if(documentSelector.config.sortBy == "title") { + sortFunction = function(fileDesc) { + return fileDesc.title.toLowerCase(); + }; + } + else if(documentSelector.config.sortBy == "mru") { + sortFunction = function(fileDesc) { + return -fileDesc.selectTime; + }; + } + + var shortcutClick = false; $(".action-open-file").click(function() { + if($("#file-selector:parent").is(".open")) { + return; + } filterFileSelector(); + if(shortcutClick === true) { + return; + } _.defer(function() { $("#file-search").val("").focus(); }); @@ -110,12 +130,45 @@ define([ }).click(function(event) { event.stopPropagation(); }); - /* - $("#wmd-input").keydown(function(event) { - if(event.ctrlKey && event.keyCode == documentSelector.config.keyShortcut) { - console.log(event.keyCode); + + // Handle key shortcut + var shortcutLi = undefined; + Mousetrap.bind('ctrl+' + documentSelector.config.keyPrevious, function() { + shortcutClick = true; + if(shortcutLi === undefined) { + $(".action-open-file").click(); + shortcutLi = liMap[selectFileDesc.fileIndex]; } - });*/ + var liIndex = _.indexOf(liArray, shortcutLi) - 1; + if(liIndex === -2) { + liIndex = -1; + } + shortcutLi = liArray[(liIndex + liArray.length) % liArray.length]; + _.defer(function() { + shortcutLi.find("a").focus(); + }); + return false; + }); + Mousetrap.bind('ctrl+' + documentSelector.config.keyNext, function() { + shortcutClick = true; + if(shortcutLi === undefined) { + $(".action-open-file").click(); + shortcutLi = liMap[selectFileDesc.fileIndex]; + } + var liIndex = _.indexOf(liArray, shortcutLi) + 1; + shortcutLi = liArray[liIndex % liArray.length]; + _.defer(function() { + shortcutLi.find("a").focus(); + }); + return false; + }); + Mousetrap.bind('ctrl', function() { + shortcutClick = false; + if(shortcutLi !== undefined) { + shortcutLi.find("a").click(); + shortcutLi = undefined; + } + }, "keyup"); }; return documentSelector; diff --git a/js/extensions/notifications.js b/js/extensions/notifications.js index 4a31d871..6e131803 100644 --- a/js/extensions/notifications.js +++ b/js/extensions/notifications.js @@ -42,7 +42,7 @@ define([ }; function showMessage(message, iconClass, options) { - logger.log(message); + logger.info(message); if(!message) { return; } diff --git a/js/file-manager.js b/js/file-manager.js index 4eb4361d..fc686d03 100644 --- a/js/file-manager.js +++ b/js/file-manager.js @@ -19,50 +19,72 @@ define([ this._editorStart = parseInt(localStorage[fileIndex + ".editorStart"]) || 0; this._editorEnd = parseInt(localStorage[fileIndex + ".editorEnd"]) || 0; this._previewScrollTop = parseInt(localStorage[fileIndex + ".previewScrollTop"]) || 0; + this._selectTime = parseInt(localStorage[fileIndex + ".selectTime"]) || 0; this.syncLocations = syncLocations || {}; this.publishLocations = publishLocations || {}; - this.__defineGetter__("title", function() { - return this._title; + Object.defineProperty(this, 'title', { + get: function() { + return this._title; + }, + set: function(title) { + this._title = title; + localStorage[this.fileIndex + ".title"] = title; + extensionMgr.onTitleChanged(this); + } }); - this.__defineSetter__("title", function(title) { - this._title = title; - localStorage[this.fileIndex + ".title"] = title; - extensionMgr.onTitleChanged(this); + Object.defineProperty(this, 'content', { + get: function() { + return localStorage[this.fileIndex + ".content"]; + }, + set: function(content) { + localStorage[this.fileIndex + ".content"] = content; + extensionMgr.onContentChanged(this); + } }); - this.__defineGetter__("content", function() { - return localStorage[this.fileIndex + ".content"]; + Object.defineProperty(this, 'editorScrollTop', { + get: function() { + return this._editorScrollTop; + }, + set: function(editorScrollTop) { + this._editorScrollTop = editorScrollTop; + localStorage[this.fileIndex + ".editorScrollTop"] = editorScrollTop; + } }); - this.__defineSetter__("content", function(content) { - localStorage[this.fileIndex + ".content"] = content; - extensionMgr.onContentChanged(this); + Object.defineProperty(this, 'editorStart', { + get: function() { + return this._editorStart; + }, + set: function(editorStart) { + this._editorStart = editorStart; + localStorage[this.fileIndex + ".editorStart"] = editorStart; + } }); - this.__defineGetter__("editorScrollTop", function() { - return this._editorScrollTop; + Object.defineProperty(this, 'editorEnd', { + get: function() { + return this._editorEnd; + }, + set: function(editorEnd) { + this._editorEnd = editorEnd; + localStorage[this.fileIndex + ".editorEnd"] = editorEnd; + } }); - this.__defineSetter__("editorScrollTop", function(editorScrollTop) { - this._editorScrollTop = editorScrollTop; - localStorage[this.fileIndex + ".editorScrollTop"] = editorScrollTop; + Object.defineProperty(this, 'previewScrollTop', { + get: function() { + return this._previewScrollTop; + }, + set: function(previewScrollTop) { + this._previewScrollTop = previewScrollTop; + localStorage[this.fileIndex + ".previewScrollTop"] = previewScrollTop; + } }); - this.__defineGetter__("editorStart", function() { - return this._editorStart; - }); - this.__defineSetter__("editorStart", function(editorStart) { - this._editorStart = editorStart; - localStorage[this.fileIndex + ".editorStart"] = editorStart; - }); - this.__defineGetter__("editorEnd", function() { - return this._editorEnd; - }); - this.__defineSetter__("editorEnd", function(editorEnd) { - this._editorEnd = editorEnd; - localStorage[this.fileIndex + ".editorEnd"] = editorEnd; - }); - this.__defineGetter__("previewScrollTop", function() { - return this._previewScrollTop; - }); - this.__defineSetter__("previewScrollTop", function(previewScrollTop) { - this._previewScrollTop = previewScrollTop; - localStorage[this.fileIndex + ".previewScrollTop"] = previewScrollTop; + Object.defineProperty(this, 'selectTime', { + get: function() { + return this._selectTime; + }, + set: function(selectTime) { + this._selectTime = selectTime; + localStorage[this.fileIndex + ".selectTime"] = selectTime; + } }); } @@ -110,6 +132,7 @@ define([ if(fileMgr.isCurrentFile(fileDesc) === false) { fileMgr.setCurrentFile(fileDesc); + fileDesc.selectTime = new Date().getTime(); // Notify extensions extensionMgr.onFileSelected(fileDesc); @@ -177,7 +200,7 @@ define([ // Remove the index from the file list utils.removeIndexFromArray("file.list", fileDesc.fileIndex); delete fileSystem[fileDesc.fileIndex]; - + if(fileMgr.isCurrentFile(fileDesc) === true) { // Unset the current fileDesc fileMgr.setCurrentFile(); diff --git a/js/main.js b/js/main.js index f016b838..2b386ecb 100644 --- a/js/main.js +++ b/js/main.js @@ -29,6 +29,9 @@ requirejs.config({ 'libs/jquery.mousewheel': [ 'jquery' ], + 'libs/jquery.hotkeys': [ + 'jquery' + ], 'libs/layout': [ 'libs/jquery-ui' ],