diff --git a/dist/renderjs-0.10.0.js b/dist/renderjs-0.10.0.js new file mode 100644 index 0000000000000000000000000000000000000000..06dee4707220cd396e14f8469f22dd8634bbca30 --- /dev/null +++ b/dist/renderjs-0.10.0.js @@ -0,0 +1,2138 @@ +/* + * js_channel is a very lightweight abstraction on top of + * postMessage which defines message formats and semantics + * to support interactions more rich than just message passing + * js_channel supports: + * + query/response - traditional rpc + * + query/update/response - incremental async return of results + * to a query + * + notifications - fire and forget + * + error handling + * + * js_channel is based heavily on json-rpc, but is focused at the + * problem of inter-iframe RPC. + * + * Message types: + * There are 5 types of messages that can flow over this channel, + * and you may determine what type of message an object is by + * examining its parameters: + * 1. Requests + * + integer id + * + string method + * + (optional) any params + * 2. Callback Invocations (or just "Callbacks") + * + integer id + * + string callback + * + (optional) params + * 3. Error Responses (or just "Errors) + * + integer id + * + string error + * + (optional) string message + * 4. Responses + * + integer id + * + (optional) any result + * 5. Notifications + * + string method + * + (optional) any params + */ + +;var Channel = (function() { + "use strict"; + + // current transaction id, start out at a random *odd* number between 1 and a million + // There is one current transaction counter id per page, and it's shared between + // channel instances. That means of all messages posted from a single javascript + // evaluation context, we'll never have two with the same id. + var s_curTranId = Math.floor(Math.random()*1000001); + + // no two bound channels in the same javascript evaluation context may have the same origin, scope, and window. + // futher if two bound channels have the same window and scope, they may not have *overlapping* origins + // (either one or both support '*'). This restriction allows a single onMessage handler to efficiently + // route messages based on origin and scope. The s_boundChans maps origins to scopes, to message + // handlers. Request and Notification messages are routed using this table. + // Finally, channels are inserted into this table when built, and removed when destroyed. + var s_boundChans = { }; + + // add a channel to s_boundChans, throwing if a dup exists + function s_addBoundChan(win, origin, scope, handler) { + function hasWin(arr) { + for (var i = 0; i < arr.length; i++) if (arr[i].win === win) return true; + return false; + } + + // does she exist? + var exists = false; + + + if (origin === '*') { + // we must check all other origins, sadly. + for (var k in s_boundChans) { + if (!s_boundChans.hasOwnProperty(k)) continue; + if (k === '*') continue; + if (typeof s_boundChans[k][scope] === 'object') { + exists = hasWin(s_boundChans[k][scope]); + if (exists) break; + } + } + } else { + // we must check only '*' + if ((s_boundChans['*'] && s_boundChans['*'][scope])) { + exists = hasWin(s_boundChans['*'][scope]); + } + if (!exists && s_boundChans[origin] && s_boundChans[origin][scope]) + { + exists = hasWin(s_boundChans[origin][scope]); + } + } + if (exists) throw "A channel is already bound to the same window which overlaps with origin '"+ origin +"' and has scope '"+scope+"'"; + + if (typeof s_boundChans[origin] != 'object') s_boundChans[origin] = { }; + if (typeof s_boundChans[origin][scope] != 'object') s_boundChans[origin][scope] = [ ]; + s_boundChans[origin][scope].push({win: win, handler: handler}); + } + + function s_removeBoundChan(win, origin, scope) { + var arr = s_boundChans[origin][scope]; + for (var i = 0; i < arr.length; i++) { + if (arr[i].win === win) { + arr.splice(i,1); + } + } + if (s_boundChans[origin][scope].length === 0) { + delete s_boundChans[origin][scope]; + } + } + + function s_isArray(obj) { + if (Array.isArray) return Array.isArray(obj); + else { + return (obj.constructor.toString().indexOf("Array") != -1); + } + } + + // No two outstanding outbound messages may have the same id, period. Given that, a single table + // mapping "transaction ids" to message handlers, allows efficient routing of Callback, Error, and + // Response messages. Entries are added to this table when requests are sent, and removed when + // responses are received. + var s_transIds = { }; + + // class singleton onMessage handler + // this function is registered once and all incoming messages route through here. This + // arrangement allows certain efficiencies, message data is only parsed once and dispatch + // is more efficient, especially for large numbers of simultaneous channels. + var s_onMessage = function(e) { + try { + var m = JSON.parse(e.data); + if (typeof m !== 'object' || m === null) throw "malformed"; + } catch(e) { + // just ignore any posted messages that do not consist of valid JSON + return; + } + + var w = e.source; + var o = e.origin; + var s, i, meth; + + if (typeof m.method === 'string') { + var ar = m.method.split('::'); + if (ar.length == 2) { + s = ar[0]; + meth = ar[1]; + } else { + meth = m.method; + } + } + + if (typeof m.id !== 'undefined') i = m.id; + + // w is message source window + // o is message origin + // m is parsed message + // s is message scope + // i is message id (or undefined) + // meth is unscoped method name + // ^^ based on these factors we can route the message + + // if it has a method it's either a notification or a request, + // route using s_boundChans + if (typeof meth === 'string') { + var delivered = false; + if (s_boundChans[o] && s_boundChans[o][s]) { + for (var j = 0; j < s_boundChans[o][s].length; j++) { + if (s_boundChans[o][s][j].win === w) { + s_boundChans[o][s][j].handler(o, meth, m); + delivered = true; + break; + } + } + } + + if (!delivered && s_boundChans['*'] && s_boundChans['*'][s]) { + for (var j = 0; j < s_boundChans['*'][s].length; j++) { + if (s_boundChans['*'][s][j].win === w) { + s_boundChans['*'][s][j].handler(o, meth, m); + break; + } + } + } + } + // otherwise it must have an id (or be poorly formed + else if (typeof i != 'undefined') { + if (s_transIds[i]) s_transIds[i](o, meth, m); + } + }; + + // Setup postMessage event listeners + if (window.addEventListener) window.addEventListener('message', s_onMessage, false); + else if(window.attachEvent) window.attachEvent('onmessage', s_onMessage); + + /* a messaging channel is constructed from a window and an origin. + * the channel will assert that all messages received over the + * channel match the origin + * + * Arguments to Channel.build(cfg): + * + * cfg.window - the remote window with which we'll communicate + * cfg.origin - the expected origin of the remote window, may be '*' + * which matches any origin + * cfg.scope - the 'scope' of messages. a scope string that is + * prepended to message names. local and remote endpoints + * of a single channel must agree upon scope. Scope may + * not contain double colons ('::'). + * cfg.debugOutput - A boolean value. If true and window.console.log is + * a function, then debug strings will be emitted to that + * function. + * cfg.debugOutput - A boolean value. If true and window.console.log is + * a function, then debug strings will be emitted to that + * function. + * cfg.postMessageObserver - A function that will be passed two arguments, + * an origin and a message. It will be passed these immediately + * before messages are posted. + * cfg.gotMessageObserver - A function that will be passed two arguments, + * an origin and a message. It will be passed these arguments + * immediately after they pass scope and origin checks, but before + * they are processed. + * cfg.onReady - A function that will be invoked when a channel becomes "ready", + * this occurs once both sides of the channel have been + * instantiated and an application level handshake is exchanged. + * the onReady function will be passed a single argument which is + * the channel object that was returned from build(). + */ + return { + build: function(cfg) { + var debug = function(m) { + if (cfg.debugOutput && window.console && window.console.log) { + // try to stringify, if it doesn't work we'll let javascript's built in toString do its magic + try { if (typeof m !== 'string') m = JSON.stringify(m); } catch(e) { } + console.log("["+chanId+"] " + m); + } + }; + + /* browser capabilities check */ + if (!window.postMessage) throw("jschannel cannot run this browser, no postMessage"); + if (!window.JSON || !window.JSON.stringify || ! window.JSON.parse) { + throw("jschannel cannot run this browser, no JSON parsing/serialization"); + } + + /* basic argument validation */ + if (typeof cfg != 'object') throw("Channel build invoked without a proper object argument"); + + if (!cfg.window || !cfg.window.postMessage) throw("Channel.build() called without a valid window argument"); + + /* we'd have to do a little more work to be able to run multiple channels that intercommunicate the same + * window... Not sure if we care to support that */ + if (window === cfg.window) throw("target window is same as present window -- not allowed"); + + // let's require that the client specify an origin. if we just assume '*' we'll be + // propagating unsafe practices. that would be lame. + var validOrigin = false; + if (typeof cfg.origin === 'string') { + var oMatch; + if (cfg.origin === "*") validOrigin = true; + // allow valid domains under http and https. Also, trim paths off otherwise valid origins. + else if (null !== (oMatch = cfg.origin.match(/^https?:\/\/(?:[-a-zA-Z0-9_\.])+(?::\d+)?/))) { + cfg.origin = oMatch[0].toLowerCase(); + validOrigin = true; + } + } + + if (!validOrigin) throw ("Channel.build() called with an invalid origin"); + + if (typeof cfg.scope !== 'undefined') { + if (typeof cfg.scope !== 'string') throw 'scope, when specified, must be a string'; + if (cfg.scope.split('::').length > 1) throw "scope may not contain double colons: '::'"; + } + + /* private variables */ + // generate a random and psuedo unique id for this channel + var chanId = (function () { + var text = ""; + var alpha = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; + for(var i=0; i < 5; i++) text += alpha.charAt(Math.floor(Math.random() * alpha.length)); + return text; + })(); + + // registrations: mapping method names to call objects + var regTbl = { }; + // current oustanding sent requests + var outTbl = { }; + // current oustanding received requests + var inTbl = { }; + // are we ready yet? when false we will block outbound messages. + var ready = false; + var pendingQueue = [ ]; + + var createTransaction = function(id,origin,callbacks) { + var shouldDelayReturn = false; + var completed = false; + + return { + origin: origin, + invoke: function(cbName, v) { + // verify in table + if (!inTbl[id]) throw "attempting to invoke a callback of a nonexistent transaction: " + id; + // verify that the callback name is valid + var valid = false; + for (var i = 0; i < callbacks.length; i++) if (cbName === callbacks[i]) { valid = true; break; } + if (!valid) throw "request supports no such callback '" + cbName + "'"; + + // send callback invocation + postMessage({ id: id, callback: cbName, params: v}); + }, + error: function(error, message) { + completed = true; + // verify in table + if (!inTbl[id]) throw "error called for nonexistent message: " + id; + + // remove transaction from table + delete inTbl[id]; + + // send error + postMessage({ id: id, error: error, message: message }); + }, + complete: function(v) { + completed = true; + // verify in table + if (!inTbl[id]) throw "complete called for nonexistent message: " + id; + // remove transaction from table + delete inTbl[id]; + // send complete + postMessage({ id: id, result: v }); + }, + delayReturn: function(delay) { + if (typeof delay === 'boolean') { + shouldDelayReturn = (delay === true); + } + return shouldDelayReturn; + }, + completed: function() { + return completed; + } + }; + }; + + var setTransactionTimeout = function(transId, timeout, method) { + return window.setTimeout(function() { + if (outTbl[transId]) { + // XXX: what if client code raises an exception here? + var msg = "timeout (" + timeout + "ms) exceeded on method '" + method + "'"; + (1,outTbl[transId].error)("timeout_error", msg); + delete outTbl[transId]; + delete s_transIds[transId]; + } + }, timeout); + }; + + var onMessage = function(origin, method, m) { + // if an observer was specified at allocation time, invoke it + if (typeof cfg.gotMessageObserver === 'function') { + // pass observer a clone of the object so that our + // manipulations are not visible (i.e. method unscoping). + // This is not particularly efficient, but then we expect + // that message observers are primarily for debugging anyway. + try { + cfg.gotMessageObserver(origin, m); + } catch (e) { + debug("gotMessageObserver() raised an exception: " + e.toString()); + } + } + + // now, what type of message is this? + if (m.id && method) { + // a request! do we have a registered handler for this request? + if (regTbl[method]) { + var trans = createTransaction(m.id, origin, m.callbacks ? m.callbacks : [ ]); + inTbl[m.id] = { }; + try { + // callback handling. we'll magically create functions inside the parameter list for each + // callback + if (m.callbacks && s_isArray(m.callbacks) && m.callbacks.length > 0) { + for (var i = 0; i < m.callbacks.length; i++) { + var path = m.callbacks[i]; + var obj = m.params; + var pathItems = path.split('/'); + for (var j = 0; j < pathItems.length - 1; j++) { + var cp = pathItems[j]; + if (typeof obj[cp] !== 'object') obj[cp] = { }; + obj = obj[cp]; + } + obj[pathItems[pathItems.length - 1]] = (function() { + var cbName = path; + return function(params) { + return trans.invoke(cbName, params); + }; + })(); + } + } + var resp = regTbl[method](trans, m.params); + if (!trans.delayReturn() && !trans.completed()) trans.complete(resp); + } catch(e) { + // automagic handling of exceptions: + var error = "runtime_error"; + var message = null; + // * if it's a string then it gets an error code of 'runtime_error' and string is the message + if (typeof e === 'string') { + message = e; + } else if (typeof e === 'object') { + // either an array or an object + // * if it's an array of length two, then array[0] is the code, array[1] is the error message + if (e && s_isArray(e) && e.length == 2) { + error = e[0]; + message = e[1]; + } + // * if it's an object then we'll look form error and message parameters + else if (typeof e.error === 'string') { + error = e.error; + if (!e.message) message = ""; + else if (typeof e.message === 'string') message = e.message; + else e = e.message; // let the stringify/toString message give us a reasonable verbose error string + } + } + + // message is *still* null, let's try harder + if (message === null) { + try { + message = JSON.stringify(e); + /* On MSIE8, this can result in 'out of memory', which + * leaves message undefined. */ + if (typeof(message) == 'undefined') + message = e.toString(); + } catch (e2) { + message = e.toString(); + } + } + + trans.error(error,message); + } + } + } else if (m.id && m.callback) { + if (!outTbl[m.id] ||!outTbl[m.id].callbacks || !outTbl[m.id].callbacks[m.callback]) + { + debug("ignoring invalid callback, id:"+m.id+ " (" + m.callback +")"); + } else { + // XXX: what if client code raises an exception here? + outTbl[m.id].callbacks[m.callback](m.params); + } + } else if (m.id) { + if (!outTbl[m.id]) { + debug("ignoring invalid response: " + m.id); + } else { + // XXX: what if client code raises an exception here? + if (m.error) { + (1,outTbl[m.id].error)(m.error, m.message); + } else { + if (m.result !== undefined) (1,outTbl[m.id].success)(m.result); + else (1,outTbl[m.id].success)(); + } + delete outTbl[m.id]; + delete s_transIds[m.id]; + } + } else if (method) { + // tis a notification. + if (regTbl[method]) { + // yep, there's a handler for that. + // transaction has only origin for notifications. + regTbl[method]({ origin: origin }, m.params); + // if the client throws, we'll just let it bubble out + // what can we do? Also, here we'll ignore return values + } + } + }; + + // now register our bound channel for msg routing + s_addBoundChan(cfg.window, cfg.origin, ((typeof cfg.scope === 'string') ? cfg.scope : ''), onMessage); + + // scope method names based on cfg.scope specified when the Channel was instantiated + var scopeMethod = function(m) { + if (typeof cfg.scope === 'string' && cfg.scope.length) m = [cfg.scope, m].join("::"); + return m; + }; + + // a small wrapper around postmessage whose primary function is to handle the + // case that clients start sending messages before the other end is "ready" + var postMessage = function(msg, force) { + if (!msg) throw "postMessage called with null message"; + + // delay posting if we're not ready yet. + var verb = (ready ? "post " : "queue "); + debug(verb + " message: " + JSON.stringify(msg)); + if (!force && !ready) { + pendingQueue.push(msg); + } else { + if (typeof cfg.postMessageObserver === 'function') { + try { + cfg.postMessageObserver(cfg.origin, msg); + } catch (e) { + debug("postMessageObserver() raised an exception: " + e.toString()); + } + } + + cfg.window.postMessage(JSON.stringify(msg), cfg.origin); + } + }; + + var onReady = function(trans, type) { + debug('ready msg received'); + if (ready) throw "received ready message while in ready state. help!"; + + if (type === 'ping') { + chanId += '-R'; + } else { + chanId += '-L'; + } + + obj.unbind('__ready'); // now this handler isn't needed any more. + ready = true; + debug('ready msg accepted.'); + + if (type === 'ping') { + obj.notify({ method: '__ready', params: 'pong' }); + } + + // flush queue + while (pendingQueue.length) { + postMessage(pendingQueue.pop()); + } + + // invoke onReady observer if provided + if (typeof cfg.onReady === 'function') cfg.onReady(obj); + }; + + var obj = { + // tries to unbind a bound message handler. returns false if not possible + unbind: function (method) { + if (regTbl[method]) { + if (!(delete regTbl[method])) throw ("can't delete method: " + method); + return true; + } + return false; + }, + bind: function (method, cb) { + if (!method || typeof method !== 'string') throw "'method' argument to bind must be string"; + if (!cb || typeof cb !== 'function') throw "callback missing from bind params"; + + if (regTbl[method]) throw "method '"+method+"' is already bound!"; + regTbl[method] = cb; + return this; + }, + call: function(m) { + if (!m) throw 'missing arguments to call function'; + if (!m.method || typeof m.method !== 'string') throw "'method' argument to call must be string"; + if (!m.success || typeof m.success !== 'function') throw "'success' callback missing from call"; + + // now it's time to support the 'callback' feature of jschannel. We'll traverse the argument + // object and pick out all of the functions that were passed as arguments. + var callbacks = { }; + var callbackNames = [ ]; + + var pruneFunctions = function (path, obj) { + if (typeof obj === 'object') { + for (var k in obj) { + if (!obj.hasOwnProperty(k)) continue; + var np = path + (path.length ? '/' : '') + k; + if (typeof obj[k] === 'function') { + callbacks[np] = obj[k]; + callbackNames.push(np); + delete obj[k]; + } else if (typeof obj[k] === 'object') { + pruneFunctions(np, obj[k]); + } + } + } + }; + pruneFunctions("", m.params); + + // build a 'request' message and send it + var msg = { id: s_curTranId, method: scopeMethod(m.method), params: m.params }; + if (callbackNames.length) msg.callbacks = callbackNames; + + if (m.timeout) + // XXX: This function returns a timeout ID, but we don't do anything with it. + // We might want to keep track of it so we can cancel it using clearTimeout() + // when the transaction completes. + setTransactionTimeout(s_curTranId, m.timeout, scopeMethod(m.method)); + + // insert into the transaction table + outTbl[s_curTranId] = { callbacks: callbacks, error: m.error, success: m.success }; + s_transIds[s_curTranId] = onMessage; + + // increment current id + s_curTranId++; + + postMessage(msg); + }, + notify: function(m) { + if (!m) throw 'missing arguments to notify function'; + if (!m.method || typeof m.method !== 'string') throw "'method' argument to notify must be string"; + + // no need to go into any transaction table + postMessage({ method: scopeMethod(m.method), params: m.params }); + }, + destroy: function () { + s_removeBoundChan(cfg.window, cfg.origin, ((typeof cfg.scope === 'string') ? cfg.scope : '')); + if (window.removeEventListener) window.removeEventListener('message', onMessage, false); + else if(window.detachEvent) window.detachEvent('onmessage', onMessage); + ready = false; + regTbl = { }; + inTbl = { }; + outTbl = { }; + cfg.origin = null; + pendingQueue = [ ]; + debug("channel destroyed"); + chanId = ""; + } + }; + + obj.bind('__ready', onReady); + setTimeout(function() { + postMessage({ method: scopeMethod('__ready'), params: "ping" }, true); + }, 0); + + return obj; + } + }; +})(); +;/* + * DOMParser HTML extension + * 2012-09-04 + * + * By Eli Grey, http://eligrey.com + * Public domain. + * NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK. + */ +/*! @source https://gist.github.com/1129031 */ +(function (DOMParser) { + "use strict"; + var DOMParser_proto = DOMParser.prototype, + real_parseFromString = DOMParser_proto.parseFromString; + + // Firefox/Opera/IE throw errors on unsupported types + try { + // WebKit returns null on unsupported types + if ((new DOMParser()).parseFromString("", "text/html")) { + // text/html parsing is natively supported + return; + } + } catch (ignore) {} + + DOMParser_proto.parseFromString = function (markup, type) { + var result, doc, doc_elt, first_elt; + if (/^\s*text\/html\s*(?:;|$)/i.test(type)) { + doc = document.implementation.createHTMLDocument(""); + doc_elt = doc.documentElement; + + doc_elt.innerHTML = markup; + first_elt = doc_elt.firstElementChild; + + if (doc_elt.childElementCount === 1 + && first_elt.localName.toLowerCase() === "html") { + doc.replaceChild(first_elt, doc_elt); + } + + result = doc; + } else { + result = real_parseFromString.apply(this, arguments); + } + return result; + }; +}(DOMParser)); + +;// IE does not support have Document.prototype.contains. +if (typeof document.contains !== 'function') { + Document.prototype.contains = function(node) { + if (node === this || node.parentNode === this) + return true; + return this.documentElement.contains(node); + } +} +;/*! RenderJs */ +/*jslint nomen: true*/ + +/* + * renderJs - Generic Gadget library renderer. + * http://www.renderjs.org/documentation + */ +(function (document, window, RSVP, DOMParser, Channel, MutationObserver, + Node, FileReader, Blob, navigator, Event) { + "use strict"; + + function readBlobAsDataURL(blob) { + var fr = new FileReader(); + return new RSVP.Promise(function (resolve, reject) { + fr.addEventListener("load", function (evt) { + resolve(evt.target.result); + }); + fr.addEventListener("error", reject); + fr.readAsDataURL(blob); + }, function () { + fr.abort(); + }); + } + + function ajax(url) { + var xhr; + function resolver(resolve, reject) { + function handler() { + try { + if (xhr.readyState === 0) { + // UNSENT + reject(xhr); + } else if (xhr.readyState === 4) { + // DONE + if ((xhr.status < 200) || (xhr.status >= 300) || + (!/^text\/html[;]?/.test( + xhr.getResponseHeader("Content-Type") || "" + ))) { + reject(xhr); + } else { + resolve(xhr); + } + } + } catch (e) { + reject(e); + } + } + + xhr = new XMLHttpRequest(); + xhr.open("GET", url); + xhr.onreadystatechange = handler; + xhr.setRequestHeader('Accept', 'text/html'); + xhr.withCredentials = true; + xhr.send(); + } + + function canceller() { + if ((xhr !== undefined) && (xhr.readyState !== xhr.DONE)) { + xhr.abort(); + } + } + return new RSVP.Promise(resolver, canceller); + } + + var gadget_model_dict = {}, + javascript_registration_dict = {}, + stylesheet_registration_dict = {}, + gadget_loading_klass, + loading_klass_promise, + renderJS, + Monitor, + scope_increment = 0, + isAbsoluteOrDataURL = new RegExp('^(?:[a-z]+:)?//|data:', 'i'), + is_page_unloaded = false, + error_list = []; + + window.addEventListener('error', function (error) { + error_list.push(error); + }); + + window.addEventListener('beforeunload', function () { + // XXX If another listener cancel the page unload, + // it will not restore renderJS crash report + is_page_unloaded = true; + }); + + ///////////////////////////////////////////////////////////////// + // Helper functions + ///////////////////////////////////////////////////////////////// + function removeHash(url) { + var index = url.indexOf('#'); + if (index > 0) { + url = url.substring(0, index); + } + return url; + } + + function letsCrash(e) { + var i, + body, + container, + paragraph, + link, + error; + if (is_page_unloaded) { + /*global console*/ + console.info('-- Error dropped, as page is unloaded'); + console.info(e); + return; + } + + error_list.push(e); + // Add error handling stack + error_list.push(new Error('stopping renderJS')); + + body = document.getElementsByTagName('body')[0]; + while (body.firstChild) { + body.removeChild(body.firstChild); + } + + container = document.createElement("section"); + paragraph = document.createElement("h1"); + paragraph.textContent = 'Unhandled Error'; + container.appendChild(paragraph); + + paragraph = document.createElement("p"); + paragraph.textContent = 'Please report this error to the support team'; + container.appendChild(paragraph); + + paragraph = document.createElement("p"); + paragraph.textContent = 'Location: '; + link = document.createElement("a"); + link.href = link.textContent = window.location.toString(); + paragraph.appendChild(link); + container.appendChild(paragraph); + + paragraph = document.createElement("p"); + paragraph.textContent = 'User-agent: ' + navigator.userAgent; + container.appendChild(paragraph); + + body.appendChild(container); + + for (i = 0; i < error_list.length; i += 1) { + error = error_list[i]; + + if (error instanceof Event) { + error = { + string: error.toString(), + message: error.message, + type: error.type, + target: error.target + }; + if (error.target !== undefined) { + error_list.splice(i + 1, 0, error.target); + } + } + + if (error instanceof XMLHttpRequest) { + error = { + message: error.toString(), + readyState: error.readyState, + status: error.status, + statusText: error.statusText, + response: error.response, + responseUrl: error.responseUrl, + response_headers: error.getAllResponseHeaders() + }; + } + if (error.constructor === Array || + error.constructor === String || + error.constructor === Object) { + try { + error = JSON.stringify(error); + } catch (ignore) { + } + } + + container = document.createElement("section"); + + paragraph = document.createElement("h2"); + paragraph.textContent = error.message || error; + container.appendChild(paragraph); + + if (error.fileName !== undefined) { + paragraph = document.createElement("p"); + paragraph.textContent = 'File: ' + + error.fileName + + ': ' + error.lineNumber; + container.appendChild(paragraph); + } + + if (error.stack !== undefined) { + paragraph = document.createElement("pre"); + paragraph.textContent = 'Stack: ' + error.stack; + container.appendChild(paragraph); + } + + body.appendChild(container); + } + // XXX Do not crash the application if it fails + // Where to write the error? + /*global console*/ + console.error(e.stack); + console.error(e); + } + + ///////////////////////////////////////////////////////////////// + // Service Monitor promise + ///////////////////////////////////////////////////////////////// + function ResolvedMonitorError(message) { + this.name = "resolved"; + if ((message !== undefined) && (typeof message !== "string")) { + throw new TypeError('You must pass a string.'); + } + this.message = message || "Default Message"; + } + ResolvedMonitorError.prototype = new Error(); + ResolvedMonitorError.prototype.constructor = ResolvedMonitorError; + + Monitor = function () { + var monitor = this, + promise_list = [], + promise, + reject, + notify, + resolved; + + if (!(this instanceof Monitor)) { + return new Monitor(); + } + + function canceller() { + var len = promise_list.length, + i; + for (i = 0; i < len; i += 1) { + promise_list[i].cancel(); + } + // Clean it to speed up other canceller run + promise_list = []; + } + + promise = new RSVP.Promise(function (done, fail, progress) { + reject = function (rejectedReason) { + if (resolved) { + return; + } + monitor.isRejected = true; + monitor.rejectedReason = rejectedReason; + resolved = true; + canceller(); + return fail(rejectedReason); + }; + notify = progress; + }, canceller); + + monitor.cancel = function () { + if (resolved) { + return; + } + resolved = true; + promise.cancel(); + promise.fail(function (rejectedReason) { + monitor.isRejected = true; + monitor.rejectedReason = rejectedReason; + }); + }; + monitor.then = function () { + return promise.then.apply(promise, arguments); + }; + monitor.fail = function () { + return promise.fail.apply(promise, arguments); + }; + + monitor.monitor = function (promise_to_monitor) { + if (resolved) { + throw new ResolvedMonitorError(); + } + var queue = new RSVP.Queue() + .push(function () { + return promise_to_monitor; + }) + .push(function (fulfillmentValue) { + // Promise to monitor is fullfilled, remove it from the list + var len = promise_list.length, + sub_promise_to_monitor, + new_promise_list = [], + i; + for (i = 0; i < len; i += 1) { + sub_promise_to_monitor = promise_list[i]; + if (!(sub_promise_to_monitor.isFulfilled || + sub_promise_to_monitor.isRejected)) { + new_promise_list.push(sub_promise_to_monitor); + } + } + promise_list = new_promise_list; + }, function (rejectedReason) { + if (rejectedReason instanceof RSVP.CancellationError) { + if (!(promise_to_monitor.isFulfilled && + promise_to_monitor.isRejected)) { + // The queue could be cancelled before the first push is run + promise_to_monitor.cancel(); + } + } + reject(rejectedReason); + throw rejectedReason; + }, function (notificationValue) { + notify(notificationValue); + return notificationValue; + }); + + promise_list.push(queue); + + return this; + }; + }; + + Monitor.prototype = Object.create(RSVP.Promise.prototype); + Monitor.prototype.constructor = Monitor; + + ///////////////////////////////////////////////////////////////// + // RenderJSGadget + ///////////////////////////////////////////////////////////////// + function RenderJSGadget() { + if (!(this instanceof RenderJSGadget)) { + return new RenderJSGadget(); + } + } + RenderJSGadget.prototype.__title = ""; + RenderJSGadget.prototype.__interface_list = []; + RenderJSGadget.prototype.__path = ""; + RenderJSGadget.prototype.__html = ""; + RenderJSGadget.prototype.__required_css_list = []; + RenderJSGadget.prototype.__required_js_list = []; + + function createMonitor(g) { + if (g.__monitor !== undefined) { + g.__monitor.cancel(); + } + g.__monitor = new Monitor(); + g.__monitor.fail(function (error) { + if (!(error instanceof RSVP.CancellationError)) { + return g.aq_reportServiceError(error); + } + }).fail(function (error) { + // Crash the application if the acquisition generates an error. + return letsCrash(error); + }); + } + + function clearGadgetInternalParameters(g) { + g.__sub_gadget_dict = {}; + createMonitor(g); + } + + function loadSubGadgetDOMDeclaration(g) { + var element_list = g.__element.querySelectorAll('[data-gadget-url]'), + element, + promise_list = [], + scope, + url, + sandbox, + i; + + for (i = 0; i < element_list.length; i += 1) { + element = element_list[i]; + scope = element.getAttribute("data-gadget-scope"); + url = element.getAttribute("data-gadget-url"); + sandbox = element.getAttribute("data-gadget-sandbox"); + if (url !== null) { + promise_list.push(g.declareGadget(url, { + element: element, + scope: scope || undefined, + sandbox: sandbox || undefined + })); + } + } + + return RSVP.all(promise_list); + } + + RenderJSGadget.__ready_list = [clearGadgetInternalParameters, + loadSubGadgetDOMDeclaration]; + RenderJSGadget.ready = function (callback) { + this.__ready_list.push(callback); + return this; + }; + + RenderJSGadget.__service_list = []; + RenderJSGadget.declareService = function (callback) { + this.__service_list.push(callback); + return this; + }; + + function startService(gadget) { + gadget.__monitor.monitor(new RSVP.Queue() + .push(function () { + var i, + service_list = gadget.constructor.__service_list; + for (i = 0; i < service_list.length; i += 1) { + gadget.__monitor.monitor(service_list[i].apply(gadget)); + } + }) + ); + } + + ///////////////////////////////////////////////////////////////// + // RenderJSGadget.declareMethod + ///////////////////////////////////////////////////////////////// + RenderJSGadget.declareMethod = function (name, callback) { + this.prototype[name] = function () { + var context = this, + argument_list = arguments; + + return new RSVP.Queue() + .push(function () { + return callback.apply(context, argument_list); + }); + }; + // Allow chain + return this; + }; + + RenderJSGadget + .declareMethod('getInterfaceList', function () { + // Returns the list of gadget prototype + return this.__interface_list; + }) + .declareMethod('getRequiredCSSList', function () { + // Returns a list of CSS required by the gadget + return this.__required_css_list; + }) + .declareMethod('getRequiredJSList', function () { + // Returns a list of JS required by the gadget + return this.__required_js_list; + }) + .declareMethod('getPath', function () { + // Returns the path of the code of a gadget + return this.__path; + }) + .declareMethod('getTitle', function () { + // Returns the title of a gadget + return this.__title; + }) + .declareMethod('getElement', function () { + // Returns the DOM Element of a gadget + if (this.__element === undefined) { + throw new Error("No element defined"); + } + return this.__element; + }); + + ///////////////////////////////////////////////////////////////// + // RenderJSGadget.declareAcquiredMethod + ///////////////////////////////////////////////////////////////// + function acquire(child_gadget, method_name, argument_list) { + var gadget = this, + key, + gadget_scope; + + for (key in gadget.__sub_gadget_dict) { + if (gadget.__sub_gadget_dict.hasOwnProperty(key)) { + if (gadget.__sub_gadget_dict[key] === child_gadget) { + gadget_scope = key; + } + } + } + return new RSVP.Queue() + .push(function () { + // Do not specify default __acquired_method_dict on prototype + // to prevent modifying this default value (with + // allowPublicAcquiredMethod for example) + var aq_dict = gadget.__acquired_method_dict || {}; + if (aq_dict.hasOwnProperty(method_name)) { + return aq_dict[method_name].apply(gadget, + [argument_list, gadget_scope]); + } + throw new renderJS.AcquisitionError("aq_dynamic is not defined"); + }) + .push(undefined, function (error) { + if (error instanceof renderJS.AcquisitionError) { + return gadget.__aq_parent(method_name, argument_list); + } + throw error; + }); + } + + RenderJSGadget.declareAcquiredMethod = + function (name, method_name_to_acquire) { + this.prototype[name] = function () { + var argument_list = Array.prototype.slice.call(arguments, 0), + gadget = this; + return new RSVP.Queue() + .push(function () { + return gadget.__aq_parent(method_name_to_acquire, argument_list); + }); + }; + + // Allow chain + return this; + }; + RenderJSGadget.declareAcquiredMethod("aq_reportServiceError", + "reportServiceError"); + + ///////////////////////////////////////////////////////////////// + // RenderJSGadget.allowPublicAcquisition + ///////////////////////////////////////////////////////////////// + RenderJSGadget.allowPublicAcquisition = + function (method_name, callback) { + this.prototype.__acquired_method_dict[method_name] = callback; + + // Allow chain + return this; + }; + + // Set aq_parent on gadget_instance which call acquire on parent_gadget + function setAqParent(gadget_instance, parent_gadget) { + gadget_instance.__aq_parent = function (method_name, argument_list) { + return acquire.apply(parent_gadget, [gadget_instance, method_name, + argument_list]); + }; + } + + ///////////////////////////////////////////////////////////////// + // RenderJSEmbeddedGadget + ///////////////////////////////////////////////////////////////// + // Class inheritance + function RenderJSEmbeddedGadget() { + if (!(this instanceof RenderJSEmbeddedGadget)) { + return new RenderJSEmbeddedGadget(); + } + RenderJSGadget.call(this); + } + RenderJSEmbeddedGadget.__ready_list = RenderJSGadget.__ready_list.slice(); + RenderJSEmbeddedGadget.__service_list = + RenderJSGadget.__service_list.slice(); + RenderJSEmbeddedGadget.ready = + RenderJSGadget.ready; + RenderJSEmbeddedGadget.declareService = + RenderJSGadget.declareService; + RenderJSEmbeddedGadget.prototype = new RenderJSGadget(); + RenderJSEmbeddedGadget.prototype.constructor = RenderJSEmbeddedGadget; + + ///////////////////////////////////////////////////////////////// + // privateDeclarePublicGadget + ///////////////////////////////////////////////////////////////// + function privateDeclarePublicGadget(url, options, parent_gadget) { + var gadget_instance; + if (options.element === undefined) { + options.element = document.createElement("div"); + } + + function loadDependency(method, url) { + return function () { + return method(url); + }; + } + + return new RSVP.Queue() + .push(function () { + return renderJS.declareGadgetKlass(url); + }) + // Get the gadget class and instanciate it + .push(function (Klass) { + var i, + template_node_list = Klass.__template_element.body.childNodes; + gadget_loading_klass = Klass; + gadget_instance = new Klass(); + gadget_instance.__element = options.element; + for (i = 0; i < template_node_list.length; i += 1) { + gadget_instance.__element.appendChild( + template_node_list[i].cloneNode(true) + ); + } + setAqParent(gadget_instance, parent_gadget); + // Load dependencies if needed + return RSVP.all([ + gadget_instance.getRequiredJSList(), + gadget_instance.getRequiredCSSList() + ]); + }) + // Load all JS/CSS + .push(function (all_list) { + var q = new RSVP.Queue(), + i; + // Load JS + for (i = 0; i < all_list[0].length; i += 1) { + q.push(loadDependency(renderJS.declareJS, all_list[0][i])); + } + // Load CSS + for (i = 0; i < all_list[1].length; i += 1) { + q.push(loadDependency(renderJS.declareCSS, all_list[1][i])); + } + return q; + }) + .push(function () { + return gadget_instance; + }); + } + + ///////////////////////////////////////////////////////////////// + // RenderJSIframeGadget + ///////////////////////////////////////////////////////////////// + function RenderJSIframeGadget() { + if (!(this instanceof RenderJSIframeGadget)) { + return new RenderJSIframeGadget(); + } + RenderJSGadget.call(this); + } + RenderJSIframeGadget.__ready_list = RenderJSGadget.__ready_list.slice(); + RenderJSIframeGadget.ready = + RenderJSGadget.ready; + RenderJSIframeGadget.__service_list = RenderJSGadget.__service_list.slice(); + RenderJSIframeGadget.declareService = + RenderJSGadget.declareService; + RenderJSIframeGadget.prototype = new RenderJSGadget(); + RenderJSIframeGadget.prototype.constructor = RenderJSIframeGadget; + + ///////////////////////////////////////////////////////////////// + // privateDeclareIframeGadget + ///////////////////////////////////////////////////////////////// + function privateDeclareIframeGadget(url, options, parent_gadget) { + var gadget_instance, + iframe, + iframe_loading_deferred = RSVP.defer(); + if (options.element === undefined) { + throw new Error("DOM element is required to create Iframe Gadget " + + url); + } + + // Check if the element is attached to the DOM + if (!document.contains(options.element)) { + throw new Error("The parent element is not attached to the DOM for " + + url); + } + + gadget_instance = new RenderJSIframeGadget(); + setAqParent(gadget_instance, parent_gadget); + iframe = document.createElement("iframe"); +// gadget_instance.element.setAttribute("seamless", "seamless"); + iframe.setAttribute("src", url); + gadget_instance.__path = url; + gadget_instance.__element = options.element; + // Attach it to the DOM + options.element.appendChild(iframe); + + // XXX Manage unbind when deleting the gadget + + // Create the communication channel with the iframe + gadget_instance.__chan = Channel.build({ + window: iframe.contentWindow, + origin: "*", + scope: "renderJS" + }); + + // Create new method from the declareMethod call inside the iframe + gadget_instance.__chan.bind("declareMethod", + function (trans, method_name) { + gadget_instance[method_name] = function () { + var argument_list = arguments, + wait_promise = new RSVP.Promise(function (resolve, reject) { + gadget_instance.__chan.call({ + method: "methodCall", + params: [ + method_name, + Array.prototype.slice.call(argument_list, 0)], + success: function (s) { + resolve(s); + }, + error: function (e) { + reject(e); + } + }); + }); + return new RSVP.Queue() + .push(function () { + return wait_promise; + }); + }; + return "OK"; + }); + + // Wait for the iframe to be loaded before continuing + gadget_instance.__chan.bind("ready", function (trans) { + iframe_loading_deferred.resolve(gadget_instance); + return "OK"; + }); + gadget_instance.__chan.bind("failed", function (trans, params) { + iframe_loading_deferred.reject(params); + return "OK"; + }); + gadget_instance.__chan.bind("acquire", function (trans, params) { + gadget_instance.__aq_parent.apply(gadget_instance, params) + .then(function (g) { + trans.complete(g); + }).fail(function (e) { + trans.error(e.toString()); + }); + trans.delayReturn(true); + }); + + return RSVP.any([ + iframe_loading_deferred.promise, + // Timeout to prevent non renderJS embeddable gadget + // XXX Maybe using iframe.onload/onerror would be safer? + new RSVP.Queue() + .push(function () { + return RSVP.timeout(5000); + }) + .push(undefined, function () { + throw new Error('Timeout while loading: ' + url); + }) + ]); + } + + ///////////////////////////////////////////////////////////////// + // privateDeclareDataUrlGadget + ///////////////////////////////////////////////////////////////// + function privateDeclareDataUrlGadget(url, options, parent_gadget) { + + return new RSVP.Queue() + .push(function () { + return ajax(url); + }) + .push(function (xhr) { + // Insert a "base" element, in order to resolve all relative links + // which could get broken with a data url + var doc = (new DOMParser()).parseFromString(xhr.responseText, + 'text/html'), + base = doc.createElement('base'), + blob; + base.href = url; + doc.head.insertBefore(base, doc.head.firstChild); + blob = new Blob([doc.documentElement.outerHTML], + {type: "text/html;charset=UTF-8"}); + return readBlobAsDataURL(blob); + }) + .push(function (data_url) { + return privateDeclareIframeGadget(data_url, options, parent_gadget); + }); + } + + ///////////////////////////////////////////////////////////////// + // RenderJSGadget.declareGadget + ///////////////////////////////////////////////////////////////// + RenderJSGadget + .declareMethod('declareGadget', function (url, options) { + var queue, + parent_gadget = this, + local_loading_klass_promise, + previous_loading_klass_promise = loading_klass_promise; + + if (options === undefined) { + options = {}; + } + if (options.sandbox === undefined) { + options.sandbox = "public"; + } + + // transform url to absolute url if it is relative + url = renderJS.getAbsoluteURL(url, this.__path); + // Change the global variable to update the loading queue + loading_klass_promise = new RSVP.Queue() + // Wait for previous gadget loading to finish first + .push(function () { + return previous_loading_klass_promise; + }) + .push(undefined, function () { + // Forget previous declareGadget error + return; + }) + .push(function () { + var method; + if (options.sandbox === "public") { + method = privateDeclarePublicGadget; + } else if (options.sandbox === "iframe") { + method = privateDeclareIframeGadget; + } else if (options.sandbox === "dataurl") { + method = privateDeclareDataUrlGadget; + } else { + throw new Error("Unsupported sandbox options '" + + options.sandbox + "'"); + } + return method(url, options, parent_gadget); + }) + // Set the HTML context + .push(function (gadget_instance) { + // Drop the current loading klass info used by selector + gadget_loading_klass = undefined; + return gadget_instance; + }) + .push(undefined, function (e) { + // Drop the current loading klass info used by selector + // even in case of error + gadget_loading_klass = undefined; + throw e; + }); + local_loading_klass_promise = loading_klass_promise; + + queue = new RSVP.Queue() + .push(function () { + return local_loading_klass_promise; + }) + // Set the HTML context + .push(function (gadget_instance) { + var i, + scope; + // Trigger calling of all ready callback + function ready_wrapper() { + return gadget_instance; + } + for (i = 0; i < gadget_instance.constructor.__ready_list.length; + i += 1) { + // Put a timeout? + queue.push(gadget_instance.constructor.__ready_list[i]); + // Always return the gadget instance after ready function + queue.push(ready_wrapper); + } + + // Store local reference to the gadget instance + scope = options.scope; + if (scope === undefined) { + scope = 'RJS_' + scope_increment; + scope_increment += 1; + while (parent_gadget.__sub_gadget_dict.hasOwnProperty(scope)) { + scope = 'RJS_' + scope_increment; + scope_increment += 1; + } + } + parent_gadget.__sub_gadget_dict[scope] = gadget_instance; + gadget_instance.__element.setAttribute("data-gadget-scope", + scope); + + // Put some attribute to ease page layout comprehension + gadget_instance.__element.setAttribute("data-gadget-url", url); + gadget_instance.__element.setAttribute("data-gadget-sandbox", + options.sandbox); + gadget_instance.__element._gadget = gadget_instance; + + if (document.contains(gadget_instance.__element)) { + // Put a timeout + queue.push(startService); + } + // Always return the gadget instance after ready function + queue.push(ready_wrapper); + + return gadget_instance; + }); + return queue; + }) + .declareMethod('getDeclaredGadget', function (gadget_scope) { + if (!this.__sub_gadget_dict.hasOwnProperty(gadget_scope)) { + throw new Error("Gadget scope '" + gadget_scope + "' is not known."); + } + return this.__sub_gadget_dict[gadget_scope]; + }) + .declareMethod('dropGadget', function (gadget_scope) { + if (!this.__sub_gadget_dict.hasOwnProperty(gadget_scope)) { + throw new Error("Gadget scope '" + gadget_scope + "' is not known."); + } + // http://perfectionkills.com/understanding-delete/ + delete this.__sub_gadget_dict[gadget_scope]; + }); + + ///////////////////////////////////////////////////////////////// + // renderJS selector + ///////////////////////////////////////////////////////////////// + renderJS = function (selector) { + var result; + if (selector === window) { + // window is the 'this' value when loading a javascript file + // In this case, use the current loading gadget constructor + result = gadget_loading_klass; + } + if (result === undefined) { + throw new Error("Unknown selector '" + selector + "'"); + } + return result; + }; + + ///////////////////////////////////////////////////////////////// + // renderJS.AcquisitionError + ///////////////////////////////////////////////////////////////// + renderJS.AcquisitionError = function (message) { + this.name = "AcquisitionError"; + if ((message !== undefined) && (typeof message !== "string")) { + throw new TypeError('You must pass a string.'); + } + this.message = message || "Acquisition failed"; + }; + renderJS.AcquisitionError.prototype = new Error(); + renderJS.AcquisitionError.prototype.constructor = + renderJS.AcquisitionError; + + ///////////////////////////////////////////////////////////////// + // renderJS.getAbsoluteURL + ///////////////////////////////////////////////////////////////// + renderJS.getAbsoluteURL = function (url, base_url) { + var doc, base, link, + html = "<!doctype><html><head></head></html>"; + + if (url && base_url && !isAbsoluteOrDataURL.test(url)) { + doc = (new DOMParser()).parseFromString(html, 'text/html'); + base = doc.createElement('base'); + link = doc.createElement('link'); + doc.head.appendChild(base); + doc.head.appendChild(link); + base.href = base_url; + link.href = url; + return link.href; + } + return url; + }; + + ///////////////////////////////////////////////////////////////// + // renderJS.declareJS + ///////////////////////////////////////////////////////////////// + renderJS.declareJS = function (url) { + // Prevent infinite recursion if loading render.js + // more than once + var result; + if (javascript_registration_dict.hasOwnProperty(url)) { + result = RSVP.resolve(); + } else { + result = new RSVP.Promise(function (resolve, reject) { + var newScript; + newScript = document.createElement('script'); + newScript.type = 'text/javascript'; + newScript.src = url; + newScript.onload = function () { + javascript_registration_dict[url] = null; + resolve(); + }; + newScript.onerror = function (e) { + reject(e); + }; + document.head.appendChild(newScript); + }); + } + return result; + }; + + ///////////////////////////////////////////////////////////////// + // renderJS.declareCSS + ///////////////////////////////////////////////////////////////// + renderJS.declareCSS = function (url) { + // https://github.com/furf/jquery-getCSS/blob/master/jquery.getCSS.js + // No way to cleanly check if a css has been loaded + // So, always resolve the promise... + // http://requirejs.org/docs/faq-advanced.html#css + var result; + if (stylesheet_registration_dict.hasOwnProperty(url)) { + result = RSVP.resolve(); + } else { + result = new RSVP.Promise(function (resolve, reject) { + var link; + link = document.createElement('link'); + link.rel = 'stylesheet'; + link.type = 'text/css'; + link.href = url; + link.onload = function () { + stylesheet_registration_dict[url] = null; + resolve(); + }; + link.onerror = function (e) { + reject(e); + }; + document.head.appendChild(link); + }); + } + return result; + }; + + ///////////////////////////////////////////////////////////////// + // renderJS.declareGadgetKlass + ///////////////////////////////////////////////////////////////// + renderJS.declareGadgetKlass = function (url) { + var result; + + function parse(xhr) { + var tmp_constructor, + key, + parsed_html; + if (!gadget_model_dict.hasOwnProperty(url)) { + // Class inheritance + tmp_constructor = function () { + RenderJSGadget.call(this); + }; + tmp_constructor.__ready_list = RenderJSGadget.__ready_list.slice(); + tmp_constructor.__service_list = RenderJSGadget.__service_list.slice(); + tmp_constructor.declareMethod = + RenderJSGadget.declareMethod; + tmp_constructor.declareAcquiredMethod = + RenderJSGadget.declareAcquiredMethod; + tmp_constructor.allowPublicAcquisition = + RenderJSGadget.allowPublicAcquisition; + tmp_constructor.ready = + RenderJSGadget.ready; + tmp_constructor.declareService = + RenderJSGadget.declareService; + tmp_constructor.prototype = new RenderJSGadget(); + tmp_constructor.prototype.constructor = tmp_constructor; + tmp_constructor.prototype.__path = url; + tmp_constructor.prototype.__acquired_method_dict = {}; + // https://developer.mozilla.org/en-US/docs/HTML_in_XMLHttpRequest + // https://developer.mozilla.org/en-US/docs/Web/API/DOMParser + // https://developer.mozilla.org/en-US/docs/Code_snippets/HTML_to_DOM + tmp_constructor.__template_element = + (new DOMParser()).parseFromString(xhr.responseText, "text/html"); + parsed_html = renderJS.parseGadgetHTMLDocument( + tmp_constructor.__template_element, + url + ); + for (key in parsed_html) { + if (parsed_html.hasOwnProperty(key)) { + tmp_constructor.prototype['__' + key] = parsed_html[key]; + } + } + + gadget_model_dict[url] = tmp_constructor; + } + + return gadget_model_dict[url]; + } + + if (gadget_model_dict.hasOwnProperty(url)) { + // Return klass object if it already exists + result = RSVP.resolve(gadget_model_dict[url]); + } else { + // Fetch the HTML page and parse it + result = new RSVP.Queue() + .push(function () { + return ajax(url); + }) + .push(function (xhr) { + return parse(xhr); + }); + } + return result; + }; + + ///////////////////////////////////////////////////////////////// + // renderJS.clearGadgetKlassList + ///////////////////////////////////////////////////////////////// + // For test purpose only + renderJS.clearGadgetKlassList = function () { + gadget_model_dict = {}; + javascript_registration_dict = {}; + stylesheet_registration_dict = {}; + }; + + ///////////////////////////////////////////////////////////////// + // renderJS.parseGadgetHTMLDocument + ///////////////////////////////////////////////////////////////// + renderJS.parseGadgetHTMLDocument = function (document_element, url) { + var settings = { + title: "", + interface_list: [], + required_css_list: [], + required_js_list: [] + }, + i, + element; + + if (!url || !isAbsoluteOrDataURL.test(url)) { + throw new Error("The url should be absolute: " + url); + } + + if (document_element.nodeType === 9) { + settings.title = document_element.title; + + if (document_element.head !== null) { + for (i = 0; i < document_element.head.children.length; i += 1) { + element = document_element.head.children[i]; + if (element.href !== null) { + // XXX Manage relative URL during extraction of URLs + // element.href returns absolute URL in firefox but "" in chrome; + if (element.rel === "stylesheet") { + settings.required_css_list.push( + renderJS.getAbsoluteURL(element.getAttribute("href"), url) + ); + } else if (element.nodeName === "SCRIPT" && + (element.type === "text/javascript" || + !element.type)) { + settings.required_js_list.push( + renderJS.getAbsoluteURL(element.getAttribute("src"), url) + ); + } else if (element.rel === + "http://www.renderjs.org/rel/interface") { + settings.interface_list.push( + renderJS.getAbsoluteURL(element.getAttribute("href"), url) + ); + } + } + } + } + } else { + throw new Error("The first parameter should be an HTMLDocument"); + } + return settings; + }; + + ///////////////////////////////////////////////////////////////// + // global + ///////////////////////////////////////////////////////////////// + window.rJS = window.renderJS = renderJS; + window.__RenderJSGadget = RenderJSGadget; + window.__RenderJSEmbeddedGadget = RenderJSEmbeddedGadget; + window.__RenderJSIframeGadget = RenderJSIframeGadget; + + /////////////////////////////////////////////////// + // Bootstrap process. Register the self gadget. + /////////////////////////////////////////////////// + + function bootstrap() { + var url = removeHash(window.location.href), + tmp_constructor, + root_gadget, + loading_gadget_promise = new RSVP.Queue(), + declare_method_count = 0, + embedded_channel, + notifyReady, + notifyDeclareMethod, + gadget_ready = false, + iframe_top_gadget, + last_acquisition_gadget, + declare_method_list_waiting = [], + gadget_failed = false, + gadget_error, + connection_ready = false; + + // Create the gadget class for the current url + if (gadget_model_dict.hasOwnProperty(url)) { + throw new Error("bootstrap should not be called twice"); + } + loading_klass_promise = new RSVP.Promise(function (resolve, reject) { + + last_acquisition_gadget = new RenderJSGadget(); + last_acquisition_gadget.__acquired_method_dict = { + reportServiceError: function (param_list) { + letsCrash(param_list[0]); + } + }; + // Stop acquisition on the last acquisition gadget + // Do not put this on the klass, as their could be multiple instances + last_acquisition_gadget.__aq_parent = function (method_name) { + throw new renderJS.AcquisitionError( + "No gadget provides " + method_name + ); + }; + + //we need to determine tmp_constructor's value before exit bootstrap + //because of function : renderJS + //but since the channel checking is async, + //we can't use code structure like: + // if channel communication is ok + // tmp_constructor = RenderJSGadget + // else + // tmp_constructor = RenderJSEmbeddedGadget + if (window.self === window.top) { + // XXX Copy/Paste from declareGadgetKlass + tmp_constructor = function () { + RenderJSGadget.call(this); + }; + tmp_constructor.declareMethod = RenderJSGadget.declareMethod; + tmp_constructor.declareAcquiredMethod = + RenderJSGadget.declareAcquiredMethod; + tmp_constructor.allowPublicAcquisition = + RenderJSGadget.allowPublicAcquisition; + tmp_constructor.__ready_list = RenderJSGadget.__ready_list.slice(); + tmp_constructor.ready = RenderJSGadget.ready; + tmp_constructor.__service_list = RenderJSGadget.__service_list.slice(); + tmp_constructor.declareService = + RenderJSGadget.declareService; + tmp_constructor.prototype = new RenderJSGadget(); + tmp_constructor.prototype.constructor = tmp_constructor; + tmp_constructor.prototype.__path = url; + gadget_model_dict[url] = tmp_constructor; + + // Create the root gadget instance and put it in the loading stack + root_gadget = new gadget_model_dict[url](); + + setAqParent(root_gadget, last_acquisition_gadget); + + } else { + // Create the root gadget instance and put it in the loading stack + tmp_constructor = RenderJSEmbeddedGadget; + tmp_constructor.__ready_list = RenderJSGadget.__ready_list.slice(); + tmp_constructor.__service_list = RenderJSGadget.__service_list.slice(); + tmp_constructor.prototype.__path = url; + root_gadget = new RenderJSEmbeddedGadget(); + setAqParent(root_gadget, last_acquisition_gadget); + + // Create the communication channel + embedded_channel = Channel.build({ + window: window.parent, + origin: "*", + scope: "renderJS", + onReady: function () { + var k; + iframe_top_gadget = false; + //Default: Define __aq_parent to inform parent window + root_gadget.__aq_parent = + tmp_constructor.prototype.__aq_parent = function (method_name, + argument_list, time_out) { + return new RSVP.Promise(function (resolve, reject) { + embedded_channel.call({ + method: "acquire", + params: [ + method_name, + argument_list + ], + success: function (s) { + resolve(s); + }, + error: function (e) { + reject(e); + }, + timeout: time_out + }); + }); + }; + + // Channel is ready, so now declare Function + notifyDeclareMethod = function (name) { + declare_method_count += 1; + embedded_channel.call({ + method: "declareMethod", + params: name, + success: function () { + declare_method_count -= 1; + notifyReady(); + }, + error: function () { + declare_method_count -= 1; + } + }); + }; + for (k = 0; k < declare_method_list_waiting.length; k += 1) { + notifyDeclareMethod(declare_method_list_waiting[k]); + } + declare_method_list_waiting = []; + // If Gadget Failed Notify Parent + if (gadget_failed) { + embedded_channel.notify({ + method: "failed", + params: gadget_error + }); + return; + } + connection_ready = true; + notifyReady(); + //the channel is ok + //so bind calls to renderJS method on the instance + embedded_channel.bind("methodCall", function (trans, v) { + root_gadget[v[0]].apply(root_gadget, v[1]) + .then(function (g) { + trans.complete(g); + }).fail(function (e) { + trans.error(e.toString()); + }); + trans.delayReturn(true); + }); + } + }); + + // Notify parent about gadget instanciation + notifyReady = function () { + if ((declare_method_count === 0) && (gadget_ready === true)) { + embedded_channel.notify({method: "ready"}); + } + }; + + // Inform parent gadget about declareMethod calls here. + notifyDeclareMethod = function (name) { + declare_method_list_waiting.push(name); + }; + + notifyDeclareMethod("getInterfaceList"); + notifyDeclareMethod("getRequiredCSSList"); + notifyDeclareMethod("getRequiredJSList"); + notifyDeclareMethod("getPath"); + notifyDeclareMethod("getTitle"); + + // Surcharge declareMethod to inform parent window + tmp_constructor.declareMethod = function (name, callback) { + var result = RenderJSGadget.declareMethod.apply( + this, + [name, callback] + ); + notifyDeclareMethod(name); + return result; + }; + + tmp_constructor.declareService = + RenderJSGadget.declareService; + tmp_constructor.declareAcquiredMethod = + RenderJSGadget.declareAcquiredMethod; + tmp_constructor.allowPublicAcquisition = + RenderJSGadget.allowPublicAcquisition; + + iframe_top_gadget = true; + } + + tmp_constructor.prototype.__acquired_method_dict = {}; + gadget_loading_klass = tmp_constructor; + + function init() { + // XXX HTML properties can only be set when the DOM is fully loaded + var settings = renderJS.parseGadgetHTMLDocument(document, url), + j, + key; + for (key in settings) { + if (settings.hasOwnProperty(key)) { + tmp_constructor.prototype['__' + key] = settings[key]; + } + } + tmp_constructor.__template_element = document.createElement("div"); + root_gadget.__element = document.body; + for (j = 0; j < root_gadget.__element.childNodes.length; j += 1) { + tmp_constructor.__template_element.appendChild( + root_gadget.__element.childNodes[j].cloneNode(true) + ); + } + RSVP.all([root_gadget.getRequiredJSList(), + root_gadget.getRequiredCSSList()]) + .then(function (all_list) { + var i, + js_list = all_list[0], + css_list = all_list[1]; + for (i = 0; i < js_list.length; i += 1) { + javascript_registration_dict[js_list[i]] = null; + } + for (i = 0; i < css_list.length; i += 1) { + stylesheet_registration_dict[css_list[i]] = null; + } + gadget_loading_klass = undefined; + }).then(function () { + + // select the target node + var target = document.querySelector('body'), + // create an observer instance + observer = new MutationObserver(function (mutations) { + var i, k, len, len2, node, added_list; + mutations.forEach(function (mutation) { + if (mutation.type === 'childList') { + + len = mutation.removedNodes.length; + for (i = 0; i < len; i += 1) { + node = mutation.removedNodes[i]; + if (node.nodeType === Node.ELEMENT_NODE) { + if (node.hasAttribute("data-gadget-url") && + (node._gadget !== undefined)) { + createMonitor(node._gadget); + } + added_list = + node.querySelectorAll("[data-gadget-url]"); + len2 = added_list.length; + for (k = 0; k < len2; k += 1) { + node = added_list[k]; + if (node._gadget !== undefined) { + createMonitor(node._gadget); + } + } + } + } + + len = mutation.addedNodes.length; + for (i = 0; i < len; i += 1) { + node = mutation.addedNodes[i]; + if (node.nodeType === Node.ELEMENT_NODE) { + if (node.hasAttribute("data-gadget-url") && + (node._gadget !== undefined)) { + if (document.contains(node)) { + startService(node._gadget); + } + } + added_list = + node.querySelectorAll("[data-gadget-url]"); + len2 = added_list.length; + for (k = 0; k < len2; k += 1) { + node = added_list[k]; + if (document.contains(node)) { + if (node._gadget !== undefined) { + startService(node._gadget); + } + } + } + } + } + + } + }); + }), + // configuration of the observer: + config = { + childList: true, + subtree: true, + attributes: false, + characterData: false + }; + + // pass in the target node, as well as the observer options + observer.observe(target, config); + + return root_gadget; + }).then(resolve, function (e) { + reject(e); + console.error(e); + throw e; + }); + } + document.addEventListener('DOMContentLoaded', init, false); + }); + + loading_gadget_promise + .push(function () { + return loading_klass_promise; + }) + .push(function (root_gadget) { + var i; + + function ready_wrapper() { + return root_gadget; + } + + tmp_constructor.ready(function (g) { + return startService(g); + }); + + loading_gadget_promise.push(ready_wrapper); + for (i = 0; i < tmp_constructor.__ready_list.length; i += 1) { + // Put a timeout? + loading_gadget_promise + .push(tmp_constructor.__ready_list[i]) + // Always return the gadget instance after ready function + .push(ready_wrapper); + } + }); + if (window.self === window.top) { + loading_gadget_promise + .fail(function (e) { + letsCrash(e); + throw e; + }); + } else { + // Inform parent window that gadget is correctly loaded + loading_gadget_promise + .then(function () { + gadget_ready = true; + if (connection_ready) { + notifyReady(); + } + }) + .fail(function (e) { + //top gadget in iframe + if (iframe_top_gadget) { + gadget_failed = true; + gadget_error = e.toString(); + letsCrash(e); + } else { + embedded_channel.notify({method: "failed", params: e.toString()}); + } + throw e; + }); + } + + } + bootstrap(); + +}(document, window, RSVP, DOMParser, Channel, MutationObserver, Node, + FileReader, Blob, navigator, Event)); diff --git a/dist/renderjs-0.10.0.min.js b/dist/renderjs-0.10.0.min.js new file mode 100644 index 0000000000000000000000000000000000000000..c2881f4d178fbdf9f1b86014c22649a09c6c76f9 --- /dev/null +++ b/dist/renderjs-0.10.0.min.js @@ -0,0 +1 @@ +var Channel=function(){"use strict";function a(a,b,c,d){function f(b){for(var c=0;c<b.length;c++)if(b[c].win===a)return!0;return!1}var g=!1;if("*"===b){for(var h in e)if(e.hasOwnProperty(h)&&"*"!==h&&"object"==typeof e[h][c]&&(g=f(e[h][c])))break}else e["*"]&&e["*"][c]&&(g=f(e["*"][c])),!g&&e[b]&&e[b][c]&&(g=f(e[b][c]));if(g)throw"A channel is already bound to the same window which overlaps with origin '"+b+"' and has scope '"+c+"'";"object"!=typeof e[b]&&(e[b]={}),"object"!=typeof e[b][c]&&(e[b][c]=[]),e[b][c].push({win:a,handler:d})}function b(a,b,c){for(var d=e[b][c],f=0;f<d.length;f++)d[f].win===a&&d.splice(f,1);0===e[b][c].length&&delete e[b][c]}function c(a){return Array.isArray?Array.isArray(a):-1!=a.constructor.toString().indexOf("Array")}var d=Math.floor(1000001*Math.random()),e={},f={},g=function(a){try{var b=JSON.parse(a.data);if("object"!=typeof b||null===b)throw"malformed"}catch(a){return}var c,d,g,h=a.source,i=a.origin;if("string"==typeof b.method){var j=b.method.split("::");2==j.length?(c=j[0],g=j[1]):g=b.method}if("undefined"!=typeof b.id&&(d=b.id),"string"==typeof g){var k=!1;if(e[i]&&e[i][c])for(var l=0;l<e[i][c].length;l++)if(e[i][c][l].win===h){e[i][c][l].handler(i,g,b),k=!0;break}if(!k&&e["*"]&&e["*"][c])for(var l=0;l<e["*"][c].length;l++)if(e["*"][c][l].win===h){e["*"][c][l].handler(i,g,b);break}}else"undefined"!=typeof d&&f[d]&&f[d](i,g,b)};return window.addEventListener?window.addEventListener("message",g,!1):window.attachEvent&&window.attachEvent("onmessage",g),{build:function(e){var g=function(a){if(e.debugOutput&&window.console&&window.console.log){try{"string"!=typeof a&&(a=JSON.stringify(a))}catch(b){}console.log("["+j+"] "+a)}};if(!window.postMessage)throw"jschannel cannot run this browser, no postMessage";if(!window.JSON||!window.JSON.stringify||!window.JSON.parse)throw"jschannel cannot run this browser, no JSON parsing/serialization";if("object"!=typeof e)throw"Channel build invoked without a proper object argument";if(!e.window||!e.window.postMessage)throw"Channel.build() called without a valid window argument";if(window===e.window)throw"target window is same as present window -- not allowed";var h=!1;if("string"==typeof e.origin){var i;"*"===e.origin?h=!0:null!==(i=e.origin.match(/^https?:\/\/(?:[-a-zA-Z0-9_\.])+(?::\d+)?/))&&(e.origin=i[0].toLowerCase(),h=!0)}if(!h)throw"Channel.build() called with an invalid origin";if("undefined"!=typeof e.scope){if("string"!=typeof e.scope)throw"scope, when specified, must be a string";if(e.scope.split("::").length>1)throw"scope may not contain double colons: '::'"}var j=function(){for(var a="",b="abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789",c=0;5>c;c++)a+=b.charAt(Math.floor(Math.random()*b.length));return a}(),k={},l={},m={},n=!1,o=[],p=function(a,b,c){var d=!1,e=!1;return{origin:b,invoke:function(b,d){if(!m[a])throw"attempting to invoke a callback of a nonexistent transaction: "+a;for(var e=!1,f=0;f<c.length;f++)if(b===c[f]){e=!0;break}if(!e)throw"request supports no such callback '"+b+"'";t({id:a,callback:b,params:d})},error:function(b,c){if(e=!0,!m[a])throw"error called for nonexistent message: "+a;delete m[a],t({id:a,error:b,message:c})},complete:function(b){if(e=!0,!m[a])throw"complete called for nonexistent message: "+a;delete m[a],t({id:a,result:b})},delayReturn:function(a){return"boolean"==typeof a&&(d=a===!0),d},completed:function(){return e}}},q=function(a,b,c){return window.setTimeout(function(){if(l[a]){var d="timeout ("+b+"ms) exceeded on method '"+c+"'";l[a].error("timeout_error",d),delete l[a],delete f[a]}},b)},r=function(a,b,d){if("function"==typeof e.gotMessageObserver)try{e.gotMessageObserver(a,d)}catch(h){g("gotMessageObserver() raised an exception: "+h.toString())}if(d.id&&b){if(k[b]){var i=p(d.id,a,d.callbacks?d.callbacks:[]);m[d.id]={};try{if(d.callbacks&&c(d.callbacks)&&d.callbacks.length>0)for(var j=0;j<d.callbacks.length;j++){for(var n=d.callbacks[j],o=d.params,q=n.split("/"),r=0;r<q.length-1;r++){var s=q[r];"object"!=typeof o[s]&&(o[s]={}),o=o[s]}o[q[q.length-1]]=function(){var a=n;return function(b){return i.invoke(a,b)}}()}var t=k[b](i,d.params);i.delayReturn()||i.completed()||i.complete(t)}catch(h){var u="runtime_error",v=null;if("string"==typeof h?v=h:"object"==typeof h&&(h&&c(h)&&2==h.length?(u=h[0],v=h[1]):"string"==typeof h.error&&(u=h.error,h.message?"string"==typeof h.message?v=h.message:h=h.message:v="")),null===v)try{v=JSON.stringify(h),"undefined"==typeof v&&(v=h.toString())}catch(w){v=h.toString()}i.error(u,v)}}}else d.id&&d.callback?l[d.id]&&l[d.id].callbacks&&l[d.id].callbacks[d.callback]?l[d.id].callbacks[d.callback](d.params):g("ignoring invalid callback, id:"+d.id+" ("+d.callback+")"):d.id?l[d.id]?(d.error?l[d.id].error(d.error,d.message):void 0!==d.result?l[d.id].success(d.result):l[d.id].success(),delete l[d.id],delete f[d.id]):g("ignoring invalid response: "+d.id):b&&k[b]&&k[b]({origin:a},d.params)};a(e.window,e.origin,"string"==typeof e.scope?e.scope:"",r);var s=function(a){return"string"==typeof e.scope&&e.scope.length&&(a=[e.scope,a].join("::")),a},t=function(a,b){if(!a)throw"postMessage called with null message";var c=n?"post ":"queue ";if(g(c+" message: "+JSON.stringify(a)),b||n){if("function"==typeof e.postMessageObserver)try{e.postMessageObserver(e.origin,a)}catch(d){g("postMessageObserver() raised an exception: "+d.toString())}e.window.postMessage(JSON.stringify(a),e.origin)}else o.push(a)},u=function(a,b){if(g("ready msg received"),n)throw"received ready message while in ready state. help!";for(j+="ping"===b?"-R":"-L",v.unbind("__ready"),n=!0,g("ready msg accepted."),"ping"===b&&v.notify({method:"__ready",params:"pong"});o.length;)t(o.pop());"function"==typeof e.onReady&&e.onReady(v)},v={unbind:function(a){if(k[a]){if(!delete k[a])throw"can't delete method: "+a;return!0}return!1},bind:function(a,b){if(!a||"string"!=typeof a)throw"'method' argument to bind must be string";if(!b||"function"!=typeof b)throw"callback missing from bind params";if(k[a])throw"method '"+a+"' is already bound!";return k[a]=b,this},call:function(a){if(!a)throw"missing arguments to call function";if(!a.method||"string"!=typeof a.method)throw"'method' argument to call must be string";if(!a.success||"function"!=typeof a.success)throw"'success' callback missing from call";var b={},c=[],e=function(a,d){if("object"==typeof d)for(var f in d)if(d.hasOwnProperty(f)){var g=a+(a.length?"/":"")+f;"function"==typeof d[f]?(b[g]=d[f],c.push(g),delete d[f]):"object"==typeof d[f]&&e(g,d[f])}};e("",a.params);var g={id:d,method:s(a.method),params:a.params};c.length&&(g.callbacks=c),a.timeout&&q(d,a.timeout,s(a.method)),l[d]={callbacks:b,error:a.error,success:a.success},f[d]=r,d++,t(g)},notify:function(a){if(!a)throw"missing arguments to notify function";if(!a.method||"string"!=typeof a.method)throw"'method' argument to notify must be string";t({method:s(a.method),params:a.params})},destroy:function(){b(e.window,e.origin,"string"==typeof e.scope?e.scope:""),window.removeEventListener?window.removeEventListener("message",r,!1):window.detachEvent&&window.detachEvent("onmessage",r),n=!1,k={},m={},l={},e.origin=null,o=[],g("channel destroyed"),j=""}};return v.bind("__ready",u),setTimeout(function(){t({method:s("__ready"),params:"ping"},!0)},0),v}}}();!function(a){"use strict";var b=a.prototype,c=b.parseFromString;try{if((new a).parseFromString("","text/html"))return}catch(d){}b.parseFromString=function(a,b){var d,e,f,g;return/^\s*text\/html\s*(?:;|$)/i.test(b)?(e=document.implementation.createHTMLDocument(""),f=e.documentElement,f.innerHTML=a,g=f.firstElementChild,1===f.childElementCount&&"html"===g.localName.toLowerCase()&&e.replaceChild(g,f),d=e):d=c.apply(this,arguments),d}}(DOMParser),"function"!=typeof document.contains&&(Document.prototype.contains=function(a){return a===this||a.parentNode===this?!0:this.documentElement.contains(a)}),function(a,b,c,d,e,f,g,h,i,j,k){"use strict";function l(a){var b=new h;return new c.Promise(function(c,d){b.addEventListener("load",function(a){c(a.target.result)}),b.addEventListener("error",d),b.readAsDataURL(a)},function(){b.abort()})}function m(a){function b(b,c){function d(){try{0===e.readyState?c(e):4===e.readyState&&(e.status<200||e.status>=300||!/^text\/html[;]?/.test(e.getResponseHeader("Content-Type")||"")?c(e):b(e))}catch(a){c(a)}}e=new XMLHttpRequest,e.open("GET",a),e.onreadystatechange=d,e.setRequestHeader("Accept","text/html"),e.withCredentials=!0,e.send()}function d(){void 0!==e&&e.readyState!==e.DONE&&e.abort()}var e;return new c.Promise(b,d)}function n(a){var b=a.indexOf("#");return b>0&&(a=a.substring(0,b)),a}function o(c){var d,e,f,g,h,i;if(M)return console.info("-- Error dropped, as page is unloaded"),void console.info(c);for(N.push(c),N.push(new Error("stopping renderJS")),e=a.getElementsByTagName("body")[0];e.firstChild;)e.removeChild(e.firstChild);for(f=a.createElement("section"),g=a.createElement("h1"),g.textContent="Unhandled Error",f.appendChild(g),g=a.createElement("p"),g.textContent="Please report this error to the support team",f.appendChild(g),g=a.createElement("p"),g.textContent="Location: ",h=a.createElement("a"),h.href=h.textContent=b.location.toString(),g.appendChild(h),f.appendChild(g),g=a.createElement("p"),g.textContent="User-agent: "+j.userAgent,f.appendChild(g),e.appendChild(f),d=0;d<N.length;d+=1){if(i=N[d],i instanceof k&&(i={string:i.toString(),message:i.message,type:i.type,target:i.target},void 0!==i.target&&N.splice(d+1,0,i.target)),i instanceof XMLHttpRequest&&(i={message:i.toString(),readyState:i.readyState,status:i.status,statusText:i.statusText,response:i.response,responseUrl:i.responseUrl,response_headers:i.getAllResponseHeaders()}),i.constructor===Array||i.constructor===String||i.constructor===Object)try{i=JSON.stringify(i)}catch(l){}f=a.createElement("section"),g=a.createElement("h2"),g.textContent=i.message||i,f.appendChild(g),void 0!==i.fileName&&(g=a.createElement("p"),g.textContent="File: "+i.fileName+": "+i.lineNumber,f.appendChild(g)),void 0!==i.stack&&(g=a.createElement("pre"),g.textContent="Stack: "+i.stack,f.appendChild(g)),e.appendChild(f)}console.error(c.stack),console.error(c)}function p(a){if(this.name="resolved",void 0!==a&&"string"!=typeof a)throw new TypeError("You must pass a string.");this.message=a||"Default Message"}function q(){return this instanceof q?void 0:new q}function r(a){void 0!==a.__monitor&&a.__monitor.cancel(),a.__monitor=new G,a.__monitor.fail(function(b){return b instanceof c.CancellationError?void 0:a.aq_reportServiceError(b)}).fail(function(a){return o(a)})}function s(a){a.__sub_gadget_dict={},r(a)}function t(a){var b,d,e,f,g,h=a.__element.querySelectorAll("[data-gadget-url]"),i=[];for(g=0;g<h.length;g+=1)b=h[g],d=b.getAttribute("data-gadget-scope"),e=b.getAttribute("data-gadget-url"),f=b.getAttribute("data-gadget-sandbox"),null!==e&&i.push(a.declareGadget(e,{element:b,scope:d||void 0,sandbox:f||void 0}));return c.all(i)}function u(a){a.__monitor.monitor((new c.Queue).push(function(){var b,c=a.constructor.__service_list;for(b=0;b<c.length;b+=1)a.__monitor.monitor(c[b].apply(a))}))}function v(a,b,d){var e,f,g=this;for(e in g.__sub_gadget_dict)g.__sub_gadget_dict.hasOwnProperty(e)&&g.__sub_gadget_dict[e]===a&&(f=e);return(new c.Queue).push(function(){var a=g.__acquired_method_dict||{};if(a.hasOwnProperty(b))return a[b].apply(g,[d,f]);throw new F.AcquisitionError("aq_dynamic is not defined")}).push(void 0,function(a){if(a instanceof F.AcquisitionError)return g.__aq_parent(b,d);throw a})}function w(a,b){a.__aq_parent=function(c,d){return v.apply(b,[a,c,d])}}function x(){return this instanceof x?void q.call(this):new x}function y(b,d,e){function f(a,b){return function(){return a(b)}}var g;return void 0===d.element&&(d.element=a.createElement("div")),(new c.Queue).push(function(){return F.declareGadgetKlass(b)}).push(function(a){var b,f=a.__template_element.body.childNodes;for(D=a,g=new a,g.__element=d.element,b=0;b<f.length;b+=1)g.__element.appendChild(f[b].cloneNode(!0));return w(g,e),c.all([g.getRequiredJSList(),g.getRequiredCSSList()])}).push(function(a){var b,d=new c.Queue;for(b=0;b<a[0].length;b+=1)d.push(f(F.declareJS,a[0][b]));for(b=0;b<a[1].length;b+=1)d.push(f(F.declareCSS,a[1][b]));return d}).push(function(){return g})}function z(){return this instanceof z?void q.call(this):new z}function A(b,d,f){var g,h,i=c.defer();if(void 0===d.element)throw new Error("DOM element is required to create Iframe Gadget "+b);if(!a.contains(d.element))throw new Error("The parent element is not attached to the DOM for "+b);return g=new z,w(g,f),h=a.createElement("iframe"),h.setAttribute("src",b),g.__path=b,g.__element=d.element,d.element.appendChild(h),g.__chan=e.build({window:h.contentWindow,origin:"*",scope:"renderJS"}),g.__chan.bind("declareMethod",function(a,b){return g[b]=function(){var a=arguments,d=new c.Promise(function(c,d){g.__chan.call({method:"methodCall",params:[b,Array.prototype.slice.call(a,0)],success:function(a){c(a)},error:function(a){d(a)}})});return(new c.Queue).push(function(){return d})},"OK"}),g.__chan.bind("ready",function(a){return i.resolve(g),"OK"}),g.__chan.bind("failed",function(a,b){return i.reject(b),"OK"}),g.__chan.bind("acquire",function(a,b){g.__aq_parent.apply(g,b).then(function(b){a.complete(b)}).fail(function(b){a.error(b.toString())}),a.delayReturn(!0)}),c.any([i.promise,(new c.Queue).push(function(){return c.timeout(5e3)}).push(void 0,function(){throw new Error("Timeout while loading: "+b)})])}function B(a,b,e){return(new c.Queue).push(function(){return m(a)}).push(function(b){var c,e=(new d).parseFromString(b.responseText,"text/html"),f=e.createElement("base");return f.href=a,e.head.insertBefore(f,e.head.firstChild),c=new i([e.documentElement.outerHTML],{type:"text/html;charset=UTF-8"}),l(c)}).push(function(a){return A(a,b,e)})}function C(){var d,h,i,j,k,l,m,p,s=n(b.location.href),t=new c.Queue,v=0,y=!1,z=[],A=!1,B=!1;if(H.hasOwnProperty(s))throw new Error("bootstrap should not be called twice");E=new c.Promise(function(n,t){function C(){var b,e,i=F.parseGadgetHTMLDocument(a,s);for(e in i)i.hasOwnProperty(e)&&(d.prototype["__"+e]=i[e]);for(d.__template_element=a.createElement("div"),h.__element=a.body,b=0;b<h.__element.childNodes.length;b+=1)d.__template_element.appendChild(h.__element.childNodes[b].cloneNode(!0));c.all([h.getRequiredJSList(),h.getRequiredCSSList()]).then(function(a){var b,c=a[0],d=a[1];for(b=0;b<c.length;b+=1)I[c[b]]=null;for(b=0;b<d.length;b+=1)J[d[b]]=null;D=void 0}).then(function(){var b=a.querySelector("body"),c=new f(function(b){var c,d,e,f,h,i;b.forEach(function(b){if("childList"===b.type){for(e=b.removedNodes.length,c=0;e>c;c+=1)if(h=b.removedNodes[c],h.nodeType===g.ELEMENT_NODE)for(h.hasAttribute("data-gadget-url")&&void 0!==h._gadget&&r(h._gadget),i=h.querySelectorAll("[data-gadget-url]"),f=i.length,d=0;f>d;d+=1)h=i[d],void 0!==h._gadget&&r(h._gadget);for(e=b.addedNodes.length,c=0;e>c;c+=1)if(h=b.addedNodes[c],h.nodeType===g.ELEMENT_NODE)for(h.hasAttribute("data-gadget-url")&&void 0!==h._gadget&&a.contains(h)&&u(h._gadget),i=h.querySelectorAll("[data-gadget-url]"),f=i.length,d=0;f>d;d+=1)h=i[d],a.contains(h)&&void 0!==h._gadget&&u(h._gadget)}})}),d={childList:!0,subtree:!0,attributes:!1,characterData:!1};return c.observe(b,d),h}).then(n,function(a){throw t(a),console.error(a),a})}m=new q,m.__acquired_method_dict={reportServiceError:function(a){o(a[0])}},m.__aq_parent=function(a){throw new F.AcquisitionError("No gadget provides "+a)},b.self===b.top?(d=function(){q.call(this)},d.declareMethod=q.declareMethod,d.declareAcquiredMethod=q.declareAcquiredMethod,d.allowPublicAcquisition=q.allowPublicAcquisition,d.__ready_list=q.__ready_list.slice(),d.ready=q.ready,d.__service_list=q.__service_list.slice(),d.declareService=q.declareService,d.prototype=new q,d.prototype.constructor=d,d.prototype.__path=s,H[s]=d,h=new H[s],w(h,m)):(d=x,d.__ready_list=q.__ready_list.slice(),d.__service_list=q.__service_list.slice(),d.prototype.__path=s,h=new x,w(h,m),i=e.build({window:b.parent,origin:"*",scope:"renderJS",onReady:function(){var a;for(l=!1,h.__aq_parent=d.prototype.__aq_parent=function(a,b,d){return new c.Promise(function(c,e){i.call({method:"acquire",params:[a,b],success:function(a){c(a)},error:function(a){e(a)},timeout:d})})},k=function(a){v+=1,i.call({method:"declareMethod",params:a,success:function(){v-=1,j()},error:function(){v-=1}})},a=0;a<z.length;a+=1)k(z[a]);return z=[],A?void i.notify({method:"failed",params:p}):(B=!0,j(),void i.bind("methodCall",function(a,b){h[b[0]].apply(h,b[1]).then(function(b){a.complete(b)}).fail(function(b){a.error(b.toString())}),a.delayReturn(!0)}))}}),j=function(){0===v&&y===!0&&i.notify({method:"ready"})},k=function(a){z.push(a)},k("getInterfaceList"),k("getRequiredCSSList"),k("getRequiredJSList"),k("getPath"),k("getTitle"),d.declareMethod=function(a,b){var c=q.declareMethod.apply(this,[a,b]);return k(a),c},d.declareService=q.declareService,d.declareAcquiredMethod=q.declareAcquiredMethod,d.allowPublicAcquisition=q.allowPublicAcquisition,l=!0),d.prototype.__acquired_method_dict={},D=d,a.addEventListener("DOMContentLoaded",C,!1)}),t.push(function(){return E}).push(function(a){function b(){return a}var c;for(d.ready(function(a){return u(a)}),t.push(b),c=0;c<d.__ready_list.length;c+=1)t.push(d.__ready_list[c]).push(b)}),b.self===b.top?t.fail(function(a){throw o(a),a}):t.then(function(){y=!0,B&&j()}).fail(function(a){throw l?(A=!0,p=a.toString(),o(a)):i.notify({method:"failed",params:a.toString()}),a})}var D,E,F,G,H={},I={},J={},K=0,L=new RegExp("^(?:[a-z]+:)?//|data:","i"),M=!1,N=[];b.addEventListener("error",function(a){N.push(a)}),b.addEventListener("beforeunload",function(){M=!0}),p.prototype=new Error,p.prototype.constructor=p,G=function(){function a(){var a,b=h.length;for(a=0;b>a;a+=1)h[a].cancel();h=[]}var b,d,e,f,g=this,h=[];return this instanceof G?(b=new c.Promise(function(b,c,h){d=function(b){return f?void 0:(g.isRejected=!0,g.rejectedReason=b,f=!0,a(),c(b))},e=h},a),g.cancel=function(){f||(f=!0,b.cancel(),b.fail(function(a){g.isRejected=!0,g.rejectedReason=a}))},g.then=function(){return b.then.apply(b,arguments)},g.fail=function(){return b.fail.apply(b,arguments)},void(g.monitor=function(a){if(f)throw new p;var b=(new c.Queue).push(function(){return a}).push(function(a){var b,c,d=h.length,e=[];for(c=0;d>c;c+=1)b=h[c],b.isFulfilled||b.isRejected||e.push(b);h=e},function(b){throw b instanceof c.CancellationError&&(a.isFulfilled&&a.isRejected||a.cancel()),d(b),b},function(a){return e(a),a});return h.push(b),this})):new G},G.prototype=Object.create(c.Promise.prototype),G.prototype.constructor=G,q.prototype.__title="",q.prototype.__interface_list=[],q.prototype.__path="",q.prototype.__html="",q.prototype.__required_css_list=[],q.prototype.__required_js_list=[],q.__ready_list=[s,t],q.ready=function(a){return this.__ready_list.push(a),this},q.__service_list=[],q.declareService=function(a){return this.__service_list.push(a),this},q.declareMethod=function(a,b){return this.prototype[a]=function(){var a=this,d=arguments;return(new c.Queue).push(function(){return b.apply(a,d)})},this},q.declareMethod("getInterfaceList",function(){return this.__interface_list}).declareMethod("getRequiredCSSList",function(){return this.__required_css_list}).declareMethod("getRequiredJSList",function(){return this.__required_js_list}).declareMethod("getPath",function(){return this.__path}).declareMethod("getTitle",function(){return this.__title}).declareMethod("getElement",function(){if(void 0===this.__element)throw new Error("No element defined");return this.__element}),q.declareAcquiredMethod=function(a,b){return this.prototype[a]=function(){var a=Array.prototype.slice.call(arguments,0),d=this;return(new c.Queue).push(function(){return d.__aq_parent(b,a)})},this},q.declareAcquiredMethod("aq_reportServiceError","reportServiceError"),q.allowPublicAcquisition=function(a,b){return this.prototype.__acquired_method_dict[a]=b,this},x.__ready_list=q.__ready_list.slice(),x.__service_list=q.__service_list.slice(),x.ready=q.ready,x.declareService=q.declareService,x.prototype=new q,x.prototype.constructor=x,z.__ready_list=q.__ready_list.slice(),z.ready=q.ready,z.__service_list=q.__service_list.slice(),z.declareService=q.declareService,z.prototype=new q,z.prototype.constructor=z,q.declareMethod("declareGadget",function(b,d){var e,f,g=this,h=E;return void 0===d&&(d={}),void 0===d.sandbox&&(d.sandbox="public"),b=F.getAbsoluteURL(b,this.__path),E=(new c.Queue).push(function(){return h}).push(void 0,function(){}).push(function(){var a;if("public"===d.sandbox)a=y;else if("iframe"===d.sandbox)a=A;else{if("dataurl"!==d.sandbox)throw new Error("Unsupported sandbox options '"+d.sandbox+"'");a=B}return a(b,d,g)}).push(function(a){return D=void 0,a}).push(void 0,function(a){throw D=void 0,a}),f=E,e=(new c.Queue).push(function(){return f}).push(function(c){function f(){return c}var h,i;for(h=0;h<c.constructor.__ready_list.length;h+=1)e.push(c.constructor.__ready_list[h]),e.push(f);if(i=d.scope,void 0===i)for(i="RJS_"+K,K+=1;g.__sub_gadget_dict.hasOwnProperty(i);)i="RJS_"+K,K+=1;return g.__sub_gadget_dict[i]=c,c.__element.setAttribute("data-gadget-scope",i),c.__element.setAttribute("data-gadget-url",b),c.__element.setAttribute("data-gadget-sandbox",d.sandbox),c.__element._gadget=c,a.contains(c.__element)&&e.push(u),e.push(f),c})}).declareMethod("getDeclaredGadget",function(a){if(!this.__sub_gadget_dict.hasOwnProperty(a))throw new Error("Gadget scope '"+a+"' is not known.");return this.__sub_gadget_dict[a]}).declareMethod("dropGadget",function(a){if(!this.__sub_gadget_dict.hasOwnProperty(a))throw new Error("Gadget scope '"+a+"' is not known.");delete this.__sub_gadget_dict[a]}),F=function(a){var c;if(a===b&&(c=D),void 0===c)throw new Error("Unknown selector '"+a+"'");return c},F.AcquisitionError=function(a){if(this.name="AcquisitionError",void 0!==a&&"string"!=typeof a)throw new TypeError("You must pass a string.");this.message=a||"Acquisition failed"},F.AcquisitionError.prototype=new Error,F.AcquisitionError.prototype.constructor=F.AcquisitionError,F.getAbsoluteURL=function(a,b){var c,e,f,g="<!doctype><html><head></head></html>";return a&&b&&!L.test(a)?(c=(new d).parseFromString(g,"text/html"),e=c.createElement("base"),f=c.createElement("link"),c.head.appendChild(e),c.head.appendChild(f),e.href=b,f.href=a,f.href):a},F.declareJS=function(b){var d;return d=I.hasOwnProperty(b)?c.resolve():new c.Promise(function(c,d){var e;e=a.createElement("script"),e.type="text/javascript",e.src=b,e.onload=function(){I[b]=null,c()},e.onerror=function(a){d(a)},a.head.appendChild(e)})},F.declareCSS=function(b){var d;return d=J.hasOwnProperty(b)?c.resolve():new c.Promise(function(c,d){var e;e=a.createElement("link"),e.rel="stylesheet",e.type="text/css",e.href=b,e.onload=function(){J[b]=null,c()},e.onerror=function(a){d(a)},a.head.appendChild(e)})},F.declareGadgetKlass=function(a){function b(b){var c,e,f;if(!H.hasOwnProperty(a)){c=function(){q.call(this)},c.__ready_list=q.__ready_list.slice(),c.__service_list=q.__service_list.slice(),c.declareMethod=q.declareMethod,c.declareAcquiredMethod=q.declareAcquiredMethod,c.allowPublicAcquisition=q.allowPublicAcquisition,c.ready=q.ready,c.declareService=q.declareService,c.prototype=new q,c.prototype.constructor=c,c.prototype.__path=a,c.prototype.__acquired_method_dict={},c.__template_element=(new d).parseFromString(b.responseText,"text/html"),f=F.parseGadgetHTMLDocument(c.__template_element,a);for(e in f)f.hasOwnProperty(e)&&(c.prototype["__"+e]=f[e]);H[a]=c}return H[a]}var e;return e=H.hasOwnProperty(a)?c.resolve(H[a]):(new c.Queue).push(function(){return m(a)}).push(function(a){return b(a)})},F.clearGadgetKlassList=function(){H={},I={},J={}},F.parseGadgetHTMLDocument=function(a,b){var c,d,e={title:"",interface_list:[],required_css_list:[],required_js_list:[]};if(!b||!L.test(b))throw new Error("The url should be absolute: "+b);if(9!==a.nodeType)throw new Error("The first parameter should be an HTMLDocument");if(e.title=a.title,null!==a.head)for(c=0;c<a.head.children.length;c+=1)d=a.head.children[c],null!==d.href&&("stylesheet"===d.rel?e.required_css_list.push(F.getAbsoluteURL(d.getAttribute("href"),b)):"SCRIPT"!==d.nodeName||"text/javascript"!==d.type&&d.type?"http://www.renderjs.org/rel/interface"===d.rel&&e.interface_list.push(F.getAbsoluteURL(d.getAttribute("href"),b)):e.required_js_list.push(F.getAbsoluteURL(d.getAttribute("src"),b)));return e},b.rJS=b.renderJS=F,b.__RenderJSGadget=q,b.__RenderJSEmbeddedGadget=x,b.__RenderJSIframeGadget=z,C()}(document,window,RSVP,DOMParser,Channel,MutationObserver,Node,FileReader,Blob,navigator,Event); \ No newline at end of file diff --git a/dist/renderjs-latest.js b/dist/renderjs-latest.js index 556331c40404b54b8e905310e86b1d725b219623..06dee4707220cd396e14f8469f22dd8634bbca30 100644 --- a/dist/renderjs-latest.js +++ b/dist/renderjs-latest.js @@ -673,7 +673,7 @@ if (typeof document.contains !== 'function') { * http://www.renderjs.org/documentation */ (function (document, window, RSVP, DOMParser, Channel, MutationObserver, - Node, FileReader, Blob) { + Node, FileReader, Blob, navigator, Event) { "use strict"; function readBlobAsDataURL(blob) { @@ -737,7 +737,19 @@ if (typeof document.contains !== 'function') { renderJS, Monitor, scope_increment = 0, - isAbsoluteOrDataURL = new RegExp('^(?:[a-z]+:)?//|data:', 'i'); + isAbsoluteOrDataURL = new RegExp('^(?:[a-z]+:)?//|data:', 'i'), + is_page_unloaded = false, + error_list = []; + + window.addEventListener('error', function (error) { + error_list.push(error); + }); + + window.addEventListener('beforeunload', function () { + // XXX If another listener cancel the page unload, + // it will not restore renderJS crash report + is_page_unloaded = true; + }); ///////////////////////////////////////////////////////////////// // Helper functions @@ -751,23 +763,107 @@ if (typeof document.contains !== 'function') { } function letsCrash(e) { - if (e.constructor === XMLHttpRequest) { - e = { - readyState: e.readyState, - status: e.status, - statusText: e.statusText, - response_headers: e.getAllResponseHeaders() - }; + var i, + body, + container, + paragraph, + link, + error; + if (is_page_unloaded) { + /*global console*/ + console.info('-- Error dropped, as page is unloaded'); + console.info(e); + return; + } + + error_list.push(e); + // Add error handling stack + error_list.push(new Error('stopping renderJS')); + + body = document.getElementsByTagName('body')[0]; + while (body.firstChild) { + body.removeChild(body.firstChild); } - if (e.constructor === Array || - e.constructor === String || - e.constructor === Object) { - try { - e = JSON.stringify(e); - } catch (ignore) { + + container = document.createElement("section"); + paragraph = document.createElement("h1"); + paragraph.textContent = 'Unhandled Error'; + container.appendChild(paragraph); + + paragraph = document.createElement("p"); + paragraph.textContent = 'Please report this error to the support team'; + container.appendChild(paragraph); + + paragraph = document.createElement("p"); + paragraph.textContent = 'Location: '; + link = document.createElement("a"); + link.href = link.textContent = window.location.toString(); + paragraph.appendChild(link); + container.appendChild(paragraph); + + paragraph = document.createElement("p"); + paragraph.textContent = 'User-agent: ' + navigator.userAgent; + container.appendChild(paragraph); + + body.appendChild(container); + + for (i = 0; i < error_list.length; i += 1) { + error = error_list[i]; + + if (error instanceof Event) { + error = { + string: error.toString(), + message: error.message, + type: error.type, + target: error.target + }; + if (error.target !== undefined) { + error_list.splice(i + 1, 0, error.target); + } } + + if (error instanceof XMLHttpRequest) { + error = { + message: error.toString(), + readyState: error.readyState, + status: error.status, + statusText: error.statusText, + response: error.response, + responseUrl: error.responseUrl, + response_headers: error.getAllResponseHeaders() + }; + } + if (error.constructor === Array || + error.constructor === String || + error.constructor === Object) { + try { + error = JSON.stringify(error); + } catch (ignore) { + } + } + + container = document.createElement("section"); + + paragraph = document.createElement("h2"); + paragraph.textContent = error.message || error; + container.appendChild(paragraph); + + if (error.fileName !== undefined) { + paragraph = document.createElement("p"); + paragraph.textContent = 'File: ' + + error.fileName + + ': ' + error.lineNumber; + container.appendChild(paragraph); + } + + if (error.stack !== undefined) { + paragraph = document.createElement("pre"); + paragraph.textContent = 'Stack: ' + error.stack; + container.appendChild(paragraph); + } + + body.appendChild(container); } - document.getElementsByTagName('body')[0].textContent = e; // XXX Do not crash the application if it fails // Where to write the error? /*global console*/ @@ -1273,7 +1369,13 @@ if (typeof document.contains !== 'function') { iframe_loading_deferred.promise, // Timeout to prevent non renderJS embeddable gadget // XXX Maybe using iframe.onload/onerror would be safer? - RSVP.timeout(5000) + new RSVP.Queue() + .push(function () { + return RSVP.timeout(5000); + }) + .push(undefined, function () { + throw new Error('Timeout while loading: ' + url); + }) ]); } @@ -2033,4 +2135,4 @@ if (typeof document.contains !== 'function') { bootstrap(); }(document, window, RSVP, DOMParser, Channel, MutationObserver, Node, - FileReader, Blob)); + FileReader, Blob, navigator, Event)); diff --git a/dist/renderjs-latest.min.js b/dist/renderjs-latest.min.js index 073cf158ff00dc7b96bd9da44d6269383dcb2129..c2881f4d178fbdf9f1b86014c22649a09c6c76f9 100644 --- a/dist/renderjs-latest.min.js +++ b/dist/renderjs-latest.min.js @@ -1 +1 @@ -var Channel=function(){"use strict";function a(a,b,c,d){function f(b){for(var c=0;c<b.length;c++)if(b[c].win===a)return!0;return!1}var g=!1;if("*"===b){for(var h in e)if(e.hasOwnProperty(h)&&"*"!==h&&"object"==typeof e[h][c]&&(g=f(e[h][c])))break}else e["*"]&&e["*"][c]&&(g=f(e["*"][c])),!g&&e[b]&&e[b][c]&&(g=f(e[b][c]));if(g)throw"A channel is already bound to the same window which overlaps with origin '"+b+"' and has scope '"+c+"'";"object"!=typeof e[b]&&(e[b]={}),"object"!=typeof e[b][c]&&(e[b][c]=[]),e[b][c].push({win:a,handler:d})}function b(a,b,c){for(var d=e[b][c],f=0;f<d.length;f++)d[f].win===a&&d.splice(f,1);0===e[b][c].length&&delete e[b][c]}function c(a){return Array.isArray?Array.isArray(a):-1!=a.constructor.toString().indexOf("Array")}var d=Math.floor(1000001*Math.random()),e={},f={},g=function(a){try{var b=JSON.parse(a.data);if("object"!=typeof b||null===b)throw"malformed"}catch(a){return}var c,d,g,h=a.source,i=a.origin;if("string"==typeof b.method){var j=b.method.split("::");2==j.length?(c=j[0],g=j[1]):g=b.method}if("undefined"!=typeof b.id&&(d=b.id),"string"==typeof g){var k=!1;if(e[i]&&e[i][c])for(var l=0;l<e[i][c].length;l++)if(e[i][c][l].win===h){e[i][c][l].handler(i,g,b),k=!0;break}if(!k&&e["*"]&&e["*"][c])for(var l=0;l<e["*"][c].length;l++)if(e["*"][c][l].win===h){e["*"][c][l].handler(i,g,b);break}}else"undefined"!=typeof d&&f[d]&&f[d](i,g,b)};return window.addEventListener?window.addEventListener("message",g,!1):window.attachEvent&&window.attachEvent("onmessage",g),{build:function(e){var g=function(a){if(e.debugOutput&&window.console&&window.console.log){try{"string"!=typeof a&&(a=JSON.stringify(a))}catch(b){}console.log("["+j+"] "+a)}};if(!window.postMessage)throw"jschannel cannot run this browser, no postMessage";if(!window.JSON||!window.JSON.stringify||!window.JSON.parse)throw"jschannel cannot run this browser, no JSON parsing/serialization";if("object"!=typeof e)throw"Channel build invoked without a proper object argument";if(!e.window||!e.window.postMessage)throw"Channel.build() called without a valid window argument";if(window===e.window)throw"target window is same as present window -- not allowed";var h=!1;if("string"==typeof e.origin){var i;"*"===e.origin?h=!0:null!==(i=e.origin.match(/^https?:\/\/(?:[-a-zA-Z0-9_\.])+(?::\d+)?/))&&(e.origin=i[0].toLowerCase(),h=!0)}if(!h)throw"Channel.build() called with an invalid origin";if("undefined"!=typeof e.scope){if("string"!=typeof e.scope)throw"scope, when specified, must be a string";if(e.scope.split("::").length>1)throw"scope may not contain double colons: '::'"}var j=function(){for(var a="",b="abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789",c=0;5>c;c++)a+=b.charAt(Math.floor(Math.random()*b.length));return a}(),k={},l={},m={},n=!1,o=[],p=function(a,b,c){var d=!1,e=!1;return{origin:b,invoke:function(b,d){if(!m[a])throw"attempting to invoke a callback of a nonexistent transaction: "+a;for(var e=!1,f=0;f<c.length;f++)if(b===c[f]){e=!0;break}if(!e)throw"request supports no such callback '"+b+"'";t({id:a,callback:b,params:d})},error:function(b,c){if(e=!0,!m[a])throw"error called for nonexistent message: "+a;delete m[a],t({id:a,error:b,message:c})},complete:function(b){if(e=!0,!m[a])throw"complete called for nonexistent message: "+a;delete m[a],t({id:a,result:b})},delayReturn:function(a){return"boolean"==typeof a&&(d=a===!0),d},completed:function(){return e}}},q=function(a,b,c){return window.setTimeout(function(){if(l[a]){var d="timeout ("+b+"ms) exceeded on method '"+c+"'";l[a].error("timeout_error",d),delete l[a],delete f[a]}},b)},r=function(a,b,d){if("function"==typeof e.gotMessageObserver)try{e.gotMessageObserver(a,d)}catch(h){g("gotMessageObserver() raised an exception: "+h.toString())}if(d.id&&b){if(k[b]){var i=p(d.id,a,d.callbacks?d.callbacks:[]);m[d.id]={};try{if(d.callbacks&&c(d.callbacks)&&d.callbacks.length>0)for(var j=0;j<d.callbacks.length;j++){for(var n=d.callbacks[j],o=d.params,q=n.split("/"),r=0;r<q.length-1;r++){var s=q[r];"object"!=typeof o[s]&&(o[s]={}),o=o[s]}o[q[q.length-1]]=function(){var a=n;return function(b){return i.invoke(a,b)}}()}var t=k[b](i,d.params);i.delayReturn()||i.completed()||i.complete(t)}catch(h){var u="runtime_error",v=null;if("string"==typeof h?v=h:"object"==typeof h&&(h&&c(h)&&2==h.length?(u=h[0],v=h[1]):"string"==typeof h.error&&(u=h.error,h.message?"string"==typeof h.message?v=h.message:h=h.message:v="")),null===v)try{v=JSON.stringify(h),"undefined"==typeof v&&(v=h.toString())}catch(w){v=h.toString()}i.error(u,v)}}}else d.id&&d.callback?l[d.id]&&l[d.id].callbacks&&l[d.id].callbacks[d.callback]?l[d.id].callbacks[d.callback](d.params):g("ignoring invalid callback, id:"+d.id+" ("+d.callback+")"):d.id?l[d.id]?(d.error?l[d.id].error(d.error,d.message):void 0!==d.result?l[d.id].success(d.result):l[d.id].success(),delete l[d.id],delete f[d.id]):g("ignoring invalid response: "+d.id):b&&k[b]&&k[b]({origin:a},d.params)};a(e.window,e.origin,"string"==typeof e.scope?e.scope:"",r);var s=function(a){return"string"==typeof e.scope&&e.scope.length&&(a=[e.scope,a].join("::")),a},t=function(a,b){if(!a)throw"postMessage called with null message";var c=n?"post ":"queue ";if(g(c+" message: "+JSON.stringify(a)),b||n){if("function"==typeof e.postMessageObserver)try{e.postMessageObserver(e.origin,a)}catch(d){g("postMessageObserver() raised an exception: "+d.toString())}e.window.postMessage(JSON.stringify(a),e.origin)}else o.push(a)},u=function(a,b){if(g("ready msg received"),n)throw"received ready message while in ready state. help!";for(j+="ping"===b?"-R":"-L",v.unbind("__ready"),n=!0,g("ready msg accepted."),"ping"===b&&v.notify({method:"__ready",params:"pong"});o.length;)t(o.pop());"function"==typeof e.onReady&&e.onReady(v)},v={unbind:function(a){if(k[a]){if(!delete k[a])throw"can't delete method: "+a;return!0}return!1},bind:function(a,b){if(!a||"string"!=typeof a)throw"'method' argument to bind must be string";if(!b||"function"!=typeof b)throw"callback missing from bind params";if(k[a])throw"method '"+a+"' is already bound!";return k[a]=b,this},call:function(a){if(!a)throw"missing arguments to call function";if(!a.method||"string"!=typeof a.method)throw"'method' argument to call must be string";if(!a.success||"function"!=typeof a.success)throw"'success' callback missing from call";var b={},c=[],e=function(a,d){if("object"==typeof d)for(var f in d)if(d.hasOwnProperty(f)){var g=a+(a.length?"/":"")+f;"function"==typeof d[f]?(b[g]=d[f],c.push(g),delete d[f]):"object"==typeof d[f]&&e(g,d[f])}};e("",a.params);var g={id:d,method:s(a.method),params:a.params};c.length&&(g.callbacks=c),a.timeout&&q(d,a.timeout,s(a.method)),l[d]={callbacks:b,error:a.error,success:a.success},f[d]=r,d++,t(g)},notify:function(a){if(!a)throw"missing arguments to notify function";if(!a.method||"string"!=typeof a.method)throw"'method' argument to notify must be string";t({method:s(a.method),params:a.params})},destroy:function(){b(e.window,e.origin,"string"==typeof e.scope?e.scope:""),window.removeEventListener?window.removeEventListener("message",r,!1):window.detachEvent&&window.detachEvent("onmessage",r),n=!1,k={},m={},l={},e.origin=null,o=[],g("channel destroyed"),j=""}};return v.bind("__ready",u),setTimeout(function(){t({method:s("__ready"),params:"ping"},!0)},0),v}}}();!function(a){"use strict";var b=a.prototype,c=b.parseFromString;try{if((new a).parseFromString("","text/html"))return}catch(d){}b.parseFromString=function(a,b){var d,e,f,g;return/^\s*text\/html\s*(?:;|$)/i.test(b)?(e=document.implementation.createHTMLDocument(""),f=e.documentElement,f.innerHTML=a,g=f.firstElementChild,1===f.childElementCount&&"html"===g.localName.toLowerCase()&&e.replaceChild(g,f),d=e):d=c.apply(this,arguments),d}}(DOMParser),"function"!=typeof document.contains&&(Document.prototype.contains=function(a){return a===this||a.parentNode===this?!0:this.documentElement.contains(a)}),function(a,b,c,d,e,f,g,h,i){"use strict";function j(a){var b=new h;return new c.Promise(function(c,d){b.addEventListener("load",function(a){c(a.target.result)}),b.addEventListener("error",d),b.readAsDataURL(a)},function(){b.abort()})}function k(a){function b(b,c){function d(){try{0===e.readyState?c(e):4===e.readyState&&(e.status<200||e.status>=300||!/^text\/html[;]?/.test(e.getResponseHeader("Content-Type")||"")?c(e):b(e))}catch(a){c(a)}}e=new XMLHttpRequest,e.open("GET",a),e.onreadystatechange=d,e.setRequestHeader("Accept","text/html"),e.withCredentials=!0,e.send()}function d(){void 0!==e&&e.readyState!==e.DONE&&e.abort()}var e;return new c.Promise(b,d)}function l(a){var b=a.indexOf("#");return b>0&&(a=a.substring(0,b)),a}function m(b){if(b.constructor===XMLHttpRequest&&(b={readyState:b.readyState,status:b.status,statusText:b.statusText,response_headers:b.getAllResponseHeaders()}),b.constructor===Array||b.constructor===String||b.constructor===Object)try{b=JSON.stringify(b)}catch(c){}a.getElementsByTagName("body")[0].textContent=b,console.error(b.stack),console.error(b)}function n(a){if(this.name="resolved",void 0!==a&&"string"!=typeof a)throw new TypeError("You must pass a string.");this.message=a||"Default Message"}function o(){return this instanceof o?void 0:new o}function p(a){void 0!==a.__monitor&&a.__monitor.cancel(),a.__monitor=new E,a.__monitor.fail(function(b){return b instanceof c.CancellationError?void 0:a.aq_reportServiceError(b)}).fail(function(a){return m(a)})}function q(a){a.__sub_gadget_dict={},p(a)}function r(a){var b,d,e,f,g,h=a.__element.querySelectorAll("[data-gadget-url]"),i=[];for(g=0;g<h.length;g+=1)b=h[g],d=b.getAttribute("data-gadget-scope"),e=b.getAttribute("data-gadget-url"),f=b.getAttribute("data-gadget-sandbox"),null!==e&&i.push(a.declareGadget(e,{element:b,scope:d||void 0,sandbox:f||void 0}));return c.all(i)}function s(a){a.__monitor.monitor((new c.Queue).push(function(){var b,c=a.constructor.__service_list;for(b=0;b<c.length;b+=1)a.__monitor.monitor(c[b].apply(a))}))}function t(a,b,d){var e,f,g=this;for(e in g.__sub_gadget_dict)g.__sub_gadget_dict.hasOwnProperty(e)&&g.__sub_gadget_dict[e]===a&&(f=e);return(new c.Queue).push(function(){var a=g.__acquired_method_dict||{};if(a.hasOwnProperty(b))return a[b].apply(g,[d,f]);throw new D.AcquisitionError("aq_dynamic is not defined")}).push(void 0,function(a){if(a instanceof D.AcquisitionError)return g.__aq_parent(b,d);throw a})}function u(a,b){a.__aq_parent=function(c,d){return t.apply(b,[a,c,d])}}function v(){return this instanceof v?void o.call(this):new v}function w(b,d,e){function f(a,b){return function(){return a(b)}}var g;return void 0===d.element&&(d.element=a.createElement("div")),(new c.Queue).push(function(){return D.declareGadgetKlass(b)}).push(function(a){var b,f=a.__template_element.body.childNodes;for(B=a,g=new a,g.__element=d.element,b=0;b<f.length;b+=1)g.__element.appendChild(f[b].cloneNode(!0));return u(g,e),c.all([g.getRequiredJSList(),g.getRequiredCSSList()])}).push(function(a){var b,d=new c.Queue;for(b=0;b<a[0].length;b+=1)d.push(f(D.declareJS,a[0][b]));for(b=0;b<a[1].length;b+=1)d.push(f(D.declareCSS,a[1][b]));return d}).push(function(){return g})}function x(){return this instanceof x?void o.call(this):new x}function y(b,d,f){var g,h,i=c.defer();if(void 0===d.element)throw new Error("DOM element is required to create Iframe Gadget "+b);if(!a.contains(d.element))throw new Error("The parent element is not attached to the DOM for "+b);return g=new x,u(g,f),h=a.createElement("iframe"),h.setAttribute("src",b),g.__path=b,g.__element=d.element,d.element.appendChild(h),g.__chan=e.build({window:h.contentWindow,origin:"*",scope:"renderJS"}),g.__chan.bind("declareMethod",function(a,b){return g[b]=function(){var a=arguments,d=new c.Promise(function(c,d){g.__chan.call({method:"methodCall",params:[b,Array.prototype.slice.call(a,0)],success:function(a){c(a)},error:function(a){d(a)}})});return(new c.Queue).push(function(){return d})},"OK"}),g.__chan.bind("ready",function(a){return i.resolve(g),"OK"}),g.__chan.bind("failed",function(a,b){return i.reject(b),"OK"}),g.__chan.bind("acquire",function(a,b){g.__aq_parent.apply(g,b).then(function(b){a.complete(b)}).fail(function(b){a.error(b.toString())}),a.delayReturn(!0)}),c.any([i.promise,c.timeout(5e3)])}function z(a,b,e){return(new c.Queue).push(function(){return k(a)}).push(function(b){var c,e=(new d).parseFromString(b.responseText,"text/html"),f=e.createElement("base");return f.href=a,e.head.insertBefore(f,e.head.firstChild),c=new i([e.documentElement.outerHTML],{type:"text/html;charset=UTF-8"}),j(c)}).push(function(a){return y(a,b,e)})}function A(){var d,h,i,j,k,n,q,r,t=l(b.location.href),w=new c.Queue,x=0,y=!1,z=[],A=!1,E=!1;if(F.hasOwnProperty(t))throw new Error("bootstrap should not be called twice");C=new c.Promise(function(l,w){function C(){var b,e,i=D.parseGadgetHTMLDocument(a,t);for(e in i)i.hasOwnProperty(e)&&(d.prototype["__"+e]=i[e]);for(d.__template_element=a.createElement("div"),h.__element=a.body,b=0;b<h.__element.childNodes.length;b+=1)d.__template_element.appendChild(h.__element.childNodes[b].cloneNode(!0));c.all([h.getRequiredJSList(),h.getRequiredCSSList()]).then(function(a){var b,c=a[0],d=a[1];for(b=0;b<c.length;b+=1)G[c[b]]=null;for(b=0;b<d.length;b+=1)H[d[b]]=null;B=void 0}).then(function(){var b=a.querySelector("body"),c=new f(function(b){var c,d,e,f,h,i;b.forEach(function(b){if("childList"===b.type){for(e=b.removedNodes.length,c=0;e>c;c+=1)if(h=b.removedNodes[c],h.nodeType===g.ELEMENT_NODE)for(h.hasAttribute("data-gadget-url")&&void 0!==h._gadget&&p(h._gadget),i=h.querySelectorAll("[data-gadget-url]"),f=i.length,d=0;f>d;d+=1)h=i[d],void 0!==h._gadget&&p(h._gadget);for(e=b.addedNodes.length,c=0;e>c;c+=1)if(h=b.addedNodes[c],h.nodeType===g.ELEMENT_NODE)for(h.hasAttribute("data-gadget-url")&&void 0!==h._gadget&&a.contains(h)&&s(h._gadget),i=h.querySelectorAll("[data-gadget-url]"),f=i.length,d=0;f>d;d+=1)h=i[d],a.contains(h)&&void 0!==h._gadget&&s(h._gadget)}})}),d={childList:!0,subtree:!0,attributes:!1,characterData:!1};return c.observe(b,d),h}).then(l,function(a){throw w(a),console.error(a),a})}q=new o,q.__acquired_method_dict={reportServiceError:function(a){m(a[0])}},q.__aq_parent=function(a){throw new D.AcquisitionError("No gadget provides "+a)},b.self===b.top?(d=function(){o.call(this)},d.declareMethod=o.declareMethod,d.declareAcquiredMethod=o.declareAcquiredMethod,d.allowPublicAcquisition=o.allowPublicAcquisition,d.__ready_list=o.__ready_list.slice(),d.ready=o.ready,d.__service_list=o.__service_list.slice(),d.declareService=o.declareService,d.prototype=new o,d.prototype.constructor=d,d.prototype.__path=t,F[t]=d,h=new F[t],u(h,q)):(d=v,d.__ready_list=o.__ready_list.slice(),d.__service_list=o.__service_list.slice(),d.prototype.__path=t,h=new v,u(h,q),i=e.build({window:b.parent,origin:"*",scope:"renderJS",onReady:function(){var a;for(n=!1,h.__aq_parent=d.prototype.__aq_parent=function(a,b,d){return new c.Promise(function(c,e){i.call({method:"acquire",params:[a,b],success:function(a){c(a)},error:function(a){e(a)},timeout:d})})},k=function(a){x+=1,i.call({method:"declareMethod",params:a,success:function(){x-=1,j()},error:function(){x-=1}})},a=0;a<z.length;a+=1)k(z[a]);return z=[],A?void i.notify({method:"failed",params:r}):(E=!0,j(),void i.bind("methodCall",function(a,b){h[b[0]].apply(h,b[1]).then(function(b){a.complete(b)}).fail(function(b){a.error(b.toString())}),a.delayReturn(!0)}))}}),j=function(){0===x&&y===!0&&i.notify({method:"ready"})},k=function(a){z.push(a)},k("getInterfaceList"),k("getRequiredCSSList"),k("getRequiredJSList"),k("getPath"),k("getTitle"),d.declareMethod=function(a,b){var c=o.declareMethod.apply(this,[a,b]);return k(a),c},d.declareService=o.declareService,d.declareAcquiredMethod=o.declareAcquiredMethod,d.allowPublicAcquisition=o.allowPublicAcquisition,n=!0),d.prototype.__acquired_method_dict={},B=d,a.addEventListener("DOMContentLoaded",C,!1)}),w.push(function(){return C}).push(function(a){function b(){return a}var c;for(d.ready(function(a){return s(a)}),w.push(b),c=0;c<d.__ready_list.length;c+=1)w.push(d.__ready_list[c]).push(b)}),b.self===b.top?w.fail(function(a){throw m(a),a}):w.then(function(){y=!0,E&&j()}).fail(function(a){throw n?(A=!0,r=a.toString(),m(a)):i.notify({method:"failed",params:a.toString()}),a})}var B,C,D,E,F={},G={},H={},I=0,J=new RegExp("^(?:[a-z]+:)?//|data:","i");n.prototype=new Error,n.prototype.constructor=n,E=function(){function a(){var a,b=h.length;for(a=0;b>a;a+=1)h[a].cancel();h=[]}var b,d,e,f,g=this,h=[];return this instanceof E?(b=new c.Promise(function(b,c,h){d=function(b){return f?void 0:(g.isRejected=!0,g.rejectedReason=b,f=!0,a(),c(b))},e=h},a),g.cancel=function(){f||(f=!0,b.cancel(),b.fail(function(a){g.isRejected=!0,g.rejectedReason=a}))},g.then=function(){return b.then.apply(b,arguments)},g.fail=function(){return b.fail.apply(b,arguments)},void(g.monitor=function(a){if(f)throw new n;var b=(new c.Queue).push(function(){return a}).push(function(a){var b,c,d=h.length,e=[];for(c=0;d>c;c+=1)b=h[c],b.isFulfilled||b.isRejected||e.push(b);h=e},function(b){throw b instanceof c.CancellationError&&(a.isFulfilled&&a.isRejected||a.cancel()),d(b),b},function(a){return e(a),a});return h.push(b),this})):new E},E.prototype=Object.create(c.Promise.prototype),E.prototype.constructor=E,o.prototype.__title="",o.prototype.__interface_list=[],o.prototype.__path="",o.prototype.__html="",o.prototype.__required_css_list=[],o.prototype.__required_js_list=[],o.__ready_list=[q,r],o.ready=function(a){return this.__ready_list.push(a),this},o.__service_list=[],o.declareService=function(a){return this.__service_list.push(a),this},o.declareMethod=function(a,b){return this.prototype[a]=function(){var a=this,d=arguments;return(new c.Queue).push(function(){return b.apply(a,d)})},this},o.declareMethod("getInterfaceList",function(){return this.__interface_list}).declareMethod("getRequiredCSSList",function(){return this.__required_css_list}).declareMethod("getRequiredJSList",function(){return this.__required_js_list}).declareMethod("getPath",function(){return this.__path}).declareMethod("getTitle",function(){return this.__title}).declareMethod("getElement",function(){if(void 0===this.__element)throw new Error("No element defined");return this.__element}),o.declareAcquiredMethod=function(a,b){return this.prototype[a]=function(){var a=Array.prototype.slice.call(arguments,0),d=this;return(new c.Queue).push(function(){return d.__aq_parent(b,a)})},this},o.declareAcquiredMethod("aq_reportServiceError","reportServiceError"),o.allowPublicAcquisition=function(a,b){return this.prototype.__acquired_method_dict[a]=b,this},v.__ready_list=o.__ready_list.slice(),v.__service_list=o.__service_list.slice(),v.ready=o.ready,v.declareService=o.declareService,v.prototype=new o,v.prototype.constructor=v,x.__ready_list=o.__ready_list.slice(),x.ready=o.ready,x.__service_list=o.__service_list.slice(),x.declareService=o.declareService,x.prototype=new o,x.prototype.constructor=x,o.declareMethod("declareGadget",function(b,d){var e,f,g=this,h=C;return void 0===d&&(d={}),void 0===d.sandbox&&(d.sandbox="public"),b=D.getAbsoluteURL(b,this.__path),C=(new c.Queue).push(function(){return h}).push(void 0,function(){}).push(function(){var a;if("public"===d.sandbox)a=w;else if("iframe"===d.sandbox)a=y;else{if("dataurl"!==d.sandbox)throw new Error("Unsupported sandbox options '"+d.sandbox+"'");a=z}return a(b,d,g)}).push(function(a){return B=void 0,a}).push(void 0,function(a){throw B=void 0,a}),f=C,e=(new c.Queue).push(function(){return f}).push(function(c){function f(){return c}var h,i;for(h=0;h<c.constructor.__ready_list.length;h+=1)e.push(c.constructor.__ready_list[h]),e.push(f);if(i=d.scope,void 0===i)for(i="RJS_"+I,I+=1;g.__sub_gadget_dict.hasOwnProperty(i);)i="RJS_"+I,I+=1;return g.__sub_gadget_dict[i]=c,c.__element.setAttribute("data-gadget-scope",i),c.__element.setAttribute("data-gadget-url",b),c.__element.setAttribute("data-gadget-sandbox",d.sandbox),c.__element._gadget=c,a.contains(c.__element)&&e.push(s),e.push(f),c})}).declareMethod("getDeclaredGadget",function(a){if(!this.__sub_gadget_dict.hasOwnProperty(a))throw new Error("Gadget scope '"+a+"' is not known.");return this.__sub_gadget_dict[a]}).declareMethod("dropGadget",function(a){if(!this.__sub_gadget_dict.hasOwnProperty(a))throw new Error("Gadget scope '"+a+"' is not known.");delete this.__sub_gadget_dict[a]}),D=function(a){var c;if(a===b&&(c=B),void 0===c)throw new Error("Unknown selector '"+a+"'");return c},D.AcquisitionError=function(a){if(this.name="AcquisitionError",void 0!==a&&"string"!=typeof a)throw new TypeError("You must pass a string.");this.message=a||"Acquisition failed"},D.AcquisitionError.prototype=new Error,D.AcquisitionError.prototype.constructor=D.AcquisitionError,D.getAbsoluteURL=function(a,b){var c,e,f,g="<!doctype><html><head></head></html>";return a&&b&&!J.test(a)?(c=(new d).parseFromString(g,"text/html"),e=c.createElement("base"),f=c.createElement("link"),c.head.appendChild(e),c.head.appendChild(f),e.href=b,f.href=a,f.href):a},D.declareJS=function(b){var d;return d=G.hasOwnProperty(b)?c.resolve():new c.Promise(function(c,d){var e;e=a.createElement("script"),e.type="text/javascript",e.src=b,e.onload=function(){G[b]=null,c()},e.onerror=function(a){d(a)},a.head.appendChild(e)})},D.declareCSS=function(b){var d;return d=H.hasOwnProperty(b)?c.resolve():new c.Promise(function(c,d){var e;e=a.createElement("link"),e.rel="stylesheet",e.type="text/css",e.href=b,e.onload=function(){H[b]=null,c()},e.onerror=function(a){d(a)},a.head.appendChild(e)})},D.declareGadgetKlass=function(a){function b(b){var c,e,f;if(!F.hasOwnProperty(a)){c=function(){o.call(this)},c.__ready_list=o.__ready_list.slice(),c.__service_list=o.__service_list.slice(),c.declareMethod=o.declareMethod,c.declareAcquiredMethod=o.declareAcquiredMethod,c.allowPublicAcquisition=o.allowPublicAcquisition,c.ready=o.ready,c.declareService=o.declareService,c.prototype=new o,c.prototype.constructor=c,c.prototype.__path=a,c.prototype.__acquired_method_dict={},c.__template_element=(new d).parseFromString(b.responseText,"text/html"),f=D.parseGadgetHTMLDocument(c.__template_element,a);for(e in f)f.hasOwnProperty(e)&&(c.prototype["__"+e]=f[e]);F[a]=c}return F[a]}var e;return e=F.hasOwnProperty(a)?c.resolve(F[a]):(new c.Queue).push(function(){return k(a)}).push(function(a){return b(a)})},D.clearGadgetKlassList=function(){F={},G={},H={}},D.parseGadgetHTMLDocument=function(a,b){var c,d,e={title:"",interface_list:[],required_css_list:[],required_js_list:[]};if(!b||!J.test(b))throw new Error("The url should be absolute: "+b);if(9!==a.nodeType)throw new Error("The first parameter should be an HTMLDocument");if(e.title=a.title,null!==a.head)for(c=0;c<a.head.children.length;c+=1)d=a.head.children[c],null!==d.href&&("stylesheet"===d.rel?e.required_css_list.push(D.getAbsoluteURL(d.getAttribute("href"),b)):"SCRIPT"!==d.nodeName||"text/javascript"!==d.type&&d.type?"http://www.renderjs.org/rel/interface"===d.rel&&e.interface_list.push(D.getAbsoluteURL(d.getAttribute("href"),b)):e.required_js_list.push(D.getAbsoluteURL(d.getAttribute("src"),b)));return e},b.rJS=b.renderJS=D,b.__RenderJSGadget=o,b.__RenderJSEmbeddedGadget=v,b.__RenderJSIframeGadget=x,A()}(document,window,RSVP,DOMParser,Channel,MutationObserver,Node,FileReader,Blob); \ No newline at end of file +var Channel=function(){"use strict";function a(a,b,c,d){function f(b){for(var c=0;c<b.length;c++)if(b[c].win===a)return!0;return!1}var g=!1;if("*"===b){for(var h in e)if(e.hasOwnProperty(h)&&"*"!==h&&"object"==typeof e[h][c]&&(g=f(e[h][c])))break}else e["*"]&&e["*"][c]&&(g=f(e["*"][c])),!g&&e[b]&&e[b][c]&&(g=f(e[b][c]));if(g)throw"A channel is already bound to the same window which overlaps with origin '"+b+"' and has scope '"+c+"'";"object"!=typeof e[b]&&(e[b]={}),"object"!=typeof e[b][c]&&(e[b][c]=[]),e[b][c].push({win:a,handler:d})}function b(a,b,c){for(var d=e[b][c],f=0;f<d.length;f++)d[f].win===a&&d.splice(f,1);0===e[b][c].length&&delete e[b][c]}function c(a){return Array.isArray?Array.isArray(a):-1!=a.constructor.toString().indexOf("Array")}var d=Math.floor(1000001*Math.random()),e={},f={},g=function(a){try{var b=JSON.parse(a.data);if("object"!=typeof b||null===b)throw"malformed"}catch(a){return}var c,d,g,h=a.source,i=a.origin;if("string"==typeof b.method){var j=b.method.split("::");2==j.length?(c=j[0],g=j[1]):g=b.method}if("undefined"!=typeof b.id&&(d=b.id),"string"==typeof g){var k=!1;if(e[i]&&e[i][c])for(var l=0;l<e[i][c].length;l++)if(e[i][c][l].win===h){e[i][c][l].handler(i,g,b),k=!0;break}if(!k&&e["*"]&&e["*"][c])for(var l=0;l<e["*"][c].length;l++)if(e["*"][c][l].win===h){e["*"][c][l].handler(i,g,b);break}}else"undefined"!=typeof d&&f[d]&&f[d](i,g,b)};return window.addEventListener?window.addEventListener("message",g,!1):window.attachEvent&&window.attachEvent("onmessage",g),{build:function(e){var g=function(a){if(e.debugOutput&&window.console&&window.console.log){try{"string"!=typeof a&&(a=JSON.stringify(a))}catch(b){}console.log("["+j+"] "+a)}};if(!window.postMessage)throw"jschannel cannot run this browser, no postMessage";if(!window.JSON||!window.JSON.stringify||!window.JSON.parse)throw"jschannel cannot run this browser, no JSON parsing/serialization";if("object"!=typeof e)throw"Channel build invoked without a proper object argument";if(!e.window||!e.window.postMessage)throw"Channel.build() called without a valid window argument";if(window===e.window)throw"target window is same as present window -- not allowed";var h=!1;if("string"==typeof e.origin){var i;"*"===e.origin?h=!0:null!==(i=e.origin.match(/^https?:\/\/(?:[-a-zA-Z0-9_\.])+(?::\d+)?/))&&(e.origin=i[0].toLowerCase(),h=!0)}if(!h)throw"Channel.build() called with an invalid origin";if("undefined"!=typeof e.scope){if("string"!=typeof e.scope)throw"scope, when specified, must be a string";if(e.scope.split("::").length>1)throw"scope may not contain double colons: '::'"}var j=function(){for(var a="",b="abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789",c=0;5>c;c++)a+=b.charAt(Math.floor(Math.random()*b.length));return a}(),k={},l={},m={},n=!1,o=[],p=function(a,b,c){var d=!1,e=!1;return{origin:b,invoke:function(b,d){if(!m[a])throw"attempting to invoke a callback of a nonexistent transaction: "+a;for(var e=!1,f=0;f<c.length;f++)if(b===c[f]){e=!0;break}if(!e)throw"request supports no such callback '"+b+"'";t({id:a,callback:b,params:d})},error:function(b,c){if(e=!0,!m[a])throw"error called for nonexistent message: "+a;delete m[a],t({id:a,error:b,message:c})},complete:function(b){if(e=!0,!m[a])throw"complete called for nonexistent message: "+a;delete m[a],t({id:a,result:b})},delayReturn:function(a){return"boolean"==typeof a&&(d=a===!0),d},completed:function(){return e}}},q=function(a,b,c){return window.setTimeout(function(){if(l[a]){var d="timeout ("+b+"ms) exceeded on method '"+c+"'";l[a].error("timeout_error",d),delete l[a],delete f[a]}},b)},r=function(a,b,d){if("function"==typeof e.gotMessageObserver)try{e.gotMessageObserver(a,d)}catch(h){g("gotMessageObserver() raised an exception: "+h.toString())}if(d.id&&b){if(k[b]){var i=p(d.id,a,d.callbacks?d.callbacks:[]);m[d.id]={};try{if(d.callbacks&&c(d.callbacks)&&d.callbacks.length>0)for(var j=0;j<d.callbacks.length;j++){for(var n=d.callbacks[j],o=d.params,q=n.split("/"),r=0;r<q.length-1;r++){var s=q[r];"object"!=typeof o[s]&&(o[s]={}),o=o[s]}o[q[q.length-1]]=function(){var a=n;return function(b){return i.invoke(a,b)}}()}var t=k[b](i,d.params);i.delayReturn()||i.completed()||i.complete(t)}catch(h){var u="runtime_error",v=null;if("string"==typeof h?v=h:"object"==typeof h&&(h&&c(h)&&2==h.length?(u=h[0],v=h[1]):"string"==typeof h.error&&(u=h.error,h.message?"string"==typeof h.message?v=h.message:h=h.message:v="")),null===v)try{v=JSON.stringify(h),"undefined"==typeof v&&(v=h.toString())}catch(w){v=h.toString()}i.error(u,v)}}}else d.id&&d.callback?l[d.id]&&l[d.id].callbacks&&l[d.id].callbacks[d.callback]?l[d.id].callbacks[d.callback](d.params):g("ignoring invalid callback, id:"+d.id+" ("+d.callback+")"):d.id?l[d.id]?(d.error?l[d.id].error(d.error,d.message):void 0!==d.result?l[d.id].success(d.result):l[d.id].success(),delete l[d.id],delete f[d.id]):g("ignoring invalid response: "+d.id):b&&k[b]&&k[b]({origin:a},d.params)};a(e.window,e.origin,"string"==typeof e.scope?e.scope:"",r);var s=function(a){return"string"==typeof e.scope&&e.scope.length&&(a=[e.scope,a].join("::")),a},t=function(a,b){if(!a)throw"postMessage called with null message";var c=n?"post ":"queue ";if(g(c+" message: "+JSON.stringify(a)),b||n){if("function"==typeof e.postMessageObserver)try{e.postMessageObserver(e.origin,a)}catch(d){g("postMessageObserver() raised an exception: "+d.toString())}e.window.postMessage(JSON.stringify(a),e.origin)}else o.push(a)},u=function(a,b){if(g("ready msg received"),n)throw"received ready message while in ready state. help!";for(j+="ping"===b?"-R":"-L",v.unbind("__ready"),n=!0,g("ready msg accepted."),"ping"===b&&v.notify({method:"__ready",params:"pong"});o.length;)t(o.pop());"function"==typeof e.onReady&&e.onReady(v)},v={unbind:function(a){if(k[a]){if(!delete k[a])throw"can't delete method: "+a;return!0}return!1},bind:function(a,b){if(!a||"string"!=typeof a)throw"'method' argument to bind must be string";if(!b||"function"!=typeof b)throw"callback missing from bind params";if(k[a])throw"method '"+a+"' is already bound!";return k[a]=b,this},call:function(a){if(!a)throw"missing arguments to call function";if(!a.method||"string"!=typeof a.method)throw"'method' argument to call must be string";if(!a.success||"function"!=typeof a.success)throw"'success' callback missing from call";var b={},c=[],e=function(a,d){if("object"==typeof d)for(var f in d)if(d.hasOwnProperty(f)){var g=a+(a.length?"/":"")+f;"function"==typeof d[f]?(b[g]=d[f],c.push(g),delete d[f]):"object"==typeof d[f]&&e(g,d[f])}};e("",a.params);var g={id:d,method:s(a.method),params:a.params};c.length&&(g.callbacks=c),a.timeout&&q(d,a.timeout,s(a.method)),l[d]={callbacks:b,error:a.error,success:a.success},f[d]=r,d++,t(g)},notify:function(a){if(!a)throw"missing arguments to notify function";if(!a.method||"string"!=typeof a.method)throw"'method' argument to notify must be string";t({method:s(a.method),params:a.params})},destroy:function(){b(e.window,e.origin,"string"==typeof e.scope?e.scope:""),window.removeEventListener?window.removeEventListener("message",r,!1):window.detachEvent&&window.detachEvent("onmessage",r),n=!1,k={},m={},l={},e.origin=null,o=[],g("channel destroyed"),j=""}};return v.bind("__ready",u),setTimeout(function(){t({method:s("__ready"),params:"ping"},!0)},0),v}}}();!function(a){"use strict";var b=a.prototype,c=b.parseFromString;try{if((new a).parseFromString("","text/html"))return}catch(d){}b.parseFromString=function(a,b){var d,e,f,g;return/^\s*text\/html\s*(?:;|$)/i.test(b)?(e=document.implementation.createHTMLDocument(""),f=e.documentElement,f.innerHTML=a,g=f.firstElementChild,1===f.childElementCount&&"html"===g.localName.toLowerCase()&&e.replaceChild(g,f),d=e):d=c.apply(this,arguments),d}}(DOMParser),"function"!=typeof document.contains&&(Document.prototype.contains=function(a){return a===this||a.parentNode===this?!0:this.documentElement.contains(a)}),function(a,b,c,d,e,f,g,h,i,j,k){"use strict";function l(a){var b=new h;return new c.Promise(function(c,d){b.addEventListener("load",function(a){c(a.target.result)}),b.addEventListener("error",d),b.readAsDataURL(a)},function(){b.abort()})}function m(a){function b(b,c){function d(){try{0===e.readyState?c(e):4===e.readyState&&(e.status<200||e.status>=300||!/^text\/html[;]?/.test(e.getResponseHeader("Content-Type")||"")?c(e):b(e))}catch(a){c(a)}}e=new XMLHttpRequest,e.open("GET",a),e.onreadystatechange=d,e.setRequestHeader("Accept","text/html"),e.withCredentials=!0,e.send()}function d(){void 0!==e&&e.readyState!==e.DONE&&e.abort()}var e;return new c.Promise(b,d)}function n(a){var b=a.indexOf("#");return b>0&&(a=a.substring(0,b)),a}function o(c){var d,e,f,g,h,i;if(M)return console.info("-- Error dropped, as page is unloaded"),void console.info(c);for(N.push(c),N.push(new Error("stopping renderJS")),e=a.getElementsByTagName("body")[0];e.firstChild;)e.removeChild(e.firstChild);for(f=a.createElement("section"),g=a.createElement("h1"),g.textContent="Unhandled Error",f.appendChild(g),g=a.createElement("p"),g.textContent="Please report this error to the support team",f.appendChild(g),g=a.createElement("p"),g.textContent="Location: ",h=a.createElement("a"),h.href=h.textContent=b.location.toString(),g.appendChild(h),f.appendChild(g),g=a.createElement("p"),g.textContent="User-agent: "+j.userAgent,f.appendChild(g),e.appendChild(f),d=0;d<N.length;d+=1){if(i=N[d],i instanceof k&&(i={string:i.toString(),message:i.message,type:i.type,target:i.target},void 0!==i.target&&N.splice(d+1,0,i.target)),i instanceof XMLHttpRequest&&(i={message:i.toString(),readyState:i.readyState,status:i.status,statusText:i.statusText,response:i.response,responseUrl:i.responseUrl,response_headers:i.getAllResponseHeaders()}),i.constructor===Array||i.constructor===String||i.constructor===Object)try{i=JSON.stringify(i)}catch(l){}f=a.createElement("section"),g=a.createElement("h2"),g.textContent=i.message||i,f.appendChild(g),void 0!==i.fileName&&(g=a.createElement("p"),g.textContent="File: "+i.fileName+": "+i.lineNumber,f.appendChild(g)),void 0!==i.stack&&(g=a.createElement("pre"),g.textContent="Stack: "+i.stack,f.appendChild(g)),e.appendChild(f)}console.error(c.stack),console.error(c)}function p(a){if(this.name="resolved",void 0!==a&&"string"!=typeof a)throw new TypeError("You must pass a string.");this.message=a||"Default Message"}function q(){return this instanceof q?void 0:new q}function r(a){void 0!==a.__monitor&&a.__monitor.cancel(),a.__monitor=new G,a.__monitor.fail(function(b){return b instanceof c.CancellationError?void 0:a.aq_reportServiceError(b)}).fail(function(a){return o(a)})}function s(a){a.__sub_gadget_dict={},r(a)}function t(a){var b,d,e,f,g,h=a.__element.querySelectorAll("[data-gadget-url]"),i=[];for(g=0;g<h.length;g+=1)b=h[g],d=b.getAttribute("data-gadget-scope"),e=b.getAttribute("data-gadget-url"),f=b.getAttribute("data-gadget-sandbox"),null!==e&&i.push(a.declareGadget(e,{element:b,scope:d||void 0,sandbox:f||void 0}));return c.all(i)}function u(a){a.__monitor.monitor((new c.Queue).push(function(){var b,c=a.constructor.__service_list;for(b=0;b<c.length;b+=1)a.__monitor.monitor(c[b].apply(a))}))}function v(a,b,d){var e,f,g=this;for(e in g.__sub_gadget_dict)g.__sub_gadget_dict.hasOwnProperty(e)&&g.__sub_gadget_dict[e]===a&&(f=e);return(new c.Queue).push(function(){var a=g.__acquired_method_dict||{};if(a.hasOwnProperty(b))return a[b].apply(g,[d,f]);throw new F.AcquisitionError("aq_dynamic is not defined")}).push(void 0,function(a){if(a instanceof F.AcquisitionError)return g.__aq_parent(b,d);throw a})}function w(a,b){a.__aq_parent=function(c,d){return v.apply(b,[a,c,d])}}function x(){return this instanceof x?void q.call(this):new x}function y(b,d,e){function f(a,b){return function(){return a(b)}}var g;return void 0===d.element&&(d.element=a.createElement("div")),(new c.Queue).push(function(){return F.declareGadgetKlass(b)}).push(function(a){var b,f=a.__template_element.body.childNodes;for(D=a,g=new a,g.__element=d.element,b=0;b<f.length;b+=1)g.__element.appendChild(f[b].cloneNode(!0));return w(g,e),c.all([g.getRequiredJSList(),g.getRequiredCSSList()])}).push(function(a){var b,d=new c.Queue;for(b=0;b<a[0].length;b+=1)d.push(f(F.declareJS,a[0][b]));for(b=0;b<a[1].length;b+=1)d.push(f(F.declareCSS,a[1][b]));return d}).push(function(){return g})}function z(){return this instanceof z?void q.call(this):new z}function A(b,d,f){var g,h,i=c.defer();if(void 0===d.element)throw new Error("DOM element is required to create Iframe Gadget "+b);if(!a.contains(d.element))throw new Error("The parent element is not attached to the DOM for "+b);return g=new z,w(g,f),h=a.createElement("iframe"),h.setAttribute("src",b),g.__path=b,g.__element=d.element,d.element.appendChild(h),g.__chan=e.build({window:h.contentWindow,origin:"*",scope:"renderJS"}),g.__chan.bind("declareMethod",function(a,b){return g[b]=function(){var a=arguments,d=new c.Promise(function(c,d){g.__chan.call({method:"methodCall",params:[b,Array.prototype.slice.call(a,0)],success:function(a){c(a)},error:function(a){d(a)}})});return(new c.Queue).push(function(){return d})},"OK"}),g.__chan.bind("ready",function(a){return i.resolve(g),"OK"}),g.__chan.bind("failed",function(a,b){return i.reject(b),"OK"}),g.__chan.bind("acquire",function(a,b){g.__aq_parent.apply(g,b).then(function(b){a.complete(b)}).fail(function(b){a.error(b.toString())}),a.delayReturn(!0)}),c.any([i.promise,(new c.Queue).push(function(){return c.timeout(5e3)}).push(void 0,function(){throw new Error("Timeout while loading: "+b)})])}function B(a,b,e){return(new c.Queue).push(function(){return m(a)}).push(function(b){var c,e=(new d).parseFromString(b.responseText,"text/html"),f=e.createElement("base");return f.href=a,e.head.insertBefore(f,e.head.firstChild),c=new i([e.documentElement.outerHTML],{type:"text/html;charset=UTF-8"}),l(c)}).push(function(a){return A(a,b,e)})}function C(){var d,h,i,j,k,l,m,p,s=n(b.location.href),t=new c.Queue,v=0,y=!1,z=[],A=!1,B=!1;if(H.hasOwnProperty(s))throw new Error("bootstrap should not be called twice");E=new c.Promise(function(n,t){function C(){var b,e,i=F.parseGadgetHTMLDocument(a,s);for(e in i)i.hasOwnProperty(e)&&(d.prototype["__"+e]=i[e]);for(d.__template_element=a.createElement("div"),h.__element=a.body,b=0;b<h.__element.childNodes.length;b+=1)d.__template_element.appendChild(h.__element.childNodes[b].cloneNode(!0));c.all([h.getRequiredJSList(),h.getRequiredCSSList()]).then(function(a){var b,c=a[0],d=a[1];for(b=0;b<c.length;b+=1)I[c[b]]=null;for(b=0;b<d.length;b+=1)J[d[b]]=null;D=void 0}).then(function(){var b=a.querySelector("body"),c=new f(function(b){var c,d,e,f,h,i;b.forEach(function(b){if("childList"===b.type){for(e=b.removedNodes.length,c=0;e>c;c+=1)if(h=b.removedNodes[c],h.nodeType===g.ELEMENT_NODE)for(h.hasAttribute("data-gadget-url")&&void 0!==h._gadget&&r(h._gadget),i=h.querySelectorAll("[data-gadget-url]"),f=i.length,d=0;f>d;d+=1)h=i[d],void 0!==h._gadget&&r(h._gadget);for(e=b.addedNodes.length,c=0;e>c;c+=1)if(h=b.addedNodes[c],h.nodeType===g.ELEMENT_NODE)for(h.hasAttribute("data-gadget-url")&&void 0!==h._gadget&&a.contains(h)&&u(h._gadget),i=h.querySelectorAll("[data-gadget-url]"),f=i.length,d=0;f>d;d+=1)h=i[d],a.contains(h)&&void 0!==h._gadget&&u(h._gadget)}})}),d={childList:!0,subtree:!0,attributes:!1,characterData:!1};return c.observe(b,d),h}).then(n,function(a){throw t(a),console.error(a),a})}m=new q,m.__acquired_method_dict={reportServiceError:function(a){o(a[0])}},m.__aq_parent=function(a){throw new F.AcquisitionError("No gadget provides "+a)},b.self===b.top?(d=function(){q.call(this)},d.declareMethod=q.declareMethod,d.declareAcquiredMethod=q.declareAcquiredMethod,d.allowPublicAcquisition=q.allowPublicAcquisition,d.__ready_list=q.__ready_list.slice(),d.ready=q.ready,d.__service_list=q.__service_list.slice(),d.declareService=q.declareService,d.prototype=new q,d.prototype.constructor=d,d.prototype.__path=s,H[s]=d,h=new H[s],w(h,m)):(d=x,d.__ready_list=q.__ready_list.slice(),d.__service_list=q.__service_list.slice(),d.prototype.__path=s,h=new x,w(h,m),i=e.build({window:b.parent,origin:"*",scope:"renderJS",onReady:function(){var a;for(l=!1,h.__aq_parent=d.prototype.__aq_parent=function(a,b,d){return new c.Promise(function(c,e){i.call({method:"acquire",params:[a,b],success:function(a){c(a)},error:function(a){e(a)},timeout:d})})},k=function(a){v+=1,i.call({method:"declareMethod",params:a,success:function(){v-=1,j()},error:function(){v-=1}})},a=0;a<z.length;a+=1)k(z[a]);return z=[],A?void i.notify({method:"failed",params:p}):(B=!0,j(),void i.bind("methodCall",function(a,b){h[b[0]].apply(h,b[1]).then(function(b){a.complete(b)}).fail(function(b){a.error(b.toString())}),a.delayReturn(!0)}))}}),j=function(){0===v&&y===!0&&i.notify({method:"ready"})},k=function(a){z.push(a)},k("getInterfaceList"),k("getRequiredCSSList"),k("getRequiredJSList"),k("getPath"),k("getTitle"),d.declareMethod=function(a,b){var c=q.declareMethod.apply(this,[a,b]);return k(a),c},d.declareService=q.declareService,d.declareAcquiredMethod=q.declareAcquiredMethod,d.allowPublicAcquisition=q.allowPublicAcquisition,l=!0),d.prototype.__acquired_method_dict={},D=d,a.addEventListener("DOMContentLoaded",C,!1)}),t.push(function(){return E}).push(function(a){function b(){return a}var c;for(d.ready(function(a){return u(a)}),t.push(b),c=0;c<d.__ready_list.length;c+=1)t.push(d.__ready_list[c]).push(b)}),b.self===b.top?t.fail(function(a){throw o(a),a}):t.then(function(){y=!0,B&&j()}).fail(function(a){throw l?(A=!0,p=a.toString(),o(a)):i.notify({method:"failed",params:a.toString()}),a})}var D,E,F,G,H={},I={},J={},K=0,L=new RegExp("^(?:[a-z]+:)?//|data:","i"),M=!1,N=[];b.addEventListener("error",function(a){N.push(a)}),b.addEventListener("beforeunload",function(){M=!0}),p.prototype=new Error,p.prototype.constructor=p,G=function(){function a(){var a,b=h.length;for(a=0;b>a;a+=1)h[a].cancel();h=[]}var b,d,e,f,g=this,h=[];return this instanceof G?(b=new c.Promise(function(b,c,h){d=function(b){return f?void 0:(g.isRejected=!0,g.rejectedReason=b,f=!0,a(),c(b))},e=h},a),g.cancel=function(){f||(f=!0,b.cancel(),b.fail(function(a){g.isRejected=!0,g.rejectedReason=a}))},g.then=function(){return b.then.apply(b,arguments)},g.fail=function(){return b.fail.apply(b,arguments)},void(g.monitor=function(a){if(f)throw new p;var b=(new c.Queue).push(function(){return a}).push(function(a){var b,c,d=h.length,e=[];for(c=0;d>c;c+=1)b=h[c],b.isFulfilled||b.isRejected||e.push(b);h=e},function(b){throw b instanceof c.CancellationError&&(a.isFulfilled&&a.isRejected||a.cancel()),d(b),b},function(a){return e(a),a});return h.push(b),this})):new G},G.prototype=Object.create(c.Promise.prototype),G.prototype.constructor=G,q.prototype.__title="",q.prototype.__interface_list=[],q.prototype.__path="",q.prototype.__html="",q.prototype.__required_css_list=[],q.prototype.__required_js_list=[],q.__ready_list=[s,t],q.ready=function(a){return this.__ready_list.push(a),this},q.__service_list=[],q.declareService=function(a){return this.__service_list.push(a),this},q.declareMethod=function(a,b){return this.prototype[a]=function(){var a=this,d=arguments;return(new c.Queue).push(function(){return b.apply(a,d)})},this},q.declareMethod("getInterfaceList",function(){return this.__interface_list}).declareMethod("getRequiredCSSList",function(){return this.__required_css_list}).declareMethod("getRequiredJSList",function(){return this.__required_js_list}).declareMethod("getPath",function(){return this.__path}).declareMethod("getTitle",function(){return this.__title}).declareMethod("getElement",function(){if(void 0===this.__element)throw new Error("No element defined");return this.__element}),q.declareAcquiredMethod=function(a,b){return this.prototype[a]=function(){var a=Array.prototype.slice.call(arguments,0),d=this;return(new c.Queue).push(function(){return d.__aq_parent(b,a)})},this},q.declareAcquiredMethod("aq_reportServiceError","reportServiceError"),q.allowPublicAcquisition=function(a,b){return this.prototype.__acquired_method_dict[a]=b,this},x.__ready_list=q.__ready_list.slice(),x.__service_list=q.__service_list.slice(),x.ready=q.ready,x.declareService=q.declareService,x.prototype=new q,x.prototype.constructor=x,z.__ready_list=q.__ready_list.slice(),z.ready=q.ready,z.__service_list=q.__service_list.slice(),z.declareService=q.declareService,z.prototype=new q,z.prototype.constructor=z,q.declareMethod("declareGadget",function(b,d){var e,f,g=this,h=E;return void 0===d&&(d={}),void 0===d.sandbox&&(d.sandbox="public"),b=F.getAbsoluteURL(b,this.__path),E=(new c.Queue).push(function(){return h}).push(void 0,function(){}).push(function(){var a;if("public"===d.sandbox)a=y;else if("iframe"===d.sandbox)a=A;else{if("dataurl"!==d.sandbox)throw new Error("Unsupported sandbox options '"+d.sandbox+"'");a=B}return a(b,d,g)}).push(function(a){return D=void 0,a}).push(void 0,function(a){throw D=void 0,a}),f=E,e=(new c.Queue).push(function(){return f}).push(function(c){function f(){return c}var h,i;for(h=0;h<c.constructor.__ready_list.length;h+=1)e.push(c.constructor.__ready_list[h]),e.push(f);if(i=d.scope,void 0===i)for(i="RJS_"+K,K+=1;g.__sub_gadget_dict.hasOwnProperty(i);)i="RJS_"+K,K+=1;return g.__sub_gadget_dict[i]=c,c.__element.setAttribute("data-gadget-scope",i),c.__element.setAttribute("data-gadget-url",b),c.__element.setAttribute("data-gadget-sandbox",d.sandbox),c.__element._gadget=c,a.contains(c.__element)&&e.push(u),e.push(f),c})}).declareMethod("getDeclaredGadget",function(a){if(!this.__sub_gadget_dict.hasOwnProperty(a))throw new Error("Gadget scope '"+a+"' is not known.");return this.__sub_gadget_dict[a]}).declareMethod("dropGadget",function(a){if(!this.__sub_gadget_dict.hasOwnProperty(a))throw new Error("Gadget scope '"+a+"' is not known.");delete this.__sub_gadget_dict[a]}),F=function(a){var c;if(a===b&&(c=D),void 0===c)throw new Error("Unknown selector '"+a+"'");return c},F.AcquisitionError=function(a){if(this.name="AcquisitionError",void 0!==a&&"string"!=typeof a)throw new TypeError("You must pass a string.");this.message=a||"Acquisition failed"},F.AcquisitionError.prototype=new Error,F.AcquisitionError.prototype.constructor=F.AcquisitionError,F.getAbsoluteURL=function(a,b){var c,e,f,g="<!doctype><html><head></head></html>";return a&&b&&!L.test(a)?(c=(new d).parseFromString(g,"text/html"),e=c.createElement("base"),f=c.createElement("link"),c.head.appendChild(e),c.head.appendChild(f),e.href=b,f.href=a,f.href):a},F.declareJS=function(b){var d;return d=I.hasOwnProperty(b)?c.resolve():new c.Promise(function(c,d){var e;e=a.createElement("script"),e.type="text/javascript",e.src=b,e.onload=function(){I[b]=null,c()},e.onerror=function(a){d(a)},a.head.appendChild(e)})},F.declareCSS=function(b){var d;return d=J.hasOwnProperty(b)?c.resolve():new c.Promise(function(c,d){var e;e=a.createElement("link"),e.rel="stylesheet",e.type="text/css",e.href=b,e.onload=function(){J[b]=null,c()},e.onerror=function(a){d(a)},a.head.appendChild(e)})},F.declareGadgetKlass=function(a){function b(b){var c,e,f;if(!H.hasOwnProperty(a)){c=function(){q.call(this)},c.__ready_list=q.__ready_list.slice(),c.__service_list=q.__service_list.slice(),c.declareMethod=q.declareMethod,c.declareAcquiredMethod=q.declareAcquiredMethod,c.allowPublicAcquisition=q.allowPublicAcquisition,c.ready=q.ready,c.declareService=q.declareService,c.prototype=new q,c.prototype.constructor=c,c.prototype.__path=a,c.prototype.__acquired_method_dict={},c.__template_element=(new d).parseFromString(b.responseText,"text/html"),f=F.parseGadgetHTMLDocument(c.__template_element,a);for(e in f)f.hasOwnProperty(e)&&(c.prototype["__"+e]=f[e]);H[a]=c}return H[a]}var e;return e=H.hasOwnProperty(a)?c.resolve(H[a]):(new c.Queue).push(function(){return m(a)}).push(function(a){return b(a)})},F.clearGadgetKlassList=function(){H={},I={},J={}},F.parseGadgetHTMLDocument=function(a,b){var c,d,e={title:"",interface_list:[],required_css_list:[],required_js_list:[]};if(!b||!L.test(b))throw new Error("The url should be absolute: "+b);if(9!==a.nodeType)throw new Error("The first parameter should be an HTMLDocument");if(e.title=a.title,null!==a.head)for(c=0;c<a.head.children.length;c+=1)d=a.head.children[c],null!==d.href&&("stylesheet"===d.rel?e.required_css_list.push(F.getAbsoluteURL(d.getAttribute("href"),b)):"SCRIPT"!==d.nodeName||"text/javascript"!==d.type&&d.type?"http://www.renderjs.org/rel/interface"===d.rel&&e.interface_list.push(F.getAbsoluteURL(d.getAttribute("href"),b)):e.required_js_list.push(F.getAbsoluteURL(d.getAttribute("src"),b)));return e},b.rJS=b.renderJS=F,b.__RenderJSGadget=q,b.__RenderJSEmbeddedGadget=x,b.__RenderJSIframeGadget=z,C()}(document,window,RSVP,DOMParser,Channel,MutationObserver,Node,FileReader,Blob,navigator,Event); \ No newline at end of file diff --git a/package.json b/package.json index bcb43b3d4c0abec630779f12e4ed7fe0a35ff759..fe676e4f2dc3a9159d7561a1a0223f5c21e4671d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "renderjs", - "version": "0.9.0", + "version": "0.10.0", "description": "RenderJs provides HTML5 gadgets", "main": "dist/renderjs-latest.js", "dependencies": {