Skip to content
Browse files

intital commit

  • Loading branch information...
0 parents commit 5c1ff1409302a0692248cae81f117f2d3041e4b6 @phlipper committed Apr 17, 2012
17 .gitignore
@@ -0,0 +1,17 @@
+*.gem
+*.rbc
+.bundle
+.config
+.yardoc
+Gemfile.lock
+InstalledFiles
+_yardoc
+coverage
+doc/
+lib/bundler/man
+pkg
+rdoc
+spec/reports
+test/tmp
+test/version_tmp
+tmp
1 .rvmrc
@@ -0,0 +1 @@
+rvm use 1.9.3@keyboardjs-rails --create
4 Gemfile
@@ -0,0 +1,4 @@
+source "https://rubygems.org"
+
+# Specify your gem's dependencies in sugar-rails.gemspec
+gemspec
20 LICENSE.txt
@@ -0,0 +1,20 @@
+The MIT License (MIT)
+Copyright © 2012 Phil Cohen <github@phlippers.net>
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the “Software”), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
94 README.md
@@ -0,0 +1,94 @@
+# keyboardjs-rails
+
+[KeyboardJS](http://robertwhurst.github.com/KeyboardJS/), typed up for Rails 3.0 and up. Sweet!
+
+
+## Rails 3.1+
+
+For Rails 3.1 and greater, the files will be added to the asset pipeline and available for you to use.
+
+To enable:
+
+* Add the following line to the file `app/assets/javascripts/application.js` (or other [sprockets](https://github.com/sstephenson/sprockets) manifest):
+
+``` javascript
+//= require keyboardjs
+```
+
+
+### Installation
+
+* Add `keyboardjs-rails` to your Gemfile
+* Run `bundle`
+
+Enjoy!
+
+
+## Rails 3.0
+
+This gem adds a single generator to Rails 3, `keyboardjs:install`. Running the generator will install the JavaScript file necessary to use KeyboardJS.
+
+### Installation
+
+* Add `keyboardjs-rails` to your Gemfile
+* Run `bundle`
+* Invoke the generator: `rails generate keyboardjs:install`
+* Add the following to your layout or view files:
+
+```erb
+<%= javascript_include_tag "keyboardjs" %>
+```
+
+You're done!
+
+
+## Contributing
+
+1. Fork it
+2. Create your feature branch (`git checkout -b my-new-feature`)
+3. Commit your changes (`git commit -am 'Added some feature'`)
+4. Push to the branch (`git push origin my-new-feature`)
+5. Create new Pull Request
+
+
+## License
+
+**keyboardjs-rails**
+
+* Freely distributable and licensed under the [MIT license](http://phlipper.mit-license.org/2012/license.html).
+* Copyright (c) 2012 Phil Cohen (github@phlippers.net) [![endorse](http://api.coderwall.com/phlipper/endorsecount.png)](http://coderwall.com/phlipper)
+* http://phlippers.net/
+
+**KeyboardJS**
+
+* Licenced under the BSD License
+* Copyright (c) 2011 Robert Hurst
+* http://robertwhurst.github.com/KeyboardJS/
+
+```
+Copyright 2011 Robert Hurst. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without modification, are
+permitted provided that the following conditions are met:
+
+ 1. Redistributions of source code must retain the above copyright notice, this list of
+ conditions and the following disclaimer.
+
+ 2. Redistributions in binary form must reproduce the above copyright notice, this list
+ of conditions and the following disclaimer in the documentation and/or other materials
+ provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY ROBERT HURST ''AS IS'' AND ANY EXPRESS OR IMPLIED
+WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL ROBERT HURST OR
+CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+The views and conclusions contained in the software and documentation are those of the
+authors and should not be interpreted as representing official policies, either expressed
+or implied, of Robert Hurst.
+```
2 Rakefile
@@ -0,0 +1,2 @@
+#!/usr/bin/env rake
+require "bundler/gem_tasks"
21 keyboardjs-rails.gemspec
@@ -0,0 +1,21 @@
+# -*- encoding: utf-8 -*-
+require File.expand_path('../lib/keyboardjs/rails/version', __FILE__)
+
+Gem::Specification.new do |gem|
+ gem.authors = ["Phil Cohen"]
+ gem.email = ["github@phlippers.net"]
+ gem.description = %q{KeyboardJS, typed up for the Rails 3.0 and up. Sweet!}
+ gem.summary = %q{KeyboardJS, typed up for the Rails 3.0 and up. Sweet!}
+ gem.homepage = "https://github.com/phlipper/keyboardjs-rails"
+
+ gem.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
+ gem.files = `git ls-files`.split("\n")
+ gem.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
+ gem.name = "keyboardjs-rails"
+ gem.require_paths = ["lib"]
+ gem.version = KeyboardJS::Rails::VERSION
+
+ gem.add_dependency "railties", ">= 3.0.0"
+ gem.add_development_dependency "bundler", "~> 1.1.3"
+ gem.add_development_dependency "rake", "~> 0.9.2.2"
+end
35 lib/generators/keyboardjs/install/install_generator.rb
@@ -0,0 +1,35 @@
+require "rails"
+
+# Supply generator for Rails 3.0.x or if asset pipeline is not enabled
+if ::Rails.version < "3.1" || !::Rails.application.config.assets.enabled
+ module KeyboardJS
+ module Generators
+ class InstallGenerator < ::Rails::Generators::Base
+ desc "This generator installs KeyboardJS #{KeyboardJS::Rails::KEYBOARDJS_VERSION}"
+ source_root File.expand_path("../../../../../vendor/assets", __FILE__)
+
+ def copy_javascript
+ say_status("copying", "KeyboardJS", :green)
+ copy_file "javascripts/keyboardjs.js", "public/javascripts/keyboardjs.js"
+ end
+ end
+ end
+ end
+else
+ module KeyboardJS
+ module Generators
+ class InstallGenerator < ::Rails::Generators::Base
+ desc "Just show instructions so people will know what to do when mistakenly using generator for Rails 3.1 apps"
+
+ def do_nothing
+ say_status("deprecated", "You are using Rails 3.1 with the asset pipeline enabled, so this generator is not needed.")
+ say_status("", "The necessary files are already in your asset pipeline.")
+ say_status("", "Just add `//= require keyboardjs` to your app/assets/javascripts/application.js")
+ say_status("", "If you upgraded your app from Rails 3.0 and still have keyboardjs.js in your public assets folders, be sure to remove them.")
+ say_status("", "If you do not want the asset pipeline enabled, you may turn it off in application.rb and re-run this generator.")
+ # ok, nothing
+ end
+ end
+ end
+ end
+end
1 lib/keyboardjs-rails.rb
@@ -0,0 +1 @@
+require "keyboardjs/rails"
6 lib/keyboardjs/rails.rb
@@ -0,0 +1,6 @@
+module KeyboardJS
+ module Rails
+ require "keyboardjs/rails/engine"
+ require "keyboardjs/rails/version"
+ end
+end
7 lib/keyboardjs/rails/engine.rb
@@ -0,0 +1,7 @@
+# Configure Rails 3.1 to have assets in the load path
+module KeyboardJS
+ module Rails
+ class Engine < ::Rails::Engine
+ end
+ end
+end
6 lib/keyboardjs/rails/version.rb
@@ -0,0 +1,6 @@
+module KeyboardJS
+ module Rails
+ VERSION = "0.2.2"
+ KEYBOARDJS_VERSION = "0.2.2"
+ end
+end
483 vendor/assets/javascripts/keyboardjs.js
@@ -0,0 +1,483 @@
+/*!
+ * KeyboardJS
+ *
+ * Copyright 2011, Robert William Hurst
+ * Licenced under the BSD License.
+ * See https://raw.github.com/RobertWHurst/KeyboardJS/master/license.txt
+ */
+(function (root, factory) {
+ if (typeof define === 'function' && define.amd) {
+ // AMD. Register as an anonymous module.
+ define(factory);
+ } else {
+ // Browser globals
+ root.KeyboardJS = factory();
+ }
+}(this, function() {
+
+ //polyfills for ms's peice o' shit browsers
+ function bind(target, type, handler) { if (target.addEventListener) { target.addEventListener(type, handler, false); } else { target.attachEvent("on" + type, function(event) { return handler.call(target, event); });Â } }
+ [].indexOf||(Array.prototype.indexOf=function(a,b,c){for(c=this.length,b=(c+~~b)%c;b<c&&(!(b in this)||this[b]!==a);b++);return b^c?b:-1;});
+
+ //locals
+ var locals = {
+ 'us': {
+ "backspace": 8,
+ "tab": 9,
+ "enter": 13,
+ "shift": 16,
+ "ctrl": 17,
+ "alt": 18,
+ "pause": 19, "break": 19,
+ "capslock": 20,
+ "escape": 27, "esc": 27,
+ "space": 32, "spacebar": 32,
+ "pageup": 33,
+ "pagedown": 34,
+ "end": 35,
+ "home": 36,
+ "left": 37,
+ "up": 38,
+ "right": 39,
+ "down": 40,
+ "insert": 45,
+ "delete": 46,
+ "0": 48, "1": 49, "2": 50, "3": 51, "4": 52, "5": 53, "6": 54, "7": 55, "8": 56, "9": 57,
+ "a": 65, "b": 66, "c": 67, "d": 68, "e": 69, "f": 70, "g": 71, "h": 72, "i": 73, "j": 74, "k": 75, "l": 76, "m": 77, "n": 78, "o": 79, "p": 80, "q": 81, "r": 82, "s": 83, "t": 84, "u": 85, "v": 86, "w": 87, "x": 88, "y": 89, "z": 90,
+ "meta": 91, "command": 91, "windows": 91, "win": 91,
+ "_91": 92,
+ "select": 93,
+ "num0": 96, "num1": 97, "num2": 98, "num3": 99, "num4": 100, "num5": 101, "num6": 102, "num7": 103, "num8": 104, "num9": 105,
+ "multiply": 106,
+ "add": 107,
+ "subtract": 109,
+ "decimal": 110,
+ "divide": 111,
+ "f1": 112, "f2": 113, "f3": 114, "f4": 115, "f5": 116, "f6": 117, "f7": 118, "f8": 119, "f9": 120, "f10": 121, "f11": 122, "f12": 123,
+ "numlock": 144, "num": 144,
+ "scrolllock": 145, "scroll": 145,
+ "semicolon": 186,
+ "equal": 187, "equalsign": 187,
+ "comma": 188,
+ "dash": 189,
+ "period": 190,
+ "slash": 191, "forwardslash": 191,
+ "graveaccent": 192,
+ "openbracket": 219,
+ "backslash": 220,
+ "closebracket": 221,
+ "singlequote": 222
+ }
+
+ //If you create a new local please submit it as a pull request or post it in the issue tracker at
+ // http://github.com/RobertWhurst/KeyboardJS/issues/
+ }
+
+ //keys
+ var keys = locals['us'],
+ activeKeys = [],
+ activeBindings = {},
+ keyBindingGroups = [];
+
+ //adds keys to the active keys array
+ bind(document, "keydown", function(event) {
+
+ //lookup the key pressed and save it to the active keys array
+ for (var key in keys) {
+ if(keys.hasOwnProperty(key) && event.keyCode === keys[key]) {
+ if(activeKeys.indexOf(key) < 0) {
+ activeKeys.push(key);
+ }
+ }
+ }
+
+ //execute the first callback the longest key binding that matches the active keys
+ return executeActiveKeyBindings(event);
+
+ });
+
+ //removes keys from the active array
+ bind(document, "keyup", function (event) {
+
+ //lookup the key released and prune it from the active keys array
+ for(var key in keys) {
+ if(keys.hasOwnProperty(key) && event.keyCode === keys[key]) {
+
+ var iAK = activeKeys.indexOf(key);
+
+ if(iAK > -1) {
+ activeKeys.splice(iAK, 1);
+ }
+ }
+ }
+
+ //execute the end callback on the active key binding
+ return pruneActiveKeyBindings(event);
+
+ });
+
+ /**
+ * Generates an array of active key bindings
+ */
+ function queryActiveBindings() {
+ var bindingStack = [];
+
+ //loop through the key binding groups by number of keys.
+ for(var keyCount = keyBindingGroups.length; keyCount > -1; keyCount -= 1) {
+ if(keyBindingGroups[keyCount]) {
+ var KeyBindingGroup = keyBindingGroups[keyCount];
+
+ //loop through the key bindings of the same key length.
+ for(var bindingIndex = 0; bindingIndex < KeyBindingGroup.length; bindingIndex += 1) {
+ var binding = KeyBindingGroup[bindingIndex],
+
+ //assume the binding is active till a required key is found to be unsatisfied
+ keyBindingActive = true;
+
+ //loop through each key required by the binding.
+ for(var keyIndex = 0; keyIndex < binding.keys.length; keyIndex += 1) {
+ var key = binding.keys[keyIndex];
+
+ //if the current key is not in the active keys array the mark the binding as inactive
+ if(activeKeys.indexOf(key) < 0) {
+ keyBindingActive = false;
+ }
+ }
+
+ //if the key combo is still active then push it into the binding stack
+ if(keyBindingActive) {
+ bindingStack.push(binding);
+ }
+ }
+ }
+ }
+
+ return bindingStack;
+ }
+
+ /**
+ * Collects active keys, sets active binds and fires on key down callbacks
+ * @param event
+ */
+ function executeActiveKeyBindings(event) {
+
+ if(activeKeys < 1) {
+ return true;
+ }
+
+ var bindingStack = queryActiveBindings(),
+ spentKeys = [],
+ output;
+
+ //loop through each active binding
+ for (var bindingIndex = 0; bindingIndex < bindingStack.length; bindingIndex += 1) {
+ var binding = bindingStack[bindingIndex],
+ usesSpentKey = false;
+
+ //check each of the required keys. Make sure they have not been used by another binding
+ for(var keyIndex = 0; keyIndex < binding.keys.length; keyIndex += 1) {
+ var key = binding.keys[keyIndex];
+ if(spentKeys.indexOf(key) > -1) {
+ usesSpentKey = true;
+ break;
+ }
+ }
+
+ //if the binding does not use a key that has been spent then execute it
+ if(!usesSpentKey) {
+
+ //fire the callback
+ if(typeof binding.callback === "function") {
+ if(!binding.callback(event, binding.keys, binding.keyCombo)) {
+ output = false
+ }
+ }
+
+ //add the binding's combo to the active bindings array
+ if(!activeBindings[binding.keyCombo]) {
+ activeBindings[binding.keyCombo] = binding;
+ }
+
+ //add the current key binding's keys to the spent keys array
+ for(var keyIndex = 0; keyIndex < binding.keys.length; keyIndex += 1) {
+ var key = binding.keys[keyIndex];
+ if(spentKeys.indexOf(key) < 0) {
+ spentKeys.push(key);
+ }
+ }
+ }
+ }
+
+ //if there are spent keys then we know a binding was fired
+ // and that we need to tell jQuery to prevent event bubbling.
+ if(spentKeys.length) {
+ return false;
+ }
+
+ return output;
+ }
+
+ /**
+ * Removes no longer active keys and fires the on key up callbacks for associated active bindings.
+ * @param event
+ */
+ function pruneActiveKeyBindings(event) {
+ var bindingStack = queryActiveBindings();
+ var output;
+
+ //loop through the active combos
+ for(var bindingCombo in activeBindings) {
+ if(activeBindings.hasOwnProperty(bindingCombo)) {
+ var binding = activeBindings[bindingCombo],
+ active = false;
+
+ //loop thorugh the active bindings
+ for(var bindingIndex = 0; bindingIndex < bindingStack.length; bindingIndex += 1) {
+ var activeCombo = bindingStack[bindingIndex].keyCombo;
+
+ //check to see if the combo is still active
+ if(activeCombo === bindingCombo) {
+ active = true;
+ break;
+ }
+ }
+
+ //if the combo is no longer active then fire its end callback and remove it
+ if(!active) {
+
+ if(typeof binding.endCallback === "function") {
+ if(!binding.endCallback(event, binding.keys, binding.keyCombo)) {
+ output = false
+ }
+ }
+
+ delete activeBindings[bindingCombo];
+ }
+ }
+ }
+
+ return output;
+ }
+
+ /**
+ * Binds a on key down and on key up callback to a key or key combo. Accepts a string containing the name of each
+ * key you want to bind to comma separated. If you want to bind a combo the use the plus sign to link keys together.
+ * Example: 'ctrl + x, ctrl + c' Will fire if Control and x or y are pressed at the same time.
+ * @param keyCombo
+ * @param callback
+ * @param endCallback
+ */
+ function bindKey(keyCombo, callback, endCallback) {
+
+ function clear() {
+ if(keys && keys.length) {
+ var keyBindingGroup = keyBindingGroups[keys.length];
+
+ if(keyBindingGroup.indexOf(keyBinding) > -1) {
+ var index = keyBindingGroups[keys.length].indexOf(keyBinding);
+ keyBindingGroups[keys.length].splice(index, 1);
+ }
+ }
+ }
+
+ //create an array of combos from the first argument
+ var bindSets = keyCombo.toLowerCase().replace(/\s/g, '').split(',');
+
+ //create a binding for each key combo
+ for(var i = 0; i < bindSets.length; i += 1) {
+
+ //split up the keys
+ var keys = bindSets[i].split('+');
+
+ //if there are keys in the current combo
+ if(keys.length) {
+ if(!keyBindingGroups[keys.length]) { keyBindingGroups[keys.length] = []; }
+
+ //define the
+ var keyBinding = {
+ "callback": callback,
+ "endCallback": endCallback,
+ "keyCombo": bindSets[i],
+ "keys": keys
+ };
+
+ //save the binding sorted by length
+ keyBindingGroups[keys.length].push(keyBinding);
+ }
+ }
+
+ return {
+ "clear": clear
+ }
+ }
+
+ /**
+ * Binds keys or key combos to an axis. The keys should be in the following order; up, down, left, right. If any
+ * of the the binded key or key combos are active the callback will fire. The callback will be passed an array
+ * containing two numbers. The first represents x and the second represents y. Both have a possible range of -1,
+ * 0, or 1 depending on the axis direction.
+ * @param up
+ * @param down
+ * @param left
+ * @param right
+ * @param callback
+ */
+ function bindAxis(up, down, left, right, callback) {
+
+ function clear() {
+ if(typeof clearUp === 'function') { clearUp(); }
+ if(typeof clearDown === 'function') { clearDown(); }
+ if(typeof clearLeft === 'function') { clearLeft(); }
+ if(typeof clearRight === 'function') { clearRight(); }
+ if(typeof timer === 'function') { clearInterval(timer); }
+ }
+
+ var axis = [0, 0];
+
+ if(typeof callback !== 'function') {
+ return false;
+ }
+
+ //up
+ var clearUp = bindKey(up, function () {
+ if(axis[0] === 0) {
+ axis[0] = -1;
+ }
+ }, function() {
+ axis[0] = 0;
+ }).clear;
+
+ //down
+ var clearDown = bindKey(down, function () {
+ if(axis[0] === 0) {
+ axis[0] = 1;
+ }
+ }, function() {
+ axis[0] = 0;
+ }).clear;
+
+ //left
+ var clearLeft = bindKey(left, function () {
+ if(axis[1] === 0) {
+ axis[1] = -1;
+ }
+ }, function() {
+ axis[1] = 0;
+ }).clear;
+
+ //right
+ var clearRight = bindKey(right, function () {
+ if(axis[1] === 0) {
+ axis[1] = 1;
+ }
+ }, function() {
+ axis[1] = 0;
+ }).clear;
+
+ var timer = setInterval(function(){
+
+ //NO CHANGE
+ if(axis[0] === 0 && axis[1] === 0) {
+ return;
+ }
+
+ //run the callback
+ callback(axis);
+
+ }, 1);
+
+ return {
+ "clear": clear
+ }
+ }
+
+ /**
+ * Clears all key and key combo binds containing a given key or keys.
+ * @param keys
+ */
+ function unbindKey(keys) {
+
+ if(keys === 'all') {
+ keyBindingGroups = [];
+ return;
+ }
+
+ keys = keys.replace(/\s/g, '').split(',');
+
+ //loop through the key binding groups.
+ for(var iKCL = keyBindingGroups.length; iKCL > -1; iKCL -= 1) {
+ if(keyBindingGroups[iKCL]) {
+ var KeyBindingGroup = keyBindingGroups[iKCL];
+
+ //loop through the key bindings.
+ for(var iB = 0; iB < KeyBindingGroup.length; iB += 1) {
+ var keyBinding = KeyBindingGroup[iB],
+ remove = false;
+
+ //loop through the current key binding keys.
+ for(var iKB = 0; iKB < keyBinding.keys.length; iKB += 1) {
+ var key = keyBinding.keys[iKB];
+
+ //loop through the keys to be removed
+ for(var iKR = 0; iKR < keys.length; iKR += 1) {
+ var keyToRemove = keys[iKR];
+ if(keyToRemove === key) {
+ remove = true;
+ break;
+ }
+ }
+ if(remove) { break; }
+ }
+ if(remove) {
+ keyBindingGroups[iKCL].splice(iB, 1); iB -= 1;
+ if(keyBindingGroups[iKCL].length < 1) {
+ delete keyBindingGroups[iKCL];
+ }
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Gets an array of active keys
+ */
+ function getActiveKeys() {
+ return activeKeys;
+ }
+
+ /**
+ * Adds a new keyboard local not supported by keyboard JS
+ * @param local
+ * @param keys
+ */
+ function addLocale(local, keys) {
+ locals[local] = keys;
+ }
+
+ /**
+ * Changes the keyboard local
+ * @param local
+ */
+ function setLocale(local) {
+ if(locals[local]) {
+ keys = locals[local];
+ }
+
+ }
+
+ return {
+ "bind": {
+ "key": bindKey,
+ "axis": bindAxis
+ },
+ "activeKeys": getActiveKeys,
+ "unbind": {
+ "key": unbindKey
+ },
+ "locale": {
+ "add": addLocale,
+ "set": setLocale
+ }
+ }
+}));

0 comments on commit 5c1ff14

Please sign in to comment.
Something went wrong with that request. Please try again.