summaryrefslogtreecommitdiffstats
path: root/sca-cpp/branches/lightweight-sca/hosting/server/htdocs/app/index.html
diff options
context:
space:
mode:
Diffstat (limited to 'sca-cpp/branches/lightweight-sca/hosting/server/htdocs/app/index.html')
-rw-r--r--sca-cpp/branches/lightweight-sca/hosting/server/htdocs/app/index.html1012
1 files changed, 1012 insertions, 0 deletions
diff --git a/sca-cpp/branches/lightweight-sca/hosting/server/htdocs/app/index.html b/sca-cpp/branches/lightweight-sca/hosting/server/htdocs/app/index.html
new file mode 100644
index 0000000000..cddf4fb477
--- /dev/null
+++ b/sca-cpp/branches/lightweight-sca/hosting/server/htdocs/app/index.html
@@ -0,0 +1,1012 @@
+<!DOCTYPE html>
+<!--
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+-->
+<html manifest="cache-manifest.cmf">
+<head>
+<title></title>
+<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0"/>
+<meta name="apple-mobile-web-app-capable" content="yes"/>
+<meta name="apple-mobile-web-app-status-bar-style" content="black"/>
+<link rel="apple-touch-icon" href="/public/touchicon.png"/>
+<base href="/"/>
+<script type="text/javascript">
+(function() {
+
+window.appcache = {};
+
+/**
+ * Get and cache a resource.
+ */
+appcache.get = function(uri) {
+ var h = uri.indexOf('#');
+ var u = h == -1? uri : uri.substring(0, h);
+
+ // Get resource from local storage first
+ var ls = window.lstorage || localStorage;
+ var item = null;
+ try { item = ls.getItem(u); } catch(e) {}
+ if (item != null && item != '')
+ return item;
+
+ // Get resource from network
+ var http = new XMLHttpRequest();
+ http.open("GET", u, false);
+ http.setRequestHeader("Accept", "*/*");
+ http.send(null);
+ if (http.status == 200) {
+ if (http.getResponseHeader("X-Login") != null) {
+ if (window.debug) debug('http error', u, 'X-Login');
+ // Redirect to login page if not signed in
+ document.location = '/login/';
+ return null;
+ } else if (http.responseText == '' || http.getResponseHeader("Content-Type") == null) {
+ if (window.debug) debug('http error', u, 'No-Content');
+ return null;
+ }
+ try { ls.setItem(u, http.responseText); } catch(e) {}
+ return http.responseText;
+ }
+ if (window.debug) debug('http error', u, http.status, http.statusText);
+ // Redirect to login page if not signed in
+ if (http.status == 403)
+ document.location = '/login/';
+ return null;
+};
+
+})();
+
+/**
+ * Load Javascript and CSS.
+ */
+(function() {
+
+var bootjs = document.createElement('script');
+bootjs.type = 'text/javascript';
+bootjs.text = appcache.get('/all-min.js');
+document.head.appendChild(bootjs);
+document.head.appendChild(ui.declareCSS(appcache.get('/ui-min.css')));
+
+})();
+
+/**
+ * Redirect to login page if not signed in.
+ */
+(function() {
+
+if (document.location.protocol == 'https:' && !hasauthcookie())
+ document.location = '/login/';
+
+})();
+
+</script>
+</head>
+<body class="delayed">
+<div id="mainbodydiv" class="mainbodydiv">
+
+<div id="headdiv" class="hsection">
+<script type="text/javascript">
+(function() {
+
+$('headdiv').appendChild(ui.declareScript(appcache.get('/config-min.js')));
+
+})();
+</script>
+</div>
+
+<div id="content">
+</div>
+
+<script type="text/javascript">
+(function() {
+
+/**
+ * Get the app name
+ */
+var appname = location.pathname.split('/')[1];
+
+/**
+ * Set page title.
+ */
+document.title = appname;
+
+/**
+ * The main page div.
+ */
+var contentdiv = $('content');
+
+/**
+ * The main app composite and page definitions.
+ */
+var appcomposite = null;
+var apppage = null;
+
+/**
+ * Initialize the app HTTP clients.
+ */
+var appComp = sca.component('App');
+var pagecomp = sca.reference(appComp, 'pages');
+var composcomp = sca.reference(appComp, 'composites');
+var startcomp = sca.httpclient('start', '/' + appname + '/start');
+var stopcomp = sca.httpclient('stop', '/' + appname + '/stop');
+var timercomp = sca.httpclient('timer', '/' + appname + '/timer');
+var animationcomp = sca.httpclient('animation', '/' + appname + '/animation');
+var locationcomp = sca.httpclient('location', '/' + appname + '/location');
+
+/**
+ * Pre-fetch app resources.
+ */
+var appresources = [
+ ['/all-min.js'],
+ ['/ui-min.css'],
+ ['/config-min.js'],
+ ['/public/config-min.js']
+];
+
+/**
+ * Handle application cache events.
+ */
+applicationCache.addEventListener('checking', function(e) {
+ //debug('appcache checking', e);
+}, false);
+applicationCache.addEventListener('error', function(e) {
+ //debug('appcache error', e);
+}, false);
+applicationCache.addEventListener('noupdate', function(e) {
+ //debug('appcache noupdate', e);
+}, false);
+applicationCache.addEventListener('downloading', function(e) {
+ //debug('appcache downloading', e);
+}, false);
+applicationCache.addEventListener('progress', function(e) {
+ //debug('appcache progress', e);
+}, false);
+applicationCache.addEventListener('updateready', function(e) {
+ //debug('appcache updateready', e);
+ try {
+ applicationCache.swapCache();
+ } catch(e) {}
+ //debug('appcache swapped', e);
+}, false);
+applicationCache.addEventListener('cached', function(e) {
+ //debug('appcache cached', e);
+ map(function(res) {
+ appcache.get(res[0]);
+ }, appresources);
+}, false);
+
+/**
+ * Handle network offline/online events.
+ */
+window.addEventListener('offline', function(e) {
+ //debug('going offline');
+}, false);
+window.addEventListener('online', function(e) {
+ //debug('going online');
+}, false);
+
+//debug(navigator.onLine? 'online' : 'offline');
+
+/**
+ * Find a named value in a tree of elements. The value name is given
+ * as a list of ids.
+ */
+function namedvalue(l, id) {
+ if (isNil(l))
+ return null;
+ var e = car(l);
+
+ // Element matches id segment
+ if (car(id) == elementName(e)) {
+
+ // Found element matching the whole id path
+ if (isNil(cdr(id)))
+ return e;
+
+ // Search for next id segments in child elements
+ if (!elementHasValue(e)) {
+ var v = namedvalue(elementChildren(e), cdr(id));
+ if (v != null)
+ return v;
+ }
+ }
+
+ // Search for id through the whole element tree
+ if (!elementHasValue(e)) {
+ var v = namedvalue(elementChildren(e), id);
+ if (v != null)
+ return v;
+ }
+ return namedvalue(cdr(l), id);
+}
+
+/**
+ * Return the value of an input element.
+ */
+function inputvalue(e) {
+ if (e.className == 'entry' || e.className == 'password') {
+ return car(childElements(e)).value;
+ }
+ if (e.className == 'button') {
+ return car(childElements(e)).value;
+ }
+ if (e.className == 'checkbox') {
+ if (!car(childElements(e)).checked)
+ return null;
+ return car(childElements(e)).value;
+ }
+ if (e.className == 'select') {
+ return car(childElements(car(childElements(e)))).value;
+ }
+ return null;
+};
+
+/**
+ * Set a value into a widget.
+ */
+function setwidgetvalue(e, dv) {
+ var htattrs = namedElementChild("'htattrs", dv);
+
+ function attr(ce) {
+ return mklist(elementName(ce) == "'htstyle"? 'style' : elementName(ce).substring(1), elementHasValue(ce)? elementValue(ce) : elementChildren(ce));
+ }
+
+ function vattr(dv) {
+ return (elementHasValue(dv) && !isNil(elementValue(dv)))? mklist(mklist('value', isNil(elementValue(dv))? '' : elementValue(dv))) : mklist();
+ }
+
+ function sattr(dv) {
+ var s = namedElementChild("'htstyle", dv);
+ return isNil(s)? mklist() : mklist(mklist('style', elementHasValue(s)? elementValue(s) : elementChildren(s)))
+ }
+
+ var attrs = append(append(isNil(htattrs)? mklist() : map(attr, elementChildren(htattrs)), vattr(dv)), sattr(dv));
+
+ // Set the attributes of the widget
+ function setattrs(vsetter, attrs, ce) {
+ return map(function(a) {
+ if (car(a) == 'value')
+ return vsetter(a, ce);
+
+ if (car(a) == 'style') {
+ // Split a style property between a style attribute
+ // and a stylesheet definition in the document's head
+
+ function prop(s) {
+ if (s == ';')
+ return '';
+ var i = s.indexOf('<style>');
+ if (i == -1)
+ return s;
+ var j = s.indexOf('</style>');
+ return s.substring(0, i) + prop(s.substring(j + 8));
+ }
+
+ function sheet(s) {
+ var i = s.indexOf('<style>');
+ if (i == -1)
+ return '';
+ var j = s.indexOf('</style>');
+ return s.substring(i + 7, j) + sheet(s.substring(j + 8));
+ }
+
+ var st = cadr(a).replace(new RegExp('{id}', 'g'), e.id);
+ var p = prop(st);
+ var s = sheet(st);
+
+ // Define the stylesheet
+ if (s != '') {
+ var esheet = ui.elementByID(contentdiv, 'style_' + e.id);
+ if (isNil(esheet)) {
+ var nesheet = document.createElement('style');
+ nesheet.id = 'style_' + e.id;
+ nesheet.type = 'text/css';
+ document.head.appendChild(nesheet);
+ nesheet.innerHTML = s;
+ } else {
+ esheet.innerHTML = s;
+ }
+ }
+
+ var aname = ce.style.webkitAnimationName;
+
+ // Set the style attribute
+ ce.setAttribute('style', p);
+
+ // Restart current animation if necessary
+ if (!isNil(aname) && ce.style.webkitAnimationName == aname) {
+ ce.style.webkitAnimationName = '';
+ setTimeout(function() {
+ ce.style.webkitAnimationName = aname;
+ }, 0);
+ }
+ return a;
+ }
+
+ ce.setAttribute(car(a), cadr(a));
+ return a;
+ }, attrs);
+ }
+
+ if (e.className == 'h1' || e.className == 'h2' || e.className == 'text' || e.className == 'section') {
+ var ce = car(childElements(e));
+ return setattrs(function(a, ce) { ce.innerHTML = cadr(a); }, attrs, ce);
+ }
+ if (e.className == 'entry' || e.className == 'password') {
+ var ce = car(childElements(e));
+ return setattrs(function(a, ce) { ce.defaultValue = cadr(a); }, attrs, ce);
+ }
+ if (e.className == 'button') {
+ var ce = car(childElements(e));
+ return setattrs(function(a, ce) { ce.value = cadr(a); }, attrs, ce);
+ }
+ if (e.className == 'checkbox') {
+ var ce = car(childElements(e));
+
+ function setcheckvalue(a, ce) {
+ var v = cadr(a);
+ ce.value = v;
+ map(function(n) { if (n.nodeName == "SPAN") n.innerHTML = v; return n; }, nodeList(e.childNodes));
+ return true;
+ }
+
+ return setattrs(setcheckvalue, attrs, ce);
+ }
+ if (e.className == 'select') {
+ var ce = car(childElements(car(childElements(e))));
+
+ function setselectvalue(a, ce) {
+ var v = cadr(a);
+ ce.value = v;
+ ce.innerHTML = v;
+ return true;
+ }
+
+ return setattrs(setselectvalue, attrs, ce);
+ }
+ if (e.className == 'list') {
+ var dl = ui.datalist(isNil(dv)? mklist() : mklist(dv));
+ e.innerHTML = dl;
+ return dl;
+ }
+ if (e.className == 'table') {
+ var dl = ui.datatable(isNil(dv)? mklist() : mklist(dv));
+ e.innerHTML = dl;
+ return dl;
+ }
+ if (e.className == 'link') {
+ var ce = car(childElements(e));
+
+ function setlinkvalue(a, ce) {
+ var v = cadr(a);
+ if (isList(v)) {
+ ce.href = car(v);
+ ce.innerHTML = cadr(v);
+ return true;
+ }
+ ce.href = v;
+ ce.innerHTML = v;
+ return true;
+ }
+
+ return setattrs(setlinkvalue, attrs, ce);
+ }
+ if (e.className == 'img') {
+ var ce = car(childElements(e));
+ return setattrs(function(a, ce) { ce.setAttribute('src', cadr(a)); }, attrs, ce);
+ }
+ if (e.className == 'iframe') {
+ var ce = car(childElements(e));
+ return setattrs(function(a, ce) { ce.setAttribute('src', cadr(a)); }, attrs, ce);
+ }
+ return '';
+};
+
+/**
+ * Update the app page with the given app data.
+ */
+function updatepage(l) {
+ if (isNil(l))
+ return true;
+
+ // Update the widgets values
+ function updatewidget(e) {
+ var dv = namedvalue(l, map(function(t) { return "'" + t; }, e.id.split('.')));
+ if (dv == null || isNil(dv))
+ return e;
+ setwidgetvalue(e, dv);
+ return e;
+ }
+
+ map(updatewidget, filter(function(e) { return !isNil(e.id) && e.id.substring(0, 5) != 'page:'; }, nodeList(ui.elementByID(contentdiv, 'page').childNodes)));
+ return true;
+}
+
+/**
+ * Convert a document to application data.
+ */
+function docdata(doc) {
+ if (isNil(doc))
+ return null;
+
+ if (json.isJSON(mklist(doc)))
+ return json.readJSON(mklist(doc));
+
+ if (atom.isATOMEntry(mklist(doc)))
+ return atom.readATOMEntry(mklist(doc));
+
+ if (atom.isATOMFeed(mklist(doc)))
+ return atom.readATOMFeed(mklist(doc));
+
+ return doc;
+}
+
+/**
+ * Bind a handler to a widget.
+ */
+function bindwidgethandler(e, appname) {
+ if (e.className == 'button') {
+ var b = car(childElements(e));
+ b.name = e.id;
+ b.onclick = function() { return buttonClickHandler(b.value, appname); };
+ return e;
+ }
+ if (e.className == 'link') {
+ var l = car(childElements(e));
+ var hr = l.href;
+ if (hr.substring(0, 5) == 'link:' && hr.indexOf('://') == -1) {
+ var f = function(e) {
+ e.preventDefault();
+ return buttonClickHandler(hr.substring(5), appname);
+ };
+ l.ontouchstart = l.onclick = f;
+ l.href = 'javascript:void()';
+ }
+ return e;
+ }
+ if (e.className == 'entry' || e.className == 'password' || e.className == 'checkbox') {
+ car(childElements(e)).name = e.id;
+ return e;
+ }
+ if (e.className == 'select') {
+ var ce = car(childElements(car(childElements(e))));
+ ce.name = e.id;
+ return e;
+ }
+ return e;
+}
+
+/**
+ * Initial fixup of a widget.
+ */
+function fixupwidget(e) {
+ if (e.className == 'h1' || e.className == 'h2' || e.className == 'text' || e.className == 'section') {
+ if (e.className == 'section')
+ e.style.width = '100%';
+ var ce = car(childElements(e));
+ if (ce.innerHTML == '=' + e.id)
+ ce.innerHTML = '';
+ return e;
+ }
+ if (e.className == 'entry' || e.className == 'password') {
+ var ce = car(childElements(e));
+ if (ce.defaultValue == '=' + e.id)
+ ce.defaultValue = '';
+ return e;
+ }
+ if (e.className == 'button') {
+ var ce = car(childElements(e));
+ if (ce.value == '=' + e.id)
+ ce.value = '';
+ return e;
+ }
+ if (e.className == 'checkbox') {
+ var ce = car(childElements(e));
+ if (ce.value == '=' + e.id) {
+ ce.value = '';
+ map(function(n) { if (n.nodeName == "SPAN") n.innerHTML = ''; return n; }, nodeList(e.childNodes));
+ }
+ return e;
+ }
+ if (e.className == 'select') {
+ var ce = car(childElements(car(childElements(e))));
+ if (ce.value == '=' + e.id) {
+ ce.value = '';
+ ce.innerHTML = '';
+ }
+ return e;
+ }
+ if (e.className == 'list') {
+ car(childElements(e)).innerHTML = '';
+ e.style.width = '100%';
+ car(childElements(e)).style.width = '100%';
+ return e;
+ }
+ if (e.className == 'table') {
+ car(childElements(e)).innerHTML = '';
+ e.style.width = '100%';
+ car(childElements(e)).style.width = '100%';
+ return e;
+ }
+ if (e.className == 'link') {
+ var ce = car(childElements(e));
+ if (ce.innerHTML == '=' + e.id)
+ ce.innerHTML = '';
+ return e;
+ }
+ if (e.className == 'img') {
+ var ce = car(childElements(e));
+ return e;
+ }
+ if (e.className == 'iframe') {
+ var ce = car(childElements(e));
+ e.innerHTML = '<iframe src="' + ce.href + '" frameborder="no" scrolling="no"></iframe>';
+ return e;
+ }
+ return e;
+}
+
+/**
+ * Set initial value of a widget.
+ */
+function initwidget(e) {
+ if (!isNil(e.id) && e.id.substring(0, 5) != 'page:')
+ setwidgetvalue(e, mklist());
+ return e;
+}
+
+/**
+ * Return the component bound to a uri.
+ */
+function isbound(uri, comps) {
+ return !isNil(filter(function(comp) {
+ return !isNil(filter(function(svc) {
+ return !isNil(filter(function(b) {
+ return uri == scdl.uri(b);
+ }, scdl.bindings(svc)));
+ }, scdl.services(comp)));
+ }, comps));
+}
+
+/**
+ * Get app data from the main app page component.
+ */
+function getappdata(appname, page, compos) {
+ try {
+
+ // Eval a component init script
+ function evalcompinit(doc) {
+ if (isNil(doc))
+ return true;
+ var js = car(json.readJSON(mklist(doc)));
+ if (!elementHasValue(js))
+ return true;
+ eval(elementValue(js));
+ return true;
+ }
+
+ // Initial setup of a widget
+ function setupwidget(e) {
+ initwidget(e);
+ fixupwidget(e);
+ bindwidgethandler(e, appname);
+ }
+
+ // Setup the widgets
+ map(setupwidget, filter(function(e) { return !isNil(e.id); }, nodeList(ui.elementByID(contentdiv, 'page').childNodes)));
+
+ // Get the app components
+ var comps = scdl.components(compos);
+
+ // Get the component app data
+ if (isbound("start", comps)) {
+ startcomp.get(location.search, function(doc, e) {
+ if (isNil(doc)) {
+ debug('error on get(start, ' + location.search + ')', e);
+ return false;
+ }
+
+ // Display data on the page
+ updatepage(docdata(doc));
+ });
+ }
+
+ // Get and eval the optional timer, animation and location watch setup scripts
+ if (isbound("timer", comps)) {
+ timercomp.get('setup', function(doc, e) {
+ if (isNil(doc)) {
+ debug('error on get(timer, setup)', e);
+ return false;
+ }
+
+ // Evaluate the component init expression
+ return evalcompinit(doc);
+ });
+ }
+
+ if (isbound("animation", comps)) {
+ animationcomp.get('setup', function(doc, e) {
+ if (isNil(doc)) {
+ debug('error on get(animation, setup)', e);
+ return false;
+ }
+
+ // Evaluate the component init expression
+ return evalcompinit(doc);
+ });
+ }
+
+ if (isbound("location", comps)) {
+ locationcomp.get('setup', function(doc, e) {
+ if (isNil(doc)) {
+ debug('error on get(location, setup)', e);
+ return false;
+ }
+
+ // Evaluate the component init expression
+ return evalcompinit(doc);
+ });
+ }
+
+ return true;
+
+ } catch(e) {
+ debug('error in getappdata()', e);
+ return true;
+ }
+}
+
+/**
+ * Return the page in an ATOM entry.
+ */
+function atompage(doc) {
+ var entry = atom.readATOMEntry(mklist(doc));
+ if (isNil(entry))
+ return mklist();
+ var content = namedElementChild("'content", car(entry));
+ if (content == null)
+ return mklist();
+ return elementChildren(content);
+}
+
+/**
+ * Get the app page.
+ */
+function getapppage(appname, compos) {
+ pagecomp.get(appname, function(doc, e) {
+ //debug('page get');
+ if (isNil(doc)) {
+ debug('error in getapppage', e);
+ return false;
+ }
+
+ // Set the app HTML page into the content div
+ var page = atompage(doc);
+ contentdiv.innerHTML = writeStrings(writeXML(page, false));
+ apppage = page;
+
+ // Merge in the app data
+ if (!isNil(appcomposite))
+ getappdata(appname, apppage, appcomposite);
+});
+
+}
+
+/**
+ * Build a query string from the values of the page's input fields.
+ */
+function compquery() {
+ function queryarg(e) {
+ return e.id + '=' + inputvalue(e);
+ }
+
+ function childrenList(n) {
+ return append(nodeList(n.childNodes), reduce(append, mklist(), map(childrenList, nodeList(n.childNodes))));
+ }
+
+ var args = map(queryarg, filter(function(e) { return !isNil(e.id) && !isNil(inputvalue(e)); }, childrenList(ui.elementByID(contentdiv, 'page'))));
+
+ // Append current location properties if known
+ if (!isNil(geoposition)) {
+ var g = geoposition;
+ args = append(args, mklist('latitude=' + g.coords.latitude, 'longitude=' + g.coords.longitude, 'altitude=' + g.coords.altitude,
+ 'accuracy=' + g.coords.accuracy, 'altitudeAccuracy=' + g.coords.altitudeAccuracy, 'heading=' + g.coords.heading,
+ 'speed=' + g.coords.speed));
+ }
+
+ return '?' + args.join('&');
+}
+
+/**
+ * Handle a button click event.
+ */
+function buttonClickHandler(id, appname) {
+ try {
+ var uri = compquery();
+ return sca.component(id, appname).get(uri, function(doc, e) {
+ if (isNil(doc)) {
+ debug('error on get(button, ' + uri + ')', e);
+ return false;
+ }
+
+ // Inject data into the page
+ updatepage(docdata(doc));
+ });
+ } catch(e) {
+ debug('error in buttonClickHandler()', e);
+ return true;
+ }
+}
+
+/**
+ * Handle a timer interval event.
+ */
+function intervalHandler() {
+ try {
+ var uri = compquery();
+ return timercomp.get(uri, function(doc, e) {
+ if (isNil(doc)) {
+ debug('error on get(timer, ' + uri + ')', e);
+ return false;
+ }
+
+ // Inject data into the page
+ updatepage(docdata(doc));
+ });
+ } catch(e) {
+ debug('error in intervalHandler()', e);
+ return true;
+ }
+}
+
+/**
+ * Setup an interval timer.
+ */
+function setupIntervalHandler(msec) {
+ intervalHandler();
+ try {
+ return setInterval(intervalHandler, msec);
+ } catch(e) {
+ debug('error in setupIntervalHandler()', e);
+ return true;
+ }
+}
+
+/**
+ * Handle an animation event.
+ */
+var animationData = null;
+var gettingAnimationData = false;
+var currentAnimationData = null;
+var animationLoop = 0;
+var currentAnimationLoop = 0;
+
+function animationHandler() {
+ try {
+ function applyAnimation() {
+ // Update page with current animation data
+ updatepage(car(currentAnimationData));
+
+ // End of animation?
+ if (isNil(cdr(currentAnimationData))) {
+ if (currentAnimationLoop == -1) {
+ // Repeat current animation forever
+ currentAnimationData = animationData;
+ return true;
+ }
+
+ currentAnimationLoop = currentAnimationLoop - 1;
+ if (currentAnimationLoop <= 0) {
+ // Get next animation data
+ currentAnimationData = null;
+ animationData = null;
+ return true;
+ }
+
+ // Repeat animation
+ currentAnimationData = animationData;
+ return true;
+ }
+
+ // Move to the next animation frame
+ currentAnimationData = cdr(currentAnimationData);
+ return true;
+ }
+
+ // Get new animation data if necessary
+ if (isNil(animationData)) {
+ if (gettingAnimationData)
+ return true;
+ var uri = compquery();
+ return animationcomp.get(uri, function(doc, e) {
+ if (isNil(doc)) {
+ debug('error on get(animation, ' + uri + ')', e);
+ return false;
+ }
+
+ // Apply the new animation
+ currentAnimationData = docdata(doc);
+ currentAnimationLoop = animationLoop;
+ gettingAnimationData = false;
+ applyAnimation();
+ });
+ }
+
+ // Apply the current animation
+ return applyAnimation();
+
+ } catch(e) {
+ debug('error in animationHandler()', e);
+ return true;
+ }
+}
+
+/**
+ * Setup an animation.
+ */
+function setupAnimationHandler(msec, loop) {
+ animationLoop = loop;
+ animationHandler();
+ try {
+ return setInterval(animationHandler, msec);
+ } catch(e) {
+ debug('error in setupAnimationHandler()', e);
+ return true;
+ }
+}
+
+/**
+ * Handle a location watch event.
+ */
+var locationWatch = null;
+var geoposition = null;
+
+function locationHandler(pos) {
+ try {
+ geoposition = pos;
+ var uri = compquery();
+ return locationcomp.get(uri, function(doc, e) {
+ if (isNil(doc)) {
+ debug('error on get(location, ' + uri + ')', e);
+ return false;
+ }
+
+ // Inject data into the page
+ updatepage(docdata(doc));
+ });
+ } catch(e) {
+ return locationErrorHandler(e);
+ }
+}
+
+function locationErrorHandler(e) {
+ debug('location error', e);
+ if (!isNil(locationWatch)) {
+ try {
+ navigator.geolocation.clearWatch(locationWatch);
+ } catch(e) {}
+ locationWatch = null;
+ }
+ return true;
+}
+
+/**
+ * Setup a location watch handler.
+ */
+function setupLocationHandler() {
+ function installLocationHandler() {
+ if (!isNil(locationWatch))
+ return true;
+ try {
+ locationWatch = navigator.geolocation.watchPosition(locationHandler, locationErrorHandler);
+ } catch(e) {
+ debug('error in installLocationHandler()', e);
+ }
+ return true;
+ }
+
+ installLocationHandler();
+ setInterval(installLocationHandler, 10000);
+ return true;
+}
+
+/**
+ * Handle orientation change.
+ */
+document.body.onorientationchange = function(e) {
+ //debug('onorientationchange');
+
+ // Scroll to the top and hide the address bar
+ window.scrollTo(0, 0);
+
+ return true;
+};
+
+/**
+ * Return the composite in an ATOM entry.
+ */
+function atomcomposite(doc) {
+ var entry = atom.readATOMEntry(mklist(doc));
+ if (isNil(entry))
+ return mklist();
+ var content = namedElementChild("'content", car(entry));
+ if (content == null)
+ return mklist();
+ return elementChildren(content);
+}
+
+/**
+ * Get the app composite.
+ */
+function getappcomposite(appname) {
+ return composcomp.get(appname, function(doc, e) {
+ //debug('page get');
+ if (isNil(doc)) {
+ debug('error in getappcomposite', e);
+ return false;
+ }
+
+ var compos = atomcomposite(doc);
+ if (isNil(compos)) {
+
+ // Create a default empty composite if necessary
+ var x = '<composite xmlns="http://docs.oasis-open.org/ns/opencsa/sca/200912" ' +
+ 'targetNamespace="http://app" name="app"></composite>';
+ compos = readXML(mklist(x));
+ }
+ appcomposite = compos;
+
+ // Merge in the app data
+ if (!isNil(apppage))
+ getappdata(appname, apppage, appcomposite);
+ });
+}
+
+/**
+ * Initialize the document.
+ */
+function onload() {
+ //debug('onload');
+
+ // Scroll to the top and hide the address bar
+ window.scrollTo(0, 0);
+
+ // Show the page
+ document.body.style.visibility = 'visible';
+
+ // Initialize the app composite
+ getappcomposite(appname);
+
+ // Initialize the app page
+ getapppage(appname);
+
+ return true;
+}
+
+onload();
+
+})();
+</script>
+
+<div id="footdiv" class="fsection">
+</div>
+
+</div>
+</body>
+</html>
+