
git-svn-id: http://svn.us.apache.org/repos/asf/tuscany@1444660 13f79535-47bb-0310-9956-ffa450edef68
1228 lines
36 KiB
HTML
1228 lines
36 KiB
HTML
<!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.
|
|
-->
|
|
<div id="bodydiv" class="body">
|
|
|
|
<div id="viewcontent" class="viewcontent">
|
|
<div id="pagecontainer">
|
|
<div id="pagediv" class="pagediv">
|
|
</div>
|
|
|
|
<!--
|
|
<div class="guide" style="position: absolute; left: 0px; top: 0px; width: 320px; height: 460px;"></div>
|
|
<div class="guide" style="position: absolute; left: 0px; top: 0px; width: 480px; height: 300px;"></div>
|
|
<div class="guide" style="position: absolute; left: 0px; top: 0px; width: 768px; height: 911px;"></div>
|
|
<div class="guide" style="position: absolute; left: 0px; top: 0px; width: 1024px; height: 655px;"></div>
|
|
-->
|
|
|
|
</div>
|
|
|
|
<div id="playdiv" class="playdiv" style="display: none;"></div>
|
|
|
|
</div>
|
|
|
|
<div id="palettecontainer">
|
|
<div id="paletteview" style="display: none;">
|
|
|
|
<div id="palettecontent" class="palettecontent">
|
|
<table class="palettetable">
|
|
<tr><td class="palettetd"><span class="hd1" id="palette:h1"><span>Header 1</span></span></td></tr>
|
|
<tr><td class="palettetd"><span class="hd2" id="palette:h2"><span>Header 2</span></span></td></tr>
|
|
<tr><td class="palettetd"><span class="section" id="palette:section"><span>Section</span></span></td></tr>
|
|
<tr><td class="palettetd"><span class="text" id="palette:text"><span>Some text</span></span></td></tr>
|
|
<tr><td class="palettetd"><span class="link" id="palette:link"><a href="/"><span>Link</span></a></span></td></tr>
|
|
<tr><td class="palettetd"><span class="button" id="palette:graybutton"><input type="button" value="Button" class="graybutton"/></span></td></tr>
|
|
<tr><td class="palettetd"><span class="button" id="palette:redbutton"><input type="button" value="Button" class="redbutton"/></span></td></tr>
|
|
<tr><td class="palettetd"><span class="button" id="palette:greenbutton"><input type="button" value="Button" class="greenbutton"/></span></td></tr>
|
|
<tr><td class="palettetd"><span class="button" id="palette:bluebutton"><input type="button" value="Button" class="bluebutton"/></span></td></tr>
|
|
<tr><td class="palettetd"><span class="entry" id="palette:entry"><input type="text" value="Entry Field" class="flatentry" size="10" autocapitalize="off" readonly="true" style="cursor: default;"/></span></td></tr>
|
|
<tr><td class="palettetd"><span class="password" id="palette:password"><input type="password" value="Password" class="flatentry" size="10" readonly="true" style="cursor: default;"/></span></td></tr>
|
|
<tr><td class="palettetd"><span class="checkbox" id="palette:checkbox"><input type="checkbox" value="Checkbox" class="flatcheckbox"/><span>Checkbox</span></span></td></tr>
|
|
<!--
|
|
<tr><td class="palettetd"><span class="select" id="palette:select"><select disabled="true"><option value="select">Selection</option></select></span></td></tr>
|
|
-->
|
|
<tr><td class="palettetd"><span class="list" id="palette:list">
|
|
<table class="datatable">
|
|
<tr><td class="datatd"><span>List</span></td></tr>
|
|
<tr><td class="datatd"><span>List</span></td></tr>
|
|
<tr><td class="datatd"><span>List</span></td></tr>
|
|
</table>
|
|
</span></td></tr>
|
|
<tr><td class="palettetd"><span class="table" id="palette:table">
|
|
<table class="datatable">
|
|
<tr><td class="datatdl"><span>Table</span></td><td class="datatdr"><span>Table</span></td></tr>
|
|
<tr><td class="datatdl"><span>Table</span></td><td class="datatdr"><span>Table</span></td></tr>
|
|
<tr><td class="datatdl"><span>Table</span></td><td class="datatdr"><span>Table</span></td></tr>
|
|
</table>
|
|
</span></td></tr>
|
|
<tr><td class="palettetd"><span class="img" id="palette:img"><img id="imgimg"/></span></td></tr>
|
|
</table>
|
|
</div>
|
|
|
|
</div>
|
|
</div>
|
|
|
|
<div id="xhtmlbuffer" style="display: none;"></div>
|
|
|
|
<script type="text/javascript">
|
|
(function pagebody() {
|
|
|
|
/**
|
|
* Get the current app name.
|
|
*/
|
|
var appname = ui.fragmentParams(location)['app'];
|
|
|
|
/**
|
|
* Setup page layout.
|
|
*/
|
|
(function layout() {
|
|
document.title = config.windowtitle() + ' - Page - ' + appname;
|
|
|
|
$('viewhead').innerHTML = '<span id="appTitle" class="cmenu">' + appname + '</span>' +
|
|
'<input type="button" id="deleteWidgetButton" title="Delete a widget" class="redbutton plusminus" style="position: absolute; top: 4px; left: 2px;" disabled="true" value="-"/>' +
|
|
'<span style="position: absolute; top: 0px; left: 37px; right: 110px; padding: 0px; background: transparent;"><input id="widgetValue" type="text" value="" class="flatentry" title="Widget value" autocapitalize="off" placeholder="Value" style="position: absolute; left: 0px; top: 4px; width: 100%; display: none;" readonly="readonly"/></span>' +
|
|
'<input type="button" id="playPageButton" title="View page" class="greenbutton plusminus" style="position: absolute; top: 4px; right: 72px;" value=">"/>' +
|
|
'<input type="button" id="copyWidgetButton" title="Copy a widget" class="bluebutton" style="position: absolute; top: 4px; right: 37px; font-size: 16px;" disabled="true" value="C"/>' +
|
|
'<input type="button" id="addWidgetButton" title="Add a widget" class="bluebutton plusminus" style="position: absolute; top: 4px; right: 2px;" disabled="true" value="+"/>';
|
|
//'<input type="button" id="appInfoButton" title="View app info" class="bluebutton" style="position: absolute; top: 4px; right: 2px; font-size: 16px;" value="i"/>';
|
|
|
|
if (ui.isMobile()) {
|
|
$('palettecontainer').className = 'palettecontainer3dm';
|
|
$('paletteview').className = 'paletteloaded3dm';
|
|
} else {
|
|
$('viewcontent').className = 'viewcontent flatscrollbars';
|
|
$('palettecontainer').className = 'palettecontainer3d';
|
|
$('paletteview').className = 'paletteloaded3d';
|
|
$('palettecontent').className = 'palettecontent flatscrollbars';
|
|
}
|
|
|
|
$('imgimg').src = ui.b64png(appcache.get('/public/img.b64'));
|
|
})();
|
|
|
|
/**
|
|
* Track the current page, author, and saved XHTML content.
|
|
*/
|
|
var author = '';
|
|
var editable = false;
|
|
var savedxhtml = '';
|
|
|
|
/**
|
|
* Initialize component references.
|
|
*/
|
|
var editorComp = sca.component('Editor');
|
|
var pages = sca.reference(editorComp, 'pages');
|
|
|
|
/**
|
|
* Return the transform property of a widget.
|
|
*/
|
|
var msiefixupbounds = ui.isMSIE();
|
|
function widgettransform(e) {
|
|
if (!isNull(e.xtranslate))
|
|
return [e.xtranslate, e.ytranslate];
|
|
var t = e.style.getPropertyValue('-webkit-transform') || e.style.getPropertyValue('-moz-transform') ||
|
|
e.style.getPropertyValue('-ms-transform') || e.style.getPropertyValue('-o-transform') ||
|
|
e.style.getPropertyValue('transform');
|
|
if (t) {
|
|
var xy = t.split('(')[1].split(')')[0].split(',');
|
|
return [ui.numpos(xy[0]), ui.numpos(xy[1])];
|
|
}
|
|
if (e.id.substring(0, 8) == 'palette:') {
|
|
// On Internet Explorer get the view bounding rect as the palette
|
|
// doesn't return a correct bounding rect
|
|
var pbr = msiefixupbounds? $('viewcontent').getBoundingClientRect() : $('palettecontent').getBoundingClientRect();
|
|
var br = e.getBoundingClientRect();
|
|
return [br.left - pbr.left, br.top - pbr.top];
|
|
}
|
|
return [0, 0];
|
|
}
|
|
|
|
/**
|
|
* Return the x position of a widget.
|
|
*/
|
|
function widgetxpos(e) {
|
|
var t = widgettransform(e)[0];
|
|
return ui.numpos(e.style.left) + (isNull(t)? 0 : t);
|
|
}
|
|
|
|
/**
|
|
* Return the y position of a widget.
|
|
*/
|
|
function widgetypos(e) {
|
|
var t = widgettransform(e)[1];
|
|
return ui.numpos(e.style.top) + (isNull(t)? 0 : t);
|
|
}
|
|
|
|
/**
|
|
* Return the class of a widget.
|
|
*/
|
|
function widgetclass(e) {
|
|
return e.className.split(' ')[0];
|
|
}
|
|
|
|
/**
|
|
* Initialize a widget.
|
|
*/
|
|
function fixupwidget(e) {
|
|
|
|
// Add draggable class
|
|
var wc = e.className;
|
|
e.className = ui.isMobile()? (wc + ' draggable3dm') : (wc + ' draggable3d');
|
|
|
|
// Convert widget position to a CSS transform
|
|
var x = ui.numpos(e.style.left);
|
|
var y = ui.numpos(e.style.top);
|
|
var t = 'translate(' + x + 'px,' + y + 'px)';
|
|
e.style.setProperty('-webkit-transform', t, null);
|
|
e.style.setProperty('-moz-transform', t, null);
|
|
e.style.setProperty('-o-transform', t, null);
|
|
e.style.setProperty('-ms-transform', t, null);
|
|
e.style.setProperty('transform', t, null);
|
|
e.xtranslate = x;
|
|
e.ytranslate = y;
|
|
e.style.left = ui.pixpos(0);
|
|
e.style.top = ui.pixpos(0);
|
|
|
|
if (wc == 'entry' || wc == 'password') {
|
|
var i = car(childElements(e));
|
|
i.readOnly = true;
|
|
i.style.cursor = 'default';
|
|
return e;
|
|
}
|
|
if (wc == 'link') {
|
|
var l = car(childElements(e));
|
|
l.onclick = function(e) { return false; };
|
|
return e;
|
|
}
|
|
return e;
|
|
}
|
|
|
|
/**
|
|
* Cleanup a widget before saving it.
|
|
*/
|
|
function cleanupwidget(e) {
|
|
//debug('cleanupwidget', e);
|
|
|
|
// Adjust widget class
|
|
var wc = widgetclass(e);
|
|
e.className = wc;
|
|
|
|
// Convert CSS transform to an absolute position
|
|
e.style.left = ui.pixpos(widgetxpos(e));
|
|
e.style.top = ui.pixpos(widgetypos(e));
|
|
e.style.removeProperty('-webkit-transform');
|
|
e.style.removeProperty('-moz-transform');
|
|
e.style.removeProperty('-o-transform');
|
|
e.style.removeProperty('-ms-transform');
|
|
e.style.removeProperty('transform');
|
|
e.xtranslate = null;
|
|
e.ytranslate = null;
|
|
|
|
// Clear outline
|
|
e.style.removeProperty('outline');
|
|
|
|
if (wc == 'entry' || wc == 'password') {
|
|
var i = car(childElements(e));
|
|
i.readOnly = false;
|
|
i.style.cursor = null;
|
|
return e;
|
|
}
|
|
if (wc == 'link') {
|
|
var l = car(childElements(e));
|
|
l.onclick = null;
|
|
return e;
|
|
}
|
|
return e;
|
|
}
|
|
|
|
/**
|
|
* Clone a widget.
|
|
*/
|
|
function clonewidget(e) {
|
|
|
|
/**
|
|
* Clone an element's HTML.
|
|
*/
|
|
function mkclone(e) {
|
|
var ne = document.createElement('span');
|
|
|
|
// Skip the palette: prefix
|
|
ne.id = 'page:' + e.id.substr(8);
|
|
|
|
// Copy the class and HTML content
|
|
ne.className = widgetclass(e);
|
|
ne.innerHTML = e.innerHTML;
|
|
|
|
// Fixup the widget
|
|
fixupwidget(ne);
|
|
|
|
return ne;
|
|
}
|
|
|
|
/**
|
|
* Clone an element's position.
|
|
*/
|
|
function posclone(ne, e) {
|
|
ne.style.position = 'absolute';
|
|
movewidget(ne, widgetxpos(e), widgetypos(e));
|
|
return ne;
|
|
}
|
|
|
|
return posclone(mkclone(e), e);
|
|
}
|
|
|
|
/**
|
|
* Select a widget.
|
|
*/
|
|
function selectwidget(n, s) {
|
|
//debug('selectwidget', n, s);
|
|
if (isNull(n) || !s) {
|
|
// Clear the widget value field
|
|
$('widgetValue').value = '';
|
|
$('widgetValue').readOnly = true;
|
|
$('widgetValue').style.display = 'none';
|
|
|
|
// Show the app title
|
|
$('appTitle').style.display = 'block';
|
|
|
|
// Update the copy and delete buttons
|
|
$('copyWidgetButton').disabled = true;
|
|
$('deleteWidgetButton').disabled = true;
|
|
|
|
// Clear the widget outline
|
|
if (!isNull(n))
|
|
n.style.removeProperty('outline');
|
|
return true;
|
|
}
|
|
|
|
// Outline the widget
|
|
n.style.outline = '2px solid #598edd';
|
|
|
|
// Update the widget value field
|
|
$('widgetValue').value = widgettext(n);
|
|
$('widgetValue').readOnly = false || !editable;
|
|
$('widgetValue').style.display = 'block';
|
|
|
|
// Hide the app title
|
|
$('appTitle').style.display = 'none';
|
|
|
|
// Update the copy and delete buttons
|
|
$('copyWidgetButton').disabled = false || !editable;
|
|
$('deleteWidgetButton').disabled = false || !editable;
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Return the text of a widget.
|
|
*/
|
|
function widgettext(e) {
|
|
function formula(e) {
|
|
var f = e.id;
|
|
if (f.substring(0, 5) != 'page:')
|
|
return '=' + f;
|
|
return '';
|
|
}
|
|
|
|
function constant(e, f) {
|
|
var wc = widgetclass(e);
|
|
if (wc == 'hd1' || wc == 'hd2' || wc == 'text' || wc == 'section') {
|
|
var t = car(childElements(e)).innerHTML;
|
|
return t == f? '' : t;
|
|
}
|
|
if (wc == 'button' || wc == 'checkbox') {
|
|
var t = car(childElements(e)).value;
|
|
return t == f? '' : t;
|
|
}
|
|
if (wc == 'entry' || wc == 'password') {
|
|
var t = car(childElements(e)).defaultValue;
|
|
return t == f? '' : t;
|
|
}
|
|
if (wc == 'select') {
|
|
var t = car(childElements(car(childElements(e)))).value;
|
|
return t == f? '' : t;
|
|
}
|
|
if (wc == 'link') {
|
|
var lhr = car(childElements(e)).href;
|
|
var hr = lhr.substring(0, 5) == 'link:'? lhr.substring(5) : '';
|
|
var t = car(childElements(car(childElements(e)))).innerHTML;
|
|
return t == f? hr : (t == hr? hr : (t == ''? hr : hr + ',' + t));
|
|
}
|
|
if (wc == 'img') {
|
|
var src = car(childElements(e)).src;
|
|
return src == location.href? '' : src;
|
|
}
|
|
if (wc == 'list')
|
|
return '';
|
|
if (wc == 'table')
|
|
return '';
|
|
return '';
|
|
}
|
|
|
|
var f = formula(e);
|
|
var c = constant(e, f);
|
|
return f == ''? c : (c == ''? f : f + ',' + c);
|
|
}
|
|
|
|
/**
|
|
* Return true if a widget has editable text.
|
|
*/
|
|
function widgethastext(e) {
|
|
var wc = widgetclass(e);
|
|
if (wc == 'hd1' || wc == 'hd2' || wc == 'text' || wc == 'section')
|
|
return true;
|
|
if (wc == 'button' || wc == 'checkbox')
|
|
return true;
|
|
if (wc == 'entry' || wc == 'password')
|
|
return true;
|
|
if (wc == 'select')
|
|
return false;
|
|
if (wc == 'link')
|
|
return true;
|
|
if (wc == 'img')
|
|
return true;
|
|
if (wc == 'list')
|
|
return false;
|
|
if (wc == 'table')
|
|
return false;
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Set the text of a widget.
|
|
*/
|
|
function setwidgettext(e, t) {
|
|
function formula(t) {
|
|
if (t.length > 1 && t.substring(0, 1) == '=')
|
|
return car(t.split(','));
|
|
return '';
|
|
}
|
|
|
|
function constant(t) {
|
|
return t.length > 1 && t.substring(0, 1) == '='? cdr(t.split(',')) : t.split(',');
|
|
}
|
|
|
|
var f = formula(t);
|
|
var c = constant(t);
|
|
|
|
var wc = widgetclass(e);
|
|
e.id = f != ''? f.substring(1) : ('page:' + wc);
|
|
|
|
if (wc == 'hd1' || wc == 'hd2' || wc == 'text' || wc == 'section') {
|
|
car(childElements(e)).innerHTML = isNull(c)? f : car(c);
|
|
return t;
|
|
}
|
|
if (wc == 'button') {
|
|
car(childElements(e)).value = isNull(c)? f : car(c);
|
|
return t;
|
|
}
|
|
if (wc == 'entry' || wc == 'password') {
|
|
car(childElements(e)).defaultValue = isNull(c)? f : car(c);
|
|
return t;
|
|
}
|
|
if (wc == 'checkbox') {
|
|
car(childElements(e)).value = isNull(c)? f : car(c);
|
|
map(function(n) { if (n.nodeName == "SPAN") n.innerHTML = isNull(c)? f : car(c); return n; }, nodeList(e.childNodes));
|
|
return t;
|
|
}
|
|
if (wc == 'select') {
|
|
var ce = car(childElements(car(childElements(e))));
|
|
ce.value = isNull(c)? f : car(c);
|
|
ce.innerHTML = isNull(c)? f : car(c);
|
|
return t;
|
|
}
|
|
if (wc == 'list') {
|
|
e.innerHTML = '<table class="datatable" style="width: 100%;;"><tr><td class="datatd">' + (isNull(c)? f : car(c)) + '</td></tr><tr><td class="datatd">...</td></tr></table>';
|
|
return t;
|
|
}
|
|
if (wc == 'table') {
|
|
e.innerHTML = '<table class="datatable" style="width: 100%;"><tr><td class="datatdl">' + (isNull(c)? f : car(c)) + '</td><td class="datatdr">...</td></tr><tr><td class="datatdl">...</td><td class="datatdr">...</td></tr></table>';
|
|
return t;
|
|
}
|
|
if (wc == 'link') {
|
|
var ce = car(childElements(e));
|
|
ce.href = isNull(c)? 'link:/index.html' : ('link:' + car(c));
|
|
car(childElements(ce)).innerHTML = isNull(c)? (f != ''? f : '/index.html') : isNull(cdr(c))? (f != ''? f : car(c)) : cadr(c);
|
|
return t;
|
|
}
|
|
if (wc == 'img') {
|
|
car(childElements(e)).src = isNull(c)? '/public/img.png' : car(c);
|
|
return t;
|
|
}
|
|
return '';
|
|
}
|
|
|
|
/**
|
|
* Align a pos along a 9pixel grid.
|
|
*/
|
|
function snaptogrid(x) {
|
|
return Math.round(x / 10) * 10;
|
|
}
|
|
|
|
/**
|
|
* Bring a node to the top.
|
|
*/
|
|
function bringtotop(n) {
|
|
n.parentNode.appendChild(n);
|
|
}
|
|
|
|
/**
|
|
* Move a widget.
|
|
*/
|
|
var iefixuptransform = ui.isMSIE();
|
|
var fffixupoutline = ui.isFirefox() && (ui.firefoxVersion() > 4);
|
|
function movewidget(e, x, y) {
|
|
var t = 'translate(' + x + 'px,' + y + 'px)';
|
|
e.style.setProperty('-webkit-transform', t, null);
|
|
e.style.setProperty('-moz-transform', t, null);
|
|
e.style.setProperty('-o-transform', t, null);
|
|
// On Internet Explorer set the property directly as setProperty
|
|
// doesn't seem to apply
|
|
if (iefixuptransform)
|
|
e.style.msTransform = t;
|
|
e.style.setProperty('transform', t, null);
|
|
e.xtranslate = x;
|
|
e.ytranslate = y;
|
|
return e;
|
|
}
|
|
|
|
/**
|
|
* Return a widget bounding rect.
|
|
*/
|
|
var fffixupbounds = ui.isFirefox() && (ui.firefoxVersion() < 12);
|
|
function widgetbounds(e) {
|
|
var br = e.getBoundingClientRect();
|
|
if (!fffixupbounds)
|
|
return br;
|
|
|
|
// On Firefox < 12, apply CSS transform translation to bounding rect manually
|
|
//debug('fixup br', e, br.left, br.top, br.right, br.bottom, t[0], t[1]);
|
|
function fixuptransform(e) {
|
|
var t = widgettransform(e);
|
|
if (!isNull(e.xtranslate))
|
|
return [e.xtranslate, e.ytranslate];
|
|
var t = e.style.getPropertyValue('-webkit-transform') || e.style.getPropertyValue('-moz-transform') ||
|
|
e.style.getPropertyValue('-ms-transform') || e.style.getPropertyValue('-o-transform') ||
|
|
e.style.getPropertyValue('transform');
|
|
if (t) {
|
|
var xy = t.split('(')[1].split(')')[0].split(',');
|
|
return [ui.numpos(xy[0]), ui.numpos(xy[1])];
|
|
}
|
|
return [0, 0];
|
|
}
|
|
|
|
var t = fixuptransform(e);
|
|
var fbr = new Object();
|
|
fbr.left = br.left + t[0];
|
|
fbr.top = br.top + t[1];
|
|
fbr.right = fbr.left + e.offsetWidth;
|
|
fbr.bottom = fbr.top + e.offsetHeight;
|
|
return fbr;
|
|
}
|
|
|
|
/**
|
|
* Find a draggable element in a list.
|
|
*/
|
|
function draggable(x, y, l) {
|
|
//debug('draggable?', x, y, l);
|
|
if (isNull(l))
|
|
return null;
|
|
var n = car(l);
|
|
if (isNull(n.id) || n.id == '') {
|
|
var d = draggable(x, y, reverse(nodeList(n.childNodes)));
|
|
if (!isNull(d))
|
|
return d;
|
|
return draggable(x, y, cdr(l));
|
|
}
|
|
var br = widgetbounds(n);
|
|
//debug('element br', n, br.left, br.top, br.right, br.bottom);
|
|
if (x >= br.left && x <= br.right && y >= br.top && y <= br.bottom)
|
|
return n;
|
|
return draggable(x, y, cdr(l));
|
|
}
|
|
|
|
/**
|
|
* Play page in a frame.
|
|
*/
|
|
function showplaying() {
|
|
$('playPageButton').value = '<';
|
|
$('playdiv').style.display = 'block';
|
|
$('playdiv').visible = true;
|
|
$('playdiv').innerHTML = '';
|
|
$('playdiv').innerHTML = '<iframe id="playappframe" style="position: relative; border: 0px;" scrolling="no" frameborder="0" src="/' + appname + '"></iframe>';
|
|
if ($('pagediv').visible) {
|
|
$('pagediv').style.display = 'none'
|
|
$('pagediv').visible = false;
|
|
}
|
|
hidepalette();
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Show the page editor.
|
|
*/
|
|
function showeditor() {
|
|
$('playPageButton').value = '>';
|
|
$('pagediv').style.display = 'block'
|
|
$('pagediv').visible = true;
|
|
if ($('playdiv').visible) {
|
|
$('playdiv').style.display = 'none';
|
|
$('playdiv').innerHTML = '';
|
|
$('playdiv').visible = false;
|
|
}
|
|
hidepalette();
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Palette animation.
|
|
*/
|
|
function palettetransitionend(e) {
|
|
if ($('paletteview').className == 'paletteunloaded3dm')
|
|
$('paletteview').style.display = 'none';
|
|
}
|
|
|
|
$('paletteview').addEventListener('webkitTransitionEnd', palettetransitionend, false);
|
|
$('paletteview').addEventListener('transitionend', palettetransitionend, false);
|
|
|
|
/**
|
|
* Show the palette.
|
|
*/
|
|
function showpalette() {
|
|
if (ui.isMobile()) {
|
|
$('paletteview').className = 'paletteloading3dm';
|
|
$('paletteview').style.display = 'block';
|
|
$('paletteview').visible = true;
|
|
ui.delay(function transitionview() {
|
|
$('paletteview').className = 'paletteloaded3dm';
|
|
});
|
|
} else {
|
|
$('paletteview').className = 'paletteloaded3d';
|
|
$('paletteview').style.display = 'block';
|
|
$('paletteview').visible = true;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Hide the palette.
|
|
*/
|
|
function hidepalette() {
|
|
if (ui.isMobile()) {
|
|
$('paletteview').className = 'paletteunloading3dm';
|
|
$('paletteview').visible = false;
|
|
ui.delay(function transitionview() {
|
|
$('paletteview').className = 'paletteunloaded3dm';
|
|
});
|
|
} else {
|
|
$('paletteview').className = 'paletteunloaded3d';
|
|
$('paletteview').style.display = 'none';
|
|
$('paletteview').visible = false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Create page editor.
|
|
*/
|
|
function mkeditor() {
|
|
|
|
// Initialize header elements
|
|
$('widgetValue').readOnly = true;
|
|
$('widgetValue').style.display = 'none';
|
|
$('appTitle').style.display = 'block';
|
|
$('copyWidgetButton').disabled = true;
|
|
$('deleteWidgetButton').disabled = true;
|
|
$('addWidgetButton').disabled = !editable;
|
|
|
|
// Track widget dragging and selection
|
|
var dragging = null;
|
|
var selected = null;
|
|
var moved = false;
|
|
var mdown = false;
|
|
var moveX = 0;
|
|
var moveY = 0;
|
|
var dragX = 0;
|
|
var dragY = 0;
|
|
|
|
/**
|
|
* Handle a page change event
|
|
*/
|
|
function onpagechange(prop) {
|
|
if (!editable)
|
|
return false;
|
|
|
|
var newxml = pagexhtml();
|
|
if (savedxhtml == newxml)
|
|
return false;
|
|
showstatus('Modified');
|
|
|
|
// Save property changes right away
|
|
if (prop)
|
|
return save(newxml);
|
|
|
|
// Autosave other changes after 1 second
|
|
ui.async(function autosave() {
|
|
if (savedxhtml == newxml) {
|
|
showstatus('Saved');
|
|
return false;
|
|
}
|
|
return save(newxml);
|
|
}, 1000);
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Handle a widget select event.
|
|
*/
|
|
function onselectwidget(w) {
|
|
if (w == selected)
|
|
return true;
|
|
selected = w;
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Render widget move animation.
|
|
*/
|
|
function onmoveanimation() {
|
|
//debug('onmoveanimation');
|
|
|
|
// Stop animation if we're not dragging an element anymore
|
|
if (dragging == null)
|
|
return true;
|
|
|
|
// Request the next animation frame
|
|
ui.animation(onmoveanimation);
|
|
|
|
// Nothing to do if the selected widget has not moved
|
|
if (moveX == dragX && moveY == dragY)
|
|
return true;
|
|
|
|
// Compute position of dragged element
|
|
var curX = widgetxpos(dragging);
|
|
var curY = widgetypos(dragging);
|
|
var newX = curX + (moveX - dragX);
|
|
var newY = curY + (moveY - dragY);
|
|
|
|
var okx = true;
|
|
if (newX + dragging.clientWidth > 1024) {
|
|
newX = 1024 - dragging.clientWidth;
|
|
okx = false;
|
|
}
|
|
if (newX < 0) {
|
|
newX = 0;
|
|
okx = false;
|
|
}
|
|
if (okx)
|
|
dragX = moveX;
|
|
var oky = true;
|
|
if (newY + dragging.clientHeight > 1024) {
|
|
newY = 1024 - dragging.clientHeight;
|
|
oky= false;
|
|
}
|
|
if (newY < 0) {
|
|
newY = 0;
|
|
oky = false;
|
|
}
|
|
if (oky)
|
|
dragY = moveY;
|
|
|
|
// On Firefox > 4, remove outline before moving widget as it's not
|
|
// correctly painted
|
|
if (fffixupoutline)
|
|
dragging.style.removeProperty('outline');
|
|
|
|
// Move the dragged element
|
|
movewidget(dragging, newX, newY);
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Handle a mouse down event.
|
|
*/
|
|
function onmousedown(e) {
|
|
// On mouse controlled devices, run component selection logic right away
|
|
if (!ui.isMobile()) {
|
|
//debug('onmousedown-click');
|
|
onclick(e);
|
|
}
|
|
|
|
// Find a draggable widget
|
|
var d = draggable(moveX, moveY, reverse(nodeList($('pagediv').childNodes)));
|
|
//debug('dragging', d, 'selected', selected);
|
|
if (d == null || d != selected)
|
|
return true;
|
|
dragging = d;
|
|
|
|
// Remember mouse position
|
|
dragX = moveX;
|
|
dragY = moveY;
|
|
|
|
// Start move animation
|
|
ui.animation(onmoveanimation);
|
|
|
|
e.preventDefault();
|
|
return true;
|
|
}
|
|
|
|
if (!ui.isMobile()) {
|
|
$('pagediv').onmousedown = function(e) {
|
|
//debug('onmousedown', e.target);
|
|
mdown = true;
|
|
moveX = e.clientX;
|
|
moveY = e.clientY;
|
|
moved = false;
|
|
return onmousedown(e);
|
|
};
|
|
$('palettecontent').onmousedown = function(e) {
|
|
//debug('onmousedown', e.target);
|
|
mdown = true;
|
|
moveX = e.clientX;
|
|
moveY = e.clientY;
|
|
moved = false;
|
|
return onmousedown(e);
|
|
};
|
|
} else {
|
|
$('pagediv').ontouchstart = function(e) {
|
|
//debug('ontouchstart');
|
|
mdown = true;
|
|
moveX = e.touches[0].clientX;
|
|
moveY = e.touches[0].clientY;
|
|
moved = false;
|
|
return onmousedown(e);
|
|
};
|
|
$('palettecontent').ontouchstart = function(e) {
|
|
//debug('ontouchstart');
|
|
mdown = true;
|
|
moveX = e.touches[0].clientX;
|
|
moveY = e.touches[0].clientY;
|
|
moved = false;
|
|
return onmousedown(e);
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Handle a mouse up event.
|
|
*/
|
|
function onmouseup(e) {
|
|
// Simulate onclick event
|
|
if (ui.isMobile() && !moved) {
|
|
//debug('ontouchend-click');
|
|
return onclick(e);
|
|
}
|
|
|
|
if (dragging == null)
|
|
return true;
|
|
|
|
// Snap dragged widget to grid
|
|
var newX = snaptogrid(widgetxpos(dragging));
|
|
var newY = snaptogrid(widgetypos(dragging));
|
|
movewidget(dragging, newX, newY);
|
|
|
|
// Forget dragged element
|
|
dragging = null;
|
|
|
|
// Trigger page change event
|
|
onpagechange(false);
|
|
|
|
// On Firefox > 4, re-apply the outline after the widget has been repositioned
|
|
if (fffixupoutline && !isNull(selected)) {
|
|
ui.delay(function() {
|
|
if (!isNull(selected))
|
|
selected.style.outline = '2px solid #598edd';
|
|
}, 32);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
if (!ui.isMobile()) {
|
|
window.onmouseup = function(e) {
|
|
//debug('onmouseup');
|
|
if (!mdown)
|
|
return true;
|
|
return onmouseup(e);
|
|
};
|
|
} else {
|
|
window.ontouchend = function(e) {
|
|
//debug('ontouchend');
|
|
if (!mdown)
|
|
return true;
|
|
return onmouseup(e);
|
|
}
|
|
}
|
|
|
|
if (!ui.isMobile()) {
|
|
window.onmousemove = function(e) {
|
|
//debug('onmousemove');
|
|
|
|
// Record mouse position
|
|
if (e.clientX != moveX) {
|
|
moved = true;
|
|
moveX = e.clientX;
|
|
}
|
|
if (e.clientY != moveY) {
|
|
moved = true;
|
|
moveY = e.clientY;
|
|
}
|
|
if (dragging == null)
|
|
return true;
|
|
return false;
|
|
};
|
|
} else {
|
|
window.ontouchmove = function(e) {
|
|
//debug('ontouchmove');
|
|
|
|
// Record touch position
|
|
var t = e.touches[0];
|
|
if (t.clientX != moveX) {
|
|
moved = true;
|
|
moveX = t.clientX;
|
|
}
|
|
if (t.clientY != moveY) {
|
|
moved = true;
|
|
moveY = t.clientY;
|
|
}
|
|
if (dragging == null)
|
|
return true;
|
|
return false;
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Handle a mouse click event.
|
|
*/
|
|
function onclick(e) {
|
|
|
|
// Find selected element
|
|
var palvis = $('paletteview').visible? true : false;
|
|
var s = draggable(moveX, moveY, reverse(nodeList((palvis? $('palettecontent') : $('pagediv')).childNodes)));
|
|
//debug('selected', s);
|
|
if (s == null) {
|
|
if (selected != null) {
|
|
|
|
// Reset current selection
|
|
selectwidget(selected, false);
|
|
selected = null;
|
|
|
|
// Trigger widget select event
|
|
onselectwidget(null);
|
|
}
|
|
|
|
// Dismiss the palette
|
|
if (palvis && isNull(draggable(moveX, moveY, mklist($('palettecontent')))))
|
|
hidepalette();
|
|
|
|
return true;
|
|
}
|
|
|
|
// Deselect the previously selected element
|
|
selectwidget(selected, false);
|
|
|
|
// Clone widget dragged from palette
|
|
if (s.id.substring(0, 8) == 'palette:') {
|
|
selected = clonewidget(s);
|
|
|
|
// Add it to the page
|
|
$('pagediv').appendChild(selected);
|
|
movewidget(selected, widgetxpos(selected) + $('viewcontent').scrollLeft, widgetypos(selected) + $('viewcontent').scrollTop);
|
|
|
|
// Hide the palette
|
|
hidepalette();
|
|
|
|
// Trigger page change event
|
|
onpagechange(true);
|
|
|
|
// Select the element
|
|
selectwidget(selected, true);
|
|
|
|
// Trigger widget select event
|
|
onselectwidget(selected);
|
|
|
|
return true;
|
|
}
|
|
|
|
// Bring selected widget to the top
|
|
selected = s;
|
|
bringtotop(selected);
|
|
|
|
// Select the widget
|
|
selectwidget(selected, true);
|
|
|
|
// Trigger widget select event
|
|
onselectwidget(selected);
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Handle field on change events.
|
|
*/
|
|
$('widgetValue').onchange = $('widgetValue').onblur = function() {
|
|
if (selected == null)
|
|
return false;
|
|
setwidgettext(selected, $('widgetValue').value);
|
|
|
|
// Trigger page change event
|
|
onpagechange(true);
|
|
return false;
|
|
};
|
|
|
|
// Handle add widget event.
|
|
ui.onclick($('addWidgetButton'), function(e) {
|
|
|
|
// Show / hide the palette
|
|
if ($('paletteview').visible)
|
|
return hidepalette();
|
|
return showpalette();
|
|
});
|
|
|
|
// Handle delete event.
|
|
ui.onclick($('deleteWidgetButton'), function(e) {
|
|
if (selected == null)
|
|
return false;
|
|
|
|
// Reset current selection
|
|
selectwidget(selected, false);
|
|
|
|
// Remove selected widget
|
|
selected.parentNode.removeChild(selected);
|
|
selected = null;
|
|
|
|
// Trigger widget select event
|
|
onselectwidget(null);
|
|
|
|
// Trigger page change event
|
|
onpagechange(true);
|
|
return false;
|
|
});
|
|
|
|
// Handle copy event.
|
|
ui.onclick($('copyWidgetButton'), function(e) {
|
|
if (selected == null)
|
|
return false;
|
|
if (selected.id.substring(0, 8) == 'palette:')
|
|
return false;
|
|
|
|
// Reset current selection
|
|
selectwidget(selected, false);
|
|
|
|
// Clone selected widget
|
|
selected = clonewidget(selected);
|
|
|
|
// Add it to the page
|
|
$('pagediv').appendChild(selected);
|
|
|
|
// Move 10 pixels down right
|
|
movewidget(selected, widgetxpos(selected) + 10, widgetypos(selected) + 10);
|
|
|
|
// Bring it to the top
|
|
bringtotop(selected);
|
|
|
|
// Select the element
|
|
selectwidget(selected, true);
|
|
|
|
// Trigger widget select event
|
|
onselectwidget(selected);
|
|
|
|
// Trigger page change event
|
|
onpagechange(true);
|
|
return false;
|
|
});
|
|
|
|
/**
|
|
* Handle play page button event.
|
|
*/
|
|
ui.onclick($('playPageButton'), function(e) {
|
|
|
|
// Show / hide the page play frame
|
|
if ($('playdiv').visible)
|
|
return showeditor();
|
|
return showplaying();
|
|
});
|
|
|
|
// Show the editor
|
|
showeditor();
|
|
|
|
return true;
|
|
};
|
|
|
|
/**
|
|
* Get and display the requested app page.
|
|
*/
|
|
(function getpage() {
|
|
if (isNull(appname))
|
|
return false;
|
|
workingstatus(true);
|
|
showstatus('Loading');
|
|
|
|
return pages.get(appname, function(doc) {
|
|
|
|
// Stop now if we didn't get a page
|
|
if (doc == null) {
|
|
errorstatus('Couldn\'t get the app info');
|
|
workingstatus(false);
|
|
return false;
|
|
}
|
|
|
|
// Get the page from the ATOM entry, convert it to XHTML and place it in a hidden buffer
|
|
var pageentry = car(atom.readATOMEntry(mklist(doc)));
|
|
var content = namedElementChild("'content", pageentry);
|
|
var el = isNull(content)? mklist() : elementChildren(content);
|
|
if (isNull(el))
|
|
$('xhtmlbuffer').innerHTML = '<div id="page"></div>';
|
|
else
|
|
$('xhtmlbuffer').innerHTML = writeStrings(writeXML(el, false));
|
|
|
|
// Remove any existing page nodes from the editor div
|
|
var fnodes = filter(function(e) {
|
|
if (isNull(e.id) || e.id == '')
|
|
return false;
|
|
return true;
|
|
}, nodeList($('pagediv').childNodes));
|
|
|
|
map(function(e) {
|
|
$('pagediv').removeChild(e);
|
|
}, fnodes);
|
|
|
|
// Fixup widgets and append them to the editor
|
|
map(function(e) {
|
|
$('pagediv').appendChild(e);
|
|
fixupwidget(e);
|
|
return e;
|
|
}, nodeList($('xhtmlbuffer').childNodes[0].childNodes));
|
|
|
|
savedxhtml = pagexhtml($('pagediv'));
|
|
|
|
// Enable author to edit the page
|
|
author = elementValue(namedElementChild("'author", pageentry));
|
|
editable = author == username;
|
|
$('addWidgetButton').disabled = !editable;
|
|
if (editable)
|
|
onlinestatus();
|
|
else
|
|
showstatus('Read only');
|
|
|
|
workingstatus(false);
|
|
return true;
|
|
});
|
|
})();
|
|
|
|
/**
|
|
* Return the current page XHTML content.
|
|
*/
|
|
function pagexhtml() {
|
|
|
|
// Copy page DOM to hidden buffer
|
|
$('xhtmlbuffer').innerHTML = '<div id="page"></div>'
|
|
var div = $('xhtmlbuffer').childNodes[0];
|
|
|
|
// Capture the nodes inside the page div
|
|
div.innerHTML = $('pagediv').innerHTML;
|
|
var nodes = nodeList(div.childNodes);
|
|
map(function(e) {
|
|
div.removeChild(e);
|
|
return e;
|
|
}, nodes);
|
|
|
|
// Filter out palette and editor artifacts, which are not
|
|
// part of the page, as well as nodes positioned out the
|
|
// editing area
|
|
var fnodes = filter(function(e) {
|
|
if (isNull(e.id) || e.id == '')
|
|
return false;
|
|
return true;
|
|
}, nodes);
|
|
|
|
// Reposition and cleanup nodes
|
|
map(function(e) {
|
|
cleanupwidget(e);
|
|
return e;
|
|
}, fnodes);
|
|
|
|
// Sort them by position
|
|
var snodes = fnodes.sort(function(a, b) {
|
|
var ay = widgetypos(a);
|
|
var by = widgetypos(b);
|
|
if (ay < by) return -1;
|
|
if (ay > by) return 1;
|
|
var ax = widgetxpos(a);
|
|
var bx = widgetxpos(b);
|
|
if (ax < bx) return -1;
|
|
if (ax > bx) return 1;
|
|
return 0;
|
|
});
|
|
|
|
// Append the sorted nodes back to the div in order
|
|
map(function(e) {
|
|
div.appendChild(e);
|
|
return e;
|
|
}, snodes);
|
|
|
|
// Convert the page to XHTML
|
|
var lxhtml = readXHTMLElement(div);
|
|
var xhtml = writeStrings(writeXML(lxhtml, false));
|
|
return xhtml;
|
|
}
|
|
|
|
/**
|
|
* Save the current page.
|
|
*/
|
|
function save(newxml) {
|
|
workingstatus(true);
|
|
showstatus('Saving');
|
|
|
|
// Get the current page XHTML content
|
|
savedxhtml = newxml;
|
|
|
|
// Update the page ATOM entry
|
|
var entry = '<?xml version="1.0" encoding="UTF-8"?>\n' + '<entry xmlns="http://www.w3.org/2005/Atom">' +
|
|
'<title type="text">' + appname + '</title><id>' + appname + '</id><author><email>' + author + '</email></author>' +
|
|
'<content type="application/xml">' + newxml + '</content></entry>';
|
|
|
|
pages.put(appname, entry, function(e) {
|
|
if (e) {
|
|
showstatus('Local copy');
|
|
workingstatus(false);
|
|
return false;
|
|
}
|
|
showstatus('Saved');
|
|
workingstatus(false);
|
|
return false;
|
|
});
|
|
return true;
|
|
};
|
|
|
|
/**
|
|
* Handle app info button event.
|
|
*/
|
|
/* Disabled for now.
|
|
ui.onclick($('appInfoButton'), function(e) {
|
|
return ui.navigate('/#view=info&app=' + appname, '_view');
|
|
});
|
|
*/
|
|
|
|
/**
|
|
* Initialize the page editor.
|
|
*/
|
|
mkeditor();
|
|
|
|
})();
|
|
</script>
|
|
|
|
</div>
|