maheshchari / js-hotkeys

Automatically exported from code.google.com/p/js-hotkeys
0 stars 0 forks source link

jQuery 1.4.2 calls the bind function twice #78

Open GoogleCodeExporter opened 8 years ago

GoogleCodeExporter commented 8 years ago
What steps will reproduce the problem?
1. Use the minified version with jQuery 1.4.2
2. Bind F1 with some function
3. Press F1 on that page

What is the expected output? What do you see instead?
The function should be called just one when F1 is pressed

What version of the product are you using? On what operating system?
jQuery 1.4.2 minified, js-hotkeys 0.7.9 minified

Please provide any additional information below.
It works perfectly with jQuery 1.4 minified. Not sure if this is a problem
inside jQuery 1.4.2?

Original issue reported on code.google.com by viliam.h...@gmail.com on 24 Feb 2010 at 4:09

GoogleCodeExporter commented 8 years ago
Appears on Firefox 3.5.6 to be more specific

Original comment by viliam.h...@gmail.com on 24 Feb 2010 at 4:10

GoogleCodeExporter commented 8 years ago
also experienced this issue on chrome 4.0.249.89 with jQuery 1.4.2 minified +
js-hotkeys 0.7.9 minified

Original comment by jebaird on 25 Feb 2010 at 8:11

GoogleCodeExporter commented 8 years ago
John Resig JQuery's creator has fork and fix these problems.
check it out at http://github.com/jeresig/jquery.hotkeys/

Original comment by Afro.Sys...@gmail.com on 26 Feb 2010 at 5:02

GoogleCodeExporter commented 8 years ago
Resig's version doesn't fix my problems. I start with a ctrl+m macro, and if I 
add another macro, it stops working. 
If I use Resig's version instead, it doesn't work at all either way. Using 
jQuery 1.4.2, and this happens in Chrome 
and Firefox at least.

Original comment by thebroke...@gmail.com on 19 Apr 2010 at 6:16

GoogleCodeExporter commented 8 years ago
@thebrokenladder in that case, I'l check this soon and get back to you with 
answers.

Original comment by Afro.Sys...@gmail.com on 20 Apr 2010 at 3:23

GoogleCodeExporter commented 8 years ago
If i use Resig's version, i lose the ability to have an textbox as the focus 
and still 
use the hotkeys, any chance you could do up the changes like Resig did but 
still keep 
the ability to use this on a textbox even with bindings like: 
$(document).bind('keydown','f2',function myFun(event) { }));???

Original comment by ray73...@gmail.com on 10 May 2010 at 4:55

GoogleCodeExporter commented 8 years ago
As of aug8, bug exists on firefox 3.6.8 and chrome 5.0.375. Resig's version 
removes the double triggering on firefox. But it still exists on chrome. It 
looks like a bounded event for a hotkey is trigged as many times as there are 
js-hotkey binded events.

Original comment by tejeshwa...@gmail.com on 8 Aug 2010 at 10:02

GoogleCodeExporter commented 8 years ago
Bug confirmed in Safari Mac 5.0 (6533.16), Firefox Mac 3.5.4, Chrome Mac 
5.0.375.125.

Both in 0.7.9 full and 0.7.9 minified together with jquery 1.4.2 full.

Confirmed that the event is triggered as many times as there are events of the 
same type (i.e. 'keydown', 'keyup') bound to the same object.

Workaround/ugly fix: bind the event to different objects, i.e. 
$(document).bind(....) and $('body').bind(....)

Original comment by tako.bru...@gmail.com on 10 Aug 2010 at 7:05

GoogleCodeExporter commented 8 years ago
[deleted comment]
GoogleCodeExporter commented 8 years ago
Hey guys I have fixed the problem of multiple bind problem as well as unbinding 
issue. There is a sinple workarround for this I have added the code below. This 
code is tested with Firefox and Internet Explorer and its working fine for 
thos, not tested for other browser's. Let me know if you guys find any problem 
in other browser's as well if you face any problem.

Here is the code:

/*
(c) Copyrights 2007 - 2008

Original idea by by Binny V A, 
http://www.openjs.com/scripts/events/keyboard_shortcuts/

jQuery Plugin by Tzury Bar Yochay 
tzury.by@gmail.com
http://evalinux.wordpress.com
http://facebook.com/profile.php?id=513676303

Project's sites: 
http://code.google.com/p/js-hotkeys/
http://github.com/tzuryby/hotkeys/tree/master

License: same as jQuery license. 

USAGE:
    // simple usage
    $(document).bind('keydown', 'Ctrl+c', function(){ alert('copy anyone?');});

    // special options such as disableInIput
    $(document).bind('keydown', {combi:'Ctrl+x', disableInInput: true} , function() {});

Note:
    This plugin wraps the following jQuery methods: $.fn.find, $.fn.bind and $.fn.unbind
*/

(function (jQuery){
    // keep reference to the original $.fn.bind, $.fn.unbind and $.fn.find
    jQuery.fn.__bind__ = jQuery.fn.bind;
    jQuery.fn.__unbind__ = jQuery.fn.unbind;
    jQuery.fn.__find__ = jQuery.fn.find;

    var hotkeys = {
        version: '0.7.9',
        override: /keypress|keydown|keyup/g,
        triggersMap: {},

        specialKeys: {
            27: 'esc',
            9: 'tab',
            32:'space',
            13: 'return',
            8:'backspace',
            145: 'scroll',
            20: 'capslock',
            144: 'numlock',
            19:'pause',
            45:'insert',
            36:'home',
            46:'del',
            35:'end', 
            33: 'pageup',
            34:'pagedown',
            37:'left',
            38:'up',
            39:'right',
            40:'down',
            109: '-', 
            112:'f1',
            113:'f2',
            114:'f3',
            115:'f4',
            116:'f5',
            117:'f6',
            118:'f7',
            119:'f8',
            120:'f9',
            121:'f10',
            122:'f11',
            123:'f12',
            191: '/'
        },

        shiftNums: {
            "`":"~",
            "1":"!",
            "2":"@",
            "3":"#",
            "4":"$",
            "5":"%",
            "6":"^",
            "7":"&",
            "8":"*",
            "9":"(",
            "0":")",
            "-":"_",
            "=":"+",
            ";":":",
            "'":"\"",
            ",":"<",
            ".":">",
            "/":"?",
            "\\":"|"
        },

        newTrigger: function (type, combi, callback) { 
            // i.e. {'keyup': {'ctrl': {cb: callback, disableInInput: false}}}
            var result = {};
            result[type] = {};
            result[type][combi] = {
                cb: callback,
                disableInInput: false
            };
            return result;
        }
    };
    // add firefox num pad char codes
    //if (jQuery.browser.mozilla){
    // add num pad char codes
    hotkeys.specialKeys = jQuery.extend(hotkeys.specialKeys, {
        96: '0',
        97:'1',
        98: '2',
        99:
        '3',
        100: '4',
        101: '5',
        102: '6',
        103: '7',
        104: '8',
        105: '9',
        106: '*',
        107: '+',
        109: '-',
        110: '.',
        111 : '/'
    });
    //}

    // a wrapper around of $.fn.find 
    // see more at: http://groups.google.com/group/jquery-en/browse_thread/thread/18f9825e8d22f18d
    jQuery.fn.find = function( selector ) {
        this.query = selector;
        return jQuery.fn.__find__.apply(this, arguments);
    };

    jQuery.fn.unbind = function (type, combi, fn){
        if (jQuery.isFunction(combi)){
            fn = combi;
            combi = null;
        }
        if (combi && typeof combi === 'string'){
            var selectorId = ((this.prevObject && this.prevObject.query) || (this[0].id && this[0].id) || this[0]).toString();
            var hkTypes = type.split(' ');
            for (var x=0; x<hkTypes.length; x++){
                delete hotkeys.triggersMap[selectorId][hkTypes[x]][combi.toLowerCase()];
            }
        }
        // call jQuery original unbind
        return  this.__unbind__(type, fn);
    };

    var flag = false;
    jQuery.fn.bind = function(type, data, fn){
        // grab keyup,keydown,keypress
        var handle = type.match(hotkeys.override);

        if (jQuery.isFunction(data) || !handle){
            // call jQuery.bind only
            return this.__bind__(type, data, fn);
        }
        else{
            // split the job
            var result = null,            
            // pass the rest to the original $.fn.bind
            pass2jq = jQuery.trim(type.replace(hotkeys.override, ''));

            // see if there are other types, pass them to the original $.fn.bind
            if (pass2jq){
                result = this.__bind__(pass2jq, data, fn);
            }            

            if (typeof data === "string"){
                data = {
                    'combi': data
                };
            }
            if(data.combi){
                for (var x=0; x < handle.length; x++){
                    var eventType = handle[x];

                    var combi = data.combi.toLowerCase(),
                    trigger = hotkeys.newTrigger(eventType, combi, fn),
                    selectorId = ((this.prevObject && this.prevObject.query) || (this[0].id && this[0].id) || this[0]).toString();

                    //trigger[eventType][combi].propagate = data.propagate;
                    trigger[eventType][combi].disableInInput = data.disableInInput;

                    // first time selector is bounded
                    if (!hotkeys.triggersMap[selectorId]) {
                        hotkeys.triggersMap[selectorId] = trigger;
                    }
                    // first time selector is bounded with this type
                    else if (!hotkeys.triggersMap[selectorId][eventType]) {
                        hotkeys.triggersMap[selectorId][eventType] = trigger[eventType];
                    }
                    // make trigger point as array so more than one handler can be bound
                    var mapPoint = hotkeys.triggersMap[selectorId][eventType][combi];
                    if (!mapPoint){
                        hotkeys.triggersMap[selectorId][eventType][combi] = [trigger[eventType][combi]];
                    }
                    else if (mapPoint.constructor !== Array){
                        hotkeys.triggersMap[selectorId][eventType][combi] = [mapPoint];
                    }
                    else {

                        hotkeys.triggersMap[selectorId][eventType][combi][mapPoint.length] = trigger[eventType][combi];
                    }
                    // add attribute and call $.event.add per matched element
                    this.each(function(){
                        // jQuery wrapper for the current element
                        var jqElem = jQuery(this);

                        // element already associated with another collection
                        if (jqElem.attr('hkId') && jqElem.attr('hkId') !== selectorId){
                            selectorId = jqElem.attr('hkId') + ";" + selectorId;
                        }
                        jqElem.attr('hkId', selectorId);
                    });
                    if(!flag)
                    {
                        flag = true;
                        result = this.__bind__(handle.join(' '), data, hotkeys.handler)
                    }
                }
            }
            return result;
        }
    };
    // work-around for opera and safari where (sometimes) the target is the element which was last 
    // clicked with the mouse and not the document event it would make sense to get the document
    hotkeys.findElement = function (elem){
        if (!jQuery(elem).attr('hkId')){
            if (jQuery.browser.opera || jQuery.browser.safari){
                while (!jQuery(elem).attr('hkId') && elem.parentNode){
                    elem = elem.parentNode;
                }
            }
        }
        return elem;
    };
    // the event handler
    hotkeys.handler = function(event) {
        var target = hotkeys.findElement(event.currentTarget), 
        jTarget = jQuery(target),
        ids = jTarget.attr('hkId');

        if(ids){
            ids = ids.split(';');
            var code = event.which,
            type = event.type,
            special = hotkeys.specialKeys[code],
            // prevent f5 overlapping with 't' (or f4 with 's', etc.)
            character = !special && String.fromCharCode(code).toLowerCase(),
            shift = event.shiftKey,
            ctrl = event.ctrlKey,
            // patch for jquery 1.2.5 && 1.2.6 see more at:
            // http://groups.google.com/group/jquery-en/browse_thread/thread/83e10b3bb1f1c32b
            alt = event.altKey || event.originalEvent.altKey,
            mapPoint = null;

            for (var x=0; x < ids.length; x++){
                if (hotkeys.triggersMap[ids[x]][type]){
                    mapPoint = hotkeys.triggersMap[ids[x]][type];
                    break;
                }
            }

            //find by: id.type.combi.options            
            if (mapPoint){ 
                var trigger;
                // event type is associated with the hkId
                if(!shift && !ctrl && !alt) { // No Modifiers
                    trigger = mapPoint[special] ||  (character && mapPoint[character]);
                }
                else{
                    // check combinations (alt|ctrl|shift+anything)
                    var modif = '';
                    if(alt) modif +='alt+';
                    if(ctrl) modif+= 'ctrl+';
                    if(shift) modif += 'shift+';
                    // modifiers + special keys or modifiers + character or modifiers + shift character or just shift character
                    trigger = mapPoint[modif+special];
                    if (!trigger){
                        if (character){
                            trigger = mapPoint[modif+character] 
                            || mapPoint[modif+hotkeys.shiftNums[character]]
                            // '$' can be triggered as 'Shift+4' or 'Shift+$' or just '$'
                            || (modif === 'shift+' && mapPoint[hotkeys.shiftNums[character]]);
                        }
                    }
                }
                if (trigger){
                    var result = false;
                    for (var x=0; x < trigger.length; x++){
                        if(trigger[x].disableInInput){
                            // double check event.currentTarget and event.target
                            var elem = jQuery(event.target);
                            if (jTarget.is("input") || jTarget.is("textarea") || jTarget.is("select") 
                                || elem.is("input") || elem.is("textarea") || elem.is("select")) {
                                return true;
                            }
                        }                       
                        // call the registered callback function
                        result = result || trigger[x].cb.apply(this, [event]);
                    }
                    return result;
                }
            }
        }
        return result;
    };
    // place it under window so it can be extended and overridden by others
    window.hotkeys = hotkeys;
    return jQuery;
})(jQuery);

Original comment by sandeep....@gmail.com on 7 Feb 2011 at 12:16

GoogleCodeExporter commented 8 years ago
Above rewritten plugin doesn't work for me. 
I made workaround, blocking execution of binded function for one second:
var keypressed = '';
$(iframeId).contents().bind('keypress', 'ctrl+u', function(){
    timestamp = Math.floor(new Date().getTime()/1000);
    if(keypressed == timestamp) {return false;}else {
        keypressed = timestamp;
        formatText('underline', rteName);
        return false;
    }
})

It is not good solution - accidentaly if user press key near end of second it 
may be triggered twice.

Original comment by kolibe...@gmail.com on 28 Apr 2011 at 10:31

GoogleCodeExporter commented 8 years ago
Version 0.8 working great using jquery 1.6.2. Tested on Chrome 13 and FF 5.0 
and IE 9

Original comment by aprudencio on 1 Sep 2011 at 5:08

GoogleCodeExporter commented 8 years ago
Hello Guys, 
Hey guys I have fixed the problem of multiple bind problem as well as unbinding 
issue. There is a sinple workarround for this I have added the code below. This 
code is tested with Firefox and Internet Explorer and its working fine for 
thos, not tested for other browser's. Let me know if you guys find any problem 
in other browser's as well if you face any problem.

Here is the code:

/*
(c) Copyrights 2007 - 2008

Original idea by by Binny V A, 
http://www.openjs.com/scripts/events/keyboard_shortcuts/

jQuery Plugin by Tzury Bar Yochay 
tzury.by@gmail.com
http://evalinux.wordpress.com
http://facebook.com/profile.php?id=513676303

Project's sites: 
http://code.google.com/p/js-hotkeys/
http://github.com/tzuryby/hotkeys/tree/master

License: same as jQuery license. 

USAGE:
    // simple usage
    $(document).bind('keydown', 'Ctrl+c', function(){ alert('copy anyone?');});

    // special options such as disableInIput
    $(document).bind('keydown', {combi:'Ctrl+x', disableInInput: true} , function() {});

Note:
    This plugin wraps the following jQuery methods: $.fn.find, $.fn.bind and $.fn.unbind
*/

(function (jQuery){
    // keep reference to the original $.fn.bind, $.fn.unbind and $.fn.find
    jQuery.fn.__bind__ = jQuery.fn.bind;
    jQuery.fn.__unbind__ = jQuery.fn.unbind;
    jQuery.fn.__find__ = jQuery.fn.find;

    var hotkeys = {
        version: '0.7.9',
        override: /keypress|keydown|keyup/g,
        triggersMap: {},

        specialKeys: {
            27: 'esc',
            9: 'tab',
            32:'space',
            13: 'return',
            8:'backspace',
            145: 'scroll',
            20: 'capslock',
            144: 'numlock',
            19:'pause',
            45:'insert',
            36:'home',
            46:'del',
            35:'end', 
            33: 'pageup',
            34:'pagedown',
            37:'left',
            38:'up',
            39:'right',
            40:'down',
            109: '-', 
            112:'f1',
            113:'f2',
            114:'f3',
            115:'f4',
            116:'f5',
            117:'f6',
            118:'f7',
            119:'f8',
            120:'f9',
            121:'f10',
            122:'f11',
            123:'f12',
            191: '/'
        },

        shiftNums: {
            "`":"~",
            "1":"!",
            "2":"@",
            "3":"#",
            "4":"$",
            "5":"%",
            "6":"^",
            "7":"&",
            "8":"*",
            "9":"(",
            "0":")",
            "-":"_",
            "=":"+",
            ";":":",
            "'":"\"",
            ",":"<",
            ".":">",
            "/":"?",
            "\\":"|"
        },

        newTrigger: function (type, combi, callback) { 
            // i.e. {'keyup': {'ctrl': {cb: callback, disableInInput: false}}}
            var result = {};
            result[type] = {};
            result[type][combi] = {
                cb: callback,
                disableInInput: false
            };
            return result;
        }
    };
    // add firefox num pad char codes
    //if (jQuery.browser.mozilla){
    // add num pad char codes
    hotkeys.specialKeys = jQuery.extend(hotkeys.specialKeys, {
        96: '0',
        97:'1',
        98: '2',
        99:
        '3',
        100: '4',
        101: '5',
        102: '6',
        103: '7',
        104: '8',
        105: '9',
        106: '*',
        107: '+',
        109: '-',
        110: '.',
        111 : '/'
    });
    //}

    // a wrapper around of $.fn.find 
    // see more at: http://groups.google.com/group/jquery-en/browse_thread/thread/18f9825e8d22f18d
    jQuery.fn.find = function( selector ) {
        this.query = selector;
        return jQuery.fn.__find__.apply(this, arguments);
    };

    jQuery.fn.unbind = function (type, combi, fn){
        if (jQuery.isFunction(combi)){
            fn = combi;
            combi = null;
        }
        if (combi && typeof combi === 'string'){
            var selectorId = ((this.prevObject && this.prevObject.query) || (this[0].id && this[0].id) || this[0]).toString();
            var hkTypes = type.split(' ');
            for (var x=0; x<hkTypes.length; x++){
                delete hotkeys.triggersMap[selectorId][hkTypes[x]][combi.toLowerCase()];
            }
        }
        // call jQuery original unbind
        return  this.__unbind__(type, fn);
    };

    var flag = false;
    jQuery.fn.bind = function(type, data, fn){
        // grab keyup,keydown,keypress
        var handle = type.match(hotkeys.override);

        if (jQuery.isFunction(data) || !handle){
            // call jQuery.bind only
            return this.__bind__(type, data, fn);
        }
        else{
            // split the job
            var result = null,            
            // pass the rest to the original $.fn.bind
            pass2jq = jQuery.trim(type.replace(hotkeys.override, ''));

            // see if there are other types, pass them to the original $.fn.bind
            if (pass2jq){
                result = this.__bind__(pass2jq, data, fn);
            }            

            if (typeof data === "string"){
                data = {
                    'combi': data
                };
            }
            if(data.combi){
                for (var x=0; x < handle.length; x++){
                    var eventType = handle[x];

                    var combi = data.combi.toLowerCase(),
                    trigger = hotkeys.newTrigger(eventType, combi, fn),
                    selectorId = ((this.prevObject && this.prevObject.query) || (this[0].id && this[0].id) || this[0]).toString();

                    //trigger[eventType][combi].propagate = data.propagate;
                    trigger[eventType][combi].disableInInput = data.disableInInput;

                    // first time selector is bounded
                    if (!hotkeys.triggersMap[selectorId]) {
                        hotkeys.triggersMap[selectorId] = trigger;
                    }
                    // first time selector is bounded with this type
                    else if (!hotkeys.triggersMap[selectorId][eventType]) {
                        hotkeys.triggersMap[selectorId][eventType] = trigger[eventType];
                    }
                    // make trigger point as array so more than one handler can be bound
                    var mapPoint = hotkeys.triggersMap[selectorId][eventType][combi];
                    if (!mapPoint){
                        hotkeys.triggersMap[selectorId][eventType][combi] = [trigger[eventType][combi]];
                    }
                    else if (mapPoint.constructor !== Array){
                        hotkeys.triggersMap[selectorId][eventType][combi] = [mapPoint];
                    }
                    else {

                        hotkeys.triggersMap[selectorId][eventType][combi][mapPoint.length] = trigger[eventType][combi];
                    }
                    // add attribute and call $.event.add per matched element
                    this.each(function(){
                        // jQuery wrapper for the current element
                        var jqElem = jQuery(this);

                        // element already associated with another collection
                        if (jqElem.attr('hkId') && jqElem.attr('hkId') !== selectorId){
                            selectorId = jqElem.attr('hkId') + ";" + selectorId;
                        }
                        jqElem.attr('hkId', selectorId);
                    });
                    if(!flag)
                    {
                        flag = true;
                        result = this.__bind__(handle.join(' '), data, hotkeys.handler)
                    }
                }
            }
            return result;
        }
    };
    // work-around for opera and safari where (sometimes) the target is the element which was last 
    // clicked with the mouse and not the document event it would make sense to get the document
    hotkeys.findElement = function (elem){
        if (!jQuery(elem).attr('hkId')){
            if (jQuery.browser.opera || jQuery.browser.safari){
                while (!jQuery(elem).attr('hkId') && elem.parentNode){
                    elem = elem.parentNode;
                }
            }
        }
        return elem;
    };
    // the event handler
    hotkeys.handler = function(event) {
        var target = hotkeys.findElement(event.currentTarget), 
        jTarget = jQuery(target),
        ids = jTarget.attr('hkId');

        if(ids){
            ids = ids.split(';');
            var code = event.which,
            type = event.type,
            special = hotkeys.specialKeys[code],
            // prevent f5 overlapping with 't' (or f4 with 's', etc.)
            character = !special && String.fromCharCode(code).toLowerCase(),
            shift = event.shiftKey,
            ctrl = event.ctrlKey,
            // patch for jquery 1.2.5 && 1.2.6 see more at:
            // http://groups.google.com/group/jquery-en/browse_thread/thread/83e10b3bb1f1c32b
            alt = event.altKey || event.originalEvent.altKey,
            mapPoint = null;

            for (var x=0; x < ids.length; x++){
                if (hotkeys.triggersMap[ids[x]][type]){
                    mapPoint = hotkeys.triggersMap[ids[x]][type];
                    break;
                }
            }

            //find by: id.type.combi.options            
            if (mapPoint){ 
                var trigger;
                // event type is associated with the hkId
                if(!shift && !ctrl && !alt) { // No Modifiers
                    trigger = mapPoint[special] ||  (character && mapPoint[character]);
                }
                else{
                    // check combinations (alt|ctrl|shift+anything)
                    var modif = '';
                    if(alt) modif +='alt+';
                    if(ctrl) modif+= 'ctrl+';
                    if(shift) modif += 'shift+';
                    // modifiers + special keys or modifiers + character or modifiers + shift character or just shift character
                    trigger = mapPoint[modif+special];
                    if (!trigger){
                        if (character){
                            trigger = mapPoint[modif+character] 
                            || mapPoint[modif+hotkeys.shiftNums[character]]
                            // '$' can be triggered as 'Shift+4' or 'Shift+$' or just '$'
                            || (modif === 'shift+' && mapPoint[hotkeys.shiftNums[character]]);
                        }
                    }
                }
                if (trigger){
                    var result = false;
                    for (var x=0; x < trigger.length; x++){
                        if(trigger[x].disableInInput){
                            // double check event.currentTarget and event.target
                            var elem = jQuery(event.target);
                            if (jTarget.is("input") || jTarget.is("textarea") || jTarget.is("select") 
                                || elem.is("input") || elem.is("textarea") || elem.is("select")) {
                                return true;
                            }
                        }                       
                        // call the registered callback function
                        result = result || trigger[x].cb.apply(this, [event]);
                    }
                    return result;
                }
            }
        }
        return result;
    };
    // place it under window so it can be extended and overridden by others
    window.hotkeys = hotkeys;
    return jQuery;
})(jQuery);

This code working fine with and resolved isssue.

Thanks

Original comment by anciwa...@gmail.com on 1 Dec 2011 at 12:34