Products.CMFPlone/5.0.x-jqtree-contextmenu: Add JS Shortcut for keyboard shortcut support.
Oshane Bailey <jenkins-z4DKO/[email protected]>
| Newsgroups | gmane.comp.web.zope.plone.cvs |
|---|---|
| Message-ID | <[email protected]> |
Repository: Products.CMFPlone Branch: refs/heads/5.0.x-jqtree-contextmenu Date: 2017-07-11T01:32:05-05:00 Author: Oshane Bailey (b4oshany) <[email protected]> Commit: https://github.com/plone/Products.CMFPlone/commit/3922d0e9de7165359710e81f27099ff602c74889 Add JS Shortcut for keyboard shortcut support. Files changed: A Products/CMFPlone/static/components/js-shortcuts/LICENSE A Products/CMFPlone/static/components/js-shortcuts/README.md A Products/CMFPlone/static/components/js-shortcuts/js-shortcuts-angular.js A Products/CMFPlone/static/components/js-shortcuts/js-shortcuts.js M CHANGES.rst M Products/CMFPlone/profiles/dependencies/registry.xml M Products/CMFPlone/static/bower.json diff --git a/CHANGES.rst b/CHANGES.rst index 8e7fabc7a..7b7e61db5 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -15,7 +15,9 @@ Breaking changes: New features: -- Create an upgrade step to 5020 from 5019. +- Add js-shortcuts package for keyboard shortcuts + [b4oshany] +- Update package version to 5020 from 5019. [b4oshany] - Add more ignores to static/.gitignore. [thet] diff --git a/Products/CMFPlone/profiles/dependencies/registry.xml b/Products/CMFPlone/profiles/dependencies/registry.xml index e05bdca7f..5720ce2fb 100644 --- a/Products/CMFPlone/profiles/dependencies/registry.xml +++ b/Products/CMFPlone/profiles/dependencies/registry.xml @@ -324,6 +324,12 @@ <value key="deps">jqtree</value> </records> + <records prefix="plone.resources/js-shortcuts" + interface='Products.CMFPlone.interfaces.IResourceRegistry'> + <value key="js">++plone++static/components/js-shortcuts/js-shortcuts.js</value> + <value key="deps">jquery</value> + </records> + <records prefix="plone.resources/jquery.form" interface='Products.CMFPlone.interfaces.IResourceRegistry'> <value key="js">++plone++static/components/jquery-form/jquery.form.js</value> diff --git a/Products/CMFPlone/static/bower.json b/Products/CMFPlone/static/bower.json index 6a152b52f..98a8726e1 100644 --- a/Products/CMFPlone/static/bower.json +++ b/Products/CMFPlone/static/bower.json @@ -28,7 +28,8 @@ "requirejs": "", "less": "2.1.2", "r.js": "2.1.15", - "cs-jqtree-contextmenu": "^0.1.0" + "cs-jqtree-contextmenu": "^0.1.0", + "js-shortcuts": "^1.0.1" }, "resolutions": { "jquery": "1.11.3", diff --git a/Products/CMFPlone/static/components/js-shortcuts/LICENSE b/Products/CMFPlone/static/components/js-shortcuts/LICENSE new file mode 100644 index 000000000..a44eb83a0 --- /dev/null +++ b/Products/CMFPlone/static/components/js-shortcuts/LICENSE @@ -0,0 +1,28 @@ +Copyright (c) 2015, Saneem +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +* 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. + +* Neither the name of js-shortcuts nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 THE COPYRIGHT HOLDER 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. + diff --git a/Products/CMFPlone/static/components/js-shortcuts/README.md b/Products/CMFPlone/static/components/js-shortcuts/README.md new file mode 100644 index 000000000..534330311 --- /dev/null +++ b/Products/CMFPlone/static/components/js-shortcuts/README.md @@ -0,0 +1,49 @@ +# js-shortcuts + +Easily add combination key-press event listeners in JavaScript. Useful for adding keyboard shortcuts to web applications. + +Here's the [original version](http://www.openjs.com/scripts/events/keyboard_shortcuts/). I'm putting this up on GitHub to make loading this overusing Bower a bit easier. + +## Usage + +Basic Version + + shortcut.add("Ctrl+B", function () { + window.alert("Make it Bold!"); + }); + +AngularJS + + app.controller('yourCtrl', function(jsShortcuts) { + + jsShortcuts.add("Ctrl+B", function () { + window.alert("Make it Bold!"); + }); + + }); + +More information available on the [original doc](http://www.openjs.com/scripts/events/keyboard_shortcuts/) page. + +## Installation + +### Bower +Install via bower: + + bower install js-shortcuts --save + +and require the script: + + <script src="bower_components/js-shortcuts/js-shortcuts.js"></script> + +### Angular +Install via bower as above. Use the AngularJS version of the script. + + <script src="bower_components/js-shortcuts/js-shortcuts-angular.js"></script> + +Add the module `js-shortcuts` as a dependency to your app + + var app = angular.module('yourAwesomeApp', ['js-shortcuts']); + + +## License +BSD License diff --git a/Products/CMFPlone/static/components/js-shortcuts/js-shortcuts-angular.js b/Products/CMFPlone/static/components/js-shortcuts/js-shortcuts-angular.js new file mode 100644 index 000000000..7c4b80705 --- /dev/null +++ b/Products/CMFPlone/static/components/js-shortcuts/js-shortcuts-angular.js @@ -0,0 +1,300 @@ +/** + @license + Adapted for Angular by Saneem + Original version by Binny V A + License: MIT + */ +(function () { + 'use strict'; + + angular.module('js-shortcuts', []).provider('jsShortcuts', function () { + + this.$get = function () { + var all_shortcuts = {}; + + return { + add: function (shortcut_combination, callback, opt) { + //Provide a set of default options + var default_options = { + 'type': 'keydown', + 'propagate': false, + 'disable_in_input': false, + 'target': document, + 'keycode': false + }; + + if (!opt) { + opt = default_options; + } else { + for (var dfo in default_options) { + if (typeof opt[dfo] === 'undefined') { + opt[dfo] = default_options[dfo]; + } + } + } + + var ele = opt.target; + if (typeof opt.target === 'string') { + ele = document.getElementById(opt.target); + } + + shortcut_combination = shortcut_combination.toLowerCase(); + + //The function to be called at keypress + var func = function (e) { + var code; + e = e || window.event; + + if (opt.disable_in_input) { //Don't enable shortcut keys in Input, Textarea fields + var element; + if (e.target) { + element = e.target; + } else if (e.srcElement) { + element = e.srcElement; + } + if (element.nodeType === 3) { + element = element.parentNode; + } + + if (element.tagName === 'INPUT' || element.tagName === 'TEXTAREA') { + return; + } + } + + //Find Which key is pressed + if (e.keyCode) { + code = e.keyCode; + } else if (e.which) { + code = e.which; + } + + var character = String.fromCharCode(code).toLowerCase(); + + 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 keys = shortcut_combination.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, + + 'scrolllock': 145, + 'scroll_lock': 145, + 'scroll': 145, + 'capslock': 20, + 'caps_lock': 20, + 'caps': 20, + 'numlock': 144, + 'num_lock': 144, + 'num': 144, + + 'pause': 19, + 'break': 19, + + 'insert': 45, + 'home': 36, + 'delete': 46, + 'end': 35, + + 'pageup': 33, + 'page_up': 33, + 'pu': 33, + + 'pagedown': 34, + 'page_down': 34, + 'pd': 34, + + 'left': 37, + 'up': 38, + 'right': 39, + 'down': 40, + + 'f1': 112, + 'f2': 113, + 'f3': 114, + 'f4': 115, + 'f5': 116, + 'f6': 117, + 'f7': 118, + 'f8': 119, + 'f9': 120, + 'f10': 121, + 'f11': 122, + 'f12': 123 + }; + + var modifiers = { + shift: { + wanted: false, + pressed: false + }, + ctrl: { + wanted: false, + pressed: false + }, + alt: { + wanted: false, + pressed: false + }, + meta: { + wanted: false, + pressed: false + } //Meta is Mac specific + }; + + if (e.ctrlKey) { + modifiers.ctrl.pressed = true; + } + if (e.shiftKey) { + modifiers.shift.pressed = true; + } + if (e.altKey) { + modifiers.alt.pressed = true; + } + if (e.metaKey) { + modifiers.meta.pressed = true; + } + + var k; + + for (var i = 0; k = keys[i], i < keys.length; i++) { + //Modifiers + if (k === 'ctrl' || k === 'control') { + kp++; + modifiers.ctrl.wanted = true; + + } else if (k === 'shift') { + kp++; + modifiers.shift.wanted = true; + + } else if (k === 'alt') { + kp++; + modifiers.alt.wanted = true; + } else if (k === 'meta') { + kp++; + modifiers.meta.wanted = true; + } else if (k.length > 1) { //If it is a special key + if (special_keys[k] === code) { + kp++; + } + + } else if (opt.keycode) { + 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++; + } + } + } + } + } + + 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) { + callback(e); + + if (!opt.propagate) { //Stop the event + //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; + } + } + }; + + all_shortcuts[shortcut_combination] = { + 'callback': func, + 'target': ele, + 'event': opt.type + }; + //Attach the function with the event + if (ele.addEventListener) { + ele.addEventListener(opt.type, func, false); + } else if (ele.attachEvent) { + ele.attachEvent('on' + opt.type, func); + } else { + ele['on' + opt.type] = func; + } + }, + + remove: function (shortcut_combination) { + shortcut_combination = shortcut_combination.toLowerCase(); + var binding = all_shortcuts[shortcut_combination]; + delete(all_shortcuts[shortcut_combination]); + if (!binding) { + return; + } + var type = binding.event; + var ele = binding.target; + var callback = binding.callback; + + if (ele.detachEvent) { + ele.detachEvent('on' + type, callback); + } else if (ele.removeEventListener) { + ele.removeEventListener(type, callback, false); + } else { + ele['on' + type] = false; + } + } + + + }; + }; + + }); + + +}).call(this); \ No newline at end of file diff --git a/Products/CMFPlone/static/components/js-shortcuts/js-shortcuts.js b/Products/CMFPlone/static/components/js-shortcuts/js-shortcuts.js new file mode 100644 index 000000000..38742f897 --- /dev/null +++ b/Products/CMFPlone/static/components/js-shortcuts/js-shortcuts.js @@ -0,0 +1,223 @@ +/** + * http://www.openjs.com/scripts/events/keyboard_shortcuts/ + * Version : 2.01.B + * By Binny V A + * License : BSD + */ +shortcut = { + 'all_shortcuts':{},//All the shortcuts are stored in this array + 'add': function(shortcut_combination,callback,opt) { + //Provide a set of default options + var default_options = { + 'type':'keydown', + 'propagate':false, + 'disable_in_input':false, + 'target':document, + 'keycode':false + } + if(!opt) opt = default_options; + else { + for(var dfo in default_options) { + if(typeof opt[dfo] == 'undefined') opt[dfo] = default_options[dfo]; + } + } + + var ele = opt.target; + if(typeof opt.target == 'string') ele = document.getElementById(opt.target); + var ths = this; + shortcut_combination = shortcut_combination.toLowerCase(); + + //The function to be called at keypress + var func = function(e) { + e = e || window.event; + + if(opt['disable_in_input']) { //Don't enable shortcut keys in Input, Textarea fields + var element; + if(e.target) element=e.target; + else if(e.srcElement) element=e.srcElement; + if(element.nodeType==3) element=element.parentNode; + + if(element.tagName == 'INPUT' || element.tagName == 'TEXTAREA') return; + } + + //Find Which key is pressed + if (e.keyCode) code = e.keyCode; + else if (e.which) code = e.which; + var character = String.fromCharCode(code).toLowerCase(); + + 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 keys = shortcut_combination.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, + + 'scrolllock':145, + 'scroll_lock':145, + 'scroll':145, + 'capslock':20, + 'caps_lock':20, + 'caps':20, + 'numlock':144, + 'num_lock':144, + 'num':144, + + 'pause':19, + 'break':19, + + 'insert':45, + 'home':36, + 'delete':46, + 'end':35, + + 'pageup':33, + 'page_up':33, + 'pu':33, + + 'pagedown':34, + 'page_down':34, + 'pd':34, + + 'left':37, + 'up':38, + 'right':39, + 'down':40, + + 'f1':112, + 'f2':113, + 'f3':114, + 'f4':115, + 'f5':116, + 'f6':117, + 'f7':118, + 'f8':119, + 'f9':120, + 'f10':121, + 'f11':122, + 'f12':123 + } + + var modifiers = { + shift: { wanted:false, pressed:false}, + ctrl : { wanted:false, pressed:false}, + alt : { wanted:false, pressed:false}, + meta : { wanted:false, pressed:false} //Meta is Mac specific + }; + + if(e.ctrlKey) modifiers.ctrl.pressed = true; + if(e.shiftKey) modifiers.shift.pressed = true; + if(e.altKey) modifiers.alt.pressed = true; + if(e.metaKey) modifiers.meta.pressed = true; + + for(var i=0; k=keys[i],i<keys.length; i++) { + //Modifiers + if(k == 'ctrl' || k == 'control') { + kp++; + modifiers.ctrl.wanted = true; + + } else if(k == 'shift') { + kp++; + modifiers.shift.wanted = true; + + } else if(k == 'alt') { + kp++; + modifiers.alt.wanted = true; + } else if(k == 'meta') { + kp++; + modifiers.meta.wanted = true; + } else if(k.length > 1) { //If it is a special key + if(special_keys[k] == code) kp++; + + } else if(opt['keycode']) { + 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++; + } + } + } + } + + 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) { + callback(e); + + if(!opt['propagate']) { //Stop the event + //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; + } + } + } + this.all_shortcuts[shortcut_combination] = { + 'callback':func, + 'target':ele, + 'event': opt['type'] + }; + //Attach the function with the event + if(ele.addEventListener) ele.addEventListener(opt['type'], func, false); + else if(ele.attachEvent) ele.attachEvent('on'+opt['type'], func); + else ele['on'+opt['type']] = func; + }, + + //Remove the shortcut - just specify the shortcut and I will remove the binding + 'remove':function(shortcut_combination) { + shortcut_combination = shortcut_combination.toLowerCase(); + var binding = this.all_shortcuts[shortcut_combination]; + delete(this.all_shortcuts[shortcut_combination]) + if(!binding) return; + var type = binding['event']; + var ele = binding['target']; + var callback = binding['callback']; + + if(ele.detachEvent) ele.detachEvent('on'+type, callback); + else if(ele.removeEventListener) ele.removeEventListener(type, callback, false); + else ele['on'+type] = false; + } +} ------------------------------------------------------------------------------ Check out the vibrant tech community on one of the world's most engaging tech sites, Slashdot.org! http://sdm.link/slashdot