Skip to content

Instantly share code, notes, and snippets.

@bennadel
Created January 23, 2012 16:07
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save bennadel/1663999 to your computer and use it in GitHub Desktop.
Save bennadel/1663999 to your computer and use it in GitHub Desktop.
Creating A Keyboard-Shortcuts Module In A Modular JavaScript Application
// Define the color swtch View controller.
define(
[
"jquery",
"signal"
],
function( $, Signal ){
// I return an initialized component.
function ColorSwatch( target ){
// Create a collection of cached DOM elements that are
// required for this view to function.
this.dom = {};
this.dom.target = target;
this.dom.action = this.dom.target.find( "a.action" );
// Define the list of colors to be used to display the
// color swatch.
this.colors = [
"#FF0066",
"#FF3366",
"#FF6666",
"#FF9966",
"#FFCC66",
"#FFFF66",
"#CCCCCC",
"#999999",
"#666666",
"#333333"
];
// Store the current color index.
this.currentColorIndex = 0;
// Listen for the click event on the action.
this.dom.action.click(
$.proxy( this.handleClick, this )
);
// Render the view based on the current internal state.
this.render();
// Return this object reference.
return( this );
}
// Define the class methods.
ColorSwatch.prototype = {
// I return the currently selected color.
getColor: function(){
// Return the color at the current index.
return( this.colors[ this.currentColorIndex ] );
},
// I handle the click of the action.
handleClick: function( event ){
// Cancel the default behavior since this isn't a
// real link.
event.preventDefault();
// Go to the next color.
this.nextColor();
},
// I move to the next color.
nextColor: function(){
// Increment the color index.
this.currentColorIndex++;
// If we moved to far, then loop back around.
if (this.currentColorIndex >= this.colors.length){
// Go back to the beginning of the color list.
this.currentColorIndex = 0;
}
// Re-render the view with the new color.
this.render();
},
// I update the display of the swatch based on the
// currently selected color;
render: function(){
// Update the swatch color.
this.dom.target.css(
"background-color",
this.getColor()
);
}
};
// -------------------------------------------------- //
// -------------------------------------------------- //
// Return the constructor for this view.
return( ColorSwatch );
}
);
// Define the Controller for the demo.
define(
[
"jquery",
"signal",
"view/toggle-keys",
"view/color-swatch",
"view/keyboard-shortcuts"
],
function( $, Signal, ToggleKeys, ColorSwatch, KeyboardShortcuts ){
// I return an initialized component.
function Demo(){
// Create our toggle keys module.
this.toggleKeys = new ToggleKeys(
$( "div.toggle" )
);
// Create our color swatch module.
this.colorSwatch = new ColorSwatch(
$( "div.swatch" )
);
// Create our keyboard shortcut module.
this.keyboardShortcuts = new KeyboardShortcuts(
$( document )
);
// Bind to the toggle module to listen for toggle events.
this.toggleKeys.events.toggled.bind(
this.handleToggle,
this
);
// Bind to the keyboard shortcuts event.
this.keyboardShortcuts.events.nextColor.bind(
this.handleNextColor,
this
);
// Check to see if the keyboard short-cuts are currently
// on or off. If they are on, we need to start watching
// the keys.
if (this.toggleKeys.isOn()){
// Start watching keyboard events.
this.keyboardShortcuts.start();
}
// Return this object reference.
return( this );
}
// Define the class methods.
Demo.prototype = {
// I handle the nextColor keyboard shortcut.
handleNextColor: function( event ){
// Pass this request onto the appropriate module.
this.colorSwatch.nextColor();
},
// I handle a change on the toggle.
handleToggle: function( event, status ){
// If the toggle is On, start watching keys.
if (event.context.isOn()){
// Start watching keys.
this.keyboardShortcuts.start();
} else {
// Stop watching keys.
this.keyboardShortcuts.stop();
}
}
};
// -------------------------------------------------- //
// -------------------------------------------------- //
// Return the constructor for this controller.
return( Demo );
}
);
// Define the keyboard shortcuts view controller.
define(
[
"jquery",
"signal"
],
function( $, Signal ){
// I return an initialized component.
function KeyboardShortcuts( stage ){
// Create a collection of cached DOM elements that are
// required for this view to function.
this.dom = {};
// Since this view isn't really associated with any
// particular DOM element, it will listen to events on
// the stage (root element).
this.dom.stage = stage;
// For optimization purposes, we'll cache an "active"
// input field so that we only have to listen to keyboard
// events that are necessary.
this.dom.activeInput = null;
// Create an event factory.
var signalFactory = Signal.forContext( this );
// Create an event landscape for subscriber binding.
// In this case, we will define an event for each
// keyboard shortcut that can be triggered. For this
// demo, there is only one.
this.events = {};
this.events.nextColor = signalFactory( "nextColor" );
// Return this object reference.
return( this );
}
// Define the class methods.
KeyboardShortcuts.prototype = {
// I handle the blur of the currently active input.
handleBlur: function(){
// Unbind our blur handler - the input is no longer
// active.
this.dom.activeInput.off(
"blur.keyboardShortcuts",
this.handleBlur
);
// Remove the active reference for our input.
this.dom.activeInput = null;
// Start watching for global keyboard events again.
this.watchKeys();
},
// I handle the global key events.
handleKeyPress: function( event ){
// Get the active element at the time the event was
// triggered.
var target = $( event.target );
// Check to see if we are in an input field. If so,
// we want to allow the user to continue using the
// UI as usual.
if (target.is( ":input" )){
// It is likely that many keyboard events will be
// triggered while the Input is focused. As such,
// let's stop listening to keyboard events until
// the user has left the input (blur).
this.stopWatchingKeys();
// Cache the active input (in case).
this.dom.activeInput = target;
// Now, wait for the blur on the Input to re-bind
// the global key handler.
target.on(
"blur.keyboardShortcuts",
$.proxy( this.handleBlur, this )
);
// Return out - we don't want to process this
// event any further.
return;
// Make sure that there are not modifier keys being
// pressed - we don't want to start overriding the
// native features of the browser.
} else if (
event.ctrlKey ||
event.altKey ||
event.metaKey
){
// The user is typing in an input - we don't want
// to intercept that kind of action.
return;
}
// If we've made it this far then the key being
// pressed is a valid candidate for our keybaord
// shortcuts. Now, get the key character.
var keyChar = String.fromCharCode( event.which )
.toLowerCase()
;
// For DEBUGGING ONLY ------ //
console.log( "Looking At: ", keyChar );
// For DEBUGGING ONLY ------ //
// Check to see if this key character matches a
// keyboard shortcut within our system.
if (keyChar === "c"){
// This is a known keyboard shortcut. Prevent the
// default behavior so we can override the native
// function of the browser.
event.preventDefault();
// Trigger the event for going to the next Color.
this.events.nextColor.trigger();
}
},
// I activate the module.
start: function(){
// Start watching the keyboard shortcuts.
this.watchKeys();
},
// I deactivate the module until explicitly told to start
// watching the keys again.
stop: function(){
// Stop watching the keys.
this.stopWatchingKeys();
// Check to see if we are watching any input for
// further activity.
if (this.dom.activeInput){
// Unbind the blur handler - this is no longer
// a relevant event in our workflow.
this.dom.activeInput.off(
"blur.keyboardShortcuts",
this.handleBlur
);
// Clear the active input.
this.dom.activeInput = null;
}
},
// I stop watching the keyboard inputs at the global
// level.
stopWatchingKeys: function(){
// Unbind our global key handler.
this.dom.stage.off(
"keypress.keyboardShortcuts",
this.handleKeyPress
);
},
// I start watching keyboard inputs at the global level.
watchKeys: function(){
// Start watching keyboard events that bubble up to
// the stage.
this.dom.stage.on(
"keypress.keyboardShortcuts",
$.proxy( this.handleKeyPress, this )
);
}
};
// -------------------------------------------------- //
// -------------------------------------------------- //
// Return the constructor for this view.
return( KeyboardShortcuts );
}
);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment