elation
Version:
Elation Javascript Component Framework
510 lines (434 loc) • 15.1 kB
JavaScript
// if (typeof require == 'function') var elation = require("utils/elation");
elation.extend("events", {
events: {},
cloneattrs: ['type', 'bubbles', 'cancelable', 'view', 'detail', 'screenX', 'screenY', 'clientX', 'clientY', 'ctrlKey', 'shiftKey', 'altKey', 'metaKey', 'button', 'relatedTarget', 'target', 'element', 'data', 'origin', 'timeStamp', 'returnValue', 'cancelBubble', 'keyCode', 'dataTransfer', 'deltaX', 'deltaY', 'deltaZ', 'deltaMode', 'inputSource', 'inputSourceObject', 'touches', 'changedTouches'],
eventstats: {},
fire: function(type, data, target, element, fn) {
var ev = this.getEvent(type, data, target, element, fn);
//console.log('fire!', ev, type, data, target, element, fn);
return elation.events.fireEvent(ev);
},
getEvent: function(type, data, target, element, fn) {
var extras = {};
if (typeof type == 'object') {
data = elation.utils.any(type.data, data);
target = type.target || target;
element = type.element || element;
fn = type.fn || fn;
var cloneev = type.event || {};
for (var i = 0; i < this.cloneattrs.length; i++) {
var attr = this.cloneattrs[i];
if (!elation.utils.isNull(type[attr])) extras[attr] = type[attr];
else if (!elation.utils.isNull(cloneev[attr])) extras[attr] = cloneev[attr];
}
if (type.event) {
var realevent = type.event;
// If we have a real event, we want our synthesized event to pass stopPropagation and preventDefault calls through
if (!extras.stopPropagation) {
extras.stopPropagation = elation.bind(extras, function() {
realevent.stopPropagation();
this.cancelBubble = true;
});
}
if (!extras.preventDefault) extras.preventDefault = elation.bind(type.event, type.event.preventDefault);
}
if (!elation.utils.isNull(type.clientX)) extras.clientX = type.clientX;
if (!elation.utils.isNull(type.clientY)) extras.clientY = type.clientY;
if (!elation.utils.isNull(type.button)) extras.button = type.button;
if (!elation.utils.isNull(type.keyCode)) extras.keyCode = type.keyCode;
extras.fn = fn;
type = type.type;
}
extras.data = data;
extras.target = target;
extras.element = element;
extras.fn = fn;
/*
var ev = {
type: type,
element: element,
fn: fn,
extras: extras,
data: data
};
*/
return extras;
},
fireEvent: function(realevent, element) {
//console.log('fireEvent:',realevent);
var type = realevent.type,
data = realevent.data,
element = element || realevent.element,
target = realevent.target,
fn = realevent.fn;
if (!type)
return false;
if (!this.eventstats[type]) this.eventstats[type] = 0;
this.eventstats[type]++;
var list = this.events[type],
original_events = [],
events = [],
event;
if (!list) {
this.events[type] = [];
return [];
}
// gather all the events associated with this event type
// filter by [element] and/or [fn] if present
for (var i=0; i<list.length; i++) {
event = list[i];
if (fn || element) {
if ((fn && event.origin == fn) || (element && event.target == element) || elation.utils.isNull(event.target)) {
original_events.push(event);
} else {
continue;
}
} else {
original_events.push(event);
}
}
// fire each event
var extrakeys = Object.keys(realevent);
for (var i=0; i<original_events.length; i++) {
var eventObj = original_events[i];
// break reference to eventObj so original doesn't get overwritten
var event = elation.events.clone(eventObj, {
type: type,
target: target,
data: data,
timeStamp: new Date().getTime()
});
for (var j = 0; j < extrakeys.length; j++) {
if (typeof realevent[extrakeys[j]] != 'undefined') {
event[extrakeys[j]] = realevent[extrakeys[j]];
}
}
if (!event.origin)
continue;
var cont = true;
if (typeof event.origin == 'function') {
cont = event.origin(event);
} else if (typeof event.origin.handleEvent != 'undefined') {
cont = event.origin.handleEvent(event);
}
events.push(event);
if (cont === false || event.cancelBubble || realevent.cancelBubble) {
// FIXME - I keep finding myself commenting this out, and then wondering why I've commented it out later
// Note to self - if you have to change this setting again, at least document why it needs to change!
event.cancelBubble = true;
break;
}
}
// return all event objects that were fired
return events;
},
register: function(types, fn, element) {
var types = types.split(','),
type;
for (var i=0; i<types.length; i++) {
type = types[i];
if (!this.events[type]) {
if (fn || element)
this._register(element, type, fn);
else
this.events[type] = [];
}
}
},
_register: function(element, type, fn, options) {
var event = {
type: type,
target: element,
origin: fn,
preventDefault: function() { this.returnValue = false; return; },
stopPropagation: function() { this.cancelBubble = true; return; },
returnValue: true,
cancelBubble: false
};
if (!elation.events.events[type])
elation.events.events[type] = [];
elation.events.events[type].push(event);
},
_unregister: function(element, type, fn) {
if (elation.events.events[type]) {
var remaining = [];
for (var i = 0; i < elation.events.events[type].length; i++) {
var ev = elation.events.events[type][i];
if (ev.type == type && ev.target == element && ev.origin == fn) {
//elation.events.events[type].splice(i--, 1);
} else {
remaining.push(ev);
}
}
if (elation.events.events[type].length != remaining.length) {
elation.events.events[type] = remaining;
}
}
},
// syntax: add(element || [ elements ], "type1,type2,type3", function || object);
add: function(elements, types, fn, options) {
if (!types || !fn || typeof types != "string")
return;
var elements = elation.utils.isNull(elements)
? [null]
: !elation.utils.isArray(elements) || elements == window
? [ elements ]
: elements,
types = types.split(',');
if (typeof fn == "string") {
fn = (function(func) { return function(ev) { eval(func); }; })(fn);
}
for (var e=0; e<elements.length; e++) {
var element = elements[e];
if (typeof element != 'object')
continue;
for (var i=0; i<types.length; i++) {
var type = types[i];
elation.events._register(element, type, fn);
if (!element)
continue;
if (type.toLowerCase() == 'transitionend') {
if ('onwebkittransitionend' in window) type = 'webkitTransitionEnd';
else if ('onotransitionend' in element || navigator.appName == 'Opera') type = 'oTransitionEnd';
}
if ("addEventListener" in element) {
//if (type == 'mousewheel' && elation.browser.type != 'safari')
// type = 'DOMMouseScroll';
if (typeof fn == "object" && fn.handleEvent) {
element[type+fn] = function(e) {
fn.handleEvent(e);
}
element.addEventListener(type, element[(type + fn)], options);
} else {
element.addEventListener(type, fn, options);
}
} else if (element.attachEvent) {
if (typeof fn == "object" && fn.handleEvent) {
element[type+fn] = function() {
fn.handleEvent(elation.events.fix(window.event));
}
} else {
element["e" + type + fn] = fn;
element[type + fn] = function() {
if (typeof element["e" + type + fn] == 'function')
element["e" + type + fn](elation.events.fix(window.event));
}
}
element.attachEvent("on" + type, element[type + fn]);
}
}
}
return this;
},
// syntax: remove(element || [ elements ], "type1,type2,type3", reference);
remove: function(elements, types, fn) {
//if (!elements || !types || !fn || typeof types != "string")
// return;
//var elements = (!elation.utils.isNull(elements.nodeName) || elements == window) ? [ elements ] : elements;
if (!elation.utils.isArray(elements)) {
elements = [elements];
}
var types = types.split(',');
for (var e=0; e<elements.length; e++) {
var element = elements[e];
if (typeof element != 'object')
continue;
for (var i=0; i<types.length; i++) {
var type = types[i];
elation.events._unregister(element, type, fn);
if (element) {
if (element.removeEventListener) {
if (typeof fn == "object" && fn.handleEvent) {
element.removeEventListener(type, element[type+fn], false);
delete element[type + fn];
} else {
element.removeEventListener(type, fn, false);
}
} else if (element.detachEvent) {
if (typeof element[type + fn] == "function")
element.detachEvent("on" + type, element[type + fn]);
element[type + fn] = null;
element["e" + type + fn] = null;
}
}
}
}
return this;
},
fix: function(event) {
event.preventDefault = function() {
this.returnValue = false;
}
event.stopPropagation = function() {
this.cancelBubble = true;
}
//event.preventDefault = this.preventDefault;
//event.stopPropagation = this.stopPropagation;
return event;
},
getTarget: function(event) {
return window.event ? event.srcElement : event.target;
},
getRelated: function(event) {
var reltg;
if (event.relatedTarget) {
reltg = event.relatedTarget;
} else {
if (event.type == "mouseover")
reltg = event.fromElement;
else if (event.type == "mouseout")
reltg = event.toElement;
else
reltg = document;
}
return reltg;
},
getEventTarget: function(event, parentClassName) {
var target;
if (!event)
var event = window.event;
if (event.target)
target = event.target;
else if (event.srcElement)
target = event.srcElement;
if (target.nodeType == 3)
target = target.parentNode; // Defeat Safari bug
if (parentClassName) {
// Make sure we're working with the correct element
var classUp, classDown;
if (parentClassName.indexOf(">")) {
var classes = parentClassName.split(">", 2);
classDown = classes[0];
classUp = classes[1];
} else {
classDown = parentClassName;
}
// First run DOWN the heirarchy to find the base class...
while (!elation.html.hasclass(target,classDown) && target.parentNode) {
target = target.parentNode;
}
// Now if we've specified a child to attach to, find it!
if (classUp) {
var elements;
elements = elation.find("." + classUp, target);
if (elements.length > 0) {
target = elements[0];
}
}
}
return target;
},
isTransition: function(ev, parent) {
var tg = this.getTarget(ev),
reltg = this.getRelated(ev);
return (elation.utils.isin(parent, tg) && !elation.utils.isin(parent, reltg));
},
// returns mouse or all finger touch coords
coords: function(event) {
if (typeof event.touches != 'undefined' && event.touches.length > 0) {
var c = {
x: event.touches[0].pageX,
y: event.touches[0].pageY
};
} else {
var c = {
x: (event.pageX || (event.clientX + document.body.scrollLeft)),
y: (event.pageY || (event.clientY + document.body.scrollTop))
};
}
return c;
},
clone: function(ev, overrides={}) {
//var newev = new Event(ev.type);
var newev = {};
for (let i = 0; i < this.cloneattrs.length; i++) {
let attr = this.cloneattrs[i];
let foo = elation.utils.any(overrides[attr], ev[attr]);
if (foo !== null) {
newev[attr] = foo;
}
}
return elation.events.fix(newev);
//return newev;
},
handleEvent: function(ev) {
if (typeof this[ev.type] == 'function') {
this[ev.type](ev);
}
},
schedule: function(args) {
/*
elation.events.schedule({ev: foo, in: 2000});
elation.events.schedule({ev: foo, every: 200});
elation.events.schedule({ev: foo, at: Date().getTime() + 3600});
*/
},
getEventsByTarget: function(target) {
var results = [];
for (var evname in this.events) {
for (var i = 0; i < this.events[evname].length; i++) {
var ev = this.events[evname][i];
if (ev.target === target) {
results.push(ev);
}
}
}
return results;
},
getEventsByOrigin: function(origin) {
var results = [];
for (var evname in this.events) {
for (var i = 0; i < this.events[evname].length; i++) {
var ev = this.events[evname][i];
if (ev.origin === origin) {
results.push(ev);
}
}
}
return results;
},
getEventsByTargetOrOrigin: function(target) {
var results = [];
for (var evname in this.events) {
for (var i = 0; i < this.events[evname].length; i++) {
var ev = this.events[evname][i];
if (ev.target === target || ev.origin === target) {
results.push(ev);
}
}
}
return results;
},
hasEventListener: function(target, type) {
var allevents = elation.events.events[type];
if (allevents) {
for (var i = 0; i < allevents.length; i++) {
var ev = allevents[i];
if (ev.target === target || ev.origin === target) {
return true;
}
}
}
return false;
},
wasDefaultPrevented: function(events) {
let allowed = true;
if (!elation.utils.isArray(events)) {
events = [events];
}
for (let i = 0; i < events.length; i++) {
allowed = allowed && (events[i].returnValue !== false);
}
return !allowed;
},
wasBubbleCancelled: function(events) {
let cancelled = false;
if (!elation.utils.isArray(events)) {
events = [events];
}
for (let i = 0; i < events.length; i++) {
cancelled = cancelled || events[i].cancelBubble;
}
return cancelled;
}
});