var elation = new function(selector, parent, first) { if (typeof selector == 'string' && typeof elation.find == 'function') elation.find(selector, parent, first); this.extend = function(name, func, clobber, inheritfrom) { var ptr = this, parts = name.split("."), i; for (i = 0; i < parts.length-1; i++) { if (typeof ptr[parts[i]] == 'undefined') ptr[parts[i]] = {}; ptr = ptr[parts[i]]; } if (typeof ptr[parts[i]] == 'undefined' || clobber == true) { ptr[parts[i]] = func; } else { console.log("elation: tried to clobber existing component '" + name + "'"); } if (typeof inheritfrom == 'function') { ptr.prototype = new inheritfrom; ptr.prototype.constructor = ptr; } } } /* // DISABLED - not really used anymore, and gets in the way of debugging if (!window.console) { // if no console, use tfconsole if available window.console = {}; window.console.log = function(txt) { if (elation.utils.logging) elation.utils.logging(txt); } } else { // output to both firebug console and tfconsole window.console.log = function(txt) { if (elation.utils.logging) elation.utils.logging(txt); if (console && typeof console.debug != 'undefined') console.debug.apply(this, arguments); } } */ elation.extend('utils.logging', function(txt) { if (elation.debug && typeof elation.debug.log != 'undefined') elation.debug.log(txt); else { if (!elation.utils.logging.prelog) elation.utils.logging.prelog = []; elation.utils.logging.prelog.push(txt); } }); elation.extend("checkhash", new function() { var init = function() { this.timer = setInterval(function() { try { if (elation.search && elation.search.backbutton) { elation.search.backbutton.check(); } } catch(e) { } }, 500); } this.fetch = function(url, callback) { elation.ajax.Queue({ url: url, callback: [ this, callback ] }); } //(function(self) { if (typeof $TF != 'undefined') { $TF(document).ready(function() { setTimeout(function() { init(); }, 500); }); } //})(this); }); elation.extend("component", new function() { this.namespace = "elation"; this.attrs = { componenttype: 'component', componentname: 'name', componentargs: 'args', componentevents: 'events', componentinit: 'initialized' }; this.registry = []; this.init = function(root) { var componentattr = "component"; var argsattr = this.namespace+':'+this.attrs.componentargs; var eventsattr = this.namespace+':' + this.attrs.componentevents;; if (root == undefined) { root = document; } // Find all elements which have a : attribute if (false && document.evaluate) { // FIXME - using jQuery to query namespace elements for now, the custom method below throws errors in IE if (document.createNSResolver) { var nsresolver = document.createNSResolver(document.documentElement); } else { var nsresolver = function(prefix) { var ns = { 'xhtml' : 'http://www.w3.org/1999/xhtml', 'elation': 'http://www.ajaxelation.com/xmlns' }; return ns[prefix] || null; } } // FIXME - I've started work to switch this over to use xpath selectors instead of jquery but namespaces make it a pain // Right now this is just selecting all elements, very inefficient... //var selector = '//*['+this.namespace+':'+this.attrs.componenttype+']'; //var selector = "//*[@*["+this.namespace+":"+this.attrs.componenttype+"]]"; //var selector = "//*[@*[namespace-uri()='http://www.ajaxelation.com/xmlns']]"; //var selector = "//*[local-name()='component']"; var selector = "//*"; var result = document.evaluate(selector, root, nsresolver, XPathResult.ANY_TYPE, null); var elements = []; var element; while (element = result.iterateNext()) { elements.push(element); } } else if (typeof $TF != 'undefined') { var elements = $TF("["+this.namespace+"\\:"+this.attrs.componenttype+"]"); } else { var elements = []; } for (var i = 0; i < elements.length; i++) { var element = elements[i]; var componentid = this.parseid(element); if (componentid.type) { var componentinitialized = element.getAttribute(this.namespace+':'+this.attrs.componentinit) || false; if (!componentinitialized) { // FIXME - this isn't working in IE, so components are getting reinitialized with each AJAX request element.setAttribute(this.namespace+':'+this.attrs.componentinit, 1); var componentargs = {}, events = {}, j; // First look for a JSON-encoded args array in the element's direct children (elation:args) if (element.children) { for (j = 0; j < element.children.length; j++) { // FIXME - IE seems to drop the namespace, might be related to above FIXME, so look for a child named "args" if (element.children[j].nodeName.toLowerCase() == argsattr || element.children[j].nodeName.toLowerCase() == "args") { var argtext = element.children[j].textContent || element.children[j].innerText; try { var content = element.children[j].innerHTML.trim(); if (content.length > 0) { componentargs = JSON.parse(content); element.removeChild(element.children[j]); if (componentargs == null) { // empty JSON could cause errors later, so reset null to an empty hash componentargs = {}; } } //break; // only one args array per block, bail out when we find one so we don't waste time with the rest } catch(e) { // Probably JSON syntax error console.log("Could not parse " + argsattr + ": " + argtext); } } else if (element.children[j].nodeName == eventsattr.toUpperCase() || element.children[j].nodeName == "events") { try { var content = element.children[j].innerHTML.trim(); if (content.length > 0) { events = JSON.parse(content); element.removeChild(element.children[j]); if (events == null) { // empty JSON could cause errors later, so reset null to an empty hash events = {}; } } //break; // only one events array per block, bail out when we find one so we don't waste time with the rest } catch(e) { // Probably JSON syntax error console.log("Could not parse " + eventsattr + ": " + element.children[j].innerHTML); } } } } // Then, loop through the attributes and parse out any individual arguments which can be specified as attributes for (j = 0; j < element.attributes.length; j++) { if (element.attributes[j].nodeName.substring(0, argsattr.length+1) == argsattr+'.') { componentargs[element.attributes[j].nodeName.substring(argsattr.length+1)] = element.attributes[j].nodeValue; } } // Instantiate the new component with all parsed arguments elation.component.create(componentid.name, componentid.type, element, componentargs, events); } } } } this.add = function(type, classdef) { // At the top level, a component is just a function which checks to see if // an instance with the given name exists already. If it doesn't we create // it, and then we return a reference to the specified instance. var component = function(name, container, args, events) { // If no name was passed, use the current object count as a name instead ("anonymous" components) if (!name && name !== 0) { name = component.objcount; } if (!component.obj[name]) { component.obj[name] = new component.base(type); component.objcount++; if (container) { container.setAttribute(elation.component.namespace+':'+elation.component.attrs.componentname, name); } } if (component.obj[name] && container) { component.obj[name].componentinit(type, name, container, args, events); if (typeof component.obj[name].init == 'function') { component.obj[name].init(name, container, args, events); } } return component.obj[name]; }; component.objcount = 0; component.obj = {}; // this is where we store all the instances of this type of component component.base = function() { } component.base.prototype = new this.base(type); if (classdef) { component.base.prototype.extend(classdef); } elation.extend(type, component); // inject the newly-created component wrapper into the main elation object } this.create = function(id, type, container, args, events) { var componentclass = elation.utils.arrayget(elation, type); if (typeof componentclass == 'function') { return componentclass.call(componentclass, id, container, args, events); } console.log("elation: tried to instantiate unknown component type '" + type + "', id '" + id + "'", componentclass); } this.get = function(id, type, container, args, events) { var componentclass = elation.utils.arrayget(elation, type); if (componentclass && typeof componentclass == 'function') { return componentclass.call(componentclass, id, container, args, events); } else { console.log('no way buddy'); this.add(type); return this.create(id, type, container, args, events); } } this.info = function(type) { var componentclass = elation.utils.arrayget(elation, type); if (componentclass && typeof componentclass == 'function') { return {objcount: componentclass.objcount}; } } this.base = function(component) { this.componentinit = function(name, id, container, args, events) { this.name = name; this.id = id; this.container = container; this.args = args || {}; this.events = events || {}; if (this.container) { for (var k in this.events) { if (typeof this.events[k] == 'string') { (function(self, type, blub) { self[type] = function(ev) { eval(blub); } //console.log(self, type, blub); elation.events.add(self.container, type, self); })(this, k, this.events[k]); } else { elation.events.add(this.container, k, this.events[k]); } } } elation.events.fire({type: "init", fn: this, data: this, element: this.container}); } this.extend = function(from) { for (var k in from) { if (k != 'constructor' && k != 'prototype') { this[k] = from[k]; } } } this.set = function(sets, value) { // special set function to send update notifications when the object (or eventually, individual values) change if (typeof sets == 'string' && value) { var k = sets; sets = {}; sets[k] = value; } var changes = 0; for (var k in sets) { if (elation.utils.arrayget(this, k) != sets[k]) { elation.utils.arrayset(this, k, sets[k]); changes++; } } if (changes > 0) { // TODO - if we supported bindings, we could send updates directly to specific observers when specific attributes are updated elation.events.fire({type:'update', origin: this, data: this, element: this.container}); return true; } return false; } this.setevents = function(events) { for (var k in events) { this.events[k] = events[k]; } } this.fetch = function(type, callback, force) { var ret; //var urlbase = "/~bai/"; // FIXME - stupid stupid stupid! move this to the right place asap! var urlbase = '/'; if (force || !this.content) { (function(self, callback) { console.log(urlbase + self.name.replace(".","/") + "." + type); var args = self.args; args.events = self.events; console.log('stupid dumb args is', args); ajaxlib.Queue({ method: "GET", url: urlbase + self.name.replace(".","/") + "." + type, args: elation.utils.encodeURLParams(args), callback: function(data) { self.content = data; if (typeof callback == 'function') { callback(data); } } }); })(this, callback); ret = ''; } else { ret = this.content; if (typeof callback == 'function') callback(this.content); } return ret; } this.handleEvent = function(ev) { if (typeof this[ev.type] == 'function') { this[ev.type](ev); } } } this.parseid = function(element) { // Parse out the elation:component and elation:name attributes, if set. Fall back on HTML id if no name specified var componentid = { type: element.getAttribute(this.namespace+':'+this.attrs.componenttype), name: element.getAttribute(this.namespace+':'+this.attrs.componentname) || element.id } return componentid; } this.fetch = function(type, name) { if (!elation.utils.isNull(type) && elation.utils.iselement(type)) { var id = this.parseid(type); } else { var id = {type: type, name: name}; } if (id.type && id.name) { var componentclass = elation.utils.arrayget(elation, id.type); return componentclass(id.name); } } }); elation.extend('onloads',new function() { this.done = false; this.onloads = []; this.add = function(expr) { this.onloads.push(expr); // if DOM already loaded, execute immediately if (this.done) this.execute(); } this.init = function() { /* for Safari */ //if (/WebKit/i.test(navigator.userAgent)) { // sniff this.timer = setInterval(function() { if (/loaded|complete/.test(document.readyState)) { elation.onloads.execute(); // call the onload handler } }, 10); // return; //} /* for Mozilla/Opera9 */ if (document.addEventListener) { document.addEventListener("DOMContentLoaded", elation.onloads.execute, false); return; } /* for Internet Explorer */ /*@cc_on @*/ /*@if (@_win32) document.write("<\/scr"+"ipt>"); var script = document.getElementById("__ie_onload"); script.onreadystatechange = function() { if (this.readyState == "complete") { elation.onloads.execute(); // call the onload handler } }; return; /*@end @*/ window.onload = elation.onloads.execute; } this.execute = function() { // quit if this function has already been called // ^--- no dont do that or else we cant execute after dom load //if (elation.onloads.done) return; // flag this function so we don't do the same thing twice elation.onloads.done = true; // kill the timer if (elation.onloads.timer) clearInterval(elation.onloads.timer); var script = ''; var expr; while (expr = elation.onloads.onloads.shift()) { if (typeof expr == 'function') { expr(); // FIXME - this causes all function references to be executed before all strings } else { script += expr + (expr.charAt(expr.length - 1) != ';' ? ';' : ''); } } eval(script); } }); //elation.onloads.init(); elation.extend("html.dimensions", function(element, ignore_size) { if (typeof element != 'object' || element === window) { var width = window.innerWidth || document.documentElement.clientWidth || document.body.clientWidth, height = window.innerHeight || document.documentElement.clientHeight || document.body.clientHeight; return { 0 : width, 1 : height, x : 0, y : 0, w : width, h : height, s : elation.html.getscroll() }; } var width = ignore_size ? 0 : element.offsetWidth, height = ignore_size ? 0 : element.offsetHeight, left = element.offsetLeft, top = element.offsetTop, scrollleft = element.scrollLeft || 0, scrolltop = element.scrollTop || 0, id = element.id || ''; try { while (element = element.offsetParent) { top += element.offsetTop - element.scrollTop; left += element.offsetLeft - element.scrollLeft; } } catch(e) { console.log('html.dimensions: '+e.message); } if (elation.browser.type == 'safari') top += elation.html.getscroll(1); return { 0 : left, 1 : top, x : left, y : top, w : width, h : height, s : [scrollleft, scrolltop] }; }); elation.extend("html.size", function(obj) { return [obj.offsetWidth, obj.offsetHeight]; }); elation.extend("html.position", function(obj) { var curleft = curtop = 0; if (obj.offsetParent) { curleft = obj.offsetLeft; curtop = obj.offsetTop; while (obj = obj.offsetParent) { curleft += obj.offsetLeft; curtop += obj.offsetTop; } } return [curleft,curtop]; }); // html.preloader will fire events and/or callback when all elements have onload'd elation.extend('html.preloader', function(elements, args) { this.elements = elements; this.args = args || { timeout: 2000, callback: false }; this.index = 0; this.init = function() { for (var i=0; i -1) { ret[property] = computed[property]; } } } return ret; }); elation.extend("html.stylecopy", function(dst, src, styles) { if (typeof styles == 'string') { styles = [styles]; } var computed = window.getComputedStyle(src, null); for (var k = 0; k < styles.length; k++) { for (var i = computed.length; i--;) { var property = elation.utils.camelize(computed[i]); if (property.indexOf(styles[k]) > -1) { dst.style[property] = computed[property]; } } } }); elation.extend("utils.camelize", function(text) { return text.replace(/[-\.]+(.)?/g, function (match, chr) { return chr ? chr.toUpperCase() : ''; }); }); elation.extend("utils.isElement", function(obj) { try { //Using W3 DOM2 (works for FF, Opera and Chrome) return obj instanceof HTMLElement; } catch(e){ //Browsers not supporting W3 DOM2 don't have HTMLElement and //an exception is thrown and we end up here. Testing some //properties that all elements have. (works on IE7) return (typeof obj==="object") && (obj.nodeType===1) && (typeof obj.style === "object") && (typeof obj.ownerDocument ==="object"); } }); elation.extend("utils.encodeURLParams", function(obj) { var value,ret = ''; if (typeof obj == "string") { ret = obj; } else { var flattened = elation.utils.flattenURLParams(obj); for (var key in flattened) { if (typeof flattened[key] != 'undefined') { ret += (ret != '' ? '&' : '') + key + '=' + encodeURIComponent(flattened[key]); } } } return ret; }); elation.extend("utils.flattenURLParams", function(obj, prefix) { var ret = {}; for (var k in obj) { var key = (prefix ? prefix + '[' + k + ']' : k); if (typeof obj[k] == 'object') { var flattened = elation.utils.flattenURLParams(obj[k], key); elation.utils.merge(flattened, ret); } else { ret[key] = obj[k]; } } return ret; }); elation.extend("utils.parseURL", function(str) { var ret = {uri: str, args: {}}; var hashparts = str.split('#'); var parts = hashparts[0].split("?"); if (parts[0]) { var fileparts = parts[0].split(/:\/\//, 2); if (fileparts[1]) { ret.scheme = fileparts[0]; if (fileparts[1][0] == '/') { ret.host = document.location.host; ret.path = fileparts[1]; } else { var pathparts = fileparts[1].split("/"); ret.host = pathparts.shift(); ret.path = '/' + pathparts.join("/"); } } else { ret.scheme = document.location.protocol.slice(0, -1); ret.host = document.location.host; ret.path = fileparts[0]; } } if (parts[1]) { var args = parts[1].split("&"); ret.args = {}; for (var i = 0; i < args.length; i++) { var argparts = args[i].split("=", 2); ret.args[argparts[0]] = decodeURIComponent(argparts[1]); } } if (hashparts[1]) { var hashargs = hashparts[1].split("&"); ret.hashargs = {}; for (var i = 0; i < hashargs.length; i++) { var hashargparts = hashargs[i].split("=", 2); ret.hashargs[hashargparts[0]] = decodeURIComponent(hashargparts[1]); } } return ret; }); elation.extend("utils.makeURL", function(obj) { var argstr = elation.utils.encodeURLParams(obj.args); return obj.scheme + "://" + obj.host + obj.path + (argstr ? '?' + argstr : ''); }); elation.extend("utils.merge", function(entities, mergeto) { if (typeof entities == 'object') { if (typeof mergeto == 'undefined' || mergeto === null) mergeto = {}; // Initialize to same type as entities for (var i in entities) { if (entities[i] !== null) { if (entities[i] instanceof Array) { if (mergeto[i] instanceof Array) { //console.log('concat array: ' + i + ' (' + mergeto[i].length + ' + ' + entities[i].length + ')'); mergeto[i] = mergeto[i].concat(entities[i]); } else { //console.log('assign array: ', i, typeof mergeto[i]); mergeto[i] = entities[i]; } } else if (entities[i] instanceof Object) { if (mergeto[i] instanceof Object) { //console.log('merge object: ', i); elation.utils.merge(entities[i], mergeto[i]); } else { //console.log('assign object: ', i, typeof mergeto[i]); mergeto[i] = entities[i]; } } else { mergeto[i] = entities[i]; } } } } return mergeto; }); /* Sets value in a multilevel object element * args: * obj -- multilevel object * element -- 'quoted' object element (as string) */ elation.extend("utils.arrayset", function(obj, element, value) { var ptr = obj; var x = element.split("."); for (var i = 0; i < x.length - 1; i++) { if (ptr==null || (typeof ptr[x[i]] != 'array' && typeof ptr[x[i]] != 'object' && i != x.length-1)) { ptr[x[i]] = {}; } ptr = ptr[x[i]]; } if (typeof ptr == "object") { ptr[x[x.length-1]] = value; } }); elation.extend("utils.arrayget", function(obj, name, defval) { var ptr = obj; var x = name.split("."); for (var i = 0; i < x.length; i++) { if (ptr==null || (typeof ptr[x[i]] != 'array' && typeof ptr[x[i]] != 'object' && i != x.length-1)) { ptr = null; break; } ptr = ptr[x[i]]; } if (typeof ptr == "undefined" || ptr === null) { return (typeof defval == "undefined" ? null : defval); } return ptr; }); elation.extend("utils.arraymin", function(array) { var value=ret=0; for (var i=total=0; i ret) ret = value; } return ret; }); elation.extend("utils.arrayavg", function(array) { return (arraySum(array) / array.length); }); elation.extend("utils.arraysum", function(array) { for (var i=total=0; i [className] elation.extend("utils.getFirstChild", function(obj, tag, className) { for (var i=0; i [className] elation.extend("utils.getLastChild", function(obj, tag, className) { for (var i=obj.childNodes.length-1; i>=0; i--) if (obj.childNodes[i].nodeName == tag.toUpperCase()) if (className && this.hasclass(obj, className)) return obj.childNodes[i]; else if (!className) return obj.childNodes[i]; return null; }); // runs through all children recursively and returns // all elements matching [className] elation.extend("utils.getAll", function(obj, tag, className) { var ret = [], all = obj.getElementsByTagName(tag); for (var i=0; i [className] elation.extend("utils.getOnly", function(obj, tag, className) { if (!obj || !tag) return; var ret = []; for (var i=0; el=obj.childNodes[i]; i++) if (el.nodeName == tag.toUpperCase()) { if (className && this.hasclass(el, className)) ret.push(el); else if (!className) ret.push(el); } return ret; }); // Navigates up the DOM from a given element looking for match elation.extend("utils.getParent", function(element, tag, classname, all_occurrences) { var ret = []; if (typeof classname != 'string' && elation.utils.isTrue(classname)) all_occurances = true; while (element && element.nodeName != 'BODY') { if (element.nodeName == tag.toUpperCase() && (!classname || elation.html.hasclass(element, classname))) { if (all_occurrences) ret.push(element); else return element; } element = element.parentNode; } return (ret.length == 0 ? false : ret); }); elation.extend("utils.isin", function(parent, element) { if (!parent || !element) return false; while (!elation.utils.isNull(element) && element != parent && element != document.body) element = element.offsetParent; return (parent == element); }); elation.extend("utils.indexOf", function(array, object) { if (typeof array == 'string') array = array.split(""); for (var i=0; i 1 ? selector[1] : false; if (id.length > 1) { elements.push(document.getElementById(id[1])); continue; } for (var i=0; i 0) result = result[0]; else result = null; return result; }); // grabs a js or css file and adds to document elation.extend('file.get', function(type, file, func) { if (!type || !file) return false; var head = document.getElementsByTagName("HEAD")[0], element = document.createElement((type == 'javascript' ? "SCRIPT" : "LINK")); if (type == 'javascript') { element.type = "text/javascript"; element.src = file; } else { element.type = "text/css"; element.rel = "stylesheet"; element.href = file; } if (func) element.onload = func; head.appendChild(element); return element; }); // create file.batch object for grabbing multiple files elation.extend('file.batch', function() { this.callbacks = []; this.files = []; this.add = function(url, type, component) { if (typeof url == 'string') { var dependency = elation.file.dependencies.add(url, this, type, component) if (dependency) this.files.push(dependency); } } this.callback = function(script) { this.callbacks.push(script); if (this.files.length == 0) this.done(true); } this.done = function(url) { if (url) for (var i=0; i 0) for (var i = 0; i < components[k].length; i++) if (components[k][i] != null) this.register(k + '.' + components[k][i], false, type); } this.checkWaiting = function(file, components, type) { var type = type || 'javascript', waiting = this.waiting[type], flag = true; for (var i=0; i 1 ? url[1].split('&') : []; for (var i=0; i 1 ? '&' : '?'; if (from && data.email) from.value = data.email; if (name && data.nickname) name.value = data.nickname; if (msg) if (elation.utils.arrayget(args, "isproduct")) { msg.value = "I just discovered this product on TheFind and wanted to share it with you.\n\n" + url + sep + "ddkey=" + elation.utils.arrayget(args, 'ddkey') + "\n\n"; } else { msg.value = "I just discovered these products on TheFind and wanted to share them with you.\n\n" + url + "\n\nCheck them out!"; } if (to) to.focus(); }); elation.extend('data', new function() { this.add = function(name, data) { if (!this[name]) this[name] = []; for (var i=0; i 0) times += (l[i] - l[(i-1)]) + 'ms, '; if (i == 2) debug = (l[l.length-1] - l[0]) + 'ms: ' + prefix; else debug = prefix + ': ' + times + 'total(' + (l[l.length-1] - l[0]) + 'ms)'; if (use_alert) alert(debug); else console.log(debug); } }); elation.extend("utils.parseXML", function(imgxml, leaf) { var node, root, parent; if (imgxml.nodeName) { node = imgxml; } else { if (window.DOMParser) { var parser = new DOMParser(); node = parser.parseFromString(imgxml,"text/xml").firstChild; } else { node = new ActiveXObject("Microsoft.XMLDOM"); node.async = "false"; node.loadXML(imgxml).firstChild; } } root = {}; if (!leaf) { root[node.tagName] = {}; parent = root[node.tagName]; //node = parent[node.tagName]; } else { parent = root; } if (node.attributes) { for (var i = 0; i < node.attributes.length; i++) { var name = node.attributes[i].nodeName; var value = node.attributes[i].nodeValue; parent[name] = value; } } if (node.childNodes) { for (var j = 0; j < node.childNodes.length; j++) { var child = node.childNodes[j]; if (node.getElementsByTagName(child.tagName).length > 1) { if (!parent._children) parent._children = {}; if (!parent._children[child.nodeName]) { parent._children[child.nodeName] = []; } parent._children[child.nodeName].push(elation.utils.parseXML(child, true)); } else if (child.nodeName) { if (child.nodeName == "#text" || child.nodeName == "#cdata-section") { // this gets confused if you have multiple text/cdata nodes... if (!child.nodeValue.match(/^[\s\n]*$/m)) { parent._content = child.nodeValue; } } else { if (!parent._children) parent._children = {}; parent._children[child.nodeName] = elation.utils.parseXML(child, true); } } } } return root; });