/* ################################################################################ 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", ""), symbols: { // fixme: make lowercase 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" } }, $window, $document, $body; $(function() { $window = $(window), $document = $(document), $body = $("body"); Ox.theme(oxui.defaultTheme); }); /* ============================================================================ Application ============================================================================ */ /* ---------------------------------------------------------------------------- Ox.App ---------------------------------------------------------------------------- */ Ox.App = function() { /* options: requestTimeout requestType requestURL */ return function(options) { options = options || {}; var self = {}, that = this; self.options = $.extend({ requestTimeout: oxui.requestTimeout, requestType: oxui.requestType, requestURL: oxui.requestURL }, options); self.change = function() { }; that.launch = function() { $.ajaxSetup({ timeout: self.options.requestTimeout, type: self.options.requestType, url: self.options.requestURL }); }; that.options = function() { return Ox.getset(self.options, Array.slice.call(arguments), self.change, that); }; that.request = function(action, data, callback) { if (arguments.length == 2) { callback = data; data = {}; } return Ox.Request.send({ url: self.options.requestURL, data: { action: action, data: JSON.stringify(data) }, callback: callback }); }; return that; }; }(); /* ---------------------------------------------------------------------------- Ox.Event ---------------------------------------------------------------------------- naming convention for event/trigger verb.id.namespace, i.e. verb.sourceId.targetId (?) ... bind("keydown.shift+dot.numpad", function() { // ... }) keyboard handler then would: $.each(stack, function(i, v) { elements[v].trigger("keydown.shift+0.numpad"); }); and the element would implement this.trigger(event, data) { } ... keyboard handler also triggers keydown.buffer */ // use dom elements / jquery instead Ox.Event = function() { var keyboardEvents = {}; $eventHandler = $("
"); function isKeyboardEvent(event) { return event.substr(0, 4) == "key_"; } return { bind: function(id, event, callback) { if (isKeyboardEvent(event)) { keyboardEvents[id] = keyboardEvents[id] || {}; keyboardEvents[id][event] = callback; } if (!isKeyboardEvent(event) || Ox.Focus.focused() == id) { $eventHandler.bind(event, callback); } }, bindKeyboard: function(id) { $.each(keyboardEvents[id] || [], function(event, callback) { Ox.Event.bind(id, event, callback); }); }, trigger: function(event, data) { Ox.print("trigger", event, data || {}); $eventHandler.trigger(event, data || {}); }, unbind: function(id, event, callback) { if (isKeyboardEvent(event)) { $.each(keyboardEvents[id] || [], function(e, callback) { if (e == event) { delete keyboardEvents[id][e]; return false; } }); } $eventHandler.unbind(event, callback); }, unbindKeyboard: function(id) { $.each(keyboardEvents[id] || [], function(event, callback) { $eventHandler.unbind(event, callback); }); } } }(); Ox.Event_ = function() { var events = {}; return { // make these bind, trigger, unbind publish: function(event, data) { if (events[event]) { $.each(events[event], function(i, v) { setTimeout(function() { v(data); }, 0); }); } }, subscribe: function(event, callback) { if (events[event]) { events[event].push(callback); } else { events[event] = [callback]; } }, unsubscribe: function(event, callback) { $.each(events[event], function(i, v) { if (Ox.startsWith(callback.toString(), v.toString())) { events[event].splice(i, 1); } }); } }; }(); /* ---------------------------------------------------------------------------- Ox.Focus ---------------------------------------------------------------------------- */ Ox.Focus = function() { var stack = []; return { focus: function(id) { var index = stack.indexOf(id); if (stack.length) { Ox.Event.unbindKeyboard(stack[stack.length - 1]) } if (index > -1) { stack.splice(index, 1); } stack.push(id); Ox.Event.bindKeyboard(id); Ox.print("focus", stack); }, focused: function() { return stack[stack.length - 1]; }, blur: function(id) { if (stack.indexOf(id) > -1) { stack.splice(stack.length - 2, 0, stack.pop()); } Ox.Event.unbindKeyboard(id); Ox.Event.bindKeyboard(stack[stack.length - 1]); Ox.print("blur", stack); } }; }(); /* ---------------------------------------------------------------------------- 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 key, keys = [], //ret = true, time; $.each(modifierNames, function(k, v) { if (event[k]) { keys.push(v); } }); // avoid pushing modifier twice 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; } Ox.Event.trigger("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 ---------------------------------------------------------------------------- */ 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) { options = $.extend({ age: -1, callback: function() {}, id: Ox.uid(), timeout: self.options.timeout, type: self.options.type, url: self.options.url }, options); var req = JSON.stringify({ url: options.url, data: options.data }); function callback(data) { delete requests[options.id]; options.callback(data); } function debug(request) { var $iframe = $("