oxjs/source/Ox.UI/js/Core/Ox.Element.js
2011-07-29 20:48:43 +02:00

377 lines
14 KiB
JavaScript

// 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 <function:Ox.JQueryElement> Basic UI element object
# Usage --------------------------------------------------------------------
([options[, self]]) -> <object> UI element
# Arguments ----------------------------------------------------------------
options <object> the options of the element
# Properties
element <string> tagname or CSS selector
tooltip <string|function> tooltip title or function that returns one
(e) -> <string> tooltip title
e <object> mouse event
options <string> tagname or CSS selector
self <object> shared private variable
# Events -------------------------------------------------------------------
anyclick <event> anyclick
fires on mouseup, but not on any subsequent mouseup within 250 ms
* <*> original event properties
doubleclick <event> doubleclick
fires on the second mousedown within 250 ms
* <*> original event properties
drag <event> drag
fires on mousemove after dragstart, stops firing on mouseup
clientDX <number> horizontal drag delta in px
clientDY <number> vertical drag delta in px
* <*> original event properties
dragend <event> dragpause
Fires on mouseup after dragstart
clientDX <number> horizontal drag delta in px
clientDY <number> vertical drag delta in px
* <*> original event properties
dragpause <event> dragpause
Fires once when the mouse doesn't move for 250 ms after drag
clientDX <number> horizontal drag delta in px
clientDY <number> vertical drag delta in px
* <*> original event properties
dragstart <event> dragstart
fires when the mouse is down for 250 ms
* <*> original event properties
mouserepeat <event> mouserepeat
fires every 50 ms after the mouse was down for 250 ms, stops firing on
mouseleave or mouseup
* <*> original event properties
singleclick <event> 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('<tagname>')
// or Ox.TestElement('cssSelector')
if (Ox.isString(self.options)) {
self.options = {
element: self.options
};
}
// create event handler
if (!self.$eventHandler) {
self.$eventHandler = $('<div>');
}
// create public object
var that = new Ox.JQueryElement(
$(self.options.element || '<div>')
)
.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
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);
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) {
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 <function> Binds a function to an event
(event, callback) -> <o> This element
({event: callback, ...}) -> <o> This element
callback <f> Callback function
data <o> event data (key/value pairs)
event <s> Event name
Event names can be namespaced, like <code>'click.foo'</code>
@*/
that.bindEvent = function() {
Ox.forEach(Ox.makeObject(arguments), function(fn, event) {
bind('bind', event, fn);
});
return that;
}
/*@
bindEventOnce <function> Binds a function to an event, once
(event, callback) -> <obj> This element object
({event: callback, ...}) -> <obj> This element object
callback <f> Callback function
data <o> event data (key/value pairs)
event <s> Event name
Event names can be namespaced, like <code>'click.foo'</code>
@*/
that.bindEventOnce = function() {
Ox.forEach(Ox.makeObject(arguments), function(fn, event) {
bind('one', event, fn);
});
return that;
};
/*@
defaults <function> Sets the default options for an element object
({key: value, ...}) -> <obj> This element object
key <str> The name of the default option
value <val> The value of the default option
@*/
that.defaults = function(defaults) {
// sets the default options
self.defaults = defaults;
self.options = defaults;
return that;
};
/*@
gainFocus <function> Makes an element object gain focus
() -> <obj> This element object
@*/
that.gainFocus = function() {
Ox.Focus.focus(that.id);
return that;
};
/*@
hasFocus <function> Returns true if an element object has focus
() -> <boolean> True if the element has focus
@*/
that.hasFocus = function() {
return Ox.Focus.focused() == that.id;
};
/*@
loseFocus <function> Makes an element object lose focus
() -> <object> This element object
@*/
that.loseFocus = function() {
Ox.Focus.blur(that.id);
return that;
};
/*@
options <function> Gets or sets the options of an element object
# Usage
() -> <obj> all options
(key) -> <any> the value of option[key]
(key, value) -> <obj> this element
Sets options[key] to value and calls self.setOption(key, value)
if the key/value pair was added or modified
({key: value, ...}) -> <obj> this element
Sets multiple options and calls self.setOption(key, value)
for every key/value pair that was added or modified
# Arguments
key <str> the name of the option
value <val> the value of the option
@*/
that.options = function() {
return Ox.getset(self.options, arguments, self.setOption, that);
};
/*@
removeElement <function> Removes an element object and its event handler
() -> <obj> This element
@*/
that.removeElement = function() {
that.loseFocus();
delete self.$eventHandler;
that.remove();
delete Ox.UI.elements[that.id];
return that;
};
/*@
triggerEvent <function> Triggers an event
(event) -> <object> This element object
(event, data) -> <object> This element object
({event: data, ...}) -> <object> This element object
event <string> Event name
data <object> Event data (key/value pairs)
@*/
that.triggerEvent = function() {
Ox.forEach(Ox.makeObject(arguments), function(data, event) {
if ([
'mousedown', 'mouserepeat', 'anyclick', 'singleclick', 'doubleclick',
'dragstart', 'drag', 'dragpause', 'dragend', '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 <function> Unbinds all callbacks from an event
To unbind a specific handler, use namespaced events, like
<code>bindEvent('click.foo', callback)</code>, and then
<code>unbindEvent('click.foo')</code>.
() -> <object> This element object
Unbinds all events
(event) -> <object> This element object
Unbinds one event
(event, event, ...) -> <object> This element object
Unbinds multiple events
([event, event, ...]) -> <object> This element object
Unbinds multiple events
event <string> 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;
};