Alien-GvaScript

 view release on metacpan or  search on metacpan

src/keyMap.js  view on Meta::CPAN


//constructor
GvaScript.KeyMap = function (rules) {
    if (!(rules instanceof Object)) throw "KeyMap: invalid argument";
    this.rules = [rules];
    return this;
};


GvaScript.KeyMap.prototype = {
  destroy: function() {
    Event.stopObserving(this.elem, this.eventType, this.eventHandler);
  },

  eventHandler: function(event) {
    var keymap = this;

    // translate key code into key name
    event.keyName = GvaScript.KeyMap.KEYS.BUILTIN_NAMES[event.keyCode]
                 || String.fromCharCode(event.keyCode);

    // add Control|Shift|Alt modifiers
    event.keyModifiers = "";
    if (event.ctrlKey  && !this.options.ignoreCtrl)  event.keyModifiers += "C_";
    if (event.shiftKey && !this.options.ignoreShift) event.keyModifiers += "S_";
    if (event.altKey   && !this.options.ignoreAlt)   event.keyModifiers += "A_";

    // but cancel all modifiers if main key is Control|Shift|Alt
    if (event.keyName.search(/^(CTRL|SHIFT|ALT)$/) == 0)
      event.keyModifiers = "";

    // try to get the corresponding handler, and call it if found
    var handler = keymap._findInStack(event, keymap.rules);
    if (handler) {
      var toStop = handler.call(keymap, event);
      Event.detailedStop(event, toStop || this.options);
    }
  },

  observe: function(eventType, elem, options) {
    this.eventType = eventType || 'keydown';
    this.elem      = elem      || document;

    // "Shift" modifier usually does not make sense for keypress events
    if (eventType == 'keypress' && !options)
      options = {ignoreShift: true};

    this.options = Class.checkOptions(Event.stopNone, this.options || {});

    this.eventHandler = this.eventHandler.bindAsEventListener(this);
    Event.observe(this.elem, this.eventType, this.eventHandler);
  },

  _findInStack: function(event, stack) {
    for (var i = stack.length - 1; i >= 0; i--) {
      var rules = stack[i];

      // trick to differentiate between C_9 (digit) and C_09 (TAB)
      var keyCode = event.keyCode>9 ? event.keyCode : ("0"+event.keyCode);

      var handler = rules[event.keyModifiers + event.keyName]
                 || rules[event.keyModifiers + keyCode]
                 || this._regex_handler(event, rules.REGEX, true)
                 || this._regex_handler(event, rules.ANTIREGEX, false);
      if (handler)
        return handler;
    }
    return null;
  },

  _regex_handler: function(event, regex_rules, want_match) {
    if (!regex_rules) return null;
    for (var j = 0; j < regex_rules.length; j++) {
      var rule      = regex_rules[j];
      var modifiers = rule[0];
      var regex     = rule[1];
      var handler   = rule[2];

      var same_modifiers = modifiers == null
                        || modifiers == event.keyModifiers;

      // build regex if it was passed as a string
      if (typeof(regex) == "string")
        regex = new RegExp("^(" + regex + ")$");

      var match = same_modifiers && regex.test(event.keyName);
      if ((match && want_match) || (!match && !want_match))
        return handler;
    }
    return null;
  }
};

GvaScript.KeyMap.MapAllKeys = function(handler) {
    return {REGEX:[[null, /.*/, handler]]}
};


GvaScript.KeyMap.Prefix = function(rules) {

    // create a specific handler for the next character ...
    var one_time_handler = function (event) {
        this.rules.pop(); // cancel prefix
        var handler = this._findInStack(event, [rules]);
        if (handler) handler.call(this, event);
    }

    // ... and push that handler on top of the current rules
    return function(event) {
        this.rules.push(GvaScript.KeyMap.MapAllKeys(one_time_handler));
    }
};

// helpers for identifying keys
GvaScript.KeyMap.KEYS = {
    BUILTIN_NAMES : {
        8: "BACKSPACE",
        9: "TAB",
        10: "LINEFEED",
        13: "RETURN",
        16: "SHIFT",
        17: "CTRL",
        18: "ALT",
        19: "PAUSE",
        20: "CAPS_LOCK",
        27: "ESCAPE",
        32: "SPACE",
        33: "PAGE_UP",
        34: "PAGE_DOWN",
        35: "END",
        36: "HOME",
        37: "LEFT",
        38: "UP",



( run in 0.321 second using v1.01-cache-2.11-cpan-02777c243ea )