// vim: et:ts=4:sw=4:sts=4:ft=js // 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 (not implemented) options tagname or CSS selector self shared private variable # Properties --------------------------------------------------------------- $element jQuery DOM object bindEvent binds an event to a function, once # Usage (event, callback) -> this element ({event: callback, ...}) -> this element # Arguments event event name callback callback function data event data bindEventOnce binds an event to a function defaults sets the default options options sets the options triggerEvent triggers an event unbindEvent unbinds an event # 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 dragpause dragpause Fires once when the mouse doesn't move for 250 ms after 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() { /* tooltip option can be any of the following: string function(e), returns string {mousemove: true, title: function(e)} */ return function(options, self) { /* // allow for 'Ox.Element()' instead of 'new 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 if (!self.$eventHandler) { self.$eventHandler = $('
'); } // create public object var that = new Ox.JQueryElement( $(self.options.element || '
') ) .mousedown(mousedown); /* self.options.tooltip && that.bind(Ox.extend({ mouseenter: mouseenter, mouseleave: mouseleave }, self.options.tooltip.mousemove ? { mousemove: mousemove } : {})); */ function bind(action, event, fn) { self.$eventHandler[action]('ox_' + event, function(event, data) { // fixme: remove second parameter fn(Ox.extend({_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); 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); 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 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) { self.$tooltip = new Ox.Tooltip({ title: Ox.isString(self.options.tooltip) ? self.options.tooltip : Ox.isFunction(self.options.tooltip) ? self.options.tooltip(e) : self.options.tooltip.title(e) }).show(); } function mouseleave(e) { self.$tooltip.hide(); } function mousemove(e) { self.$tooltip.options({ title: self.options.tooltip.title(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 (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 ({event: callback, ...}) -> This element callback Callback function data event data (key/value pairs) event Event name (can be namespaced, like "click.foo") @*/ that.bindEventOnce = function() { Ox.forEach(Ox.makeObject(arguments), function(fn, event) { bind('one', event, fn); }); return that; }; /*@ Sets the default options for an element object ({key: value, ...}) -> key the name of the default option that the element object value the value of the default option @*/ that.defaults = function(defaults) { // sets the default options self.defaults = defaults; self.options = defaults; return that; }; /*@ Makes an element object gain focus () -> that that the element object @*/ that.gainFocus = function() { Ox.Focus.focus(that.id); return that; }; /*@ Returns true if an element object has focus () -> hasFocus hasFocus true if the element has focus @*/ that.hasFocus = function() { return Ox.Focus.focused() == that.id; }; /*@ Makes an element object lose focus () -> that that the 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); }; that.removeElement = function() { /*** remove this element, including its event handler ***/ that.loseFocus(); delete self.$eventHandler; that.remove(); delete Ox.UI.elements[that.id]; return that; }; that.triggerEvent = function() { /*** triggers an event Usage triggerEvent(event) triggerEvent(event, data) triggerEvent({eventA: dataA, eventB: dataB, ...}) ***/ Ox.forEach(Ox.makeObject(arguments), function(data, event) { if ([ 'mousedown', 'mouserepeat', 'anyclick', 'singleclick', 'doubleclick', 'dragstart', 'drag', 'dragpause', 'dragend', 'playing' ].indexOf(event) == -1) { Ox.print(that.id, self.options.id, 'trigger', event, data); } self.$eventHandler.trigger('ox_' + event, data); }); return that; }; that.unbindEvent = function() { /*** unbinds an event triggered by this element Usage unbindEvent() // unbinds all events unbindEvent(event) unbindEvent(eventA, eventB, ...) unbindEvent([eventA, eventB, ...]) to unbind a specific handler, use namespaced events bind('doubleclick.x', fn) ... unbind('doubleclick.x') ***/ if (arguments.length == 0) { self.$eventHandler.unbind(); } else { Ox.makeArray(arguments).forEach(function(event) { self.$eventHandler.unbind('ox_' + event); }); } return that; }; return that; } }();