jquery.hotkeys-0.7.9.js 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245
  1. /*
  2. (c) Copyrights 2007 - 2008
  3. Original idea by by Binny V A, http://www.openjs.com/scripts/events/keyboard_shortcuts/
  4. jQuery Plugin by Tzury Bar Yochay
  5. tzury.by@gmail.com
  6. http://evalinux.wordpress.com
  7. http://facebook.com/profile.php?id=513676303
  8. Project's sites:
  9. http://code.google.com/p/js-hotkeys/
  10. http://github.com/tzuryby/hotkeys/tree/master
  11. License: same as jQuery license.
  12. USAGE:
  13. // simple usage
  14. $(document).bind('keydown', 'Ctrl+c', function(){ alert('copy anyone?');});
  15. // special options such as disableInIput
  16. $(document).bind('keydown', {combi:'Ctrl+x', disableInInput: true} , function() {});
  17. Note:
  18. This plugin wraps the following jQuery methods: $.fn.find, $.fn.bind and $.fn.unbind
  19. */
  20. (function (jQuery){
  21. // keep reference to the original $.fn.bind, $.fn.unbind and $.fn.find
  22. jQuery.fn.__bind__ = jQuery.fn.bind;
  23. jQuery.fn.__unbind__ = jQuery.fn.unbind;
  24. jQuery.fn.__find__ = jQuery.fn.find;
  25. var hotkeys = {
  26. version: '0.7.9',
  27. override: /keypress|keydown|keyup/g,
  28. triggersMap: {},
  29. specialKeys: { 27: 'esc', 9: 'tab', 32:'space', 13: 'return', 8:'backspace', 145: 'scroll',
  30. 20: 'capslock', 144: 'numlock', 19:'pause', 45:'insert', 36:'home', 46:'del',
  31. 35:'end', 33: 'pageup', 34:'pagedown', 37:'left', 38:'up', 39:'right',40:'down',
  32. 109: '-',
  33. 112:'f1',113:'f2', 114:'f3', 115:'f4', 116:'f5', 117:'f6', 118:'f7', 119:'f8',
  34. 120:'f9', 121:'f10', 122:'f11', 123:'f12', 191: '/'},
  35. shiftNums: { "`":"~", "1":"!", "2":"@", "3":"#", "4":"$", "5":"%", "6":"^", "7":"&",
  36. "8":"*", "9":"(", "0":")", "-":"_", "=":"+", ";":":", "'":"\"", ",":"<",
  37. ".":">", "/":"?", "\\":"|" },
  38. newTrigger: function (type, combi, callback) {
  39. // i.e. {'keyup': {'ctrl': {cb: callback, disableInInput: false}}}
  40. var result = {};
  41. result[type] = {};
  42. result[type][combi] = {cb: callback, disableInInput: false};
  43. return result;
  44. }
  45. };
  46. // add firefox num pad char codes
  47. //if (jQuery.browser.mozilla){
  48. // add num pad char codes
  49. hotkeys.specialKeys = jQuery.extend(hotkeys.specialKeys, { 96: '0', 97:'1', 98: '2', 99:
  50. '3', 100: '4', 101: '5', 102: '6', 103: '7', 104: '8', 105: '9', 106: '*',
  51. 107: '+', 109: '-', 110: '.', 111 : '/'
  52. });
  53. //}
  54. // a wrapper around of $.fn.find
  55. // see more at: http://groups.google.com/group/jquery-en/browse_thread/thread/18f9825e8d22f18d
  56. jQuery.fn.find = function( selector ) {
  57. this.query = selector;
  58. return jQuery.fn.__find__.apply(this, arguments);
  59. };
  60. jQuery.fn.unbind = function (type, combi, fn){
  61. if (jQuery.isFunction(combi)){
  62. fn = combi;
  63. combi = null;
  64. }
  65. if (combi && typeof combi === 'string'){
  66. var selectorId = ((this.prevObject && this.prevObject.query) || (this[0].id && this[0].id) || this[0]).toString();
  67. var hkTypes = type.split(' ');
  68. for (var x=0; x<hkTypes.length; x++){
  69. delete hotkeys.triggersMap[selectorId][hkTypes[x]][combi];
  70. }
  71. }
  72. // call jQuery original unbind
  73. return this.__unbind__(type, fn);
  74. };
  75. jQuery.fn.bind = function(type, data, fn){
  76. // grab keyup,keydown,keypress
  77. var handle = type.match(hotkeys.override);
  78. if (jQuery.isFunction(data) || !handle){
  79. // call jQuery.bind only
  80. return this.__bind__(type, data, fn);
  81. }
  82. else{
  83. // split the job
  84. var result = null,
  85. // pass the rest to the original $.fn.bind
  86. pass2jq = jQuery.trim(type.replace(hotkeys.override, ''));
  87. // see if there are other types, pass them to the original $.fn.bind
  88. if (pass2jq){
  89. result = this.__bind__(pass2jq, data, fn);
  90. }
  91. if (typeof data === "string"){
  92. data = {'combi': data};
  93. }
  94. if(data.combi){
  95. for (var x=0; x < handle.length; x++){
  96. var eventType = handle[x];
  97. var combi = data.combi.toLowerCase(),
  98. trigger = hotkeys.newTrigger(eventType, combi, fn),
  99. selectorId = ((this.prevObject && this.prevObject.query) || (this[0].id && this[0].id) || this[0]).toString();
  100. //trigger[eventType][combi].propagate = data.propagate;
  101. trigger[eventType][combi].disableInInput = data.disableInInput;
  102. // first time selector is bounded
  103. if (!hotkeys.triggersMap[selectorId]) {
  104. hotkeys.triggersMap[selectorId] = trigger;
  105. }
  106. // first time selector is bounded with this type
  107. else if (!hotkeys.triggersMap[selectorId][eventType]) {
  108. hotkeys.triggersMap[selectorId][eventType] = trigger[eventType];
  109. }
  110. // make trigger point as array so more than one handler can be bound
  111. var mapPoint = hotkeys.triggersMap[selectorId][eventType][combi];
  112. if (!mapPoint){
  113. hotkeys.triggersMap[selectorId][eventType][combi] = [trigger[eventType][combi]];
  114. }
  115. else if (mapPoint.constructor !== Array){
  116. hotkeys.triggersMap[selectorId][eventType][combi] = [mapPoint];
  117. }
  118. else {
  119. hotkeys.triggersMap[selectorId][eventType][combi][mapPoint.length] = trigger[eventType][combi];
  120. }
  121. // add attribute and call $.event.add per matched element
  122. this.each(function(){
  123. // jQuery wrapper for the current element
  124. var jqElem = jQuery(this);
  125. // element already associated with another collection
  126. if (jqElem.attr('hkId') && jqElem.attr('hkId') !== selectorId){
  127. selectorId = jqElem.attr('hkId') + ";" + selectorId;
  128. }
  129. jqElem.attr('hkId', selectorId);
  130. });
  131. result = this.__bind__(handle.join(' '), data, hotkeys.handler)
  132. }
  133. }
  134. return result;
  135. }
  136. };
  137. // work-around for opera and safari where (sometimes) the target is the element which was last
  138. // clicked with the mouse and not the document event it would make sense to get the document
  139. hotkeys.findElement = function (elem){
  140. if (!jQuery(elem).attr('hkId')){
  141. if (jQuery.browser.opera || jQuery.browser.safari){
  142. while (!jQuery(elem).attr('hkId') && elem.parentNode){
  143. elem = elem.parentNode;
  144. }
  145. }
  146. }
  147. return elem;
  148. };
  149. // the event handler
  150. hotkeys.handler = function(event) {
  151. var target = hotkeys.findElement(event.currentTarget),
  152. jTarget = jQuery(target),
  153. ids = jTarget.attr('hkId');
  154. if(ids){
  155. ids = ids.split(';');
  156. var code = event.which,
  157. type = event.type,
  158. special = hotkeys.specialKeys[code],
  159. // prevent f5 overlapping with 't' (or f4 with 's', etc.)
  160. character = !special && String.fromCharCode(code).toLowerCase(),
  161. shift = event.shiftKey,
  162. ctrl = event.ctrlKey,
  163. // patch for jquery 1.2.5 && 1.2.6 see more at:
  164. // http://groups.google.com/group/jquery-en/browse_thread/thread/83e10b3bb1f1c32b
  165. alt = event.altKey || event.originalEvent.altKey,
  166. mapPoint = null;
  167. for (var x=0; x < ids.length; x++){
  168. if (hotkeys.triggersMap[ids[x]][type]){
  169. mapPoint = hotkeys.triggersMap[ids[x]][type];
  170. break;
  171. }
  172. }
  173. //find by: id.type.combi.options
  174. if (mapPoint){
  175. var trigger;
  176. // event type is associated with the hkId
  177. if(!shift && !ctrl && !alt) { // No Modifiers
  178. trigger = mapPoint[special] || (character && mapPoint[character]);
  179. }
  180. else{
  181. // check combinations (alt|ctrl|shift+anything)
  182. var modif = '';
  183. if(alt) modif +='alt+';
  184. if(ctrl) modif+= 'ctrl+';
  185. if(shift) modif += 'shift+';
  186. // modifiers + special keys or modifiers + character or modifiers + shift character or just shift character
  187. trigger = mapPoint[modif+special];
  188. if (!trigger){
  189. if (character){
  190. trigger = mapPoint[modif+character]
  191. || mapPoint[modif+hotkeys.shiftNums[character]]
  192. // '$' can be triggered as 'Shift+4' or 'Shift+$' or just '$'
  193. || (modif === 'shift+' && mapPoint[hotkeys.shiftNums[character]]);
  194. }
  195. }
  196. }
  197. if (trigger){
  198. var result = false;
  199. for (var x=0; x < trigger.length; x++){
  200. if(trigger[x].disableInInput){
  201. // double check event.currentTarget and event.target
  202. var elem = jQuery(event.target);
  203. if (jTarget.is("input") || jTarget.is("textarea") || jTarget.is("select")
  204. || elem.is("input") || elem.is("textarea") || elem.is("select")) {
  205. return true;
  206. }
  207. }
  208. // call the registered callback function
  209. result = result || trigger[x].cb.apply(this, [event]);
  210. }
  211. return result;
  212. }
  213. }
  214. }
  215. };
  216. // place it under window so it can be extended and overridden by others
  217. window.hotkeys = hotkeys;
  218. return jQuery;
  219. })(jQuery);