git-svn-id: http://svn.us.apache.org/repos/asf/tuscany@1361914 13f79535-47bb-0310-9956-ffa450edef68
1081 lines
32 KiB
HTML
1081 lines
32 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="contentdiv" class="viewcontent" style="width: 2500px;">
|
|
<div id="pagediv" class="pagediv" style="top: 0px; left: -2500px; width: 5000px; height: 5000px;">
|
|
|
|
<!--
|
|
<div class="guide" style="position: absolute; left: 2500px; top: 0px; width: 320px; height: 460px;"></div>
|
|
<div class="guide" style="position: absolute; left: 2500px; top: 0px; width: 480px; height: 300px;"></div>
|
|
<div class="guide" style="position: absolute; left: 2500px; top: 0px; width: 768px; height: 911px;"></div>
|
|
<div class="guide" style="position: absolute; left: 2500px; top: 0px; width: 1024px; height: 655px;"></div>
|
|
-->
|
|
|
|
<span class="h1" id="palette:h1" style="position: absolute; left: 0px; top: 0px;"><h1>Header Level 1</h1></span>
|
|
<span class="h2" id="palette:h2" style="position: absolute; left: 0px; top: 30px;"><h2>Header Level 2</h2></span>
|
|
<span class="section" id="palette:section" style="position: absolute; left: 0px; top: 60px; width: 220px;"><span class="Section">Section</span></span>
|
|
<span class="text" id="palette:text" style="position: absolute; left: 0px; top: 90px;"><span>Some text</span></span>
|
|
<span class="link" id="palette:link" style="position: absolute; left: 80px; top: 90px;"><a href="/"><span>Link</span></a></span>
|
|
<span class="button" id="palette:graybutton" style="position: absolute; left: 0px; top: 120px;"><input type="button" value="Button" class="graybutton"/></span>
|
|
<span class="button" id="palette:bluebutton" style="position: absolute; left: 80px; top: 120px;"><input type="button" value="Button" class="graybutton bluebutton"/></span>
|
|
<span class="button" id="palette:redbutton" style="position: absolute; left: 160px; top: 120px;"><input type="button" value="Button" class="graybutton redbutton"/></span>
|
|
<span class="entry" id="palette:entry" style="position: absolute; left: 0px; top: 160px;"><input type="text" value="Entry Field" class="flatentry" size="20" autocapitalize="off"/></span>
|
|
<span class="password" id="palette:password" style="position: absolute; left: 0px; top: 190px;"><input type="password" value="Password" class="flatentry" size="20"/></span>
|
|
<span class="checkbox" id="palette:checkbox" style="position: absolute; left: 0px; top: 220px;"><input type="checkbox" value="Checkbox" class="flatcheckbox"/><span>Checkbox</span></span>
|
|
<!--
|
|
<span class="select" id="palette:select" style="position: absolute; left: 80px; top: 220px;"><select><option value="select">Selection</option></select></span>
|
|
-->
|
|
<span class="list" id="palette:list" style="position: absolute; left: 0px; top: 250px; width: 220px;">
|
|
<table class="datatable" style="width: 220px;">
|
|
<tr><td class="datatd">List</td></tr>
|
|
<tr><td class="datatd">List</td></tr>
|
|
<tr><td class="datatd">List</td></tr>
|
|
</table>
|
|
</span>
|
|
<span class="table" id="palette:table" style="position: absolute; left: 0px; top: 320px; width: 220px;">
|
|
<table class="datatable" style="width: 220px;">
|
|
<tr><td class="datatdl">Table</td><td class="datatdr">Table</td></tr>
|
|
<tr><td class="datatdl">Table</td><td class="datatdr">Table</td></tr>
|
|
<tr><td class="datatdl">Table</td><td class="datatdr">Table</td></tr>
|
|
</table>
|
|
</span>
|
|
<!--
|
|
<span class="iframe fakeframe" id="palette:iframe" style="position: absolute; left: 0px; top: 380px; width: 200px;"><a href="/public/iframe-min.html"><span class="fakeframe"><span>Frame ...</span></span></a></span>
|
|
-->
|
|
<span class="img" id="palette:img" style="position: absolute; left: 0px; top: 410px;"><img id="imgimg"/></span>
|
|
</div>
|
|
|
|
<div id="playdiv" style="visibility: hidden; position: absolute; top: 0px; left: 0px; width: 2500px; height: 5000px;">
|
|
</div>
|
|
|
|
</div>
|
|
|
|
<div id="buffer" style="visibility: hidden; width: 0px; height: 0px"></div>
|
|
|
|
<script type="text/javascript">
|
|
(function() {
|
|
|
|
/**
|
|
* Get the current app name.
|
|
*/
|
|
var appname = ui.fragmentParams(location)['app'];
|
|
|
|
/**
|
|
* Return the link to an app.
|
|
*/
|
|
function applink(appname) {
|
|
var protocol = location.protocol;
|
|
var host = location.hostname;
|
|
var port = ':' + location.port;
|
|
if (port == ':80' || port == ':443' || port == ':')
|
|
port = '';
|
|
var link = protocol + '//' + host + port + '/' + appname + '/';
|
|
return link;
|
|
}
|
|
|
|
/**
|
|
* Set page titles.
|
|
*/
|
|
document.title = config.windowtitle() + ' - Page - ' + appname;
|
|
|
|
/**
|
|
* Set header div.
|
|
*/
|
|
$('viewhead').innerHTML = '<span id="appTitle" class="cmenu">' + appname + '</span>' +
|
|
'<input type="button" id="deleteWidgetButton" title="Delete a Widget" class="graybutton redbutton plusminus" style="position: absolute; top: 4px; left: 5px;" disabled="true" value="-"/>' +
|
|
'<span style="position: absolute; top: 0px; left: 45px; right: 115px; 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%; visibility: hidden;" readonly="readonly"/></span>' +
|
|
'<input type="button" id="playPageButton" title="View page" class="graybutton plusminus" style="position: absolute; top: 4px; right: 75px;" value=">"/>' +
|
|
'<input type="button" id="copyWidgetButton" title="Copy a Widget" class="graybutton bluebutton" style="position: absolute; top: 4px; right: 40px; font-size: 16px;" disabled="true" value="C"/>' +
|
|
'<input type="button" id="addWidgetButton" title="Add a Widget" class="graybutton bluebutton plusminus" style="position: absolute; top: 4px; right: 5px;" disabled="true" value="+"/>';
|
|
|
|
/**
|
|
* Track the current page, author, and saved XHTML content.
|
|
*/
|
|
var author = '';
|
|
var editable = false;
|
|
var savedpagexhtml = '';
|
|
|
|
/**
|
|
* Page editor area, widget value field, add, delete and play page buttons.
|
|
*/
|
|
var cdiv = $('contentdiv');
|
|
var pagediv = $('pagediv');
|
|
var evisible = true;
|
|
var pdiv = $('playdiv');
|
|
var wadd = $('addWidgetButton');
|
|
var wdelete = $('deleteWidgetButton');
|
|
var wcopy = $('copyWidgetButton');
|
|
var wvalue = $('widgetValue');
|
|
var atitle = $('appTitle');
|
|
var pplay = $('playPageButton');
|
|
|
|
/**
|
|
* Set images.
|
|
*/
|
|
$('imgimg').src = ui.b64img(appcache.get('/public/img.b64'));
|
|
|
|
/**
|
|
* Init component references.
|
|
*/
|
|
var editorComp = sca.component('Editor');
|
|
var pages = sca.reference(editorComp, 'pages');
|
|
|
|
/**
|
|
* Page editing functions.
|
|
*/
|
|
var page = {};
|
|
|
|
/**
|
|
* Default positions and sizes.
|
|
*/
|
|
page.palcx = 2500;
|
|
|
|
/**
|
|
* Init a page editor.
|
|
*/
|
|
page.mkedit = function(pagediv, atitle, wvalue, wadd, wcopy, wdelete, onchange, onselect) {
|
|
|
|
// Track element dragging and selection
|
|
page.dragging = null;
|
|
page.selected = null;
|
|
wvalue.readOnly = true;
|
|
wvalue.style.visibility = 'hidden';
|
|
atitle.style.visibility = 'visible';
|
|
page.mousemoved = false;
|
|
wcopy.disabled = true;
|
|
wdelete.disabled = true;
|
|
wadd.disabled = !editable;
|
|
|
|
// Trigger widget select and page change events
|
|
page.onpagechange = onchange;
|
|
page.onselectwidget = onselect;
|
|
|
|
/**
|
|
* Handle a mouse down event.
|
|
*/
|
|
function onmousedown(e) {
|
|
// On mouse controlled devices, run component selection logic right away
|
|
var selected = page.selected;
|
|
if (typeof e.touches == 'undefined') {
|
|
//debug('onmousedown-click');
|
|
onclick(e);
|
|
}
|
|
|
|
// Find a draggable element
|
|
var dragging = page.draggable(e.target, pagediv);
|
|
if (dragging == null || dragging != page.selected)
|
|
return true;
|
|
page.dragging = dragging;
|
|
|
|
// Remember mouse position
|
|
var pos = typeof e.touches != "undefined"? e.touches[0] : e;
|
|
page.mousemoved = false;
|
|
page.dragX = pos.screenX;
|
|
page.dragY = pos.screenY;
|
|
page.moveX = pos.screenX;
|
|
page.moveY = pos.screenY;
|
|
|
|
// Prevent default behavior on first click on a widget
|
|
if (e.preventDefault)
|
|
e.preventDefault();
|
|
else
|
|
e.returnValue = false;
|
|
|
|
return true;
|
|
}
|
|
|
|
if (!ui.isMobile()) {
|
|
pagediv.onmousedown = function(e) {
|
|
//debug('onmousedown');
|
|
return onmousedown(e);
|
|
};
|
|
} else {
|
|
pagediv.ontouchstart = function(e) {
|
|
//debug('ontouchstart');
|
|
return onmousedown(e);
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Handle a mouse up event.
|
|
*/
|
|
function onmouseup(e) {
|
|
if (page.dragging == null)
|
|
return true;
|
|
|
|
// Snap to grid
|
|
var newX = page.gridsnap(ui.numpos(page.dragging.style.left));
|
|
var newY = page.gridsnap(ui.numpos(page.dragging.style.top));
|
|
page.dragging.style.left = ui.pixpos(newX);
|
|
page.dragging.style.top = ui.pixpos(newY);
|
|
|
|
// Fixup widget style
|
|
page.initwidget(page.dragging);
|
|
|
|
// Forget dragged element
|
|
page.dragging = null;
|
|
|
|
// Trigger page change event
|
|
page.onpagechange(false);
|
|
|
|
// Simulate onclick event
|
|
onclick(e);
|
|
|
|
return true;
|
|
}
|
|
|
|
if (!ui.isMobile()) {
|
|
pagediv.onmouseup = function(e) {
|
|
//debug('onmouseup');
|
|
return onmouseup(e);
|
|
};
|
|
} else {
|
|
pagediv.ontouchend = function(e) {
|
|
//debug('ontouchend');
|
|
return onmouseup(e);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Handle a mouse move event.
|
|
*/
|
|
function onmousemove(e) {
|
|
|
|
// Track mouse moves
|
|
page.mousemoved = true;
|
|
|
|
if (page.dragging == null)
|
|
return true;
|
|
|
|
// Ignore duplicate mouse move events
|
|
if (page.moveX == page.dragX && page.moveY == page.dragY)
|
|
return true;
|
|
|
|
// Compute position of dragged element
|
|
var curX = ui.numpos(page.dragging.style.left);
|
|
var curY = ui.numpos(page.dragging.style.top);
|
|
var newX = curX + (page.moveX - page.dragX);
|
|
var newY = curY + (page.moveY - page.dragY);
|
|
if (newX >= page.palcx)
|
|
page.dragX = page.moveX;
|
|
else
|
|
newX = page.palcx;
|
|
if (newY >= 0)
|
|
page.dragY = page.moveY;
|
|
else
|
|
newY = 0;
|
|
|
|
// Move the dragged element
|
|
page.dragging.style.left = ui.pixpos(newX);
|
|
page.dragging.style.top = ui.pixpos(newY);
|
|
page.constrainwidget(page.dragging);
|
|
|
|
return true;
|
|
}
|
|
|
|
if (!ui.isMobile()) {
|
|
window.onmousemove = function(e) {
|
|
|
|
// Remember mouse position
|
|
page.moveX = e.screenX;
|
|
page.moveY = e.screenY;
|
|
|
|
return onmousemove(e);
|
|
};
|
|
} else {
|
|
pagediv.ontouchmove = function(e) {
|
|
|
|
// Remember touch position
|
|
var pos = e.touches[0];
|
|
if (page.moveX == pos.screenX && page.moveY == pos.screenY)
|
|
return true;
|
|
page.moveX = pos.screenX;
|
|
page.moveY = pos.screenY;
|
|
if (page.moveX == page.dragX && page.moveY == page.dragY)
|
|
return true;
|
|
|
|
onmousemove(e);
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Handle a mouse click event.
|
|
*/
|
|
function onclick(e) {
|
|
|
|
// Find selected element
|
|
var selected = page.draggable(e.target, pagediv);
|
|
if (selected == null) {
|
|
if (page.selected != null) {
|
|
|
|
// Reset current selection
|
|
page.selectwidget(page.selected, false, atitle, wvalue, wcopy, wdelete);
|
|
page.selected = null;
|
|
|
|
// Trigger widget select event
|
|
page.onselectwidget(null);
|
|
}
|
|
|
|
// Dismiss the palette
|
|
if (ui.numpos(pagediv.style.left) != (page.palcx * -1))
|
|
pagediv.style.left = ui.pixpos(page.palcx * -1);
|
|
|
|
return true;
|
|
}
|
|
|
|
// Deselect the previously selected element
|
|
page.selectwidget(page.selected, false, atitle, wvalue, wcopy, wdelete);
|
|
|
|
// Clone element dragged from palette
|
|
if (selected.id.substring(0, 8) == 'palette:') {
|
|
page.selected = page.clone(selected);
|
|
|
|
// Move into the editing area and hide the palette
|
|
page.selected.style.left = ui.pixpos(ui.numpos(page.selected.style.left) + page.palcx);
|
|
page.initwidget(page.selected);
|
|
pagediv.style.left = ui.pixpos(page.palcx * -1);
|
|
page.constrainwidget(page.selected);
|
|
|
|
// Bring it to the top
|
|
page.bringtotop(page.selected);
|
|
|
|
// Trigger page change event
|
|
page.onpagechange(true);
|
|
|
|
// Select the element
|
|
page.selectwidget(page.selected, true, atitle, wvalue, wcopy, wdelete);
|
|
|
|
// Trigger widget select event
|
|
page.onselectwidget(page.selected);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
// Bring selected element to the top
|
|
page.selected = selected;
|
|
page.bringtotop(page.selected);
|
|
|
|
// Select the element
|
|
page.selectwidget(page.selected, true, atitle, wvalue, wcopy, wdelete);
|
|
|
|
// Trigger widget select event
|
|
page.onselectwidget(page.selected);
|
|
|
|
return true;
|
|
}
|
|
|
|
if (!ui.isMobile()) {
|
|
pagediv.onclick = function(e) {
|
|
//debug('onclick');
|
|
return onclick(e);
|
|
};
|
|
} else {
|
|
pagediv.onclick = function(e) {
|
|
//debug('onclick');
|
|
return onclick(e);
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Handle field on change events.
|
|
*/
|
|
wvalue.onchange = wvalue.onblur = function() {
|
|
if (page.selected == null)
|
|
return false;
|
|
page.settext(page.selected, wvalue.value);
|
|
|
|
// Trigger page change event
|
|
page.onpagechange(true);
|
|
return false;
|
|
};
|
|
|
|
// Handle add widget event.
|
|
wadd.onclick = function() {
|
|
|
|
// Show the palette
|
|
pagediv.style.left = ui.pixpos(0);
|
|
return false;
|
|
};
|
|
|
|
// Handle delete event.
|
|
wdelete.onclick = function() {
|
|
if (page.selected == null)
|
|
return false;
|
|
|
|
// Reset current selection
|
|
page.selectwidget(page.selected, false, atitle, wvalue, wcopy, wdelete);
|
|
|
|
// Remove selected widget
|
|
page.selected.parentNode.removeChild(page.selected);
|
|
page.selected = null;
|
|
|
|
// Trigger widget select event
|
|
page.onselectwidget(null);
|
|
|
|
// Trigger page change event
|
|
page.onpagechange(true);
|
|
return false;
|
|
};
|
|
|
|
// Handle copy event.
|
|
wcopy.onclick = function() {
|
|
if (page.selected == null)
|
|
return false;
|
|
if (page.selected.id.substring(0, 8) == 'palette:')
|
|
return false;
|
|
|
|
// Reset current selection
|
|
page.selectwidget(page.selected, false, atitle, wvalue, wcopy, wdelete);
|
|
|
|
// Clone selected widget
|
|
page.selected = page.clone(page.selected);
|
|
|
|
// Move 10 pixels down right
|
|
page.selected.style.left = ui.pixpos(ui.numpos(page.selected.style.left) + 10);
|
|
page.selected.style.top = ui.pixpos(ui.numpos(page.selected.style.top) + 10);
|
|
page.constrainwidget(page.selected);
|
|
|
|
// Bring it to the top
|
|
page.bringtotop(page.selected);
|
|
|
|
// Select the element
|
|
page.selectwidget(page.selected, true, atitle, wvalue, wcopy, wdelete);
|
|
|
|
// Trigger widget select event
|
|
page.onselectwidget(page.selected);
|
|
|
|
// Trigger page change event
|
|
page.onpagechange(true);
|
|
return false;
|
|
};
|
|
|
|
return pagediv;
|
|
};
|
|
|
|
/**
|
|
* Return the text of a widget.
|
|
*/
|
|
page.text = function(e) {
|
|
function formula(e) {
|
|
var f = e.id;
|
|
if (f.substring(0, 5) != 'page:')
|
|
return '=' + f;
|
|
return '';
|
|
}
|
|
|
|
function constant(e, f) {
|
|
if (e.className == 'h1' || e.className == 'h2' || e.className == 'text' || e.className == 'section') {
|
|
var t = car(childElements(e)).innerHTML;
|
|
return t == f? '' : t;
|
|
}
|
|
if (e.className == 'button' || e.className == 'checkbox') {
|
|
var t = car(childElements(e)).value;
|
|
return t == f? '' : t;
|
|
}
|
|
if (e.className == 'entry' || e.className == 'password') {
|
|
var t = car(childElements(e)).defaultValue;
|
|
return t == f? '' : t;
|
|
}
|
|
/*
|
|
if (e.className == 'select') {
|
|
var t = car(childElements(car(childElements(e)))).value;
|
|
return t == f? '' : t;
|
|
}
|
|
*/
|
|
if (e.className == '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 (e.className == 'img') {
|
|
var src = car(childElements(e)).src;
|
|
return src == location.href? '' : src;
|
|
}
|
|
/*
|
|
if (e.className == 'iframe') {
|
|
var hr = car(childElements(e)).href;
|
|
return hr == location.href? '' : hr;
|
|
}
|
|
*/
|
|
if (e.className == 'list')
|
|
return '';
|
|
if (e.className == '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.
|
|
*/
|
|
page.hastext = function(e) {
|
|
if (e.className == 'h1' || e.className == 'h2' || e.className == 'text' || e.className == 'section')
|
|
return true;
|
|
if (e.className == 'button' || e.className == 'checkbox')
|
|
return true;
|
|
if (e.className == 'entry' || e.className == 'password')
|
|
return true;
|
|
/*
|
|
if (e.className == 'select')
|
|
return false;
|
|
*/
|
|
if (e.className == 'link')
|
|
return true;
|
|
if (e.className == 'img')
|
|
return true;
|
|
/*
|
|
if (e.className == 'iframe')
|
|
return true;
|
|
*/
|
|
if (e.className == 'list')
|
|
return false;
|
|
if (e.className == 'table')
|
|
return false;
|
|
return false;
|
|
};
|
|
|
|
/**
|
|
* Set the text of a widget.
|
|
*/
|
|
page.settext = function(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);
|
|
|
|
e.id = f != ''? f.substring(1) : ('page:' + e.className);
|
|
|
|
if (e.className == 'h1' || e.className == 'h2' || e.className == 'text' || e.className == 'section') {
|
|
car(childElements(e)).innerHTML = isNil(c)? f : car(c);
|
|
return t;
|
|
}
|
|
if (e.className == 'button') {
|
|
car(childElements(e)).value = isNil(c)? f : car(c);
|
|
return t;
|
|
}
|
|
if (e.className == 'entry' || e.className == 'password') {
|
|
car(childElements(e)).defaultValue = isNil(c)? f : car(c);
|
|
return t;
|
|
}
|
|
if (e.className == 'checkbox') {
|
|
car(childElements(e)).value = isNil(c)? f : car(c);
|
|
map(function(n) { if (n.nodeName == "SPAN") n.innerHTML = isNil(c)? f : car(c); return n; }, nodeList(e.childNodes));
|
|
return t;
|
|
}
|
|
/*
|
|
if (e.className == 'select') {
|
|
var ce = car(childElements(car(childElements(e))));
|
|
ce.value = isNil(c)? f : car(c);
|
|
ce.innerHTML = isNil(c)? f : car(c);
|
|
return t;
|
|
}
|
|
*/
|
|
if (e.className == 'list') {
|
|
e.innerHTML = '<table class="datatable" style="width: 100%;;"><tr><td class="datatd">' + (isNil(c)? f : car(c)) + '</td></tr><tr><td class="datatd">...</td></tr></table>';
|
|
return t;
|
|
}
|
|
if (e.className == 'table') {
|
|
e.innerHTML = '<table class="datatable" style="width: 100%;"><tr><td class="datatdl">' + (isNil(c)? f : car(c)) + '</td><td class="datatdr">...</td></tr><tr><td class="datatdl">...</td><td class="datatdr">...</td></tr></table>';
|
|
return t;
|
|
}
|
|
if (e.className == 'link') {
|
|
var ce = car(childElements(e));
|
|
ce.href = isNil(c)? 'link:/index.html' : ('link:' + car(c));
|
|
car(childElements(ce)).innerHTML = isNil(c)? (f != ''? f : '/index.html') : isNil(cdr(c))? (f != ''? f : car(c)) : cadr(c);
|
|
return t;
|
|
}
|
|
if (e.className == 'img') {
|
|
car(childElements(e)).src = isNil(c)? '/public/img.png' : car(c);
|
|
return t;
|
|
}
|
|
/*
|
|
if (e.className == 'iframe') {
|
|
car(childElements(e)).href = isNil(c)? '/public/iframe-min.html' : car(c);
|
|
return t;
|
|
}
|
|
*/
|
|
return '';
|
|
};
|
|
|
|
/**
|
|
* Initialize a widget.
|
|
*/
|
|
page.initwidget = function(e) {
|
|
|
|
// Add a Webkit transform to leverage hardware acceleration
|
|
e.style.setProperty('-webkit-transform', 'translate(0px, 0px)', null);
|
|
|
|
/*
|
|
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 == 'text' || e.className == 'h1' || e.className == 'h2') {
|
|
return e;
|
|
}
|
|
if (e.className == 'button') {
|
|
return e;
|
|
}
|
|
if (e.className == 'checkbox') {
|
|
return e;
|
|
}
|
|
if (e.className == 'list' || e.className == 'table') {
|
|
e.style.width = '100%';
|
|
var t = car(childElements(e));
|
|
t.style.width = '100%';
|
|
return e;
|
|
}
|
|
if (e.className == 'img') {
|
|
var i = car(childElements(e));
|
|
if (i.src != '' && i.src.substring(0, 5) == 'data:')
|
|
i.src = '/public/img.png';
|
|
return e;
|
|
}
|
|
if (e.className == 'entry' || e.className == 'password') {
|
|
var i = car(childElements(e));
|
|
i.readOnly = true;
|
|
i.style.cursor = 'default';
|
|
return e;
|
|
}
|
|
if (e.className == 'link') {
|
|
var l = car(childElements(e));
|
|
l.onclick = function(e) { return false; };
|
|
return e;
|
|
}
|
|
return e;
|
|
}
|
|
|
|
/**
|
|
* Enforce widget position and style constraints.
|
|
*/
|
|
page.constrainwidget = function(e) {
|
|
if (e.className == 'section' || e.className == 'list' || e.className == 'table') {
|
|
e.style.left = ui.pixpos(page.palcx);
|
|
return e;
|
|
}
|
|
return e;
|
|
};
|
|
|
|
/**
|
|
* Cleanup of a widget before saving it.
|
|
*/
|
|
page.cleanupwidget = function(e) {
|
|
//debug('cleanupwidget', e);
|
|
|
|
// Clear outline
|
|
e.style.outline = null;
|
|
|
|
// Clear the Webkit transform
|
|
e.style.removeProperty('-webkit-transform');
|
|
|
|
if (e.className == 'entry' || e.className == 'password') {
|
|
var i = car(childElements(e));
|
|
i.readOnly = false;
|
|
i.style.cursor = null;
|
|
return e;
|
|
}
|
|
if (e.className == 'link') {
|
|
var l = car(childElements(e));
|
|
l.onclick = null;
|
|
return e;
|
|
}
|
|
return e;
|
|
}
|
|
|
|
/**
|
|
* Find a draggable element in a hierarchy of elements.
|
|
*/
|
|
page.draggable = function(n, e) {
|
|
if (n == e)
|
|
return null;
|
|
if (!isNil(n.id) && n.id != '')
|
|
return n;
|
|
return page.draggable(n.parentNode, e);
|
|
}
|
|
|
|
/**
|
|
* Align a pos along a 9pixel grid.
|
|
*/
|
|
page.gridsnap = function(x) {
|
|
return Math.round(x / 9) * 9;
|
|
}
|
|
|
|
/**
|
|
* Bring an element and its parent to the top.
|
|
*/
|
|
page.bringtotop = function(n) {
|
|
n.parentNode.appendChild(n);
|
|
}
|
|
|
|
/**
|
|
* Select a widget.
|
|
*/
|
|
page.selectwidget = function(n, s, atitle, wvalue, wcopy, wdelete) {
|
|
//debug('selectwidget', n, s);
|
|
if (isNil(n) || !s) {
|
|
// Clear the widget value field
|
|
wvalue.value = '';
|
|
wvalue.readOnly = true;
|
|
wvalue.style.visibility = 'hidden';
|
|
atitle.style.visibility = 'visible';
|
|
wcopy.disabled = true;
|
|
wdelete.disabled = true;
|
|
|
|
// Clear the widget outline
|
|
if (!isNil(n))
|
|
n.style.outline = null;
|
|
|
|
return true;
|
|
}
|
|
|
|
// Outline the widget
|
|
n.style.outline = '2px solid #598edd';
|
|
|
|
// Update the widget value field
|
|
wvalue.value = page.text(n);
|
|
wvalue.readOnly = false || !editable;
|
|
wvalue.style.visibility = 'visible';
|
|
atitle.style.visibility = 'hidden';
|
|
wcopy.disabled = false || !editable;
|
|
wdelete.disabled = false || !editable;
|
|
|
|
return true;
|
|
};
|
|
|
|
/**
|
|
* Clone a palette element.
|
|
*/
|
|
page.clone = function(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 = e.className;
|
|
ne.innerHTML = e.innerHTML;
|
|
|
|
// Fixup the widget style
|
|
page.initwidget(ne);
|
|
|
|
return ne;
|
|
}
|
|
|
|
/**
|
|
* Clone an element's position.
|
|
*/
|
|
function posclone(ne, e) {
|
|
ne.style.position = 'absolute';
|
|
ne.style.left = ui.pixpos(ui.numpos(e.style.left));
|
|
ne.style.top = ui.pixpos(ui.numpos(e.style.top));
|
|
e.parentNode.appendChild(ne);
|
|
return ne;
|
|
}
|
|
|
|
return posclone(mkclone(e), e);
|
|
};
|
|
|
|
/**
|
|
* Track the current widget.
|
|
*/
|
|
var widget = null;
|
|
|
|
/**
|
|
* Get and display an app page.
|
|
*/
|
|
function getpage(name, pagediv) {
|
|
if (isNil(name))
|
|
return false;
|
|
showStatus('Loading');
|
|
|
|
return pages.get(name, function(doc) {
|
|
|
|
// Stop now if we didn't get a page
|
|
if (doc == null) {
|
|
showError('App not available');
|
|
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 = isNil(content)? mklist() : elementChildren(content);
|
|
var buffer = $('buffer');
|
|
if (isNil(el))
|
|
buffer.innerHTML = '<div id="page"></div>';
|
|
else
|
|
buffer.innerHTML = writeStrings(writeXML(el, false));
|
|
|
|
// Remove any existing page nodes from the editor div
|
|
var fnodes = filter(function(e) {
|
|
if (isNil(e.id) || e.id == '' || e.id.substr(0, 8) == 'palette:')
|
|
return false;
|
|
var x = ui.numpos(e.style.left) - 2500;
|
|
if (x < 0 || ui.numpos(e.style.top) < 0)
|
|
return false;
|
|
return true;
|
|
}, nodeList(pagediv.childNodes));
|
|
|
|
map(function(e) {
|
|
pagediv.removeChild(e);
|
|
}, fnodes);
|
|
|
|
// Append new page nodes to editor
|
|
map(function(e) {
|
|
pagediv.appendChild(e);
|
|
if (!isNil(e.style))
|
|
e.style.left = ui.pixpos(ui.numpos(e.style.left) + 2500);
|
|
page.initwidget(e);
|
|
return e;
|
|
}, nodeList(buffer.childNodes[0].childNodes));
|
|
|
|
savedpagexhtml = pagexhtml(pagediv);
|
|
|
|
// Enable author to edit the page
|
|
author = elementValue(namedElementChild("'author", pageentry));
|
|
editable = author == username;
|
|
wadd.disabled = !editable;
|
|
showStatus(editable? onlineStatus() : 'Read only');
|
|
|
|
return true;
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Handle add widget button click event.
|
|
*/
|
|
wadd.onclick = function(e) {
|
|
// Show the widget palette
|
|
pagediv.style.left = ui.pixpos(0);
|
|
};
|
|
|
|
/**
|
|
* Return the current page XHTML content.
|
|
*/
|
|
function pagexhtml(pagediv) {
|
|
|
|
// Copy page DOM to hidden buffer
|
|
var buffer = $('buffer');
|
|
buffer.innerHTML = '<div id="page"></div>'
|
|
var div = buffer.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 (isNil(e.id) || e.id == '' || e.id.substr(0, 8) == 'palette:')
|
|
return false;
|
|
var x = ui.numpos(e.style.left) - 2500;
|
|
if (x < 0 || ui.numpos(e.style.top) < 0)
|
|
return false;
|
|
return true;
|
|
}, nodes);
|
|
|
|
// Reposition and cleanup nodes
|
|
map(function(e) {
|
|
var x = ui.numpos(e.style.left) - 2500;
|
|
e.style.left = ui.pixpos(x);
|
|
page.cleanupwidget(e);
|
|
return e;
|
|
}, fnodes);
|
|
|
|
// Sort them by position
|
|
var snodes = fnodes.sort(function(a, b) {
|
|
var ay = ui.numpos(a.style.top);
|
|
var by = ui.numpos(b.style.top);
|
|
if (ay < by) return -1;
|
|
if (ay > by) return 1;
|
|
var ax = ui.numpos(a.style.left);
|
|
var bx = ui.numpos(b.style.left);
|
|
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) {
|
|
showStatus('Saving');
|
|
|
|
// Get the current page XHTML content
|
|
savedpagexhtml = 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');
|
|
return false;
|
|
}
|
|
showStatus('Saved');
|
|
return false;
|
|
});
|
|
return true;
|
|
};
|
|
|
|
/**
|
|
* Handle a page change event
|
|
*/
|
|
function onpagechange(prop) {
|
|
if (!editable)
|
|
return false;
|
|
|
|
var newxml = pagexhtml(pagediv);
|
|
if (savedpagexhtml == newxml)
|
|
return false;
|
|
showStatus('Modified');
|
|
|
|
// Save property changes right away
|
|
if (prop)
|
|
return save(newxml);
|
|
|
|
// Autosave other changes after 1 second
|
|
setTimeout(function() {
|
|
if (savedpagexhtml == newxml) {
|
|
showStatus('Saved');
|
|
return false;
|
|
}
|
|
return save(newxml);
|
|
}, 1000);
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Handle a widget select event.
|
|
*/
|
|
function onselectwidget(w) {
|
|
if (w == widget)
|
|
return true;
|
|
widget = w;
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Play page in a frame.
|
|
*/
|
|
function playpage() {
|
|
if (!evisible)
|
|
return true;
|
|
page.selectwidget(widget, false, atitle, wvalue, wcopy, wdelete);
|
|
page.selected = null;
|
|
pplay.value = '<';
|
|
evisible = false;
|
|
pdiv.style.visibility = 'visible';
|
|
pdiv.innerHTML = '';
|
|
pdiv.innerHTML = '<iframe id="playappframe" style="position: relative; height: 5000px; width: 2500px; border: 0px;" scrolling="no" frameborder="0" src="/' +
|
|
appname + '"></iframe>';
|
|
setTimeout(function() {
|
|
pagediv.style.visibility = 'hidden'
|
|
}, 0);
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Show the page editor.
|
|
*/
|
|
function showedit() {
|
|
if (evisible)
|
|
return true;
|
|
pplay.value = '>';
|
|
pagediv.style.visibility = 'visible'
|
|
evisible = true;
|
|
page.selectwidget(widget, true, atitle, wvalue, wcopy, wdelete);
|
|
page.selected = widget;
|
|
setTimeout(function() {
|
|
pdiv.style.visibility = 'hidden';
|
|
pdiv.innerHTML = '';
|
|
}, 0);
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Handle play page button event.
|
|
*/
|
|
pplay.onclick = function() {
|
|
if (!evisible)
|
|
return showedit();
|
|
return playpage();
|
|
}
|
|
|
|
/**
|
|
* Initialize the page editor.
|
|
*/
|
|
page.mkedit(pagediv, atitle, wvalue, wadd, wcopy, wdelete, onpagechange, onselectwidget);
|
|
|
|
/**
|
|
* Get and display the current app page.
|
|
*/
|
|
getpage(appname, pagediv);
|
|
|
|
})();
|
|
</script>
|
|
|
|
</div>
|