/*!
* Copyright (c) 2009 Francesco Mele jsbeans@francescomele.com
*
* This Software is licenced under the LGPL Licence (GNU Lesser General
* Public License).
* In addition to the LGPL Licence the Software is subject to the
* following conditions:
*
* i every modification must be public and comunicated to the Author
* ii every "jsbean" added to this library must be self consistent
* except for the dependence from jsbeans-x.x.x.js
* iii copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*/
/**
* A collection of utilities for managing events
* @namespace jsbeans
* @class events
* @static
*/
jsbeans.events = {
/**
* @property an Array of managed events to flush on window unload or to get on object event remove.
* @type Array
* @private
* @static
*/
_HANDLER_CACHE: new Array(),
/**
* A way to make DOM Object dinamic attaching functions fired at given event.
* @method addHandler
* @param element {DOM} the DOM Object on which the events will be fired
* @param name {String} the name of the event (i.e. 'click') <strong>without</strong> 'on'
* @param handler {Function} the closure fired on given event
* @static
*/
addHandler: function(element, name, handler) {
jsbeans.events._HANDLER_CACHE.push({e: element, n: name, h: handler});
if (element.addEventListener) {
element.addEventListener(name, handler, false);
}
else if (element.attachEvent) {
element["e" + name + handler] = handler;
element[name + handler] = function() {
element["e" + name + handler](window.event);
};
element.attachEvent('on' + name, element[name + handler]);
}
},
/**
* A way to make DOM Object dinamic attaching functions fired at given event.<br/>
* Internally it calls <code class="methd">jsbeans.events.addHandler</code>
* @method bind
* @param element {DOM} the DOM Object on which the events will be fired
* @param name {String} the name of the event (i.e. 'click') <strong>without</strong> 'on'
* @param handler {Function} the closure fired on given event
* @static
* @see jsbeans.events.addHandler
*/
bind: function(element, name, handler) {
jsbeans.events.addHandler(element, name, handler);
},
/**
* Removes a previously attached event.
* @method removeHandler
* @param element {DOM} the DOM Object on which the events will be fired
* @param name {String} the name of the event (i.e. 'click') <strong>without</strong> 'on'
* @param handler {Function} the closure fired on given event
* @static
*/
removeHandler: function(element, name, handler) {
if (handler == null) {
for (var i = 0, hc; hc = jsbeans.events._HANDLER_CACHE[i]; i++) {
if (hc.e == element && hc.n == name) {
handler = hc.h;
break;
}
}
}
if (element.removeEventListener) {
element.removeEventListener(name, handler, false);
}
else if (element.detachEvent) {
element.detachEvent('on' + name, element[name + handler]);
}
var newCache = new Array();
for (var i = 0, hc; hc = jsbeans.events._HANDLER_CACHE[i]; i++) {
if (hc.e != element && hc.n != name) {
newCache.push(hc);
}
}
jsbeans.events._HANDLER_CACHE = newCache;
element = null;
},
/**
* Removes a previously attached event.<br/>
* Internally it calls <code class="methd">jsbeans.events.removeHandler</code>
* @method unbind
* @param element {DOM} the DOM Object on which the events will be fired
* @param name {String} the name of the event (i.e. 'click') <strong>without</strong> 'on'
* @param handler {Function} the closure fired on given event
* @static
* @see jsbeans.events.removeHandler
*/
unbind: function(element, name, handler) {
jsbeans.events.removeHandler(element, name, handler);
},
/**
* Removes all previously attached events.
* @method flush
* @static
*/
flush: function() {
for (var i = 0, hc; hc = jsbeans.events._HANDLER_CACHE[i]; i++) {
if (hc.e.removeEventListener) {
hc.e.removeEventListener(hc.n, hc.h, false);
}
else if (hc.e.detachEvent) {
hc.e.detachEvent('on' + hc.n, hc.e[hc.n + hc.h]);
}
hc.e = null;
}
},
/**
* Creates a shortcut, also known as key binding.<br/>
* <span class="fixme">FIXME</span>: due to parsing problem it's not possible to bind backslash ('\')
* @method shortcut
* @param shortcutName {String} the shortcut. Ex.: 'CRTL+U'
* @param callback {Function} the function fired on pressing given <code class="param">shortcutName</code>
* @param [options] {JSON}
* <pre>
* propagate: a_boolean, // if true it enables firing other events (it does not call cancelBubble), default false
* target: DOM_or_String // a DOM Object (or its id) on which the shortcut will be associated
* </pre>
* @static
* @author Francesco Mele
* @credits unknown (code found on the net)
*/
shortcut: function(shortcut, callback /*,options*/) {
var options = arguments[2] || {};
var propagate = options.propagate || false;
var type = options.type || "keydown";
var obj = options.target || document;
if (typeof(options.target) == 'string') {
obj = document.getElementById(options.target);
}
var _function = function(e) {
e = e || window.event;
var code;
if (e.keyCode) {
code = e.keyCode;
}
else if (e.which) {
code = e.which;
}
var character = String.fromCharCode(code).toLowerCase();
var keys = shortcut.toLowerCase().split("+");
var kp = 0;
var shift_nums = {
"`":"~", "1":"!", "2":"@", "3":"#", "4":"$", "5":"%", "6":"^",
"7":"&", "8":"*", "9":"(", "0":")", "-":"_", "=":"+", ";":":",
"'":'"', ",":"<", ".":">", "/":"?"
};
var special_keys = {'esc':27, 'escape':27, 'tab':9, 'space':32, 'return':13, 'enter':13,
'backspace':8, 'scrolllock':145, 'scroll_lock':145, 'scroll':145,
'capslock':20, 'caps_lock':20, 'caps':20, 'numlock':144, 'num_lock':144,
'num':144, 'pause':19, 'break':19, 'insert':45, 'home':36, 'delete':46,
'end':35, 'pageup':33, 'page_up':33, 'pu':33, 'pagedown':34, 'page_down':34,
'pd':34, 'left':37, 'up':38, 'right':39, 'down':40, 'f1':112, 'f2':113,
'f3':114, 'f4':115, 'f5':116, 'f6':117, 'f7':118, 'f8':119, 'f9':120,
'f10':121, 'f11':122, 'f12':123
};
for (var i = 0; k = keys[i], i < keys.length; i++) {
if (k == 'ctrl' || k == 'control') {
if (e.ctrlKey) {
kp++;
}
}
else if (k == 'shift') {
if (e.shiftKey) {
kp++;
}
}
else if (k == 'alt') {
if (e.altKey) {
kp++;
}
}
else if (k.length > 1) {
if (special_keys[k] == code) {
kp++;
}
}
else {
if (character == k) {
kp++;
}
else {
if (shift_nums[character] && e.shiftKey) {
character = shift_nums[character];
if (character == k) {
kp++;
}
}
}
}
}
if (kp == keys.length) {
callback(e);
if (!options.propagate) {
e.cancelBubble = true;
e.returnValue = false;
if (e.stopPropagation) {
e.stopPropagation();
e.preventDefault();
}
try {
window.event.keyCode = 505;
}
catch(e) {}
return false;
}
}
};
jsbeans.events.addHandler(obj, type, _function);
},
/**
* Returns the DOM Object attached on given {@param} <code class="param">event</code>
* @for events
* @method getEventTarget
* @param event {event}
* @return {DOM} the DOM Object attached on event
* @static
*/
getEventTarget: function(e) {
try {
e = e || event;
var object = e.srcElement || e.target;
if (object != null) {
return object;
}
}
catch(e) {
return null;
}
},
/**
* Returns the DOM Object attached on given {@param} <code class="param">event</code>.<br/>
* Internally it calls <code class="methd">jsbeans.events.getEventTarget</code>
* @for events
* @method getTarget
* @param event {event}
* @return {DOM} the DOM Object attached on event
* @static
* @see jsbeans.events.getEventTarget
*/
getTarget: function(e) {
return jsbeans.events.getEventTarget(e);
}
};
/**
* @config Removes alla events on window unload. It prevents memory leaks.
* @private
*/
jsbeans.events.addHandler(window, "unload", jsbeans.events.flush);