From ffeb2904230472382db15b96a11ecfb9eb32430c Mon Sep 17 00:00:00 2001 From: Max Ogden Date: Thu, 14 Jul 2011 13:03:55 -0700 Subject: [PATCH] adding keyboard shortcut bindings --- attachments/pages/index.html | 2 + attachments/script/jquery.hotkeys.js | 99 ++++++++++++++++++++++++++++ attachments/script/microevent.js | 50 ++++++++++++++ attachments/script/removalist.js | 3 + attachments/script/site.js | 15 +++-- attachments/script/util.js | 29 ++++++++ 6 files changed, 194 insertions(+), 4 deletions(-) create mode 100644 attachments/script/jquery.hotkeys.js create mode 100644 attachments/script/microevent.js diff --git a/attachments/pages/index.html b/attachments/pages/index.html index e4755c06..df30df64 100644 --- a/attachments/pages/index.html +++ b/attachments/pages/index.html @@ -9,8 +9,10 @@ + + diff --git a/attachments/script/jquery.hotkeys.js b/attachments/script/jquery.hotkeys.js new file mode 100644 index 00000000..fbd71c71 --- /dev/null +++ b/attachments/script/jquery.hotkeys.js @@ -0,0 +1,99 @@ +/* + * jQuery Hotkeys Plugin + * Copyright 2010, John Resig + * Dual licensed under the MIT or GPL Version 2 licenses. + * + * Based upon the plugin by Tzury Bar Yochay: + * http://github.com/tzuryby/hotkeys + * + * Original idea by: + * Binny V A, http://www.openjs.com/scripts/events/keyboard_shortcuts/ +*/ + +(function(jQuery){ + + jQuery.hotkeys = { + version: "0.8", + + specialKeys: { + 8: "backspace", 9: "tab", 13: "return", 16: "shift", 17: "ctrl", 18: "alt", 19: "pause", + 20: "capslock", 27: "esc", 32: "space", 33: "pageup", 34: "pagedown", 35: "end", 36: "home", + 37: "left", 38: "up", 39: "right", 40: "down", 45: "insert", 46: "del", + 96: "0", 97: "1", 98: "2", 99: "3", 100: "4", 101: "5", 102: "6", 103: "7", + 104: "8", 105: "9", 106: "*", 107: "+", 109: "-", 110: ".", 111 : "/", + 112: "f1", 113: "f2", 114: "f3", 115: "f4", 116: "f5", 117: "f6", 118: "f7", 119: "f8", + 120: "f9", 121: "f10", 122: "f11", 123: "f12", 144: "numlock", 145: "scroll", 191: "/", 224: "meta" + }, + + shiftNums: { + "`": "~", "1": "!", "2": "@", "3": "#", "4": "$", "5": "%", "6": "^", "7": "&", + "8": "*", "9": "(", "0": ")", "-": "_", "=": "+", ";": ": ", "'": "\"", ",": "<", + ".": ">", "/": "?", "\\": "|" + } + }; + + function keyHandler( handleObj ) { + // Only care when a possible input has been specified + if ( typeof handleObj.data !== "string" ) { + return; + } + + var origHandler = handleObj.handler, + keys = handleObj.data.toLowerCase().split(" "); + + handleObj.handler = function( event ) { + // Don't fire in text-accepting inputs that we didn't directly bind to + if ( this !== event.target && (/textarea|select/i.test( event.target.nodeName ) || + event.target.type === "text") ) { + return; + } + + // Keypress represents characters, not special keys + var special = event.type !== "keypress" && jQuery.hotkeys.specialKeys[ event.which ], + character = String.fromCharCode( event.which ).toLowerCase(), + key, modif = "", possible = {}; + + // check combinations (alt|ctrl|shift+anything) + if ( event.altKey && special !== "alt" ) { + modif += "alt+"; + } + + if ( event.ctrlKey && special !== "ctrl" ) { + modif += "ctrl+"; + } + + // TODO: Need to make sure this works consistently across platforms + if ( event.metaKey && !event.ctrlKey && special !== "meta" ) { + modif += "meta+"; + } + + if ( event.shiftKey && special !== "shift" ) { + modif += "shift+"; + } + + if ( special ) { + possible[ modif + special ] = true; + + } else { + possible[ modif + character ] = true; + possible[ modif + jQuery.hotkeys.shiftNums[ character ] ] = true; + + // "$" can be triggered as "Shift+4" or "Shift+$" or just "$" + if ( modif === "shift+" ) { + possible[ jQuery.hotkeys.shiftNums[ character ] ] = true; + } + } + + for ( var i = 0, l = keys.length; i < l; i++ ) { + if ( possible[ keys[i] ] ) { + return origHandler.apply( this, arguments ); + } + } + }; + } + + jQuery.each([ "keydown", "keyup", "keypress" ], function() { + jQuery.event.special[ this ] = { add: keyHandler }; + }); + +})( jQuery ); \ No newline at end of file diff --git a/attachments/script/microevent.js b/attachments/script/microevent.js new file mode 100644 index 00000000..3e74d026 --- /dev/null +++ b/attachments/script/microevent.js @@ -0,0 +1,50 @@ +/** + * MicroEvent - to make any js object an event emitter (server or browser) + * + * - pure javascript - server compatible, browser compatible + * - dont rely on the browser doms + * - super simple - you get it immediatly, no mistery, no magic involved + * + * - create a MicroEventDebug with goodies to debug + * - make it safer to use +*/ + +var MicroEvent = function(){} +MicroEvent.prototype = { + on : function(event, fct){ + this._events = this._events || {}; + this._events[event] = this._events[event] || []; + this._events[event].push(fct); + }, + clear : function(event, fct){ + this._events = this._events || {}; + if( event in this._events === false ) return; + this._events[event].splice(this._events[event].indexOf(fct), 1); + }, + trigger : function(event /* , args... */){ + this._events = this._events || {}; + if( event in this._events === false ) return; + for(var i = 0; i < this._events[event].length; i++){ + this._events[event][i].apply(this, Array.prototype.slice.call(arguments, 1)) + } + } +}; + +/** + * mixin will delegate all MicroEvent.js function in the destination object + * + * - require('MicroEvent').mixin(Foobar) will make Foobar able to use MicroEvent + * + * @param {Object} the object which will support MicroEvent +*/ +MicroEvent.mixin = function(destObject){ + var props = ['on', 'clear', 'trigger']; + for(var i = 0; i < props.length; i ++){ + destObject.prototype[props[i]] = MicroEvent.prototype[props[i]]; + } +} + +// export in common js +if( typeof module !== "undefined" && ('exports' in module)){ + module.exports = MicroEvent +} \ No newline at end of file diff --git a/attachments/script/removalist.js b/attachments/script/removalist.js index 6b313161..cb11bad8 100644 --- a/attachments/script/removalist.js +++ b/attachments/script/removalist.js @@ -118,6 +118,9 @@ var removalist = function() { } function bootstrap() { + util.registerEmitter(); + util.listenFor(["esc"]); + couch.request({url: app.baseURL + "api"}).then(function( dbInfo ) { app.dbInfo = dbInfo; diff --git a/attachments/script/site.js b/attachments/script/site.js index ffdbd74c..e55d4f78 100644 --- a/attachments/script/site.js +++ b/attachments/script/site.js @@ -83,14 +83,18 @@ app.after = { exportActions: removalist.handleMenuClick, columnActions: removalist.handleMenuClick, signIn: function() { + + util.observeExit($('.dialog-content'), function() { + util.hide('dialog'); + }) + $('.dialog-content #username-input').focus(); + $('.dialog-content').find('#sign-in-form').submit(function(e) { $('.dialog-content .okButton').click(); return false; }) - $('.dialog-content .cancelButton').click(function(e) { - util.hide('dialog'); - }) + $('.dialog-content .okButton').click(function(e) { util.hide('dialog'); util.notify("Signing you in...", {persist: true, loader: true}); @@ -106,11 +110,14 @@ app.after = { if (error.statusText === "error") util.notify(JSON.parse(error.responseText).reason); }) }) + }, bulkEdit: function() { - $('.dialog-content .cancelButton').click(function(e) { + + util.observeExit($('.dialog-content'), function() { util.hide('dialog'); }) + $('.dialog-content .okButton').click(function(e) { var funcText = $('.expression-preview-code').val(); util.hide('dialog'); diff --git a/attachments/script/util.js b/attachments/script/util.js index b3dc9be4..2407be5d 100644 --- a/attachments/script/util.js +++ b/attachments/script/util.js @@ -24,6 +24,32 @@ var util = function() { return exists; } + function registerEmitter() { + var Emitter = function(obj) { + this.emit = function(obj, channel) { + if (!channel) var channel = 'data'; + this.trigger(channel, obj); + }; + }; + MicroEvent.mixin(Emitter); + app.emitter = new Emitter(); + } + + function listenFor(keys) { + _.each(keys, function(key) { + $(document).bind('keydown', key, function() { app.emitter.emit(key, key) }); + }) + } + + function observeExit(elem, callback) { + var cancelButton = elem.find('.cancelButton'); + app.emitter.on('esc', function() { + cancelButton.click(); + app.emitter.clear('esc'); + }); + cancelButton.click(callback); + } + function show( thing ) { $('.' + thing ).show(); $('.' + thing + '-overlay').show(); @@ -148,11 +174,14 @@ var util = function() { return { inURL: inURL, + registerEmitter: registerEmitter, + listenFor: listenFor, show: show, hide: hide, position: position, render: render, notify: notify, + observeExit: observeExit, formatMetadata:formatMetadata, getBaseURL:getBaseURL, resetForm: resetForm,