(function() { var chars = { 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; 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; } function isKeyboardEvent(event) { return keyboardEventRegExp.test(event); } function isKeysEventKey(key) { return keysEventRegExp.test(key); } function onMessage(e) { var element, message = {}; try { message = Ox.extend({data: {}}, JSON.parse(e.data)); } catch (e) {} if (message.event == 'init') { if (message.data.oxid) { // The inner window receives the oxid of the outer iframe element Ox.oxid = message.data.oxid; Ox.parent.postMessage('init', {}) } else if (message.target) { // The outer window receives init from iframe Ox.elements[message.target].triggerEvent('init'); } } else { (message.target ? Ox.elements[message.target] : Ox.parent) .triggerMessage(message.event, message.data); } } 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) { var args = Ox.slice(arguments, 1), callbacks = options.callbacks, that = this; Ox.forEach(Ox.makeObject(args), function(data, originalEvent) { var events = originalEvent.split('.'); ['*'].concat(events.map(function(event, index) { return events.slice(0, index + 1).join('.'); })).forEach(function(event) { (callbacks[0][event] || []) .concat(callbacks[1][event] || []) .forEach(function(callback) { callback.call(that, data, originalEvent, that); }); }); }); 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() { if (window !== window.top) { return Ox.Message.post.apply(this, 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() { var isElement = this !== that; return bind.apply(this, [{ callbacks: isElement ? arguments[0].eventCallbacks : callbacks }].concat(Ox.slice(arguments, isElement ? 1 : 0))); }; that.bindOnce = function() { var isElement = this !== that; return bind.apply(this, [{ callbacks: isElement ? arguments[0].eventCallbacks : callbacks, once: true }].concat(Ox.slice(arguments, isElement ? 1 : 0))); }; that.trigger = function(self) { return trigger.apply(this, [{ callbacks: [callbacks, self.eventCallbacks || {}] }].concat(Ox.slice(arguments, 1))); }; that.unbind = function() { var isElement = this !== that; return unbind.apply(this, [{ callbacks: isElement ? arguments[0].eventCallbacks : callbacks }].concat(Ox.slice(arguments, isElement ? 1 : 0))); }; 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); }, 250); } else { 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); }());