From f2dbfbd1e5dada9e549179aff57abc4e0daccee8 Mon Sep 17 00:00:00 2001 From: rlx <0x0073@0x2620.org> Date: Sat, 1 Oct 2011 02:21:55 +0000 Subject: [PATCH] add some more documentation, pass some more tests --- demos/mouse/js/mouse.js | 21 +- source/Ox.Geo/Ox.Geo.js | 12 +- source/Ox.UI/js/Core/Ox.Element.js | 703 +++++++++++++----------- source/Ox.UI/js/Core/Ox.URL.js | 10 +- source/Ox.UI/js/Map/Ox.Map.js | 88 +-- source/Ox.UI/js/Panel/Ox.SplitPanel.js | 1 - source/Ox.UI/js/Video/Ox.VideoPlayer.js | 4 +- source/Ox.js | 240 ++++---- tests/tests.js | 4 +- 9 files changed, 589 insertions(+), 494 deletions(-) diff --git a/demos/mouse/js/mouse.js b/demos/mouse/js/mouse.js index bc3b2051..2198a531 100644 --- a/demos/mouse/js/mouse.js +++ b/demos/mouse/js/mouse.js @@ -35,7 +35,7 @@ Ox.load('UI', { }) .appendTo(Ox.UI.$body); [ - 'anyclick', 'singleclick', 'doubleclick', 'mouserepeat', + 'anyclick', 'singleclick', 'doubleclick', 'mousedown', 'mouserepeat', 'dragstart', 'drag', 'dragenter', 'dragleave', 'dragpause', 'dragend' ].forEach(function(event) { $target.bindEvent(event, function(e) { @@ -44,13 +44,18 @@ Ox.load('UI', { .html( Ox.formatDate(date, '%H:%M:%S') + '.' + (Ox.pad(date % 1000, 3)) + ' ' + event + ' ' + - JSON.stringify(Ox.extend(e.clientX ? { - clientX: e.clientX, - clientY: e.clientY, - } : {}, e.clientDX ? { - clientDX: e.clientDX, - clientDY: e.clientDY - } : {})) + JSON.stringify( + Ox.extend(e.clientX ? { + clientX: e.clientX, + clientY: e.clientY, + } : {}, e.clientDX ? { + clientDX: e.clientDX, + clientDY: e.clientDY + } : {}) + ) + .replace(/"/g, '') + .replace(/:/g, ': ') + .replace(/,/g, ', ') ) .prependTo($log.$element); event == 'anyclick' && Ox.print(e); diff --git a/source/Ox.Geo/Ox.Geo.js b/source/Ox.Geo/Ox.Geo.js index 835c9ca1..4257873e 100644 --- a/source/Ox.Geo/Ox.Geo.js +++ b/source/Ox.Geo/Ox.Geo.js @@ -46,9 +46,9 @@ Ox.load.Geo = function(options, callback) { }).length, // Current independent countries without dependencies or disputes Ox.COUNTRIES.filter(function(c) { - return !c.dependencies.length && !c.dependency.length && - !c.disputes.length && !c.disputed.length && - !c.dissolved.length && !c.other; + return !c.dependencies.length && !c.dependency.length + && !c.disputes.length && !c.disputed.length + && !c.dissolved.length && !c.other; }).length, // Current dependent countries Ox.COUNTRIES.filter(function(c) { @@ -68,9 +68,9 @@ Ox.load.Geo = function(options, callback) { }).length, // Former independent countries without dependencies or disputes Ox.COUNTRIES.filter(function(c) { - return !c.dependencies.length && !c.dependency.length && - !c.disputes.length && !c.disputed.length && - c.dissolved.length && !c.other; + return !c.dependencies.length && !c.dependency.length + && !c.disputes.length && !c.disputed.length + && c.dissolved.length && !c.other; }).length, // Former dependect countries Ox.COUNTRIES.filter(function(c) { diff --git a/source/Ox.UI/js/Core/Ox.Element.js b/source/Ox.UI/js/Core/Ox.Element.js index a3d8d8f4..45152429 100644 --- a/source/Ox.UI/js/Core/Ox.Element.js +++ b/source/Ox.UI/js/Core/Ox.Element.js @@ -3,23 +3,30 @@ /*@ Ox.Element Basic UI element object # Usage -------------------------------------------------------------------- - ([options[, self]]) -> UI element + (element) -> UI element + (options) -> UI element + (options, self) -> UI element # Arguments ---------------------------------------------------------------- - options the options of the element + element Tagname or CSS selector + options Options of the element # Properties - element tagname or CSS selector - tooltip tooltip title or function that returns one + element Tagname or CSS selector + tooltip Tooltip title, or a function that returns one (e) -> tooltip title e mouse event - options tagname or CSS selector - self shared private variable + self Shared private variable # Events ------------------------------------------------------------------- anyclick anyclick - Fires on mouseup, but not on any subsequent mouseup within 250 ms + Fires on mouseup, but not on any subsequent mouseup within 250 ms (this + is useful if one wants to listen for singleclicks, but not doubleclicks, + since it will fire immediately, and won't fire again in case of a + doubleclick) * <*> original event properties doubleclick doubleclick - Fires on the second mousedown within 250 ms - * <*> original event properties + Fires on the second mousedown within 250 ms (this is useful if one wants + to listen for both singleclicks and doubleclicks, since it will not + trigger a singleclick event) + * <*> original event properties drag drag Fires on mousemove after dragstart, stops firing on mouseup clientDX horizontal drag delta in px @@ -31,361 +38,391 @@ Ox.Element Basic UI element object clientDY vertical drag delta in px * <*> original event properties dragenter dragenter - Fires when entering an element during drag - dragleave dragleave - Fires when leaving an element during drag - dragpause dragpause - Fires once when the mouse doesn't move for 250 ms during drag + Fires when entering an element during drag (this fires on the element + being dragged -- the target element is the event's target property) clientDX horizontal drag delta in px clientDY vertical drag delta in px * <*> original event properties + dragleave dragleave + Fires when leaving an element during drag (this fires on the element + being dragged -- the target element is the event's target property) + clientDX horizontal drag delta in px + clientDY vertical drag delta in px + * <*> original event properties + dragpause dragpause + Fires once when the mouse doesn't move for 250 ms during drag (this is + useful in order to execute operations that are too expensive to be + attached to the drag event) + clientDX horizontal drag delta in px + clientDY vertical drag delta in px + * <*> original event properties + mousedown mousedown + Fires on mousedown (this is useful if one wants to listen for + singleclicks, but not doubleclicks or drag events, and wants the event + to fire as early as possible) + * <*> original event properties dragstart dragstart Fires when the mouse is down for 250 ms * <*> original event properties mouserepeat mouserepeat Fires every 50 ms after the mouse was down for 250 ms, stops firing on - mouseleave or mouseup - * <*> original event properties + mouseleave or mouseup (this fires like a key that is being pressed and + held, and is useful for buttons like scrollbars arrows that need to + react to both clicking and holding) singleclick singleclick - Fires 250 ms after mouseup, if there was no subsequent mousedown + Fires 250 ms after mouseup, if there was no subsequent mousedown (this + is useful if one wants to listen for both singleclicks and doubleclicks, + since it will not fire for doubleclicks) * <*> original event properties @*/ Ox.Element = function(options, self) { - // create private object - self = self || {}; - // create defaults and options objects - self.defaults = {}; - self.options = options || {}; - // allow for Ox.TestElement('') - // or Ox.TestElement('cssSelector') - if (Ox.isString(self.options)) { - self.options = { - element: self.options - }; - } - // create event handler - // (this can be passed as part of self) - if (!self.$eventHandler) { - self.$eventHandler = $('
'); - } + // create private object + self = self || {}; + // create defaults and options objects + self.defaults = {}; + self.options = options || {}; + // allow for Ox.TestElement('') + // or Ox.TestElement('cssSelector') + if (Ox.isString(self.options)) { + self.options = { + element: self.options + }; + } + // create event handler + // (this can be passed as part of self) + if (!self.$eventHandler) { + self.$eventHandler = $('
'); + } - // create public object - var that = new Ox.JQueryElement( - $(self.options.element || '
') - ) - .mousedown(mousedown); + // create public object + var that = new Ox.JQueryElement( + $(self.options.element || '
') + ) + .mousedown(mousedown); - if (self.options.tooltip) { - if (Ox.isString(self.options.tooltip)) { - that.$tooltip = Ox.Tooltip({ - title: self.options.tooltip, - }); - that.bind({ - mouseenter: mouseenter - }); - } else { - that.$tooltip = Ox.Tooltip({ - animate: false - }); - that.bind({ - mousemove: mousemove - }); - } + if (self.options.tooltip) { + if (Ox.isString(self.options.tooltip)) { + that.$tooltip = Ox.Tooltip({ + title: self.options.tooltip, + }); that.bind({ - mouseleave: mouseleave + mouseenter: mouseenter + }); + } else { + that.$tooltip = Ox.Tooltip({ + animate: false + }); + that.bind({ + mousemove: mousemove }); } + that.bind({ + mouseleave: mouseleave + }); + } - function bind(action, event, callback) { - self.$eventHandler[action]('ox_' + event, function(event, data) { - callback(Ox.extend({ - _element: that.$element, - _event: event - }, data)); - }); - } + function bind(action, event, callback) { + self.$eventHandler[action]('ox_' + event, function(event, data) { + callback(Ox.extend({ + _element: that.$element, + _event: event + }, data)); + }); + } - function mousedown(e) { - /* - better mouse events - mousedown: - trigger mousedown - within 250 msec: - mouseup: trigger anyclick ("click" would collide with click events of certain widgets) - mouseup + mousedown: trigger doubleclick - after 250 msec: - mouseup + no mousedown within 250 msec: trigger singleclick - no mouseup within 250 msec: - trigger mouserepeat every 50 msec - trigger dragstart - mousemove: trigger drag - no mousemove within 250 msec: - trigger dragpause - mouseup: trigger dragend - */ - var clientX, clientY, - dragTimeout = 0, - mouseInterval = 0; - if (!self.mouseTimeout) { - // first mousedown - that.triggerEvent('mousedown', e); - self.mouseup = false; - self.mouseTimeout = setTimeout(function() { - self.mouseTimeout = 0; - if (self.mouseup) { - // singleclick - that.triggerEvent('singleclick', e); - } else { - // mouserepeat, drag - clientX = e.clientX; - clientY = e.clientY; - that.triggerEvent('dragstart', e); - $('*').bind({ - mouseenter: dragenter, - mouseleave: dragleave - }); - mouserepeat(); - mouseInterval = setInterval(mouserepeat, 50); - Ox.UI.$window.unbind('mouseup', mouseup) - .mousemove(mousemove) - .one('mouseup', function(e) { - clearInterval(mouseInterval); - clearTimeout(dragTimeout); - Ox.UI.$window.unbind('mousemove', mousemove); - $('*').unbind({ - mouseenter: dragenter, - mouseleave: dragleave - }); - that.triggerEvent('dragend', extend(e)); - }); - that.one('mouseleave', function() { - clearInterval(mouseInterval); - }); - } - }, 250); - } else { - // second mousedown - clearTimeout(self.mouseTimeout); + function mousedown(e) { + /* + better mouse events + mousedown: + trigger mousedown + within 250 msec: + mouseup: trigger anyclick + mouseup + mousedown: trigger doubleclick + after 250 msec: + mouseup + no mousedown within 250 msec: trigger singleclick + no mouseup within 250 msec: + trigger mouserepeat every 50 msec + trigger dragstart + mousemove: trigger drag + no mousemove for 250 msec: + trigger dragpause + mouseup: trigger dragend + "anyclick" is not called "click" since this would collide with the click + events of some widgets + */ + var clientX, clientY, + dragTimeout = 0, + mouseInterval = 0; + if (!self.mouseTimeout) { + // first mousedown + that.triggerEvent('mousedown', e); + self.mouseup = false; + self.mouseTimeout = setTimeout(function() { + // 250 ms later, no subsequent click self.mouseTimeout = 0; - that.triggerEvent('doubleclick', e); - } - Ox.UI.$window.one('mouseup', mouseup); - function dragenter(e) { - that.triggerEvent('dragenter', e); - } - function dragleave(e) { - that.triggerEvent('dragleave', e); - } - function extend(e) { - return Ox.extend({ - clientDX: e.clientX - clientX, - clientDY: e.clientY - clientY - }, e); - } - function mousemove(e) { - e = extend(e); - clearTimeout(dragTimeout); - dragTimeout = setTimeout(function() { - that.triggerEvent('dragpause', e); - }, 250); - that.triggerEvent('drag', e); - } - function mouserepeat() { - that.triggerEvent('mouserepeat'); - } - function mouseup(e) { - // only trigger on first mouseup - if (!self.mouseup) { - that.triggerEvent('anyclick', e); - self.mouseup = true; + if (self.mouseup) { + // mouse went up, trigger singleclick + that.triggerEvent('singleclick', e); + } else { + // mouse is still down, trigger mouserepeat + // every 50 ms until mouseleave or mouseup + mouserepeat(); + mouseInterval = setInterval(mouserepeat, 50); + that.one('mouseleave', function() { + clearInterval(mouseInterval); + }); + // trigger dragstart, set up drag events + that.triggerEvent('dragstart', e); + $('*').bind({ + mouseenter: dragenter, + mouseleave: dragleave + }); + clientX = e.clientX; + clientY = e.clientY; + Ox.UI.$window + .unbind('mouseup', mouseup) + .mousemove(mousemove) + .one('mouseup', function(e) { + // stop checking for mouserepeat + clearInterval(mouseInterval); + // stop checking for dragpause + clearTimeout(dragTimeout); + // stop checking for drag + Ox.UI.$window.unbind('mousemove', mousemove); + // stop checking for dragenter and dragleave + $('*').unbind({ + mouseenter: dragenter, + mouseleave: dragleave + }); + // trigger dragend + that.triggerEvent('dragend', extend(e)); + }); } - } + }, 250); + } else { + // second mousedown within 250 ms, trigger doubleclick + clearTimeout(self.mouseTimeout); + self.mouseTimeout = 0; + that.triggerEvent('doubleclick', e); } - - function mouseenter(e) { - that.$tooltip.show(e); + Ox.UI.$window.one('mouseup', mouseup); + function dragenter(e) { + that.triggerEvent('dragenter', extend(e)); } - - function mouseleave(e) { - that.$tooltip.hide(); + function dragleave(e) { + that.triggerEvent('dragleave', extend(e)); + } + function extend(e) { + return Ox.extend({ + clientDX: e.clientX - clientX, + clientDY: e.clientY - clientY + }, e); } - function mousemove(e) { - //Ox.print('mousemove!!') - that.$tooltip.options({ - title: self.options.tooltip(e) - }).show(e); + e = extend(e); + clearTimeout(dragTimeout); + dragTimeout = setTimeout(function() { + // mouse did not move for 250 ms, trigger dragpause + that.triggerEvent('dragpause', e); + }, 250); + that.triggerEvent('drag', e); } - - self.setOption = function() { - // self.setOptions(key, value) - // is called when an option changes - // (to be implemented by widget) - }; - - that._self = self; // fixme: remove - - /*@ - bindEvent Binds a function to an event - (event, callback) -> This element - ({event: callback, ...}) -> This element - callback Callback function - data event data (key/value pairs) - event Event name - Event names can be namespaced, like 'click.foo' - @*/ - that.bindEvent = function() { - Ox.forEach(Ox.makeObject(arguments), function(callback, event) { - bind('bind', event, callback); - }); - return that; + function mouserepeat(e) { + that.triggerEvent('mouserepeat', e); } - - /*@ - bindEventOnce Binds a function to an event, once - (event, callback) -> This element object - ({event: callback, ...}) -> This element object - callback Callback function - data event data (key/value pairs) - event Event name - Event names can be namespaced, like 'click.foo' - @*/ - that.bindEventOnce = function() { - Ox.forEach(Ox.makeObject(arguments), function(callback, event) { - bind('one', event, callback); - }); - return that; - }; - - /*@ - defaults Sets the default options for an element object - ({key: value, ...}) -> This element object - key The name of the default option - value The value of the default option - @*/ - that.defaults = function(defaults) { - // sets the default options - self.defaults = defaults; - self.options = defaults; - return that; - }; - - /*@ - gainFocus Makes an element object gain focus - () -> This element object - @*/ - that.gainFocus = function() { - Ox.Focus.focus(that.id); - return that; - }; - - /*@ - hasFocus Returns true if an element object has focus - () -> True if the element has focus - @*/ - that.hasFocus = function() { - return Ox.Focus.focused() == that.id; - }; - - /*@ - loseFocus Makes an element object lose focus - () -> This element object - @*/ - - that.loseFocus = function() { - Ox.Focus.blur(that.id); - return that; - }; - - /*@ - options Gets or sets the options of an element object - # Usage - () -> all options - (key) -> the value of option[key] - (key, value) -> this element - Sets options[key] to value and calls self.setOption(key, value) - if the key/value pair was added or modified - ({key: value, ...}) -> this element - Sets multiple options and calls self.setOption(key, value) - for every key/value pair that was added or modified - # Arguments - key the name of the option - value the value of the option - @*/ - - that.options = function() { - return Ox.getset(self.options, arguments, self.setOption, that); - }; - - /*@ - removeElement Removes an element object and its event handler - () -> This element - @*/ - that.removeElement = function() { - that.loseFocus(); - delete self.$eventHandler; - that.remove(); - // fixme: ok to comment out the following line? - delete Ox.UI.elements[that.id]; - return that; - }; - - /*@ - triggerEvent Triggers an event - (event) -> This element object - (event, data) -> This element object - ({event: data, ...}) -> This element object - event Event name - data Event data (key/value pairs) - @*/ - that.triggerEvent = function() { - Ox.forEach(Ox.makeObject(arguments), function(data, event) { - if ([ - 'mousedown', 'mouserepeat', 'anyclick', 'singleclick', 'doubleclick', - 'dragstart', 'drag', 'dragenter', 'dragleave', 'dragpause', 'dragend', - 'draganddropstart', 'draganddrop', 'draganddropenter', 'draganddropleave', 'draganddropend', - 'playing', 'position', 'progress' - ].indexOf(event) == -1) { - if (!/^pandora_/.test(event)) { - Ox.print(that.id, self.options.id, 'trigger', event, data); - } - } - // it is necessary to check if self.$eventHandler exists, - // since, for example, when removing the element on click, - // singleclick will fire after the removal of the event handler - self.$eventHandler && self.$eventHandler.trigger('ox_' + event, data); - }); - return that; - }; - - /*@ - unbindEvent Unbinds all callbacks from an event - To unbind a specific handler, use namespaced events, like - bindEvent('click.foo', callback), and then - unbindEvent('click.foo'). - () -> This element object - Unbinds all events - (event) -> This element object - Unbinds one event - (event, event, ...) -> This element object - Unbinds multiple events - ([event, event, ...]) -> This element object - Unbinds multiple events - event Event name - @*/ - that.unbindEvent = function() { - if (arguments.length == 0) { - self.$eventHandler.unbind(); - } else { - Ox.makeArray(arguments).forEach(function(event) { - self.$eventHandler.unbind('ox_' + event); - }); + function mouseup(e) { + if (!self.mouseup) { + // mouse went up for the first time, trigger anyclick + that.triggerEvent('anyclick', e); + self.mouseup = true; } - return that; - }; + } + } + function mouseenter(e) { + that.$tooltip.show(e); + } + + function mouseleave(e) { + that.$tooltip.hide(); + } + + function mousemove(e) { + //Ox.print('mousemove!!') + that.$tooltip.options({ + title: self.options.tooltip(e) + }).show(e); + } + + self.setOption = function() { + // self.setOptions(key, value) + // is called when an option changes + // (to be implemented by widget) + }; + + that._self = self; // fixme: remove + + /*@ + bindEvent Binds a function to an event + (event, callback) -> This element + ({event: callback, ...}) -> This element + callback Callback function + data event data (key/value pairs) + event Event name + Event names can be namespaced, like 'click.foo' + @*/ + that.bindEvent = function() { + Ox.forEach(Ox.makeObject(arguments), function(callback, event) { + bind('bind', event, callback); + }); return that; + } + + /*@ + bindEventOnce Binds a function to an event, once + (event, callback) -> This element object + ({event: callback, ...}) -> This element object + callback Callback function + data event data (key/value pairs) + event Event name + Event names can be namespaced, like 'click.foo' + @*/ + that.bindEventOnce = function() { + Ox.forEach(Ox.makeObject(arguments), function(callback, event) { + bind('one', event, callback); + }); + return that; + }; + + /*@ + defaults Sets the default options for an element object + ({key: value, ...}) -> This element object + key The name of the default option + value The value of the default option + @*/ + that.defaults = function(defaults) { + // sets the default options + self.defaults = defaults; + self.options = defaults; + return that; + }; + + /*@ + gainFocus Makes an element object gain focus + () -> This element object + @*/ + that.gainFocus = function() { + Ox.Focus.focus(that.id); + return that; + }; + + /*@ + hasFocus Returns true if an element object has focus + () -> True if the element has focus + @*/ + that.hasFocus = function() { + return Ox.Focus.focused() == that.id; + }; + + /*@ + loseFocus Makes an element object lose focus + () -> This element object + @*/ + + that.loseFocus = function() { + Ox.Focus.blur(that.id); + return that; + }; + + /*@ + options Gets or sets the options of an element object + # Usage + () -> all options + (key) -> the value of option[key] + (key, value) -> this element + Sets options[key] to value and calls self.setOption(key, value) + if the key/value pair was added or modified + ({key: value, ...}) -> this element + Sets multiple options and calls self.setOption(key, value) + for every key/value pair that was added or modified + # Arguments + key the name of the option + value the value of the option + @*/ + + that.options = function() { + return Ox.getset(self.options, arguments, self.setOption, that); + }; + + /*@ + removeElement Removes an element object and its event handler + () -> This element + @*/ + that.removeElement = function() { + that.loseFocus(); + delete self.$eventHandler; + that.remove(); + // fixme: ok to comment out the following line? + delete Ox.UI.elements[that.id]; + return that; + }; + + /*@ + triggerEvent Triggers an event + (event) -> This element object + (event, data) -> This element object + ({event: data, ...}) -> This element object + event Event name + data Event data (key/value pairs) + @*/ + that.triggerEvent = function() { + Ox.forEach(Ox.makeObject(arguments), function(data, event) { + if ([ + 'mousedown', 'mouserepeat', 'anyclick', 'singleclick', 'doubleclick', + 'dragstart', 'drag', 'dragenter', 'dragleave', 'dragpause', 'dragend', + 'draganddropstart', 'draganddrop', 'draganddropenter', 'draganddropleave', 'draganddropend', + 'playing', 'position', 'progress' + ].indexOf(event) == -1) { + if (!/^pandora_/.test(event)) { + Ox.print(that.id, self.options.id, 'trigger', event, data); + } + } + // it is necessary to check if self.$eventHandler exists, + // since, for example, when removing the element on click, + // singleclick will fire after the removal of the event handler + self.$eventHandler && self.$eventHandler.trigger('ox_' + event, data); + }); + return that; + }; + + /*@ + unbindEvent Unbinds all callbacks from an event + To unbind a specific handler, use namespaced events, like + bindEvent('click.foo', callback), and then + unbindEvent('click.foo'). + () -> This element object + Unbinds all events + (event) -> This element object + Unbinds one event + (event, event, ...) -> This element object + Unbinds multiple events + ([event, event, ...]) -> This element object + Unbinds multiple events + event Event name + @*/ + that.unbindEvent = function() { + if (arguments.length == 0) { + self.$eventHandler.unbind(); + } else { + Ox.makeArray(arguments).forEach(function(event) { + self.$eventHandler.unbind('ox_' + event); + }); + } + return that; + }; + + return that; }; diff --git a/source/Ox.UI/js/Core/Ox.URL.js b/source/Ox.UI/js/Core/Ox.URL.js index 9f6a31b8..c4575104 100644 --- a/source/Ox.UI/js/Core/Ox.URL.js +++ b/source/Ox.UI/js/Core/Ox.URL.js @@ -60,8 +60,8 @@ view List or item view, like "clips" or "map". Both list and item views are per type. span Position or selection in a view, either one or two coordinates or one id, like in "video/01:00", "video/-01:00", "video/01:00,-01:00", - "video/annotationABC", "video/subtitles:23", "text/chapter42", - "map/0,0", "map/-45,-90,45,90", "map/Barcelona", "image/100,100" etc. + "video/@annotationABC", "video/@subtitles:23", "text/@chapter42", + "map/0,0", "map/-45,-90,45,90", "map/@barcelona", "image/100,100" etc. Testing id is asynchronous. sort Sort, like "title" or "-director" or "country,year,-language,+runtime" find Query, like a=x or a=x&b=y or a=x&(b=y|c=z). A query object has the form @@ -85,8 +85,8 @@ kv k v > is more than (number) k!>v k v !> is not more than (number) -k=v:w k [v,w] = is between (number), is (string) -k!=v:w k [v,w] != is not between (number), is not (string) +k=v:w k [v,w] - is between (number), is (string) +k!=v:w k [v,w] !- is not between (number), is not (string) All parts of the URL can be omitted, as long as the order is preserved. @@ -504,7 +504,7 @@ Ox.URL = function(options) { } if (!state.span && /^[A-Z@]/.test(parts[0])) { // test for span id or name - self.options.getSpan(state.item, state.view, parts[0], function(span, view) { + self.options.getSpan(state.item, state.view, parts[0].replace(/%20/g, ' '), function(span, view) { if (span) { if (!state.view) { // set list or item view diff --git a/source/Ox.UI/js/Map/Ox.Map.js b/source/Ox.UI/js/Map/Ox.Map.js index 5f165d59..4050227f 100644 --- a/source/Ox.UI/js/Map/Ox.Map.js +++ b/source/Ox.UI/js/Map/Ox.Map.js @@ -14,6 +14,7 @@ Ox.Map Basic map object options options clickable If true, clicking on the map finds a place editable If true, places are editable + find Initial query findPlaceholder Placeholder text for the find input element maxMarkers Maximum number of markers to be displayed places <[o]|f|null> Array of, or function that returns, place objects @@ -97,6 +98,7 @@ Ox.Map = function(options, self) { // fixme: isClickable? hasZoombar? clickable: false, editable: false, + find: '', findPlaceholder: 'Find', maxMarkers: 100, places: null, @@ -570,15 +572,9 @@ Ox.Map = function(options, self) { } function getMetersPerPixel() { - // fixme: this is wrong when resizing the map horizontally - var mapWidth = self.$map.width(), - span = self.map.getBounds().toSpan().lng(); - /* - if (span >= 360) { - span = 360 * mapWidth / Ox.MAP_TILE_SIZE; - } - */ - return Ox.getMetersPerDegree(self.map.getCenter().lat()) * span / mapWidth; + // m/px = m/deg * deg/px + var degreesPerPixel = 360 / (Ox.MAP_TILE_SIZE * Math.pow(2, self.map.getZoom())); + return Ox.getMetersPerDegree(self.map.getCenter().lat()) * degreesPerPixel; } function getMinZoom() { @@ -726,9 +722,18 @@ Ox.Map = function(options, self) { google.maps.event.addListener(self.map, 'zoom_changed', zoomChanged); google.maps.event.addListenerOnce(self.map, 'tilesloaded', tilesLoaded); - mapBounds && self.map.fitBounds(mapBounds); - if (self.map.getZoom() < self.minZoom) { - self.map.setZoom(self.minZoom); + if (self.options.find) { + self.$findInput.options({value: self.options.find}) + .triggerEvent('submit', {value: self.options.find}); + } else if (self.options.selected) { + selectPlace(self.options.selected); + } else { + mapBounds && self.map.fitBounds(mapBounds); + ///* + if (self.map.getZoom() < self.minZoom) { + self.map.setZoom(self.minZoom); + } + //*/ } self.places = []; @@ -754,6 +759,11 @@ Ox.Map = function(options, self) { } } + function crossesDateline() { + var bounds = self.map.getBounds(); + return bounds.getSouthWest().lng() > bounds.getNorthEast().lng(); + } + function mapChanged() { // gets called after panning or zooming Ox.print('mapChanged'); @@ -765,8 +775,7 @@ Ox.Map = function(options, self) { south = southWest.lat(), west = southWest.lng(), north = northEast.lat(), - east = northEast.lng(), - crossesDateline = west > east; + east = northEast.lng(); if (!self.isAsync) { self.places.sort(function(a, b) { var sort = { @@ -784,31 +793,18 @@ Ox.Map = function(options, self) { } }); } else { - Ox.print('QUERY', { - conditions: Ox.merge([ - {key: 'lat', value: [south, north], operator: '-'} - ], !crossesDateline ? [ - {key: 'lng', value: [west, east], operator: '-'} - ] : [ - { - conditions: [ - {key: 'lng', value: west, operator: '<'}, - {key: 'lng', value: east, operator: '>'} - ], - operator: '|' - } - ]), - operator: '&' - }); + Ox.print ('sG cD', spansGlobe(), crossesDateline()) self.options.places({ keys: self.placeKeys, query: { conditions: Ox.merge([ {key: 'lat', value: [south, north], operator: '-'} - ], !crossesDateline ? [ - {key: 'lng', value: [west, east], operator: '-'} - ] : [ + ], spansGlobe() ? [ + {key: 'lng', value: [-180, 180], operator: '-'} + ] : crossesDateline() ? [ {key: 'lng', value: [east, west], operator: '!-'} + ] : [ + {key: 'lng', value: [west, east], operator: '-'} ]), operator: '&' }, @@ -1033,16 +1029,19 @@ Ox.Map = function(options, self) { keys: self.placeKeys, query: { conditions: [ - {key: 'id', value: id, operator: '='} - ] + {key: 'id', value: id, operator: '=='} + ], + operator: '&' + } + }, function(result) { + if (result.data.items.length) { + place = new Ox.MapPlace(Ox.extend({ + map: that + }, result.data.items[0])).add(); + self.places.push(place); + select(); + that.zoomToPlace(); } - }, function(results) { - place = new Ox.MapPlace(Ox.extend({ - map: that - }, results.data.items[0])).add(); - self.places.push(place); - select(); - that.panToPlace(); }); } } else { @@ -1110,6 +1109,11 @@ Ox.Map = function(options, self) { //Ox.print('STATUS DONE'); } + function spansGlobe() { + // fixme: or self.options.width ?? + return self.$map.width() > Ox.MAP_TILE_SIZE * Math.pow(2, self.map.getZoom()); + }; + function submitFind(data) { that.findPlace(data.value, function(place) { setStatus(place); diff --git a/source/Ox.UI/js/Panel/Ox.SplitPanel.js b/source/Ox.UI/js/Panel/Ox.SplitPanel.js index e3255a28..fa17c585 100644 --- a/source/Ox.UI/js/Panel/Ox.SplitPanel.js +++ b/source/Ox.UI/js/Panel/Ox.SplitPanel.js @@ -145,7 +145,6 @@ Ox.SplitPanel = function(options, self) { css[self.edges[1]] = ( self.length == 3 ? getVisibleSize(self.options.elements[2]) : 0 ) + 'px'; - Ox.print('i==1 CSS', css) } else { if (element.size == 'auto') { css[self.edges[0]] = getVisibleSize(self.options.elements[0]) diff --git a/source/Ox.UI/js/Video/Ox.VideoPlayer.js b/source/Ox.UI/js/Video/Ox.VideoPlayer.js index 35bf0048..5b547530 100644 --- a/source/Ox.UI/js/Video/Ox.VideoPlayer.js +++ b/source/Ox.UI/js/Video/Ox.VideoPlayer.js @@ -20,7 +20,7 @@ Ox.VideoPlayer Generic Video Player enableFind If true, enable find enableFullscreen If true, enable fullscreen enableKeyboard If true, enable keyboard controls - enableMouse If true, click toggles paused + enableMouse If true, click toggles paused and drag changes position externalControls If true, controls are outside the video find Query string focus focus on 'click', 'load' or 'mouseover' @@ -34,7 +34,7 @@ Ox.VideoPlayer Generic Video Player keepLogoVisible If true, logo stays visible after mouseleave logo Logo image URL logoLink Logo link URL - logoTitle Text for tooltip + logoTitle Text for Logo tooltip // fixme: shouldn't this be logoTooltip then?s muted If true, video is muted paused If true, video is paused playInToOut If true, video plays only from in to out diff --git a/source/Ox.js b/source/Ox.js index c6b0a4b8..cf9908a3 100644 --- a/source/Ox.js +++ b/source/Ox.js @@ -6,11 +6,12 @@ Some conventions: Functions - only one var statement, in the first line of the function - - return only once, from the last line of the function body + - return only once, from the last line of the function Variable names arg argument args arguments arr array + canFoo boolean callback callback function col collection (array, string or object) date date @@ -29,18 +30,48 @@ Some conventions: v value (of a key/value pair) val value (of a key/value pair) Indentation - var a = 1, - b = 2, - c = 3; - Obj.fn1() - .fn2() - .fn3(); + Variable definitions + var a = { + key: value, + key: value, + key: value + }, + b = {key: value}, + c = {key: value}; + Method chaining + Obj.fnA({ + key: value, + key: value, + key: value + }) + .fnB({key: val}) + .fnC({key: val}); + Simple conditionals + condition && expression; + Conditionals + if (condition) { + expression; + } + Conditionals with long conditions + if ( + condition + && condition + && condition + ) { + expression; + } + Ternary operator + condition ? expression : expression; + Ternary operator with long conditions or expressions + condition ? expression + : condition ? expression + : expression; */ // todo: check http://ejohn.org/blog/ecmascript-5-strict-mode-json-and-more/ // also see https://github.com/tlrobinson/narwhal/blob/master/lib/util.js - +// Fallbacks ------------------------------------------------------------------- // see https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/filter if (!Array.prototype.filter) { @@ -176,11 +207,17 @@ Ox = function(val) { /*@ Ox.load Loads a module + A module named "Test" provides Ox.Test/Ox.Test.js, in which it + defines one method, Ox.load.Test, that takes two arguments, + options and callback, and calls + callback with one argument, true for success or + false if an error occurred. Generally, the module should + define Ox.Test and attach its own methods there. (module, callback) -> undefined (module, options, callback) -> undefined (modules, callback) -> undefined module Module name - modules Multiple modules {name: options} + modules Multiple modules {name: options, ...} options Module options callback Callback function success If true, the module has been loaded successfully @@ -235,6 +272,7 @@ Ox.uid Returns a unique id > Ox.uid() != Ox.uid() true @*/ + Ox.uid = (function() { var uid = 0; return function() { @@ -244,13 +282,14 @@ Ox.uid = (function() { /*@ Ox.wrap Wraps a value so that one can directly call any Ox function on it + Ox(value) is a shorthand for Ox.wrap(value). (value) -> wrapped value - chain wrap the return value to allow chaining - value unwrap the value wrapped by chain() + chain Wrap return values to allow chaining + value Unwrap the value wrapped by chain() value <*> Any value - > Ox.wrap("foobar").repeat(2) + > Ox("foobar").repeat(2) "foobarfoobar" - > Ox.wrap("foobar").chain().reverse().toTitleCase().value() + > Ox("foobar").chain().reverse().toTitleCase().value() "Raboof" > Ox.wrap("foobar").value() "foobar" @@ -425,32 +464,6 @@ Ox.avg = function(obj) { return Ox.sum(obj) / Ox.len(obj); }; -/*@ -Ox.clone Returns a (shallow or deep) copy of an object or array - > (function() { a = ['val']; b = Ox.clone(a); a[0] = null; return b[0]; }()) - 'val' - > (function() { a = {key: 'val'}; b = Ox.clone(a); a.key = null; return b.key; }()) - 'val' - > Ox.clone(0) - 0 -@*/ - -Ox.clone = function(col, deep) { - // fixme: is there any use case for shallow copy? - var ret = Ox.isArray(col) ? [] : {}; - if (deep) { - Ox.forEach(col, function(val, key) { - ret[key] = ['array', 'object'].indexOf(Ox.typeOf(val)) > -1 - ? Ox.clone(val, true) : val; - }); - } else { - ret = Ox.isArray(col) ? col.slice() - : Ox.isObject(col) ? Ox.extend({}, col) - : col; - } - return ret; -}; - /*@ Ox.contains Tests if a collection contains a value > Ox.contains(['foo', 'bar'], 'foo') @@ -470,6 +483,33 @@ Ox.contains = function(col, val) { return (Ox.isObject(col) ? Ox.values(col) : col).indexOf(val) > -1; }; +/*@ +Ox.copy Returns a (shallow or deep) copy of an object or array + > (function() { a = ['v']; b = Ox.copy(a); a[0] = null; return b[0]; }()) + 'v' + > (function() { a = {k: 'v'}; b = Ox.copy(a); a.k = null; return b.k; }()) + 'v' + > Ox.clone(0) + 0 +@*/ + +Ox.copy = Ox.clone = function(col, deep) { + // fixme: remove references to Ox.clone + // fixme: is there any use case for shallow copy? + var ret = Ox.isArray(col) ? [] : {}; + if (deep) { + Ox.forEach(col, function(val, key) { + ret[key] = ['array', 'object'].indexOf(Ox.typeOf(val)) > -1 + ? Ox.clone(val, true) : val; + }); + } else { + ret = Ox.isArray(col) ? col.slice() + : Ox.isObject(col) ? Ox.extend({}, col) + : col; + } + return ret; +}; + /*@ Ox.count Counts the occurences of values in a collection > Ox.count(['f', 'o', 'o']) @@ -600,7 +640,7 @@ Ox.getObjectById Returns an array element with a given id > Ox.getObjectById([{id: "foo", title: "Foo"}, {id: "bar", title: "Bar"}], "foo") {id: "foo", title: "Foo"} @*/ -// fixme: shouldn't this be getElementById() ? +// fixme: should this be getElementById() ? Ox.getObjectById = function(arr, id) { var ret = null; Ox.forEach(arr, function(v) { @@ -617,7 +657,7 @@ Ox.getPositionById Returns the index of an array element with a given id > Ox.getPositionById([{id: "foo", title: "Foo"}, {id: "bar", title: "Bar"}], "foo") 0 @*/ -// fixme: shouldn't this be getIndexById() ? +// fixme: this should be getIndexById() Ox.getPositionById = function(arr, id) { var ret = -1; Ox.forEach(arr, function(v, i) { @@ -656,7 +696,9 @@ Ox.getset Generic getter and setter function ') - '<script>alert()</script>' + '<script>alert()</script>' + > Ox.parseHTML('\'foo\' < \'bar\' && "foo" > "bar"') + '\'foo\' < \'bar\' && "foo" > "bar"' + > Ox.parseHTML('foo') + 'foo' + > Ox.parseHTML('foo') + 'foo' @*/ + Ox.parseHTML = (function() { var defaultTags = [ + // fixme: why not p? 'a', 'b', 'blockquote', 'cite', 'code', 'del', 'em', 'i', 'img', 'ins', 'li', 'ol', 'q', 'rtl', 's', @@ -3357,13 +3407,13 @@ Ox.parseHTML = (function() { html = Ox.encodeHTML(html); html = Ox.parseURLs(html); html = Ox.parseEmailAddresses(html); + Ox.print(html, 'matches', matches); matches.forEach(function(match, i) { html = html.replace(new RegExp(tab + i + tab, 'gi'), match); }); html = html.replace(/\n/g, '
\n') // close extra opening (and remove extra closing) tags - // return $('
').html(html).html(); - // fixme: this converts '"' to '"' + // note: this converts '"' to '"' return Ox.element('
').html(html).html(); } @@ -3952,7 +4002,7 @@ Ox.tokenize = (function() { 'global', 'ignoreCase', 'lastIndex', 'multiline', 'source', // Window 'applicationCache', - 'closed', 'content', 'crypto', + 'closed', 'console', 'content', 'crypto', 'defaultStatus', 'document', 'frameElement', 'frames', 'history', @@ -3966,7 +4016,6 @@ Ox.tokenize = (function() { 'self', 'sessionStorage', 'status', 'statusbar', 'toolbar', 'top' ] - // Window stuff? 'atob', 'btoa', 'console', 'document' ... }; return function(source) { @@ -4269,10 +4318,10 @@ Ox.extend = function() { Ox.serialize Parses an object into query parameters > Ox.serialize({a: 1, b: 2, c: 3}) 'a=1&b=2&c=3' - > Ox.serialize({a: 1, b: 2.3, c: [4, 5]}) - 'a=1&b=2.3&c=4,5' + > Ox.serialize({a: -1, b: 2.3, c: [4, 5]}) + 'a=-1&b=2.3&c=4,5' > Ox.serialize({string: 'foo', empty: {}, null: null, undefined: void 0}) - 'string=bar' + 'string=foo' @*/ Ox.serialize = function(obj) { var arr = []; @@ -4288,8 +4337,8 @@ Ox.serialize = function(obj) { Ox.unserialize Parses query parameters into an object > Ox.unserialize('a=1&b=2&c=3') {a: '1', b: '2', c: '3'} - > Ox.unserialize('a=1&b=2.3&c=4,5', true) - {a: 1, b: 2.3, c: [4, 5]} + > Ox.unserialize('a=-1&b=2.3&c=4,5', true) + {a: -1, b: 2.3, c: [4, 5]} @*/ Ox.unserialize = function(str, toNumber) { var obj = {}; @@ -4774,8 +4823,8 @@ Ox.truncate = function(str, len, pad, pos) { Ox.words Splits a string into words, removing punctuation (string) -> <[s]> Array of words string Any string - > Ox.words('Let\'s "walk" a tree-like key/value store--okay?') - ["let's", "walk", "a", "tree-like", "key", "value", "store", "okay"] + > Ox.words('Let\'s "split" array-likes into key/value pairs--okay?') + ["let's", "split", "array-likes", "into", "key", "value", "pairs", "okay"] @*/ Ox.words = function(str) { var arr = str.toLowerCase().split(/\b/), @@ -4783,12 +4832,11 @@ Ox.words = function(str) { len = arr.length, startsWithWord = /\w/.test(arr[0]); arr.forEach(function(v, i) { - // find single occurrences of "-" or "-" - // that are not at the beginning or end of the string - // and join the surrounding words with them + // find single occurrences of "-" or "'" that are not at the beginning + // or end of the string, and join the surrounding words with them if ( - i > 0 && i < len - 1 && - v.length == 1 && chr.indexOf(v) > -1 + i > 0 && i < len - 1 + && v.length == 1 && chr.indexOf(v) > -1 ) { arr[i + 1] = arr[i - 1] + arr[i] + arr[i + 1]; arr[i - 1] = arr[i] = ''; @@ -4986,18 +5034,17 @@ Ox.isEqual = function(a, b) { isEqual = true; } else if (type == 'date') { isEqual = a.getTime() == b.getTime(); - /* toString doesn't do it - } else if (['element', 'function'].indexOf(type) > -1) { + } else if (type == 'element') { + isEqual = a.isEqualNode(b); + } else if (type == 'function') { + // fixme: this doesn't do it isEqual = a.toString() == b.toString(); - */ } else if (type == 'regexp') { - isEqual = a.global == b.global && - a.ignore == b.ignore && - a.multiline == b.multiline && - a.source == b.source; + isEqual = a.global == b.global && a.ignore == b.ignore + && a.multiline == b.multiline && a.source == b.source; } else if ( - ['arguments', 'array', 'object'].indexOf(type) > -1 && - Ox.len(a) == Ox.len(b) + ['arguments', 'array', 'object'].indexOf(type) > -1 + && Ox.len(a) == Ox.len(b) ) { isEqual = true; Ox.forEach(a, function(v, k) { @@ -5018,7 +5065,7 @@ Ox.isFunction Tests if a value is a function > Ox.isFunction(/ /) false @*/ -Ox.isFunction = function(val) { // is in jQuery +Ox.isFunction = function(val) { return typeof val == 'function' && !Ox.isRegExp(val); }; @@ -5090,11 +5137,14 @@ Ox.isObject Tests if a value is a an object false > Ox.isObject(null) false + > Ox.isObject(/ /) + false @*/ Ox.isObject = function(val) { return typeof val == 'object' && !Ox.isArguments(val) - && !Ox.isArray(val) && !Ox.isDate(val) && !Ox.isNull(val); + && !Ox.isArray(val) && !Ox.isDate(val) + && !Ox.isNull(val) && !Ox.isRegExp(val); }; /*@ diff --git a/tests/tests.js b/tests/tests.js index 047cc8c3..b86b85be 100644 --- a/tests/tests.js +++ b/tests/tests.js @@ -1,4 +1,4 @@ -Ox.load('UI', function() { +Ox.load({UI: {}, Geo: {}}, function() { //Ox.UI.ready(function() { @@ -23,7 +23,7 @@ Ox.load('UI', function() { setBackground($tests, true); - tests(['../build/Ox.js'/*, '../build/js/ox.data.js'*/]); + tests(['../build/Ox.js', '../build/Ox.Geo/Ox.Geo.js']); function tests() { var passed = 0, failed = 0,