unified Event/Keyboard/Message handler
This commit is contained in:
parent
4cf10359ef
commit
cc183b6198
1 changed files with 297 additions and 184 deletions
|
@ -1,197 +1,310 @@
|
||||||
'use strict';
|
(function() {
|
||||||
|
|
||||||
/*@
|
var chars = {
|
||||||
Ox.Event <o> Event controller
|
comma: ',',
|
||||||
@*/
|
dot: '.',
|
||||||
|
minus: '-',
|
||||||
|
quote: '\'',
|
||||||
|
semicolon: ';',
|
||||||
|
slash: '/',
|
||||||
|
space: ' '
|
||||||
|
},
|
||||||
|
hasCallback = {},
|
||||||
|
keyboardEventRegExp = /^key(\.[\w\d.]+)?$/,
|
||||||
|
keys = '',
|
||||||
|
keysEventRegExp = new RegExp(
|
||||||
|
'^[\\w\\d](\\.numpad)?$|^(' + Object.keys(chars).join('|') + ')$'
|
||||||
|
),
|
||||||
|
resetTimeout,
|
||||||
|
triggerTimeout;
|
||||||
|
|
||||||
Ox.Event = (function() {
|
function bind(options) {
|
||||||
|
var args = Ox.slice(arguments, 1),
|
||||||
|
callbacks = options.callbacks,
|
||||||
|
that = this;
|
||||||
|
Ox.forEach(
|
||||||
|
Ox.isFunction(args[0]) ? {'*': args[0]} : Ox.makeObject(args),
|
||||||
|
function(originalCallback, event) {
|
||||||
|
event = event.replace(/^key_/, 'key.');
|
||||||
|
callbacks[event] = (callbacks[event] || []).concat(
|
||||||
|
options.once ? function callback() {
|
||||||
|
unbind.call(
|
||||||
|
that, {callbacks: callbacks}, event, callback
|
||||||
|
);
|
||||||
|
return originalCallback.apply(null, arguments);
|
||||||
|
}
|
||||||
|
: originalCallback
|
||||||
|
);
|
||||||
|
if (isKeyboardEvent(event)) {
|
||||||
|
hasCallback[event] = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
var eventHandlers = [],
|
function isKeyboardEvent(event) {
|
||||||
that = {};
|
return keyboardEventRegExp.test(event);
|
||||||
|
}
|
||||||
|
|
||||||
function log(data, event, self) {
|
function isKeysEventKey(key) {
|
||||||
var element = this,
|
return keysEventRegExp.test(key);
|
||||||
handlers = self.eventHandlers ? self.eventHandlers[event] : [];
|
}
|
||||||
if (!Ox.contains([
|
|
||||||
'mousedown', 'mouserepeat', 'anyclick', 'singleclick', 'doubleclick', 'mousewheel',
|
function onMessage(e) {
|
||||||
'dragstart', 'drag', 'dragenter', 'dragleave', 'dragpause', 'dragend',
|
var element, message = {};
|
||||||
'draganddropstart', 'draganddrop', 'draganddropenter', 'draganddropleave', 'draganddropend',
|
|
||||||
'playing', 'position', 'progress'
|
|
||||||
], event)) {
|
|
||||||
try {
|
try {
|
||||||
data = JSON.stringify(data)
|
message = Ox.extend({data: {}}, JSON.parse(e.data));
|
||||||
} catch(e) {}
|
} catch (e) {}
|
||||||
Ox.print(
|
if (message.event == 'init') {
|
||||||
'EVENT',
|
if (message.data.oxid) {
|
||||||
element.oxid,
|
// The inner window receives the oxid of the outer iframe element
|
||||||
'"' + element[0].className.split(' ').filter(function(className) {
|
Ox.oxid = message.data.oxid;
|
||||||
return /^Ox/.test(className);
|
Ox.parent.postMessage('init', {})
|
||||||
}).map(function(className) {
|
} else if (message.target) {
|
||||||
return className.replace(/^Ox/, '');
|
// The outer window receives init from iframe
|
||||||
}).join(' ') + '"',
|
Ox.elements[message.target].triggerEvent('init');
|
||||||
event,
|
}
|
||||||
data,
|
} else {
|
||||||
handlers.length,
|
(message.target ? Ox.elements[message.target] : Ox.parent)
|
||||||
handlers.map(function(handler) {
|
.triggerMessage(message.event, message.data);
|
||||||
return handler.toString().split('\n').shift();
|
}
|
||||||
})
|
}
|
||||||
|
|
||||||
|
function onKeydown(e) {
|
||||||
|
var $element = Ox.Focus.focusedElement(),
|
||||||
|
isInput = Ox.Focus.focusedElementIsInput(),
|
||||||
|
keyName = Ox.KEYS[e.keyCode],
|
||||||
|
keyBasename = keyName.split('.')[0],
|
||||||
|
key = Object.keys(Ox.MODIFIER_KEYS).filter(function(key) {
|
||||||
|
return e[key] && Ox.MODIFIER_KEYS[key] != keyBasename;
|
||||||
|
}).map(function(key) {
|
||||||
|
return Ox.MODIFIER_KEYS[key];
|
||||||
|
}).concat(keyName).join('_'),
|
||||||
|
triggerEvent = function() {
|
||||||
|
if ($element) {
|
||||||
|
$element.triggerEvent.apply($element, arguments);
|
||||||
|
} else {
|
||||||
|
Ox.Event.trigger.apply(
|
||||||
|
Ox.$body, [{}].concat(Ox.slice(arguments))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
triggerEvent('key.' + key, e);
|
||||||
|
if (isKeysEventKey(key)) {
|
||||||
|
// don't register leading spaces or trailing double spaces
|
||||||
|
if (keyName != 'space' || (
|
||||||
|
keys != '' && !Ox.endsWith(keys, ' ')
|
||||||
|
)) {
|
||||||
|
keys += chars[keyName] || keyBasename;
|
||||||
|
// clear the trigger timeout only if the key registered
|
||||||
|
clearTimeout(triggerTimeout);
|
||||||
|
triggerTimeout = setTimeout(function() {
|
||||||
|
triggerEvent('keys', Ox.extend(e, {keys: keys}));
|
||||||
|
}, 250);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// clear the reset timeout even if the key didn't register
|
||||||
|
clearTimeout(resetTimeout);
|
||||||
|
resetTimeout = setTimeout(function() {
|
||||||
|
keys = '';
|
||||||
|
}, 1000);
|
||||||
|
if (hasCallback[key]) {
|
||||||
|
e.preventDefault();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*@
|
function trigger(options) {
|
||||||
.bind <f> Adds event handler(s)
|
var args = Ox.slice(arguments, 1),
|
||||||
(callback) -> <o> Ox.Event
|
callbacks = options.callbacks,
|
||||||
Adds a global event handler
|
that = this;
|
||||||
(self, callback) -> <o> Ox.Event
|
Ox.forEach(Ox.makeObject(args), function(data, originalEvent) {
|
||||||
Adds a catch-all handler
|
var events = originalEvent.split('.');
|
||||||
(self, event, callback) -> <o> Ox.Event
|
['*'].concat(events.map(function(event, index) {
|
||||||
Adds a handler for a single event
|
return events.slice(0, index + 1).join('.');
|
||||||
(self, {event: callback, ...}) -> <o> Ox.Event
|
})).forEach(function(event) {
|
||||||
Adds handlers for multiple events
|
(callbacks[0][event] || [])
|
||||||
self <o> The element's shared private object
|
.concat(callbacks[1][event] || [])
|
||||||
callback <f> Callback function
|
.forEach(function(callback) {
|
||||||
data <o> Event data
|
callback.call(that, data, originalEvent, that);
|
||||||
event <s> Event name
|
});
|
||||||
element <o> Element
|
});
|
||||||
event <s> Event name
|
});
|
||||||
Event names can be namespaced, like `'click.foo'`
|
return this;
|
||||||
*/
|
}
|
||||||
|
|
||||||
|
function unbind(options) {
|
||||||
|
var args = Ox.slice(arguments, 1),
|
||||||
|
callbacks = options.callbacks;
|
||||||
|
if (args.length == 0) {
|
||||||
|
// unbind all handlers for all events
|
||||||
|
callbacks = [];
|
||||||
|
} else {
|
||||||
|
Ox.forEach(
|
||||||
|
Ox.isFunction(args[0]) ? {'*': args[0]}
|
||||||
|
: Ox.makeObject(args),
|
||||||
|
function(callback, event) {
|
||||||
|
if (!callback) {
|
||||||
|
// unbind all handlers for this event
|
||||||
|
delete callbacks[event];
|
||||||
|
} else if (callbacks[event]) {
|
||||||
|
// unbind this handler for this event
|
||||||
|
callbacks[event] = callbacks[event].filter(
|
||||||
|
function(eventCallback) {
|
||||||
|
return eventCallback !== callback;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
if (callbacks[event].length == 0) {
|
||||||
|
delete callbacks[event];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (isKeyboardEvent(event) && !callbacks[event]) {
|
||||||
|
delete hasCallback[event];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ox.$parent = Ox.parent = (function() {
|
||||||
|
|
||||||
|
var self = {messageCallbacks: {}},
|
||||||
|
that = {oxid: Ox.uid()};
|
||||||
|
|
||||||
|
that.bindMessage = function() {
|
||||||
|
return Ox.Message.bind.apply(
|
||||||
|
this, [self].concat(Ox.slice(arguments))
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
that.bindMessageOnce = function() {
|
||||||
|
return Ox.Message.bindOnce.apply(
|
||||||
|
this, [self].concat(Ox.slice(arguments))
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
that.postMessage = function() {
|
||||||
|
return Ox.Message.post.apply(this, Ox.slice(arguments));
|
||||||
|
};
|
||||||
|
|
||||||
|
that.triggerMessage = function() {
|
||||||
|
return Ox.Message.trigger.apply(
|
||||||
|
this, [self].concat(Ox.slice(arguments))
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
that.unbindMessage = function() {
|
||||||
|
return Ox.Message.unbind.apply(
|
||||||
|
this, [self].concat(Ox.slice(arguments))
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
return that;
|
||||||
|
|
||||||
|
}());
|
||||||
|
|
||||||
|
Ox.Event = (function() {
|
||||||
|
|
||||||
|
var callbacks = {},
|
||||||
|
that = {};
|
||||||
|
|
||||||
that.bind = function() {
|
that.bind = function() {
|
||||||
var args = Ox.slice(arguments), once, self;
|
var isElement = this !== that;
|
||||||
if (args.length == 1) {
|
return bind.apply(this, [{
|
||||||
eventHandlers.push(args[0])
|
callbacks: isElement ? arguments[0].eventCallbacks : callbacks
|
||||||
} else {
|
}].concat(Ox.slice(arguments, isElement ? 1 : 0)));
|
||||||
self = args.shift();
|
|
||||||
once = Ox.isBoolean(Ox.last(args)) ? args.pop() : false;
|
|
||||||
args = Ox.isFunction(args[0]) ? {'*': args[0]} : Ox.makeObject(args);
|
|
||||||
if (Ox.len(args) && !self.eventHandlers) {
|
|
||||||
self.eventHandlers = {};
|
|
||||||
}
|
|
||||||
Ox.forEach(args, function(callback, event) {
|
|
||||||
self.eventHandlers[event] = (
|
|
||||||
self.eventHandlers[event] || []
|
|
||||||
).concat({callback: callback, once: once});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return that;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/*@
|
|
||||||
.bindOnce <f> Adds event handler(s) that run(s) only once
|
|
||||||
(self, callback) -> <o> Ox.Event
|
|
||||||
Adds a catch-all handler
|
|
||||||
(self, event, callback) -> <o> Ox.Event
|
|
||||||
Adds an event handler for a single event
|
|
||||||
(self, {event: callback, ...}) -> <o> Ox.Event
|
|
||||||
Adds event handlers for multiple events
|
|
||||||
self <o> The element's shared private object
|
|
||||||
callback <f> Callback function
|
|
||||||
data <o> Event data
|
|
||||||
event <s> Event name
|
|
||||||
element <o> Element
|
|
||||||
event <s> Event name
|
|
||||||
Event names can be namespaced, like `'click.foo'`
|
|
||||||
*/
|
|
||||||
that.bindOnce = function() {
|
that.bindOnce = function() {
|
||||||
return that.bind.apply(null, Ox.slice(arguments).concat(true));
|
var isElement = this !== that;
|
||||||
|
return bind.apply(this, [{
|
||||||
|
callbacks: isElement ? arguments[0].eventCallbacks : callbacks,
|
||||||
|
once: true
|
||||||
|
}].concat(Ox.slice(arguments, isElement ? 1 : 0)));
|
||||||
};
|
};
|
||||||
|
|
||||||
/*@
|
|
||||||
.log <f> Turns event logging on or off
|
|
||||||
(enabled) -> <o> Ox.Event
|
|
||||||
enabled <b> Enables (`true`) or disables (`false`) event logging
|
|
||||||
*/
|
|
||||||
that.log = function(enabled) {
|
|
||||||
that[enabled ? 'bind' : 'unbind'](log);
|
|
||||||
return that;
|
|
||||||
};
|
|
||||||
|
|
||||||
/*@
|
|
||||||
.trigger <f> Triggers an event
|
|
||||||
(self, event) -> <o> Ox.Event
|
|
||||||
Triggers an event
|
|
||||||
(self, event, data) -> <o> Ox.Event
|
|
||||||
Triggers an event with data
|
|
||||||
(self, {event: data, ...}) -> Ox.Event
|
|
||||||
Triggers multiple events with data
|
|
||||||
self <o> The element's shared private object
|
|
||||||
event <s> Event name
|
|
||||||
data <o> Event data
|
|
||||||
*/
|
|
||||||
that.trigger = function(self) {
|
that.trigger = function(self) {
|
||||||
var args = arguments, element = this;
|
return trigger.apply(this, [{
|
||||||
if (self.eventHandlers) {
|
callbacks: [callbacks, self.eventCallbacks || {}]
|
||||||
Ox.forEach(Ox.makeObject(Ox.slice(args, 1)), function(data, event) {
|
}].concat(Ox.slice(arguments, 1)));
|
||||||
var triggered = event.split('.');
|
|
||||||
eventHandlers.forEach(function(callback) {
|
|
||||||
callback.call(element, data || {}, event, element);
|
|
||||||
});
|
|
||||||
triggered.map(function(v, i) {
|
|
||||||
return triggered.slice(0, i + 1).join('.');
|
|
||||||
}).concat('*').forEach(function(triggered) {
|
|
||||||
var handlers = self.eventHandlers[triggered];
|
|
||||||
handlers && handlers.forEach(function(handler, i) {
|
|
||||||
handler.once && handlers.splice(i, 1);
|
|
||||||
handler.callback.call(element, data || {}, event);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return that;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/*@
|
|
||||||
.unbind <f> Removes an event handler
|
|
||||||
() -> Ox.Event
|
|
||||||
Removes all global handlers
|
|
||||||
(callback) -> <o> Ox.Event
|
|
||||||
Removes a global handler
|
|
||||||
(self) -> <o> Ox.Event
|
|
||||||
Removes all handlers
|
|
||||||
(self, callback) -> <o> Ox.Event
|
|
||||||
Removes a specific catch-all handler
|
|
||||||
(self, event) -> <o> Ox.Event
|
|
||||||
Remove all handlers for a single event
|
|
||||||
(self, event, callback) -> <o> Ox.Event
|
|
||||||
Removes a specific handler for a single event
|
|
||||||
(self, {event: callback, ...}) -> <o> Ox.Event
|
|
||||||
Removes specific event handlers for multiple events
|
|
||||||
self <o> The element's shared private object
|
|
||||||
callback <f> Callback function
|
|
||||||
event <s> Event name
|
|
||||||
*/
|
|
||||||
that.unbind = function() {
|
that.unbind = function() {
|
||||||
var args = Ox.slice(arguments), self;
|
var isElement = this !== that;
|
||||||
if (args.length == 0) {
|
return unbind.apply(this, [{
|
||||||
eventHandlers = [];
|
callbacks: isElement ? arguments[0].eventCallbacks : callbacks
|
||||||
} else if (Ox.isFunction(args[0])) {
|
}].concat(Ox.slice(arguments, isElement ? 1 : 0)));
|
||||||
eventHandlers.forEach(function(handler, i) {
|
|
||||||
handler === args[0] && eventHandlers.splice(i, 1);
|
|
||||||
});
|
|
||||||
} else if ((self = args.shift()).eventHandlers) {
|
|
||||||
if (args.length == 0) {
|
|
||||||
delete self.eventHandlers;
|
|
||||||
} else {
|
|
||||||
if (Ox.isFunction(args[0])) {
|
|
||||||
args = {'*': args[0]};
|
|
||||||
}
|
|
||||||
Ox.forEach(Ox.makeObject(args), function(callback, event) {
|
|
||||||
if (Ox.isUndefined(callback)) {
|
|
||||||
delete self.eventHandlers[event];
|
|
||||||
} else {
|
|
||||||
self.eventHandlers[event].forEach(function(handler, i) {
|
|
||||||
if (handler.callback === callback) {
|
|
||||||
self.eventHandlers[event].splice(i, 1);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return that;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
return that;
|
return that;
|
||||||
|
|
||||||
|
}());
|
||||||
|
|
||||||
|
Ox.Message = (function() {
|
||||||
|
|
||||||
|
var callbacks = {},
|
||||||
|
that = {};
|
||||||
|
|
||||||
|
that.bind = function() {
|
||||||
|
var isElement = this !== that;
|
||||||
|
return bind.apply(this, [{
|
||||||
|
callbacks: isElement ? arguments[0].messageCallbacks
|
||||||
|
: callbacks
|
||||||
|
}].concat(Ox.slice(arguments, isElement ? 1 : 0)));
|
||||||
|
};
|
||||||
|
|
||||||
|
that.bindOnce = function() {
|
||||||
|
var isElement = this !== that;
|
||||||
|
return bind.apply(this, [{
|
||||||
|
callbacks: isElement ? arguments[0].messageCallbacks
|
||||||
|
: callbacks,
|
||||||
|
once: true
|
||||||
|
}].concat(Ox.slice(arguments, isElement ? 1 : 0)));
|
||||||
|
};
|
||||||
|
|
||||||
|
that.post = function() {
|
||||||
|
var args = arguments,
|
||||||
|
isParent = this == Ox.parent,
|
||||||
|
target = isParent ? window.parent : this[0].contentWindow,
|
||||||
|
that = this;
|
||||||
|
if (isParent && !Ox.oxid) {
|
||||||
|
// posting to parent, but not yet initialized
|
||||||
|
setTimeout(function() {
|
||||||
|
that.post.apply(Ox.parent, args);
|
||||||
|
}, 25);
|
||||||
|
}
|
||||||
|
Ox.forEach(
|
||||||
|
Ox.makeObject(Ox.slice(args)),
|
||||||
|
function(data, event) {
|
||||||
|
target.postMessage(JSON.stringify({
|
||||||
|
data: data,
|
||||||
|
event: event,
|
||||||
|
target: isParent ? Ox.oxid : null
|
||||||
|
}), '*');
|
||||||
|
}
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
that.trigger = function(self) {
|
||||||
|
return trigger.apply(this, [{
|
||||||
|
callbacks: [callbacks, self.messageCallbacks]
|
||||||
|
}].concat(Ox.slice(arguments, 1)));
|
||||||
|
};
|
||||||
|
|
||||||
|
that.unbind = function() {
|
||||||
|
var isElement = this !== that;
|
||||||
|
return unbind.apply(this, [{
|
||||||
|
callbacks: isElement ? arguments[0].messageCallbacks
|
||||||
|
: callbacks
|
||||||
|
}].concat(Ox.slice(arguments, isElement ? 1 : 0)));
|
||||||
|
};
|
||||||
|
|
||||||
|
return that;
|
||||||
|
|
||||||
|
}());
|
||||||
|
|
||||||
|
document.addEventListener('keydown', onKeydown);
|
||||||
|
window.addEventListener('message', onMessage);
|
||||||
|
|
||||||
}());
|
}());
|
Loading…
Reference in a new issue