// vim: et:ts=4:sw=4:sts=4:ft=javascript // check out http://ejohn.org/apps/learn/#36 (-#38, making fns work w/o new) /*@ Ox.Element Basic UI element object # Usage -------------------------------------------------------------------- ([options[, self]]) -> UI element # Arguments ---------------------------------------------------------------- options the options of the element # Properties element tagname or CSS selector tooltip tooltip title or function that returns one (e) -> tooltip title e mouse event options tagname or CSS selector self shared private variable # Events ------------------------------------------------------------------- anyclick anyclick Fires on mouseup, but not on any subsequent mouseup within 250 ms * <*> original event properties doubleclick doubleclick Fires on the second mousedown within 250 ms * <*> original event properties drag drag Fires on mousemove after dragstart, stops firing on mouseup clientDX horizontal drag delta in px clientDY vertical drag delta in px * <*> original event properties dragend dragpause Fires on mouseup after dragstart clientDX horizontal drag delta in px 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 clientDX horizontal drag delta in px clientDY vertical drag delta in px * <*> 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 singleclick singleclick Fires 250 ms after mouseup, if there was no subsequent mousedown * <*> original event properties @*/ Ox.Element = function(options, self) { /* // allow for 'Ox.Element()' instead of 'Ox.Element()' if (!(this instanceof arguments.callee)) { return new arguments.callee(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 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 }); } that.bind({ mouseleave: mouseleave }); } function bind(action, event, fn) { self.$eventHandler[action]('ox_' + event, function(event, data) { // fixme: remove second parameter (legacy) fn(Ox.extend({ _element: that.$element, _event: event }, data), 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); 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; } } } 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(fn, event) { bind('bind', event, fn); }); 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(fn, event) { bind('one', event, fn); }); 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) { 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; };