Skip to content

Instantly share code, notes, and snippets.

@samternent
Last active September 13, 2022 13:42
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save samternent/f12ccf6c3854bc19dc4488a94080c138 to your computer and use it in GitHub Desktop.
Save samternent/f12ccf6c3854bc19dc4488a94080c138 to your computer and use it in GitHub Desktop.
keymando
var bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; },
indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; };
window.Keymando = (function() {
function Keymando(id, params, options) {
var ref, ref1;
if (params == null) {
params = {};
}
if (options == null) {
options = {};
}
this.dispose = bind(this.dispose, this);
this.disposeOf = bind(this.disposeOf, this);
this.disposeOfChildren = bind(this.disposeOfChildren, this);
this.unpause = bind(this.unpause, this);
this.pause = bind(this.pause, this);
this.trigger = bind(this.trigger, this);
this.getComponent = bind(this.getComponent, this);
this.components = {};
this.events = {};
this.inFocus = [];
this.hasFocus = id;
this.currentTarget = id;
this.useDOM = (ref = options.useDOM) != null ? ref : true;
this.focusOnFirstChild = (ref1 = options.focusOnFirstChild) != null ? ref1 : false;
this.parent = id;
this.pausedEvents = [];
this.mousetrap = new Mousetrap();
this.register(id, params);
return;
}
Keymando.prototype.register = function(id, params) {
var event, ref, ref1, ref2, ref3, ref4, ref5, ref6, ref7, ref8, type;
if (this.components[id] != null) {
this.removeFromNavigation(this.components[id].parent, id);
}
this.components[id] = {
id: id,
parent: (ref = params.parent) != null ? ref : null,
navigation: (ref1 = params.navigation) != null ? ref1 : [],
current: (ref2 = params.current) != null ? ref2 : -1,
onFocus: (ref3 = params.onFocus) != null ? ref3 : function() {},
onBlur: (ref4 = params.onBlur) != null ? ref4 : function() {},
events: (ref5 = params.events) != null ? ref5 : {},
data: (ref6 = params.data) != null ? ref6 : null,
displayIndex: (ref7 = params.displayIndex) != null ? ref7 : null
};
if ((params.parent != null) && (params.displayIndex != null)) {
this.addToNavigation(params.parent, id, params.displayIndex);
}
ref8 = params.events;
for (type in ref8) {
event = ref8[type];
this.registerEvent(type, event);
}
if (params.initFocus) {
this.selectElement(id);
}
};
Keymando.prototype.registerEvent = function(type, event) {
var action;
if (this.events[type] != null) {
return this.events[type] = !!(event != null ? event.allowBulk : void 0);
}
this.events[type] = !!(event != null ? event.allowBulk : void 0);
action = (function(_this) {
return function(type, e) {
var i;
if (_this.components[_this.hasFocus] == null) {
return;
}
if (typeof e.preventDefault === "function") {
e.preventDefault();
}
if (_this.events[type]) {
i = _this.inFocus.length;
while (i--) {
_this.fireEventChain(type, e, _this.inFocus[i]);
}
return;
}
_this.fireEventChain(type, e, _this.hasFocus);
};
})(this);
this.mousetrap.bind(type, function(e) {
return action(type, e);
});
};
Keymando.prototype.fireEventChain = function(type, e, activeId) {
var activeComponent, newOptions, ref, ref1, ref2, ref3;
if (this.pausedEvents.indexOf(type) > -1) {
return;
}
activeComponent = this.components[activeId];
if (activeComponent == null) {
return;
}
if (((ref = activeComponent.events) != null ? ref[type] : void 0) != null) {
this.fireEvent(type, e, activeId, activeComponent);
}
if ((activeComponent.parent != null) && !((ref1 = activeComponent.events[type]) != null ? ref1.cancelBubble : void 0)) {
newOptions = (ref2 = this.components[activeComponent.parent]) != null ? (ref3 = ref2.events) != null ? ref3[type] : void 0 : void 0;
this.fireEventChain(type, e, activeComponent.parent);
}
};
Keymando.prototype.fireEvent = function(type, e, activeId, activeComponent) {
var event, overrideGlobal, ref;
ref = activeComponent.events[type], event = ref.event, overrideGlobal = ref.overrideGlobal;
if (!overrideGlobal) {
Mousetrap.trigger(type);
}
if (event == null) {
return;
}
event(e, activeId, activeComponent, {
back: this.back.bind(this),
forward: this.forward.bind(this)
});
};
Keymando.prototype.selectElement = function(id) {
var oldId;
if (this.components[id] == null) {
return;
}
oldId = this.hasFocus;
if (this.inFocus.indexOf(id) < 0) {
this.inFocus.push(id);
}
this.hasFocus = id;
this.components[id].onFocus(id, oldId);
if (!(this.useDOM && (document.getElementById(id) != null))) {
return;
}
document.getElementById(id).focus();
};
Keymando.prototype.unselectElement = function(id, next) {
var index;
if (this.components[id] == null) {
return;
}
index = this.inFocus.indexOf(id);
if (index > -1) {
this.inFocus.splice(index, 1);
}
this.components[id].onBlur(id, next);
if (!(this.useDOM && (document.getElementById(id) != null))) {
return;
}
document.getElementById(id).blur();
};
Keymando.prototype.softUnselectElement = function(id, next) {
if (this.components[id] == null) {
return;
}
this.components[id].onBlur(id, next, true);
if (!(this.useDOM && (document.getElementById(id) != null))) {
return;
}
document.getElementById(id).blur();
};
Keymando.prototype.childHasFocus = function(id) {
var child, j, len, ref;
if (this.components[id] == null) {
return;
}
ref = this.components[id].navigation;
for (j = 0, len = ref.length; j < len; j++) {
child = ref[j];
if (child === this.hasFocus) {
return true;
}
}
return false;
};
Keymando.prototype.navigate = function(id, dir, multiselect) {
var i;
if (multiselect == null) {
multiselect = false;
}
if (this.components[id] == null) {
return;
}
if (this.components[id].current + dir > -1 && this.components[id].current + dir < this.components[id].navigation.length) {
this.components[id].current += dir;
}
i = this.inFocus.length;
while (i--) {
if (multiselect) {
this.softUnselectElement(this.inFocus[i], this.components[id].navigation[this.components[id].current]);
} else {
if (this.inFocus[i] !== id) {
this.unselectElement(this.inFocus[i], this.components[id].navigation[this.components[id].current]);
}
}
}
if (this.components[id].navigation[this.components[id].current] != null) {
this.selectElement(this.components[id].navigation[this.components[id].current]);
}
};
Keymando.prototype.forward = function(id, multiselect) {
if (multiselect == null) {
multiselect = false;
}
this.navigate(id, 1, multiselect);
};
Keymando.prototype.back = function(id, multiselect) {
if (multiselect == null) {
multiselect = false;
}
this.navigate(id, -1, multiselect);
};
Keymando.prototype.focus = function(id, multiselect) {
if (multiselect == null) {
multiselect = false;
}
this.navigate(id, 0, multiselect);
};
Keymando.prototype.navigateTo = function(parent, id, multiselect) {
var i, newIndex;
if (multiselect == null) {
multiselect = false;
}
if (this.components[parent] == null) {
return;
}
newIndex = this.components[parent].navigation.indexOf(id);
if (!((newIndex != null) || newIndex < 0)) {
return;
}
this.components[parent].current = newIndex;
if (!multiselect) {
i = this.inFocus.length;
while (i--) {
this.unselectElement(this.inFocus[i], this.components[parent].navigation[this.components[parent].current]);
}
}
if (this.components[parent].navigation[this.components[parent].current] != null) {
this.selectElement(this.components[parent].navigation[this.components[parent].current]);
}
};
Keymando.prototype.addToNavigation = function(parent, id, position) {
var parentComponent;
parentComponent = this.components[parent];
if (parentComponent == null) {
return;
}
if (parentComponent.navigation.indexOf(id) > -1) {
return;
}
if (!((position != null) || !position)) {
position = parentComponent.navigation.length;
}
this.components[parent].navigation.splice(position, 0, id);
};
Keymando.prototype.removeFromNavigation = function(parent, id) {
var index, parentComponent;
parentComponent = this.components[parent];
if (parentComponent == null) {
return;
}
index = parentComponent.navigation.indexOf(id);
if (index < 0) {
return;
}
this.components[parent].navigation.splice(index, 1);
};
Keymando.prototype.getPosition = function(parent, id) {
var parentComponent;
parentComponent = this.components[parent];
if (parentComponent == null) {
return null;
}
return parentComponent.navigation.indexOf(id);
};
Keymando.prototype.getPositionFromBottom = function(parent, id) {
var bottom, position;
position = this.getPosition(parent, id);
bottom = this.getNavigationLength(parent) - 1;
return bottom - position;
};
Keymando.prototype.getNavigationLength = function(id) {
var component;
component = this.components[id];
if (component == null) {
return null;
}
return component.navigation.length;
};
Keymando.prototype.resetNavigation = function(id, soft) {
var firstNavId, ref;
if (this.components[id] == null) {
return;
}
this.components[id].current = -1;
firstNavId = (ref = this.components[id].navigation) != null ? ref[0] : void 0;
if (this.focusOnFirstChild) {
this.hasFocus = firstNavId != null ? firstNavId : id;
this.inFocus = this.inFocus.filter((function(_this) {
return function(i) {
return !(indexOf.call(_this.components[id].navigation, i) >= 0);
};
})(this));
if (firstNavId != null) {
this.inFocus.push(firstNavId);
}
}
if (!(this.useDOM && !soft)) {
return;
}
if (document.getElementById(firstNavId) != null) {
document.getElementById(firstNavId).focus();
}
if (document.getElementById(id) != null) {
document.getElementById(id).focus();
}
};
Keymando.prototype.clearNavigation = function(id) {
if (this.components[id] == null) {
return;
}
this.components[id].navigation = [];
};
Keymando.prototype.getParent = function(id) {
var ref, ref1;
if (((ref = this.components[id]) != null ? ref.parent : void 0) == null) {
return;
}
return (ref1 = this.components[this.components[id].parent]) != null ? ref1 : false;
};
Keymando.prototype.getComponent = function(id) {
var ref;
return (ref = this.components[id]) != null ? ref : false;
};
Keymando.prototype.trigger = function(key) {
this.mousetrap.trigger(key);
};
Keymando.prototype.pause = function(types) {
var j, len, type;
if (types == null) {
return this.mousetrap.pause();
}
for (j = 0, len = types.length; j < len; j++) {
type = types[j];
if (this.pausedEvents.indexOf(type) < 0) {
this.pausedEvents.push(type);
}
}
};
Keymando.prototype.unpause = function(types) {
var j, len, type;
if (types == null) {
return this.mousetrap.unpause();
}
for (j = 0, len = types.length; j < len; j++) {
type = types[j];
if (this.pausedEvents.indexOf(type) > -1) {
this.pausedEvents.splice(this.pausedEvents.indexOf(type), 1);
}
}
};
Keymando.prototype.disposeOfChildren = function(parent) {
var childId, j, len, ref;
if (this.components[parent] == null) {
return;
}
ref = this.components[parent].navigation;
for (j = 0, len = ref.length; j < len; j++) {
childId = ref[j];
delete this.components[childId];
}
this.components[parent].navigation = [];
};
Keymando.prototype.disposeOf = function(id, soft) {
var childId, j, len, parent, ref, ref1;
if (this.components[id] == null) {
return;
}
parent = this.components[id].parent;
ref = this.components[id].navigation;
for (j = 0, len = ref.length; j < len; j++) {
childId = ref[j];
this.disposeOf(childId);
}
this.removeFromNavigation(parent, id);
if ((this.hasFocus === id || this.childHasFocus(id)) && !soft) {
if (this.components[parent].navigation[0] != null) {
this.navigateTo(parent, this.components[parent].navigation[0]);
} else if (((ref1 = this.components[this.components[parent].parent]) != null ? ref1.navigation[0] : void 0) != null) {
this.navigateTo(this.components[parent].parent, parent);
} else {
this.hasFocus = null;
}
}
delete this.components[id];
};
Keymando.prototype.dispose = function() {
this.mousetrap.reset();
};
return Keymando;
})();
// ---
// generated by coffee-script 1.9.2
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment