diff --git a/src/Umbraco.Web.UI.Client/src/common/services/keyboard.service.js b/src/Umbraco.Web.UI.Client/src/common/services/keyboard.service.js index a9fb9eed84..f0b7f4dc55 100644 --- a/src/Umbraco.Web.UI.Client/src/common/services/keyboard.service.js +++ b/src/Umbraco.Web.UI.Client/src/common/services/keyboard.service.js @@ -1,270 +1,323 @@ // This service was based on OpenJS library available in BSD License // http://www.openjs.com/scripts/events/keyboard_shortcuts/index.php -angular.module('umbraco.services') -.factory('keyboardService', ['$window', '$timeout', function ($window, $timeout) { - var keyboardManagerService = {}; - var defaultOpt = { - 'type': 'keydown', - 'propagate': false, - 'inputDisabled': false, - 'target': $window.document, - 'keyCode': false - }; - var isMac = navigator.platform.toUpperCase().indexOf('MAC')>=0; +function keyboardService($window, $timeout) { + + var keyboardManagerService = {}; + + var defaultOpt = { + 'type': 'keydown', + 'propagate': false, + 'inputDisabled': false, + 'target': $window.document, + 'keyCode': false + }; - // Store all keyboard combination shortcuts - keyboardManagerService.keyboardEvent = {}; + // Work around for stupid Shift key bug created by using lowercase - as a result the shift+num combination was broken + var shift_nums = { + "`": "~", + "1": "!", + "2": "@", + "3": "#", + "4": "$", + "5": "%", + "6": "^", + "7": "&", + "8": "*", + "9": "(", + "0": ")", + "-": "_", + "=": "+", + ";": ":", + "'": "\"", + ",": "<", + ".": ">", + "/": "?", + "\\": "|" + }; + // Special Keys - and their codes + var special_keys = { + 'esc': 27, + 'escape': 27, + 'tab': 9, + 'space': 32, + 'return': 13, + 'enter': 13, + 'backspace': 8, - // Add a new keyboard combination shortcut - keyboardManagerService.bind = function (label, callback, opt) { + 'scrolllock': 145, + 'scroll_lock': 145, + 'scroll': 145, + 'capslock': 20, + 'caps_lock': 20, + 'caps': 20, + 'numlock': 144, + 'num_lock': 144, + 'num': 144, - //replace ctrl key with meta key - if(isMac && label !== "ctrl+space"){ - label = label.replace("ctrl","meta"); - } + 'pause': 19, + 'break': 19, - var fct, elt, code, k; - // Initialize opt object - opt = angular.extend({}, defaultOpt, opt); - label = label.toLowerCase(); - elt = opt.target; - if(typeof opt.target === 'string'){ - elt = document.getElementById(opt.target); - } + 'insert': 45, + 'home': 36, + 'delete': 46, + 'end': 35, + 'pageup': 33, + 'page_up': 33, + 'pu': 33, - fct = function (e) { - e = e || $window.event; + 'pagedown': 34, + 'page_down': 34, + 'pd': 34, - // Disable event handler when focus input and textarea - if (opt['inputDisabled']) { - var elt; - if (e.target){ - elt = e.target; - }else if (e.srcElement){ - elt = e.srcElement; - } + 'left': 37, + 'up': 38, + 'right': 39, + 'down': 40, - if (elt.nodeType === 3){elt = elt.parentNode;} - if (elt.tagName === 'INPUT' || elt.tagName === 'TEXTAREA'){return;} - } + 'f1': 112, + 'f2': 113, + 'f3': 114, + 'f4': 115, + 'f5': 116, + 'f6': 117, + 'f7': 118, + 'f8': 119, + 'f9': 120, + 'f10': 121, + 'f11': 122, + 'f12': 123 + }; - // Find out which key is pressed - if (e.keyCode){ - code = e.keyCode; - }else if (e.which){ - code = e.which; - } + var isMac = navigator.platform.toUpperCase().indexOf('MAC')>=0; - var character = String.fromCharCode(code).toLowerCase(); + // The event handler for bound element events + function eventHandler(e) { + e = e || $window.event; - if (code === 188){character = ",";} // If the user presses , when the type is onkeydown - if (code === 190){character = ".";} // If the user presses , when the type is onkeydown + var code, k; - var keys = label.split("+"); - // Key Pressed - counts the number of valid keypresses - if it is same as the number of keys, the shortcut function is invoked - var kp = 0; - // Work around for stupid Shift key bug created by using lowercase - as a result the shift+num combination was broken - var shift_nums = { - "`":"~", - "1":"!", - "2":"@", - "3":"#", - "4":"$", - "5":"%", - "6":"^", - "7":"&", - "8":"*", - "9":"(", - "0":")", - "-":"_", - "=":"+", - ";":":", - "'":"\"", - ",":"<", - ".":">", - "/":"?", - "\\":"|" - }; - // Special Keys - and their codes - var special_keys = { - 'esc':27, - 'escape':27, - 'tab':9, - 'space':32, - 'return':13, - 'enter':13, - 'backspace':8, + // Find out which key is pressed + if (e.keyCode) + { + code = e.keyCode; + } + else if (e.which) { + code = e.which; + } - 'scrolllock':145, - 'scroll_lock':145, - 'scroll':145, - 'capslock':20, - 'caps_lock':20, - 'caps':20, - 'numlock':144, - 'num_lock':144, - 'num':144, + var character = String.fromCharCode(code).toLowerCase(); - 'pause':19, - 'break':19, + if (code === 188){character = ",";} // If the user presses , when the type is onkeydown + if (code === 190){character = ".";} // If the user presses , when the type is onkeydown - 'insert':45, - 'home':36, - 'delete':46, - 'end':35, + var propagate = true; - 'pageup':33, - 'page_up':33, - 'pu':33, + //Now we need to determine which shortcut this event is for, we'll do this by iterating over each + //registered shortcut to find the match. We use Find here so that the loop exits as soon + //as we've found the one we're looking for + _.find(_.keys(keyboardManagerService.keyboardEvent), function(key) { - 'pagedown':34, - 'page_down':34, - 'pd':34, + var shortcutLabel = key; + var shortcutVal = keyboardManagerService.keyboardEvent[key]; - 'left':37, - 'up':38, - 'right':39, - 'down':40, + // Key Pressed - counts the number of valid keypresses - if it is same as the number of keys, the shortcut function is invoked + var kp = 0; - 'f1':112, - 'f2':113, - 'f3':114, - 'f4':115, - 'f5':116, - 'f6':117, - 'f7':118, - 'f8':119, - 'f9':120, - 'f10':121, - 'f11':122, - 'f12':123 - }; - // Some modifiers key - var modifiers = { - shift: { - wanted: false, - pressed: e.shiftKey ? true : false - }, - ctrl : { - wanted: false, - pressed: e.ctrlKey ? true : false - }, - alt : { - wanted: false, - pressed: e.altKey ? true : false - }, - meta : { //Meta is Mac specific - wanted: false, - pressed: e.metaKey ? true : false - } - }; - // Foreach keys in label (split on +) - var l = keys.length; - for (var i = 0; i < l; i++) { + // Some modifiers key + var modifiers = { + shift: { + wanted: false, + pressed: e.shiftKey ? true : false + }, + ctrl: { + wanted: false, + pressed: e.ctrlKey ? true : false + }, + alt: { + wanted: false, + pressed: e.altKey ? true : false + }, + meta: { //Meta is Mac specific + wanted: false, + pressed: e.metaKey ? true : false + } + }; - var k=keys[i]; - switch (k) { - case 'ctrl': - case 'control': - kp++; - modifiers.ctrl.wanted = true; - break; - case 'shift': - case 'alt': - case 'meta': - kp++; - modifiers[k].wanted = true; - break; - } + var keys = shortcutLabel.split("+"); + var opt = shortcutVal.opt; + var callback = shortcutVal.callback; - if (k.length > 1) { // If it is a special key - if(special_keys[k] === code){ - kp++; - } + // Foreach keys in label (split on +) + var l = keys.length; + for (var i = 0; i < l; i++) { - } else if (opt['keyCode']) { // If a specific key is set into the config - if (opt['keyCode'] === code) { - kp++; - } + var k = keys[i]; + switch (k) { + case 'ctrl': + case 'control': + kp++; + modifiers.ctrl.wanted = true; + break; + case 'shift': + case 'alt': + case 'meta': + kp++; + modifiers[k].wanted = true; + break; + } - } else { // The special keys did not match - if(character === k) { - kp++; - }else { - if(shift_nums[character] && e.shiftKey) { // Stupid Shift key bug created by using lowercase - character = shift_nums[character]; - if(character === k){ - kp++; - } - } - } - } + if (k.length > 1) { // If it is a special key + if (special_keys[k] === code) { + kp++; + } + } + else if (opt['keyCode']) { // If a specific key is set into the config + if (opt['keyCode'] === code) { + kp++; + } + } + else { // The special keys did not match + if (character === k) { + kp++; + } + else { + if (shift_nums[character] && e.shiftKey) { // Stupid Shift key bug created by using lowercase + character = shift_nums[character]; + if (character === k) { + kp++; + } + } + } + } - } //for end + } //for end - if(kp === keys.length && - modifiers.ctrl.pressed === modifiers.ctrl.wanted && - modifiers.shift.pressed === modifiers.shift.wanted && - modifiers.alt.pressed === modifiers.alt.wanted && - modifiers.meta.pressed === modifiers.meta.wanted) { - $timeout(function() { - callback(e); - }, 1); + if (kp === keys.length && + modifiers.ctrl.pressed === modifiers.ctrl.wanted && + modifiers.shift.pressed === modifiers.shift.wanted && + modifiers.alt.pressed === modifiers.alt.wanted && + modifiers.meta.pressed === modifiers.meta.wanted) { - if(!opt['propagate']) { // Stop the event - // e.cancelBubble is supported by IE - this will kill the bubbling process. - e.cancelBubble = true; - e.returnValue = false; + //found the right callback! - // e.stopPropagation works in Firefox. - if (e.stopPropagation) { - e.stopPropagation(); - e.preventDefault(); - } - return false; - } - } - }; - // Store shortcut - keyboardManagerService.keyboardEvent[label] = { - 'callback': fct, - 'target': elt, - 'event': opt['type'] - }; + // Disable event handler when focus input and textarea + if (opt['inputDisabled']) { + var elt; + if (e.target) { + elt = e.target; + } else if (e.srcElement) { + elt = e.srcElement; + } - //Attach the function with the event - if(elt.addEventListener){ - elt.addEventListener(opt['type'], fct, false); - }else if(elt.attachEvent){ - elt.attachEvent('on' + opt['type'], fct); - }else{ - elt['on' + opt['type']] = fct; - } - }; - // Remove the shortcut - just specify the shortcut and I will remove the binding - keyboardManagerService.unbind = function (label) { - label = label.toLowerCase(); - var binding = keyboardManagerService.keyboardEvent[label]; - delete(keyboardManagerService.keyboardEvent[label]); + if (elt.nodeType === 3) { elt = elt.parentNode; } + if (elt.tagName === 'INPUT' || elt.tagName === 'TEXTAREA') { + //This exits the Find loop + return true; + } + } - if(!binding){return;} + $timeout(function () { + callback(e); + }, 1); - var type = binding['event'], + if (!opt['propagate']) { // Stop the event + propagate = false; + } + + //This exits the Find loop + return true; + } + + //we haven't found one so continue looking + return false; + + }); + + // Stop the event if required + if (!propagate) { + // e.cancelBubble is supported by IE - this will kill the bubbling process. + e.cancelBubble = true; + e.returnValue = false; + + // e.stopPropagation works in Firefox. + if (e.stopPropagation) { + e.stopPropagation(); + e.preventDefault(); + } + return false; + } + } + + // Store all keyboard combination shortcuts + keyboardManagerService.keyboardEvent = {}; + + // Add a new keyboard combination shortcut + keyboardManagerService.bind = function (label, callback, opt) { + + //replace ctrl key with meta key + if(isMac && label !== "ctrl+space"){ + label = label.replace("ctrl","meta"); + } + + var elt; + // Initialize opt object + opt = angular.extend({}, defaultOpt, opt); + label = label.toLowerCase(); + elt = opt.target; + if(typeof opt.target === 'string'){ + elt = document.getElementById(opt.target); + } + + //Ensure we aren't double binding to the same element + type otherwise we'll end up multi-binding + // and raising events for now reason. So here we'll check if the event is already registered for the element + var boundValues = _.values(keyboardManagerService.keyboardEvent); + var found = _.find(boundValues, function (i) { + return i.target === elt && i.event === opt['type']; + }); + + // Store shortcut + keyboardManagerService.keyboardEvent[label] = { + 'callback': callback, + 'target': elt, + 'opt': opt['type'] + }; + + if (!found) { + //Attach the function with the event + if (elt.addEventListener) { + elt.addEventListener(opt['type'], eventHandler, false); + } else if (elt.attachEvent) { + elt.attachEvent('on' + opt['type'], eventHandler); + } else { + elt['on' + opt['type']] = eventHandler; + } + } + + }; + // Remove the shortcut - just specify the shortcut and I will remove the binding + keyboardManagerService.unbind = function (label) { + label = label.toLowerCase(); + var binding = keyboardManagerService.keyboardEvent[label]; + delete(keyboardManagerService.keyboardEvent[label]); + + if(!binding){return;} + + var type = binding['event'], elt = binding['target'], callback = binding['callback']; - if(elt.detachEvent){ - elt.detachEvent('on' + type, callback); - }else if(elt.removeEventListener){ - elt.removeEventListener(type, callback, false); - }else{ - elt['on'+type] = false; - } - }; - // + if(elt.detachEvent){ + elt.detachEvent('on' + type, callback); + }else if(elt.removeEventListener){ + elt.removeEventListener(type, callback, false); + }else{ + elt['on'+type] = false; + } + }; + // - return keyboardManagerService; -}]); \ No newline at end of file + return keyboardManagerService; +} angular.module('umbraco.services').factory('keyboardService', ['$window', '$timeout', keyboardService]); \ No newline at end of file