blob: 570d1e00f17379c585d533fa6458a1f9a625902d [file] [log] [blame]
<!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>
<head>
<title>App</title>
<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0"/>
<meta name="apple-mobile-web-app-capable" content="yes"/>
<meta name="apple-mobile-web-app-status-bar-style" content="black"/>
<script type="text/javascript">
document.title = window.location.hostname.split('.')[0];
</script>
<link rel="apple-touch-icon" href="/public/touchicon.png"/>
<link rel="stylesheet" type="text/css" href="/ui-min.css"/>
<script type="text/javascript" src="/config.js"></script>
<script type="text/javascript" src="/all-min.js"></script>
</head>
<body class="delayed" onorientationchange="ui.reload();">
<div id="bodydiv" class="devicewidth">
<div id="app"></div>
<span id="appbuffer"></span>
<span id="appebuffer"></span>
</div>
<script type="text/javascript">
/**
* The main app div.
*/
var appdiv = $('app');
/**
* Start, stop, timer, animation and location components.
*/
var startcomp = sca.httpclient('start', '/start');
var stopcomp = sca.httpclient('stop', '/stop');
var timercomp = sca.httpclient('timer', '/timer');
var animationcomp = sca.httpclient('animation', '/animation');
var locationcomp = sca.httpclient('location', '/location');
/**
* 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);
var attrs = append(isNil(htattrs)? mklist() :
map(function(ce) { return mklist(elementName(ce).substring(1), elementHasValue(ce)? elementValue(ce) : elementChildren(ce)); }, elementChildren(htattrs)),
elementHasValue(dv)? mklist(mklist('value', isNil(elementValue(dv))? '' : elementValue(dv))) : mklist());
if (e.className == 'h1' || e.className == 'h2' || e.className == 'text' || e.className == 'section') {
var ce = car(childElements(e));
return map(function(a) { car(a) == 'value'? ce.innerHTML = cadr(a) : ce.setAttribute(car(a), cadr(a)); }, attrs);
}
if (e.className == 'entry' || e.className == 'password') {
var ce = car(childElements(e));
return map(function(a) { car(a) == 'value'? ce.defaultValue = cadr(a) : ce.setAttribute(car(a), cadr(a)); }, attrs);
}
if (e.className == 'button') {
var ce = car(childElements(e));
return map(function(a) { car(a) == 'value'? ce.value = cadr(a) : ce.setAttribute(car(a), cadr(a)); }, attrs);
}
if (e.className == 'checkbox') {
var ce = car(childElements(e));
function setcheckvalue(ce, v) {
ce.value = v;
map(function(n) { if (n.nodeName == "SPAN") n.innerHTML = v; return n; }, nodeList(e.childNodes));
return true;
}
var r = map(function(a) { car(a) == 'value'? setcheckvalue(ce, cadr(a)) : ce.setAttribute(car(a), cadr(a)); }, attrs);
return r;
}
if (e.className == 'select') {
var ce = car(childElements(car(childElements(e))));
function setselectvalue(ce, v) {
ce.value = v;
ce.innerHTML = v;
return true;
}
var r = map(function(a) { car(a) == 'value'? setselectvalue(ce, cadr(a)) : ce.setAttribute(car(a), cadr(a)); }, attrs);
return r;
}
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(ce, v) {
if (isList(v)) {
ce.href = car(v);
ce.innerHTML = cadr(v);
return true;
}
ce.href = v;
ce.innerHTML = v;
return true;
}
return map(function(a) { car(a) == 'value'? setlinkvalue(ce, cadr(a)) : ce.setAttribute(car(a), cadr(a)); }, attrs);
}
if (e.className == 'img') {
var ce = car(childElements(e));
return map(function(a) { car(a) == 'value'? ce.setAttribute('src', cadr(a)) : ce.setAttribute(car(a), cadr(a)); }, attrs);
}
if (e.className == 'iframe') {
var ce = car(childElements(e));
return map(function(a) { car(a) == 'value'? ce.setAttribute('src', cadr(a)) : ce.setAttribute(car(a), cadr(a)); }, attrs);
}
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(appdiv, '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) {
if (e.className == 'button') {
var b = car(childElements(e));
b.name = e.id;
b.onclick = function() { return buttonClickHandler(b.value); };
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 == 'iframe') {
var f = car(childElements(e));
e.innerHTML = '<iframe src="' + f.href + '" frameborder="no" scrolling="no"></iframe>';
return e;
}
if (e.className == 'section') {
e.style.width = '100%';
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;
}
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;
}
/**
* Get document from a component.
*/
function getdoc(comp, name, uri) {
try {
return comp.get(uri);
} catch(e) {
log('exception on get(' + name + ', ' + uri + ')', e);
return null;
}
}
/**
* Get app data from the main app page component.
*/
function getpagedata() {
try {
// Display component data on the page
function displaypage(doc) {
updatepage(docdata(doc));
// Reveal the page
ui.showbody();
return true;
}
// 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);
}
// Get the component app data
var doc = getdoc(startcomp, 'start', window.location.search);
// Prepare app HTML page
var appFrame = $('appFrame');
if (!isNil(appFrame.contentDocument.body)) {
appdiv.innerHTML = appFrame.contentDocument.body.innerHTML;
} else {
$('appebuffer').appendChild(appFrame.contentDocument.documentElement);
appdiv.innerHTML = appebuffer.innerHTML;
appebuffer.innerHTML = '';
}
// Setup the widgets
map(setupwidget, filter(function(e) { return !isNil(e.id); }, nodeList(ui.elementByID(appdiv, 'page').childNodes)));
// Display data on the page
displaypage(doc);
// Get and eval the optional timer, animation and location watch setup scripts
evalcompinit(getdoc(timercomp, 'timer', 'setup'));
evalcompinit(getdoc(animationcomp, 'animation', 'setup'));
evalcompinit(getdoc(locationcomp, 'location', 'setup'));
return true;
} catch(e) {
log('exception in getpagedata()', e);
return true;
}
}
/**
* 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(appdiv, '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) {
try {
return updatepage(docdata(getdoc(sca.component(id), 'button', compquery())));
} catch(e) {
log('exception in buttonClickHandler()', e);
return true;
}
}
/**
* Handle a timer interval event.
*/
function intervalHandler() {
try {
return updatepage(docdata(getdoc(timercomp, 'timer', compquery())));
} catch(e) {
log('exception in intervalHandler()', e);
return true;
}
}
/**
* Setup an interval timer.
*/
function setupIntervalHandler(msec) {
intervalHandler();
try {
return setInterval(intervalHandler, msec);
} catch(e) {
log('exception in setupIntervalHandler()', e);
return true;
}
}
/**
* Handle an animation event.
*/
var animationData = null;
var currentAnimationData = null;
var animationLoop = 0;
var currentAnimationLoop = 0;
function animationHandler() {
try {
// Get animation data if necessary
if (isNil(animationData)) {
animationData = docdata(getdoc(animationcomp, 'animation', compquery()));
if (isNil(animationData)) {
// Retry later
return true;
}
currentAnimationData = animationData;
currentAnimationLoop = animationLoop;
}
// Update page with 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;
} catch(e) {
log('exception in animationHandler()', e);
return true;
}
}
/**
* Setup an animation.
*/
function setupAnimationHandler(msec, loop) {
animationLoop = loop;
animationHandler();
try {
return setInterval(animationHandler, msec);
} catch(e) {
log('exception in setupAnimationHandler()', e);
return true;
}
}
/**
* Handle a location watch event.
*/
var locationWatch = null;
var geoposition = null;
function locationHandler(pos) {
try {
geoposition = pos;
return updatepage(docdata(getdoc(locationcomp, 'location', compquery())));
} catch(e) {
locationErrorHandler(e);
}
}
function locationErrorHandler(e) {
log('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) {
log('exception in installLocationHandler()', e);
}
}
installLocationHandler();
setInterval(installLocationHandler, 10000);
return true;
}
// Load the app frame
$('appbuffer').innerHTML = '<iframe id="appFrame" class="widgetframe" src="app.html" onload="getpagedata()"></iframe>';
</script>
</body>
</html>