/* ################################################################################ ox.ui.js requires jquery-1.4.js ox.js ################################################################################ */ // also see test.js, in demos ... (function() { var oxui = { defaultTheme: 'classic', elements: {}, getDimensions: function(orientation) { return orientation == 'horizontal' ? ['width', 'height'] : ['height', 'width']; }, getEdges: function(orientation) { return orientation == 'horizontal' ? ['left', 'right', 'top', 'bottom'] : ['top', 'bottom', 'left', 'right']; }, getBarSize: function(size) { var sizes = { small: 20, medium: 24, large: 28, }; return sizes[size]; }, jQueryFunctions: function() { var functions = [], $element = $('
'); //delete $element.length; Ox.each($element, function(k, v) { if (typeof v == 'function') { functions.push(k); } }); return functions.sort(); }(), path: $('script[src*=ox.ui.js]').attr('src').replace('js/ox.ui.js', ''), scrollbarSize: $.browser.mozilla ? 16 : 12, symbols: { alt: '\u2325', apple: '\uF8FF', arrow_down: '\u2193', arrow_left: '\u2190', arrow_right: '\u2192', arrow_up: '\u2191', backspace: '\u232B', backup: '\u2707', ballot: '\u2717', black_star: '\u2605', burn: '\u2622', caps_lock: '\u21EA', check: '\u2713', //clear: '\u2327', clear: '\u00D7', click: '\uF803', close: '\u2715', command: '\u2318', control: '\u2303', cut: '\u2702', 'delete': '\u2326', diamond: '\u25C6', edit: '\uF802', eject: '\u23CF', escape: '\u238B', end: '\u2198', enter: '\u2324', fly: '\u2708', gear: '\u2699', home: '\u2196', info: '\u24D8', navigate: '\u2388', option: '\u2387', page_up: '\u21DE', page_down: '\u21DF', redo: '\u21BA', 'return': '\u21A9', //select: '\u21D5', select: '\u25BE', shift: '\u21E7', sound: '\u266B', space: '\u2423', tab: '\u21E5', trash: '\u267A', triangle_down: '\u25BC', triangle_left: '\u25C0', triangle_right: '\u25BA', triangle_up: '\u25B2', undo: '\u21BB', voltage: '\u26A1', warning: '\u26A0', white_star: '\u2606' } }, $elements = {}, $window, $document, $body; _$elements = $elements; $(function() { $window = $(window), $document = $(document), $body = $('body'), Ox.theme(oxui.defaultTheme); }); /* ============================================================================ Application ============================================================================ */ /** Creates an App instance. */ Ox.App = function() { return function(options) { options = options || {}; var self = {}, that = this; self.time = +new Date(); self.options = $.extend({ apiTimeout: 15000, apiType: 'POST', apiURL: '', config: '', init: '' }, options); function getUserAgent() { var userAgent = ''; $.each(['Chrome', 'Firefox', 'Internet Explorer', 'Opera', 'Safari'], function(i, v) { if (navigator.userAgent.indexOf(v) > -1) { userAgent = v; return false; } }); return userAgent; } function getUserData() { //return {}; return {}; return { navigator: { cookieEnabled: navigator.cookieEnabled, plugins: $.map(navigator.plugins, function(plugin, i) { return plugin.name; }), userAgent: navigator.userAgent }, screen: screen, time: (+new Date() - self.time) / 1000, window: { innerHeight: window.innerHeight, innerWidth: window.innerWidth, outerHeight: window.outerHeight, outerWidth: window.outerWidth, screenLeft: window.screenLeft, screenTop: window.screenTop } }; } function loadImages(callback) { $.getJSON(oxui.path + 'json/ox.ui.images.json', function(data) { var counter = 0, length = data.length; data.forEach(function(src, i) { image = new Image() image.src = oxui.path + src; image.onload = function() { (++counter == length) && callback(); } }); }); } self.change = function(key, value) { }; that.api = { api: function(callback) { Ox.Request.send({ url: self.options.apiURL, data: { action: 'api' }, callback: callback }); }, cancel: function(id) { Ox.Request.cancel(id); } }; that.launch = function(callback) { var time = +new Date(), userAgent = getUserAgent(), userAgents = ['Chrome', 'Firefox', 'Opera', 'Safari']; $.ajaxSetup({ timeout: self.options.apiTimeout, type: self.options.apiType, url: self.options.apiURL }); userAgents.indexOf(userAgent) > -1 ? start() : stop(); function start() { $.getJSON(self.options.config, function(data) { var config = data; Ox.print('config', config); document.title = config.site.name; loadImages(function() { that.api.api(function(result) { $.each(result.data.actions, function(i, action) { that.api[action] = function(data, callback) { if (arguments.length == 1) { callback = data; data = {}; } return Ox.Request.send({ url: self.options.apiURL, data: { action: action, data: JSON.stringify(data) }, callback: callback }); }; }); that.api[self.options.init](getUserData(), function(data) { var user = data.data.user; $(function() { var $div = $body.find('div'); $body.find('img').remove(); $div.animate({ opacity: 0 }, 1000, function() { $div.remove(); }); callback({ config: config, user: user }); }); }); }); }); }); } function stop() { that.request.send(self.options.init, getUserData(), function() {}); } return that; }; that.options = function() { return Ox.getset(self.options, Array.prototype.slice.call(arguments), self.change, that); }; return that; }; }(); /** Ox.Event event object */ /* Ox.Event = function() { var $eventHandler = $('
'), events = {}; function bindEvent(id, event, callback) { Ox.print('bind', id, event); events[id] = events[id] || {}; events[id][event] = events[id][event] || []; if ($.map(events[id][event], function(fn) { return fn.toString(); }).indexOf(callback.toString()) == -1) { events[id][event].push(callback); if (!isKeyboardEvent(event) || Ox.Focus.focused() == id) { $eventHandler.bind(event + '_' + id, callback); // requestStart/requestStop currently have '' as id } } } function unbindEvent(id, event, callback) { Ox.print('unbind', id, event); var toString = (callback || '').toString(), unbind = !event || (event && !isKeyboardEvent(event)) || Ox.Focus.focused() == id; if (events[id] && (!event || events[id][event])) { $.each(events[id], function(e, fns) { if (!event || event == e) { events[id][e] = $.map(events[id][e], function(fn, i) { if (!callback || toString == fn.toString()) { Ox.print(unbind, 'eventHandler.unbind', e, id) unbind && $eventHandler.unbind(e + '_' + id, fn); return null; } else { return fn; } }); } }); Ox.print('id/events', id, events, events[id]) if (!callback || (event && events[id][event].length == 0)) { delete events[id][event]; } if (!event || Ox.length(events[id]) == 0) { delete events[id]; } } } function isKeyboardEvent(event) { return event.substr(0, 4) == 'key_'; } return { _print: function() { Ox.print(events); }, bind: function(id, event, callback) { // bind event bindEvent(id, event, callback); }, bindKeyboard: function(id) { // bind all keyboard events //Ox.print('binding', 'id', id, 'events', events[id], Ox.length(events[id]), 'keyboardevents', events[id]['keyboard']) $.each(events[id], function(k, v) { Ox.print('|' + k + '|'); }) events[id] && $.each(events[id], function(event, callbacks) { Ox.print('BIND ID EVENT', id, event) isKeyboardEvent(event) && $.each(callbacks, function(i, callback) { Ox.print('bind', id, event); $eventHandler.bind(event, callback); }); }); }, changeId: function(oldId, newId) { // we need to reference events by options('id'), // not by that.id, since textlist events fire on list, // new lists have new that.ids, etc. // so this renaming is necessary Ox.print('changeId', oldId, newId, events[oldId]); $.each($.extend({}, events[oldId] || {}), function(event, callbacks) { $.each(callbacks, function(i, callback) { Ox.Event.unbind(oldId, event, callback); Ox.Event.bind(newId, event, callback); }); }); }, unbind: function(id, event, callback) { // unbind event // event and callback are optional unbindEvent(id, event, callback); }, unbindAll: function() { Ox.print('beginUnbindAll') $.each(events, function(id, eventsById) { $.each(eventsById, function(event, callbacks) { $.each(callbacks, function(i, callback) { Ox.Event.unbind(id, event, callback); }); }); }); Ox.print('endUnbindAll') }, trigger: function(id, event, data) { // trigger event // data is optional // keyboard handler will call this with '' as id Ox.print('trigger', id, event, data || {}); $eventHandler.trigger(event + (id ? '_' + id : ''), data || {}); }, unbindKeyboard: function(id) { // unbind all keyboard events Ox.print('unbinding', id) events[id] && $.each(events[id], function(event, callbacks) { Ox.print('UNBIND ID EVENT', id, event) isKeyboardEvent(event) && $.each(callbacks, function(i, callback) { Ox.print('unbind', event) $eventHandler.unbind(event, callback); }); }); } } }(); */ /** Ox.Focus */ Ox.Focus = function() { var stack = []; return { _print: function() { Ox.print(stack); }, blur: function(id) { Ox.print('BLUR', id, stack) var index = stack.indexOf(id); if (index == stack.length - 1) { stack.length == 1 ? stack.pop() : stack.splice(stack.length - 2, 0, stack.pop()); // fix later: Ox.Event.unbindKeyboard($elements[id].options('id')); // fix later: stack.length && Ox.Event.bindKeyboard($elements[stack[stack.length - 1]].options('id')); //$elements[id].removeClass('OxFocus'); $('.OxFocus').removeClass('OxFocus'); // fixme: the above is better, and should work stack.length && $elements[stack[stack.length - 1]].addClass('OxFocus'); Ox.print('blur', id, stack); } }, focus: function(id) { var index = stack.indexOf(id); if (index == -1 || index < stack.length - 1) { // fix later: stack.length && Ox.Event.unbindKeyboard($elements[stack[stack.length - 1]].options('id')); index > -1 && stack.splice(index, 1); stack.push(id); // fix later: Ox.Event.bindKeyboard($elements[id].options('id')); $('.OxFocus').removeClass('OxFocus'); // fixme: see above $elements[id].addClass('OxFocus'); Ox.print('focus', id, stack); } }, focused: function() { return stack.length ? stack[stack.length - 1] : null; } }; }(); /** Ox.History */ /** Ox.Keyboard */ (function() { var buffer = '', bufferTime = 0, bufferTimeout = 1000, keyNames = function() { return { 0: 'section', 8: 'backspace', 9: 'tab', 12: 'clear', 13: 'enter', 16: 'shift', 17: 'control', 18: 'alt', 20: 'capslock', 27: 'escape', 32: 'space', 33: 'pageup', 34: 'pagedown', 35: 'end', 36: 'home', 37: 'left', 38: 'up', 39: 'right', 40: 'down', 45: 'insert', 46: 'delete', 47: 'help', 48: '0', 49: '1', 50: '2', 51: '3', 52: '4', 53: '5', 54: '6', 55: '7', 56: '8', 57: '9', 65: 'a', 66: 'b', 67: 'c', 68: 'd', 69: 'e', 70: 'f', 71: 'g', 72: 'h', 73: 'i', 74: 'j', 75: 'k', 76: 'l', 77: 'm', 78: 'n', 79: 'o', 80: 'p', 81: 'q', 82: 'r', 83: 's', 84: 't', 85: 'u', 86: 'v', 87: 'w', 88: 'x', 89: 'y', 90: 'z', 91: 'meta.left', 92: 'meta.right', 93: 'select', 96: '0.numpad', 97: '1.numpad', 98: '2.numpad', 99: '3.numpad', 100: '4.numpad', 101: '5.numpad', 102: '6.numpad', 103: '7.numpad', 104: '8.numpad', 105: '9.numpad', 106: 'asterisk.numpad', 107: 'plus.numpad', 109: 'minus.numpad', 108: 'enter.numpad', 110: 'dot.numpad', 111: 'slash.numpad', 112: 'f1', 113: 'f2', 114: 'f3', 115: 'f4', 116: 'f5', 117: 'f6', 118: 'f7', 119: 'f8', 120: 'f9', 121: 'f10', 122: 'f11', 123: 'f12', 124: 'f13', 125: 'f14', 126: 'f15', 127: 'f16', 144: 'numlock', 145: 'scrolllock', 186: 'semicolon', 187: 'equal', 188: 'comma', 189: 'minus', 190: 'dot', 191: 'slash', 192: 'backtick', 219: 'openbracket', 220: 'backslash', 221: 'closebracket', 222: 'quote' // see dojo, for ex. }; }(), modifierNames = { altKey: 'alt', // mac: option ctrlKey: 'control', // metaKey: 'meta', // mac: command shiftKey: 'shift' }; $(function() { // fixme: how to do this better? // in firefox on mac, keypress doesn't fire for up/down // if the cursor is at the start/end of an input element // on linux, it doesn't seem to fire if the input element has focus if ($.browser.mozilla) { $document.keypress(keypress); $document.keydown(function(event) { var $element = $('input:focus'); if ($element.length) { if ( ( keyNames[event.keyCode] == 'up' && $element[0].selectionStart + $element[0].selectionEnd == 0 ) || ( keyNames[event.keyCode] == 'down' && $element[0].selectionStart == $element.val().length && $element[0].selectionEnd == $element.val().length ) ) { keypress(event); } } }); } else { $document.keydown(keypress); } }); function keypress(event) { var focused = Ox.Focus.focused(), key, keys = [], //ret = true, time; $.each(modifierNames, function(k, v) { if (event[k]) { keys.push(v); } }); // avoid pushing modifier twice Ox.print('keys', keys) if (keyNames[event.keyCode] && keys.indexOf(keyNames[event.keyCode]) == -1) { keys.push(keyNames[event.keyCode]); } key = keys.join('_'); if (key.match(/^[\w\d-]$|SPACE/)) { time = Ox.getTime(); if (time - bufferTime > bufferTimeout) { buffer = ''; } buffer += key == 'SPACE' ? ' ' : key; bufferTime = time; } focused && $elements[focused].triggerEvent('key_' + key); //return false; /* $.each(stack, function(i, v) { // fixme: we dont get the return value! ret = Ox.event.trigger(keyboard + Ox.toCamelCase(key) + '.' + v); return ret; }); */ } })(); /** Ox.Mouse (??) */ /** Ox.Request request object */ Ox.Request = function() { var cache = {}, pending = {}, requests = {}, self = { options: { timeout: 15000, type: 'POST', url: 'api' } }; return { cancel: function() { var index; if (arguments.length == 0) { requests = {}; } else if (Ox.isFunction(arguments[0])) { // cancel with function $.each(requests, function(id, req) { if (arguments[0](req)) { delete requests[id]; } }) } else { // cancel by id delete requests[arguments[0]] } }, emptyCache: function() { cache = {}; }, options: function(options) { return Ox.getset(self.options, options, $.noop(), this); }, send: function(options) { var options = $.extend({ age: -1, callback: function() {}, id: Ox.uid(), timeout: self.options.timeout, type: self.options.type, url: self.options.url }, options), req = JSON.stringify({ url: options.url, data: options.data }); function callback(data) { delete requests[options.id]; Ox.length(requests) == 0 && $body.trigger('requestStop'); options.callback(data); // fixme: doesn't work if callback hasn't been passed } function debug(request) { var $iframe = $('