blob: 108a35ee09ce7566fdb5508d0ad9ab82d5144975 [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 manifest="cache/cache-manifest.cmf">
<head>
<!-- Firebug inspector -->
<!--
<script type="text/javascript" src="https://getfirebug.com/releases/lite/1.3/firebug-lite.js"></script>
-->
<!-- Weinre inspector -->
<!--
<script src="http://www.example.com:9998/target/target-script-min.js#anonymous"></script>
-->
<title></title>
<meta name="viewport" content="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-precomposed" href="/public/touchicon.png"/>
<base href="/"/>
<script type="text/javascript">
try {
(function apphead() {
window.appcache = {};
/**
* Get and cache a resource.
*/
appcache.get = function(uri, mode) {
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('ui.r.' + u); } catch(e) {}
if (item != null && item != '')
return item;
// Get resource from network
var http = new XMLHttpRequest();
http.open("GET", mode == 'remote'? (u + '?t=' + new Date().getTime() + '&r=' + Math.random()) : u, false);
http.setRequestHeader("Accept", "*/*");
if (mode == 'remote')
http.setRequestHeader("If-Modified-Since", "Thu, 1 Jan 1970 00:00:00 GMT");
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('ui.r.' + 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 appboot() {
var bootjs = document.createElement('script');
bootjs.type = 'text/javascript';
bootjs.text = 'try {\n' + appcache.get('/all-min.js') + '\n' + appcache.get('/config-min.js') + '\n} catch(e) { console.log(e.stack); throw e; }\n';
var head = document.getElementsByTagName('head')[0];
head.appendChild(bootjs);
head.appendChild(ui.declareCSS(appcache.get('/ui-min.css')));
})();
} catch(e) {
if (window.debug) debug(e.stack);
throw e;
}
</script>
</head>
<body class="delayed">
<div id="content">
</div>
<script type="text/javascript">
try {
(function appbody() {
/**
* 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], 'remote');
}, 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 (isNull(l))
return null;
var e = car(l);
// Element matches id segment
if (car(id) == elementName(e)) {
// Found element matching the whole id path
if (isNull(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) && !isNull(elementValue(dv)))? mklist(mklist('value', isNull(elementValue(dv))? '' : elementValue(dv))) : mklist();
}
function sattr(dv) {
var s = namedElementChild("'htstyle", dv);
return isNull(s)? mklist() : mklist(mklist('style', elementHasValue(s)? elementValue(s) : elementChildren(s)))
}
var attrs = append(append(isNull(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(document, 'style_' + e.id);
if (isNull(esheet)) {
var nesheet = document.createElement('style');
nesheet.id = 'style_' + e.id;
nesheet.type = 'text/css';
var head = document.getElementsByTagName('head')[0];
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 (!isNull(aname) && ce.style.webkitAnimationName == aname) {
ce.style.webkitAnimationName = '';
ui.async(function restartanimation() {
ce.style.webkitAnimationName = aname;
});
}
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(isNull(dv)? mklist() : mklist(dv));
e.innerHTML = dl;
return dl;
}
if (e.className == 'table') {
var dl = ui.datatable(isNull(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 (isNull(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 || isNull(dv))
return e;
setwidgetvalue(e, dv);
return e;
}
map(updatewidget, filter(function(e) { return !isNull(e.id) && e.id.substring(0, 5) != 'page:'; }, nodeList(ui.elementByID(document, 'page').childNodes)));
return true;
}
/**
* Convert a document to application data.
*/
function docdata(doc) {
if (isNull(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;
ui.onclick(b, function(e) {
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) {
ui.onclick(l, function(e) {
e.preventDefault();
return buttonClickHandler(hr.substring(5), appname);
});
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 (!isNull(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 !isNull(filter(function(comp) {
return !isNull(filter(function(svc) {
return !isNull(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 (isNull(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 !isNull(e.id); }, nodeList(ui.elementByID(document, '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 (isNull(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 (isNull(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 (isNull(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 (isNull(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 (isNull(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 (isNull(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 (!isNull(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 !isNull(e.id) && !isNull(inputvalue(e)); }, childrenList(ui.elementByID(document, 'page'))));
// Append current location properties if known
if (!isNull(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 (isNull(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 (isNull(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 (isNull(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 (isNull(animationData)) {
if (gettingAnimationData)
return true;
var uri = compquery();
return animationcomp.get(uri, function(doc, e) {
if (isNull(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 (isNull(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 (!isNull(locationWatch)) {
try {
navigator.geolocation.clearWatch(locationWatch);
} catch(e) {}
locationWatch = null;
}
return true;
}
/**
* Setup a location watch handler.
*/
function setupLocationHandler() {
function installLocationHandler() {
if (!isNull(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');
ui.onorientationchange(e);
return true;
};
/**
* Return the composite in an ATOM entry.
*/
function atomcomposite(doc) {
var entry = atom.readATOMEntry(mklist(doc));
if (isNull(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 (isNull(doc)) {
debug('error in getappcomposite', e);
return false;
}
var compos = atomcomposite(doc);
if (isNull(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 (!isNull(apppage))
getappdata(appname, apppage, appcomposite);
});
}
/**
* Initialize the document.
*/
window.onload = function() {
//debug('onload');
ui.onload();
// Initialize the app composite
getappcomposite(appname);
// Initialize the app page
getapppage(appname);
return true;
};
})();
} catch(e) {
debug(e.stack);
throw e;
}
</script>
</body>
</html>