| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477 | <!DOCTYPE html><html><head>  <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />  <title>The source code</title>  <link href="../resources/prettify/prettify.css" type="text/css" rel="stylesheet" />  <script type="text/javascript" src="../resources/prettify/prettify.js"></script>  <style type="text/css">    .highlight { display: block; background-color: #ddd; }  </style>  <script type="text/javascript">    function highlight() {      document.getElementById(location.hash.replace(/#/, "")).className = "highlight";    }  </script></head><body onload="prettyPrint(); highlight();">  <pre class="prettyprint lang-js"><span id='Ext-util-KeyMap'>/**</span> * Handles mapping key events to handling functions for an element or a Component. One KeyMap can be used for multiple * actions. * * A KeyMap must be configured with a {@link #target} as an event source which may be an Element or a Component. * * If the target is an element, then the `keydown` event will trigger the invocation of {@link #binding}s. * * It is possible to configure the KeyMap with a custom {@link #eventName} to listen for. This may be useful when the * {@link #target} is a Component. * * The KeyMap's event handling requires that the first parameter passed is a key event. So if the Component's event * signature is different, specify a {@link #processEvent} configuration which accepts the event's parameters and * returns a key event. * * Functions specified in {@link #binding}s are called with this signature : `(String key, Ext.EventObject e)` (if the * match is a multi-key combination the callback will still be called only once). A KeyMap can also handle a string * representation of keys. By default KeyMap starts enabled. * * Usage: * *     // map one key by key code *     var map = new Ext.util.KeyMap({ *         target: "my-element", *         key: 13, // or Ext.EventObject.ENTER *         fn: myHandler, *         scope: myObject *     }); * *     // map multiple keys to one action by string *     var map = new Ext.util.KeyMap({ *         target: "my-element", *         key: "a\r\n\t", *         fn: myHandler, *         scope: myObject *     }); * *     // map multiple keys to multiple actions by strings and array of codes *     var map = new Ext.util.KeyMap({ *         target: "my-element", *         binding: [{ *             key: [10,13], *             fn: function(){ alert("Return was pressed"); } *         }, { *             key: "abc", *             fn: function(){ alert('a, b or c was pressed'); } *         }, { *             key: "\t", *             ctrl:true, *             shift:true, *             fn: function(){ alert('Control + shift + tab was pressed.'); } *         }] *     }); * * Since 4.1.0, KeyMaps can bind to Components and process key-based events fired by Components. * * To bind to a Component, use the single parameter form of constructor and include the Component event name * to listen for, and a `processEvent` implementation which returns the key event for further processing by * the KeyMap: * *     var map = new Ext.util.KeyMap({ *         target: myGridView, *         eventName: 'itemkeydown', *         processEvent: function(view, record, node, index, event) { * *             // Load the event with the extra information needed by the mappings *             event.view = view; *             event.store = view.getStore(); *             event.record = record; *             event.index = index; *             return event; *         }, *         binding: { *             key: Ext.EventObject.DELETE, *             fn: function(keyCode, e) { *                 e.store.remove(e.record); * *                 // Attempt to select the record that's now in its place *                 e.view.getSelectionModel().select(e.index); *                 e.view.el.focus(); *             } *         } *     }); */Ext.define('Ext.util.KeyMap', {    alternateClassName: 'Ext.KeyMap',<span id='Ext-util-KeyMap-cfg-target'>    /**</span>     * @cfg {Ext.Component/Ext.Element/HTMLElement/String} target     * The object on which to listen for the event specified by the {@link #eventName} config option.     */<span id='Ext-util-KeyMap-cfg-binding'>    /**</span>     * @cfg {Object/Object[][]} binding     * Either a single object describing a handling function for s specified key (or set of keys), or     * an array of such objects.     * @cfg {String/String[]} binding.key A single keycode or an array of keycodes to handle     * @cfg {Boolean}  binding.shift True to handle key only when shift is pressed, False to handle the     *  key only when shift is not pressed (defaults to undefined)     * @cfg {Boolean}  binding.ctrl True to handle key only when ctrl is pressed, False to handle the     *  key only when ctrl is not pressed (defaults to undefined)     * @cfg {Boolean}  binding.alt True to handle key only when alt is pressed, False to handle the key     *  only when alt is not pressed (defaults to undefined)     * @cfg {Function} binding.handler The function to call when KeyMap finds the expected key combination     * @cfg {Function} binding.fn Alias of handler (for backwards-compatibility)     * @cfg {Object}   binding.scope The scope of the callback function     * @cfg {String}   binding.defaultEventAction A default action to apply to the event. Possible values     *  are: stopEvent, stopPropagation, preventDefault. If no value is set no action is performed.     */<span id='Ext-util-KeyMap-cfg-processEventScope'>    /**</span>     * @cfg {Object} [processEventScope=this]     * The scope (`this` context) in which the {@link #processEvent} method is executed.     */<span id='Ext-util-KeyMap-cfg-ignoreInputFields'>    /**</span>     * @cfg {Boolean} [ignoreInputFields=false]     * Configure this as `true` if there are any input fields within the {@link #target}, and this KeyNav     * should not process events from input fields, (`&lt;input>, &lt;textarea> and elements with `contentEditable="true"`)     */<span id='Ext-util-KeyMap-cfg-eventName'>    /**</span>     * @cfg {String} eventName     * The event to listen for to pick up key events.     */    eventName: 'keydown',    constructor: function(config) {        var me = this;        // Handle legacy arg list in which the first argument is the target.        // TODO: Deprecate in V5        if ((arguments.length !== 1) || (typeof config === 'string') || config.dom || config.tagName || config === document || config.isComponent) {            me.legacyConstructor.apply(me, arguments);            return;        }        Ext.apply(me, config);        me.bindings = [];        if (!me.target.isComponent) {            me.target = Ext.get(me.target);        }        if (me.binding) {            me.addBinding(me.binding);        } else if (config.key) {            me.addBinding(config);        }        me.enable();    },<span id='Ext-util-KeyMap-method-legacyConstructor'>    /**</span>     * @private     * Old constructor signature     * @param {String/HTMLElement/Ext.Element/Ext.Component} el The element or its ID, or Component to bind to     * @param {Object} binding The binding (see {@link #addBinding})     * @param {String} [eventName="keydown"] The event to bind to     */    legacyConstructor: function(el, binding, eventName){        var me = this;        Ext.apply(me, {            target: Ext.get(el),            eventName: eventName || me.eventName,            bindings: []        });        if (binding) {            me.addBinding(binding);        }        me.enable();    },<span id='Ext-util-KeyMap-method-addBinding'>    /**</span>     * Add a new binding to this KeyMap.     *     * Usage:     *     *     // Create a KeyMap     *     var map = new Ext.util.KeyMap(document, {     *         key: Ext.EventObject.ENTER,     *         fn: handleKey,     *         scope: this     *     });     *     *     //Add a new binding to the existing KeyMap later     *     map.addBinding({     *         key: 'abc',     *         shift: true,     *         fn: handleKey,     *         scope: this     *     });     *     * @param {Object/Object[]} binding A single KeyMap config or an array of configs.     * The following config object properties are supported:     * @param {String/Array} binding.key A single keycode or an array of keycodes to handle.     * @param {Boolean} binding.shift True to handle key only when shift is pressed,     * False to handle the keyonly when shift is not pressed (defaults to undefined).     * @param {Boolean} binding.ctrl True to handle key only when ctrl is pressed,     * False to handle the key only when ctrl is not pressed (defaults to undefined).     * @param {Boolean} binding.alt True to handle key only when alt is pressed,     * False to handle the key only when alt is not pressed (defaults to undefined).     * @param {Function} binding.handler The function to call when KeyMap finds the     * expected key combination.     * @param {Function} binding.fn Alias of handler (for backwards-compatibility).     * @param {Object} binding.scope The scope of the callback function.     * @param {String} binding.defaultEventAction A default action to apply to the event.     * Possible values are: stopEvent, stopPropagation, preventDefault. If no value is     * set no action is performed..     */    addBinding : function(binding){        var keyCode = binding.key,            processed = false,            key,            keys,            keyString,            i,            len;        if (Ext.isArray(binding)) {            for (i = 0, len = binding.length; i < len; i++) {                this.addBinding(binding[i]);            }            return;        }        if (Ext.isString(keyCode)) {            keys = [];            keyString = keyCode.toUpperCase();            for (i = 0, len = keyString.length; i < len; ++i){                keys.push(keyString.charCodeAt(i));            }            keyCode = keys;            processed = true;        }        if (!Ext.isArray(keyCode)) {            keyCode = [keyCode];        }        if (!processed) {            for (i = 0, len = keyCode.length; i < len; ++i) {                key = keyCode[i];                if (Ext.isString(key)) {                    keyCode[i] = key.toUpperCase().charCodeAt(0);                }            }        }        this.bindings.push(Ext.apply({            keyCode: keyCode        }, binding));    },<span id='Ext-util-KeyMap-method-handleTargetEvent'>    /**</span>     * Process the {@link #eventName event} from the {@link #target}.     * @private     * @param {Ext.EventObject} event     */    handleTargetEvent: (function() {        var tagRe = /input|textarea/i;        return function(event) {            var me = this,                bindings, i, len,                target, contentEditable;            if (this.enabled) { //just in case                bindings = this.bindings;                i = 0;                len = bindings.length;                // Process the event                event = me.processEvent.apply(me||me.processEventScope, arguments);                // Ignore events from input fields if configured to do so                if (me.ignoreInputFields) {                    target = event.target;                    contentEditable = target.contentEditable;                    // contentEditable will default to inherit if not specified, only check if the                    // attribute has been set or explicitly set to true                    // http://html5doctor.com/the-contenteditable-attribute/                    if (tagRe.test(target.tagName) || (contentEditable === '' || contentEditable === 'true')) {                        return;                    }                }                // If the processor does not return a keyEvent, we can't process it.                // Allow them to return false to cancel processing of the event                if (!event.getKey) {                    return event;                }                for(; i < len; ++i){                    this.processBinding(bindings[i], event);                }            }        }    }()),<span id='Ext-util-KeyMap-cfg-processEvent'>    /**</span>     * @cfg {Function} processEvent     * An optional event processor function which accepts the argument list provided by the     * {@link #eventName configured event} of the {@link #target}, and returns a keyEvent for processing by the KeyMap.     *     * This may be useful when the {@link #target} is a Component with s complex event signature, where the event is not     * the first parameter. Extra information from the event arguments may be injected into the event for use by the handler     * functions before returning it.     */    processEvent: function(event){        return event;    },<span id='Ext-util-KeyMap-method-processBinding'>    /**</span>     * Process a particular binding and fire the handler if necessary.     * @private     * @param {Object} binding The binding information     * @param {Ext.EventObject} event     */    processBinding: function(binding, event){        if (this.checkModifiers(binding, event)) {            var key = event.getKey(),                handler = binding.fn || binding.handler,                scope = binding.scope || this,                keyCode = binding.keyCode,                defaultEventAction = binding.defaultEventAction,                i,                len,                keydownEvent = new Ext.EventObjectImpl(event);            for (i = 0, len = keyCode.length; i < len; ++i) {                if (key === keyCode[i]) {                    if (handler.call(scope, key, event) !== true && defaultEventAction) {                        keydownEvent[defaultEventAction]();                    }                    break;                }            }        }    },<span id='Ext-util-KeyMap-method-checkModifiers'>    /**</span>     * Check if the modifiers on the event match those on the binding     * @private     * @param {Object} binding     * @param {Ext.EventObject} event     * @return {Boolean} True if the event matches the binding     */    checkModifiers: function(binding, e) {        var keys = ['shift', 'ctrl', 'alt'],            i = 0,            len = keys.length,            val, key;        for (; i < len; ++i){            key = keys[i];            val = binding[key];            if (!(val === undefined || (val === e[key + 'Key']))) {                return false;            }        }        return true;    },<span id='Ext-util-KeyMap-method-on'>    /**</span>     * Shorthand for adding a single key listener.     *     * @param {Number/Number[]/Object} key Either the numeric key code, array of key codes or an object with the     * following options: `{key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}`     * @param {Function} fn The function to call     * @param {Object} [scope] The scope (`this` reference) in which the function is executed.     * Defaults to the browser window.     */    on: function(key, fn, scope) {        var keyCode, shift, ctrl, alt;        if (Ext.isObject(key) && !Ext.isArray(key)) {            keyCode = key.key;            shift = key.shift;            ctrl = key.ctrl;            alt = key.alt;        } else {            keyCode = key;        }        this.addBinding({            key: keyCode,            shift: shift,            ctrl: ctrl,            alt: alt,            fn: fn,            scope: scope        });    },<span id='Ext-util-KeyMap-method-isEnabled'>    /**</span>     * Returns true if this KeyMap is enabled     * @return {Boolean}     */    isEnabled : function() {        return this.enabled;    },<span id='Ext-util-KeyMap-method-enable'>    /**</span>     * Enables this KeyMap     */    enable: function() {        var me = this;                if (!me.enabled) {            me.target.on(me.eventName, me.handleTargetEvent, me);            me.enabled = true;        }    },<span id='Ext-util-KeyMap-method-disable'>    /**</span>     * Disable this KeyMap     */    disable: function() {        var me = this;                if (me.enabled) {            me.target.removeListener(me.eventName, me.handleTargetEvent, me);            me.enabled = false;        }    },<span id='Ext-util-KeyMap-method-setDisabled'>    /**</span>     * Convenience function for setting disabled/enabled by boolean.     * @param {Boolean} disabled     */    setDisabled : function(disabled) {        if (disabled) {            this.disable();        } else {            this.enable();        }    },<span id='Ext-util-KeyMap-method-destroy'>    /**</span>     * Destroys the KeyMap instance and removes all handlers.     * @param {Boolean} removeTarget True to also remove the {@link #target}     */    destroy: function(removeTarget) {        var me = this,            target = me.target;        me.bindings = [];        me.disable();        if (removeTarget === true) {            if (target.isComponent) {                target.destroy();            } else {                target.remove();            }        }        delete me.target;    }});</pre></body></html>
 |