
git-svn-id: http://svn.us.apache.org/repos/asf/tuscany@1444660 13f79535-47bb-0310-9956-ffa450edef68
2125 lines
66 KiB
HTML
2125 lines
66 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" style="width: 100%;">
|
|
<div id="graphdiv" class="graphcontent" style="top: 0px; left: -2500px; width: 5000px; height: 5000px;"></div>
|
|
<div id="playdiv" style="position: absolute; top: 0x; left: 0px; width: 2500px; height: 5000px; display: none"></div>
|
|
</div>
|
|
|
|
<script type="text/javascript">
|
|
(function graphbody() {
|
|
|
|
/**
|
|
* Get the current app name.
|
|
*/
|
|
var appname = ui.fragmentParams(location)['app'];
|
|
var ispalette = false;
|
|
if (isNull(appname)) {
|
|
appname = ui.fragmentParams(location)['palette'];
|
|
|
|
// Edit a palette instead of a regular app
|
|
if (!isNull(appname))
|
|
ispalette = true;
|
|
}
|
|
|
|
/**
|
|
* Set page title.
|
|
*/
|
|
document.title = config.windowtitle() + ' - ' + config.logic() + ' - ' + appname;
|
|
|
|
/**
|
|
* Set header div.
|
|
*/
|
|
$('viewhead').innerHTML = '<span id="appTitle" class="cmenu">' + appname + '</span>' +
|
|
'<input type="button" id="deleteCompButton" title="Delete a component" class="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="compValue" type="text" value="" class="flatentry" title="Component value" autocapitalize="off" placeholder="Value" style="position: absolute; left: 0px; top: 4px; width: 100%; display: none;" readonly="readonly"/></span>' +
|
|
'<input type="button" id="playCompButton" title="View component value" class="graybutton plusminus" style="position: absolute; top: 4px; right: 75px;" disabled="true" value=">"/>' +
|
|
'<input type="button" id="copyCompButton" title="Copy a component" class="bluebutton" style="position: absolute; top: 4px; right: 40px;" disabled="true" value="C"/>' +
|
|
'<input type="button" id="addCompButton" title="Add a component" class="bluebutton plusminus" style="position: absolute; top: 4px; right: 5px;" disabled="true" value="+"/>';
|
|
|
|
if (!ui.isMobile())
|
|
$('viewcontent').className = 'viewcontent flatscrollbars';
|
|
|
|
/**
|
|
* Track the current app composite, author, and saved XML content.
|
|
*/
|
|
var author = '';
|
|
var editable = false;
|
|
var composite;
|
|
var savedcomposxml = '';
|
|
|
|
/**
|
|
* Component value field, add, delete and play buttons.
|
|
*/
|
|
var cvalue = $('compValue');
|
|
var atitle = $('appTitle');
|
|
var cadd = $('addCompButton');
|
|
var cdelete = $('deleteCompButton');
|
|
var ccopy = $('copyCompButton');
|
|
var cplay = $('playCompButton');
|
|
|
|
/**
|
|
* Init componnent references.
|
|
*/
|
|
var editorComp = sca.component("Editor");
|
|
var palettes = sca.reference(editorComp, "palettes");
|
|
var composites = sca.reference(editorComp, ispalette? "palettes" : "composites");
|
|
|
|
/**
|
|
* Composite rendering functions.
|
|
*/
|
|
var graph = {};
|
|
|
|
/**
|
|
* Basic colors
|
|
*/
|
|
graph.colors = {};
|
|
graph.colors.black = '#000000';
|
|
graph.colors.blue = '#0000ff';
|
|
graph.colors.cyan = '#00ffff';
|
|
graph.colors.gray = '#808080'
|
|
graph.colors.lightgray = '#dcdcdc'
|
|
graph.colors.green = '#00ff00';
|
|
graph.colors.magenta = '#ff00ff';
|
|
graph.colors.orange = '#ffa500';
|
|
graph.colors.pink = '#ffc0cb';
|
|
graph.colors.purple = '#800080';
|
|
graph.colors.red = '#ff0000';
|
|
graph.colors.white = '#ffffff';
|
|
graph.colors.yellow = '#ffff00';
|
|
graph.colors.link = '#357ae8';
|
|
|
|
graph.colors.orange1 = '#ffd666';
|
|
graph.colors.green1 = '#bbe082';
|
|
graph.colors.blue1 = '#66dbdf';
|
|
graph.colors.yellow1 = '#fdf57a';
|
|
graph.colors.cyan1 = '#e6eafb';
|
|
graph.colors.lightgray1 = '#eaeaea'
|
|
graph.colors.pink1 = '#ffd9e0';
|
|
graph.colors.red1 = '#d03f41';
|
|
graph.colors.white1 = '#ffffff';
|
|
|
|
graph.colors.orange2 = '#ffbb00';
|
|
graph.colors.green2 = '#96d333';
|
|
//graph.colors.blue2 = '#0d7cc1';
|
|
graph.colors.blue2 = '#00c3c9';
|
|
graph.colors.red2 = '#d03f41';
|
|
graph.colors.yellow2 = '#fcee21';
|
|
graph.colors.magenta2 = '#c0688a';
|
|
graph.colors.cyan2 = '#d5dcf9';
|
|
graph.colors.lightgray2 = '#dcdcdc'
|
|
graph.colors.pink2 = '#ffc0cb';
|
|
graph.colors.white2 = '#ffffff';
|
|
|
|
graph.colors.orange3 = '#ffc700';
|
|
graph.colors.green3 = '#92e120';
|
|
graph.colors.blue3 = '#008fd1';
|
|
graph.colors.yellow3 = '#fdf400';
|
|
graph.colors.cyan3 = '#b4d3fd';
|
|
graph.colors.lightgray3 = '#e3e3e3'
|
|
graph.colors.pink3 = '#da749b';
|
|
graph.colors.red3 = '#ed3f48';
|
|
graph.colors.white3 = '#ffffff';
|
|
|
|
/**
|
|
* Default positions and sizes.
|
|
*/
|
|
graph.palcx = 2500;
|
|
graph.proxcx = 20;
|
|
graph.proxcy = 20;
|
|
graph.buttoncx = 55;
|
|
graph.buttoncy = 23;
|
|
graph.curvsz = 4;
|
|
graph.tabsz = 2;
|
|
graph.titlex = 4;
|
|
graph.titley = 0;
|
|
|
|
/**
|
|
* Make a composite graph editor.
|
|
*/
|
|
graph.mkedit = function(graphdiv, pos, atitle, cvalue, cadd, ccopy, cdelete, onchange, onselect) {
|
|
|
|
// Track element dragging and selection
|
|
graph.dragging = null;
|
|
graph.dragged = false;
|
|
graph.selected = null;
|
|
cvalue.readOnly = true;
|
|
cvalue.style.display = 'none';
|
|
atitle.style.display = 'block';
|
|
ccopy.disabled = true;
|
|
cdelete.disabled = true;
|
|
cadd.disabled = !editable;
|
|
|
|
// Register event listeners
|
|
graph.oncomposchange = onchange;
|
|
graph.oncompselect = onselect;
|
|
|
|
/**
|
|
* Find the first draggable element in a hierarchy of elements.
|
|
*/
|
|
function draggable(n) {
|
|
//debug('draggable', n);
|
|
if (n == graphdiv || n == null)
|
|
return null;
|
|
if (n.className == 'g' && !isNull(n.id) && n.id != '')
|
|
return n;
|
|
return draggable(n.parentNode);
|
|
}
|
|
|
|
/**
|
|
* Render component move animation.
|
|
*/
|
|
function onmoveanimation() {
|
|
//debug('onmoveanimation');
|
|
|
|
// Stop animation if we're not dragging an element anymore
|
|
if (graph.dragging == null)
|
|
return true;
|
|
|
|
// Request the next animation frame
|
|
ui.animation(onmoveanimation);
|
|
|
|
// Nothing to do if the selected component has not moved
|
|
if (graph.moveX == graph.dragX && graph.moveY == graph.dragY)
|
|
return true;
|
|
|
|
// Remember that the selected component has been dragged
|
|
graph.dragged = true;
|
|
|
|
// Cut wire to the dragged component
|
|
if (graph.dragging.parentNode != graphdiv) {
|
|
var compos = scdl.composite(graphdiv.compos);
|
|
setElement(compos, graph.sortcompos(graph.cutwire(graph.dragging, compos, graphdiv)));
|
|
|
|
// Bring component to the top
|
|
graph.bringtotop(graph.dragging, graphdiv);
|
|
}
|
|
|
|
// Calculate new position of the dragged component
|
|
var gpos = graph.relpos(graph.dragging);
|
|
var newX = gpos.x + (graph.moveX - graph.dragX);
|
|
var newY = gpos.y + (graph.moveY - graph.dragY);
|
|
if (newX >= graph.palcx)
|
|
graph.dragX = graph.moveX
|
|
else
|
|
newX = graph.palcx;
|
|
if (newY >= 0)
|
|
graph.dragY = graph.moveY;
|
|
else
|
|
newY = 0;
|
|
|
|
// Move it
|
|
graph.move(graph.dragging, graph.mkpath().pos(newX, newY));
|
|
|
|
return false;
|
|
};
|
|
|
|
/**
|
|
* Handle a mouse down or touch start event.
|
|
*/
|
|
function onmousedown(e) {
|
|
|
|
// Remember mouse or touch position
|
|
var pos = typeof e.touches != "undefined" ? e.touches[0] : e;
|
|
graph.downX = pos.screenX;
|
|
graph.downY = pos.screenY;
|
|
graph.moveX = pos.screenX;
|
|
graph.moveY = pos.screenY;
|
|
|
|
// Engage the click component selection right away
|
|
// on mouse controlled devices
|
|
if (typeof e.touches == 'undefined')
|
|
onclick(e);
|
|
|
|
// Find and remember draggable component
|
|
var dragging = draggable(e.target);
|
|
if (dragging == null || dragging != graph.selected)
|
|
return true;
|
|
graph.dragging = dragging;
|
|
graph.dragged = false;
|
|
|
|
// Remember current drag position
|
|
graph.dragX = pos.screenX;
|
|
graph.dragY = pos.screenY;
|
|
|
|
// Start move animation
|
|
ui.animation(onmoveanimation);
|
|
|
|
e.preventDefault();
|
|
return true;
|
|
};
|
|
|
|
if (!ui.isMobile()) {
|
|
graphdiv.onmousedown = function(e) {
|
|
//debug('onmousedown');
|
|
return onmousedown(e);
|
|
}
|
|
} else {
|
|
graphdiv.ontouchstart = function(e) {
|
|
//debug('ontouchstart');
|
|
return onmousedown(e);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Handle a mouse up or touch end event.
|
|
*/
|
|
function onmouseup(e) {
|
|
|
|
// Engage the click component selection now on touch devices
|
|
if (ui.isMobile()) {
|
|
if (!graph.dragged && graph.moveX == graph.downX && graph.moveY == graph.downY)
|
|
return onclick(e);
|
|
}
|
|
|
|
// Stop here if the component was not dragged
|
|
if (graph.dragging == null)
|
|
return true;
|
|
if (!graph.dragged) {
|
|
graph.dragging = null;
|
|
return true;
|
|
}
|
|
|
|
if (graph.dragging.parentNode == graphdiv && graph.dragging.id.substring(0, 8) != 'palette:') {
|
|
|
|
// Add new dragged component to the composite
|
|
if (isNull(graph.dragging.compos)) {
|
|
var compos = scdl.composite(graphdiv.compos);
|
|
setElement(compos, graph.sortcompos(graph.addcomps(mklist(graph.dragging.comp), compos)));
|
|
graph.dragging.compos = graphdiv.compos;
|
|
}
|
|
|
|
// Update component position
|
|
setElement(graph.dragging.comp, graph.movecomp(graph.dragging.comp, graph.abspos(graph.draggingg)));
|
|
|
|
// Wire component to neighboring reference
|
|
if (!isNull(graph.dragging.svcpos)) {
|
|
var compos = scdl.composite(graphdiv.compos);
|
|
setElement(compos, graph.sortcompos(graph.clonerefs(graph.wire(graph.dragging, compos, graphdiv))));
|
|
}
|
|
|
|
// Snap top level component position to grid
|
|
if (graph.dragging.parentNode == graphdiv) {
|
|
var gpos = graph.relpos(graph.dragging);
|
|
setElement(graph.dragging.comp, graph.movecomp(graph.dragging.comp, graph.mkpath().pos(graph.gridsnap(gpos.x), graph.gridsnap(gpos.y))));
|
|
}
|
|
}
|
|
|
|
// Forget current dragged component
|
|
graph.dragging = null;
|
|
graph.dragged = false;
|
|
|
|
// Refresh the composite
|
|
//debug('onmouseup refresh');
|
|
var nodes = graph.refresh(graphdiv);
|
|
|
|
// Reselect the previously selected component
|
|
if (!isNull(graph.selected)) {
|
|
graph.selected = graph.findcompnode(scdl.name(graph.selected.comp), nodes);
|
|
graph.compselect(graph.selected, true, atitle, cvalue, ccopy, cdelete);
|
|
|
|
// Trigger component select event
|
|
graph.oncompselect(graph.selected);
|
|
}
|
|
|
|
// Trigger composite change event
|
|
graph.oncomposchange(false);
|
|
return true;
|
|
};
|
|
|
|
if (!ui.isMobile()) {
|
|
graphdiv.onmouseup = function(e) {
|
|
//debug('onmouseup');
|
|
return onmouseup(e);
|
|
}
|
|
} else {
|
|
graphdiv.ontouchend = function(e) {
|
|
//debug('ontouchend');
|
|
return onmouseup(e);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Handle a mouse or touch click event.
|
|
*/
|
|
function onclick(e) {
|
|
//debug('onclick logic');
|
|
|
|
// Find selected component
|
|
var selected = draggable(e.target);
|
|
if (selected == null) {
|
|
if (graph.selected != null) {
|
|
|
|
// Reset current selection
|
|
graph.compselect(graph.selected, false, atitle, cvalue, ccopy, cdelete);
|
|
graph.selected = null;
|
|
|
|
// Trigger component select event
|
|
graph.oncompselect(null);
|
|
}
|
|
|
|
// Dismiss the palette
|
|
if (e.target == graphdiv && ui.numpos(graphdiv.style.left) != (graph.palcx * -1))
|
|
graphdiv.style.left = ui.pixpos(graph.palcx * -1);
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
// Ignore duplicate click events
|
|
if (selected == graph.selected)
|
|
return true;
|
|
if (selected.id.substring(0, 8) == 'palette:' && ui.numpos(graphdiv.style.left) != 0)
|
|
return true;
|
|
|
|
// Deselect previously selected component
|
|
graph.compselect(graph.selected, false, atitle, cvalue, ccopy, cdelete);
|
|
|
|
// Clone component from the palette
|
|
if (selected.id.substring(0, 8) == 'palette:') {
|
|
var compos = scdl.composite(graphdiv.compos);
|
|
var comp = graph.clonepalette(selected, compos);
|
|
setElement(compos, graph.sortcompos(graph.addcomps(mklist(comp), compos)));
|
|
|
|
// Move into the editing area and hide the palette
|
|
graphdiv.style.left = ui.pixpos(graph.palcx * -1);
|
|
|
|
// Refresh the composite
|
|
//debug('onclick refresh');
|
|
var nodes = graph.refresh(graphdiv);
|
|
|
|
// Reselect the previously selected component
|
|
graph.selected = graph.findcompnode(scdl.name(comp), nodes);
|
|
graph.compselect(graph.selected, true, atitle, cvalue, ccopy, cdelete);
|
|
|
|
// Trigger component select event
|
|
graph.oncompselect(graph.selected);
|
|
|
|
// Trigger composite change event
|
|
graph.oncomposchange(true);
|
|
|
|
} else {
|
|
graph.selected = selected;
|
|
|
|
// Select the component
|
|
graph.compselect(graph.selected, true, atitle, cvalue, ccopy, cdelete);
|
|
|
|
// Trigger component select event
|
|
graph.oncompselect(graph.selected);
|
|
}
|
|
|
|
//debug('comp selected');
|
|
|
|
e.preventDefault();
|
|
return true;
|
|
}
|
|
|
|
if (!ui.isMobile()) {
|
|
graphdiv.onclick = function(e) {
|
|
//debug('onclick');
|
|
return onclick(e);
|
|
}
|
|
} else {
|
|
graphdiv.onclick = function(e) {
|
|
//debug('onclick');
|
|
return onclick(e);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Handle a mouse or touch move event.
|
|
*/
|
|
if (!ui.isMobile()) {
|
|
window.onmousemove = function(e) {
|
|
//debug('onmousemove');
|
|
|
|
// Record mouse position
|
|
graph.moveX = e.screenX;
|
|
graph.moveY = e.screenY;
|
|
if (graph.dragging == null)
|
|
return true;
|
|
return false;
|
|
}
|
|
} else {
|
|
graphdiv.ontouchmove = function(e) {
|
|
//debug('ontouchmove');
|
|
|
|
// Record touch position
|
|
var pos = e.touches[0];
|
|
graph.moveX = pos.screenX;
|
|
graph.moveY = pos.screenY;
|
|
if (graph.dragging == null)
|
|
return true;
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Handle field on change events.
|
|
*/
|
|
function onvaluechange() {
|
|
if (graph.selected == null)
|
|
return false;
|
|
if (graphdiv.parentNode.style.display == 'none')
|
|
return false;
|
|
|
|
// Change component name and refactor references to it
|
|
function changename() {
|
|
var compos = scdl.composite(graphdiv.compos);
|
|
cvalue.value = graph.ucid(cvalue.value, compos, false);
|
|
graph.selected.id = cvalue.value;
|
|
setElement(compos, graph.sortcompos(graph.renamecomp(graph.selected.comp, compos, cvalue.value)));
|
|
|
|
// Refresh the composite
|
|
//debug('onchangename refresh');
|
|
var nodes = graph.refresh(graphdiv);
|
|
|
|
// Reselected the previously selected component
|
|
graph.selected = graph.findcompnode(scdl.name(graph.selected.comp), nodes);
|
|
graph.compselect(graph.selected, true, atitle, cvalue, ccopy, cdelete);
|
|
|
|
// Trigger component select event
|
|
graph.oncompselect(graph.selected);
|
|
|
|
// Trigger composite change event
|
|
graph.oncomposchange(true);
|
|
return false;
|
|
}
|
|
|
|
// Change the component property value
|
|
function changeprop() {
|
|
graph.setproperty(graph.selected.comp, cvalue.value);
|
|
var hasprop = graph.hasproperty(graph.selected.comp);
|
|
cvalue.readOnly = (hasprop? false : true) || !editable;
|
|
cvalue.style.display = hasprop? 'block' : 'none';
|
|
atitle.style.display = hasprop? 'none' : 'block';
|
|
cvalue.value = graph.property(graph.selected.comp);
|
|
|
|
// Refresh the composite
|
|
//debug('onchangeprop refresh');
|
|
var nodes = graph.refresh(graphdiv);
|
|
|
|
// Reselected the previously selected component
|
|
graph.selected = graph.findcompnode(scdl.name(graph.selected.comp), nodes);
|
|
graph.compselect(graph.selected, true, atitle, cvalue, ccopy, cdelete);
|
|
|
|
// Trigger component select event
|
|
graph.oncompselect(graph.selected);
|
|
|
|
// Trigger composite change event
|
|
graph.oncomposchange(true);
|
|
return false;
|
|
}
|
|
|
|
return graph.hasproperty(graph.selected.comp)? changeprop() : changename();
|
|
};
|
|
|
|
cvalue.onchange = function() {
|
|
return onvaluechange();
|
|
}
|
|
|
|
// Handle delete event
|
|
function ondeleteclick() {
|
|
if (graph.selected == null)
|
|
return false;
|
|
if (graph.selected.id.substring(0, 8) != 'palette:') {
|
|
|
|
// Remove selected component
|
|
var compos = scdl.composite(graphdiv.compos);
|
|
if (isNull(graph.selected.compos))
|
|
setElement(compos, graph.sortcompos(graph.cutwire(graph.selected, compos, graphdiv)));
|
|
setElement(compos, graph.sortcompos(graph.clonerefs(graph.gcollect(graph.removecomp(graph.selected.comp, compos)))));
|
|
|
|
// Reset current selection
|
|
graph.compselect(graph.selected, false, atitle, cvalue, ccopy, cdelete);
|
|
graph.selected = null;
|
|
|
|
// Refresh the composite
|
|
//debug('ondelete refresh');
|
|
graph.refresh(graphdiv);
|
|
|
|
// Trigger component select event
|
|
graph.oncompselect(null);
|
|
|
|
// Trigger composite change event
|
|
graph.oncomposchange(true);
|
|
}
|
|
return false;
|
|
};
|
|
|
|
cdelete.onclick = function() {
|
|
return ondeleteclick();
|
|
};
|
|
|
|
// Handle copy event
|
|
function oncopyclick() {
|
|
if (graph.selected == null)
|
|
return false;
|
|
if (graph.selected.id.substring(0, 8) == 'palette:')
|
|
return false;
|
|
|
|
// Clone the selected component
|
|
var compos = scdl.composite(graphdiv.compos);
|
|
var comps = graph.clonecomp(graph.selected, compos);
|
|
setElement(compos, graph.sortcompos(graph.addcomps(comps, compos)));
|
|
|
|
// Refresh the composite
|
|
//debug('oncopyclick refresh');
|
|
var nodes = graph.refresh(graphdiv);
|
|
|
|
// Select the component clone
|
|
graph.selected = graph.findcompnode(scdl.name(car(comps)), nodes);
|
|
graph.compselect(graph.selected, true, atitle, cvalue, ccopy, cdelete);
|
|
|
|
// Trigger component select event
|
|
graph.oncompselect(graph.selected);
|
|
|
|
// Trigger composite change event
|
|
graph.oncomposchange(true);
|
|
|
|
return false;
|
|
};
|
|
|
|
ccopy.onclick = function() {
|
|
return oncopyclick();
|
|
};
|
|
|
|
// Handle add event
|
|
cadd.onclick = function() {
|
|
|
|
// Show the palette
|
|
graphdiv.style.left = ui.pixpos(0);
|
|
return false;
|
|
};
|
|
|
|
// Create a hidden element to help compute the width of
|
|
// component and reference titles
|
|
graph.offtitles = document.createElement('span');
|
|
graph.offtitles.style.visibility = 'hidden';
|
|
graph.offtitles.style.display = 'block';
|
|
graph.offtitles.position = 'absolute';
|
|
graph.offtitles.top = -500;
|
|
graph.offtitles.width = 500;
|
|
graph.offtitles.height = 50;
|
|
graphdiv.appendChild(graph.offtitles);
|
|
|
|
return graphdiv;
|
|
};
|
|
|
|
/**
|
|
* Point class.
|
|
*/
|
|
graph.Point = function(x, y) {
|
|
this.x = x;
|
|
this.y = y;
|
|
};
|
|
|
|
graph.mkpoint = function(x, y) {
|
|
return new graph.Point(x, y);
|
|
};
|
|
|
|
/**
|
|
* Path class.
|
|
*/
|
|
graph.Path = function() {
|
|
this.x = 0;
|
|
this.y = 0;
|
|
this.xmin = null;
|
|
this.xmax = null;
|
|
this.xmin = -8;
|
|
this.ymax = null;
|
|
this.draw = function(ctx) {
|
|
return ctx;
|
|
};
|
|
}
|
|
graph.Path.prototype.pos = function(x, y) {
|
|
this.x = x;
|
|
this.y = y;
|
|
if (this.xmin == null || x < this.xmin) this.xmin = x;
|
|
if (this.xmax == null || x > this.xmax) this.xmax = x;
|
|
if (this.ymin == null || y < this.ymin) this.ymin = y;
|
|
if (this.ymax == null || y > this.ymax) this.ymax = y;
|
|
return this;
|
|
};
|
|
graph.Path.prototype.rmove = function(x, y) {
|
|
return this.move(this.x + x, this.y + y);
|
|
};
|
|
graph.Path.prototype.rline = function(x, y) {
|
|
return this.line(this.x + x, this.y + y);
|
|
};
|
|
graph.Path.prototype.rcurve = function(x1, y1, x, y) {
|
|
return this.curve(this.x + x1, this.y + y1, this.x + x1 + x, this.y + y1 + y);
|
|
};
|
|
graph.Path.prototype.clone = function() {
|
|
return graph.mkpath().pos(this.x, this.y);
|
|
};
|
|
graph.Path.prototype.move = function(x, y) {
|
|
var d = this.draw;
|
|
this.draw = function(ctx) {
|
|
d(ctx);
|
|
ctx.moveTo(x, y);
|
|
return ctx;
|
|
};
|
|
return this.pos(x, y);
|
|
};
|
|
graph.Path.prototype.line = function(x, y) {
|
|
var d = this.draw;
|
|
this.draw = function(ctx) {
|
|
d(ctx);
|
|
ctx.lineTo(x, y);
|
|
return ctx;
|
|
};
|
|
return this.pos(x, y);
|
|
};
|
|
graph.Path.prototype.curve = function(x1, y1, x, y) {
|
|
var d = this.draw;
|
|
this.draw = function(ctx) {
|
|
d(ctx);
|
|
ctx.quadraticCurveTo(x1, y1, x, y);
|
|
return ctx;
|
|
};
|
|
return this.pos(x, y);
|
|
};
|
|
graph.Path.prototype.end = function() {
|
|
var d = this.draw;
|
|
this.draw = function(ctx) {
|
|
ctx.beginPath();
|
|
d(ctx);
|
|
ctx.fill();
|
|
ctx.beginPath();
|
|
d(ctx);
|
|
ctx.stroke();
|
|
};
|
|
return this;
|
|
};
|
|
graph.Path.prototype.bounds = function() {
|
|
var width = this.xmin == null || this.xmax == null? 0 : this.xmax - this.xmin + 1;
|
|
var height = this.ymin == null || this.ymax == null? 0 : this.ymax - this.ymin + 1;
|
|
return graph.mkpath().pos(width, height);
|
|
};
|
|
|
|
graph.mkpath = function() {
|
|
return new graph.Path();
|
|
};
|
|
|
|
/**
|
|
* Translate the position of an element.
|
|
*/
|
|
graph.translate = function(g, x, y) {
|
|
var t = 'translate(' + ui.pixpos(x) + ',' + ui.pixpos(y) + ')';
|
|
g.style.setProperty('-webkit-transform', t, null);
|
|
g.style.setProperty('-moz-transform', t, null);
|
|
g.style.setProperty('-o-transform', t, null);
|
|
g.style.setProperty('transform', t, null);
|
|
g.ctmx = x;
|
|
g.ctmy = y;
|
|
return g;
|
|
};
|
|
|
|
/**
|
|
* Apply a path to an element.
|
|
*/
|
|
graph.drawshape = function(g) {
|
|
// Set shape element size
|
|
var b = g.path.bounds();
|
|
g.width = b.x + 4;
|
|
g.height = b.y + 4;
|
|
|
|
// Get canvas context
|
|
var ctx = g.getContext('2d');
|
|
ctx.save();
|
|
|
|
// Apply translation
|
|
ctx.translate((g.path.xmin * -1) + 2, (g.path.ymin * -1) + 2);
|
|
|
|
// Draw the shape
|
|
ctx.fillStyle = g.fillStyle;
|
|
ctx.strokeStyle = !isNull(g.strokeStyle)? g.strokeStyle : graph.colors.gray;
|
|
ctx.lineWidth = !isNull(g.lineWidth)? g.lineWidth : 1;
|
|
g.path.draw(ctx);
|
|
|
|
// Reset canvas context
|
|
ctx.restore();
|
|
return g;
|
|
}
|
|
|
|
/**
|
|
* Return an element representing a title.
|
|
*/
|
|
graph.mktitle = function(t, x, y) {
|
|
var title = document.createElement('span');
|
|
title.className = 'gtitle';
|
|
title.style.left = ui.pixpos(x);
|
|
title.style.top = ui.pixpos(y);
|
|
title.appendChild(document.createTextNode(t));
|
|
graph.offtitles.appendChild(title);
|
|
title.style.width = ui.pixpos(title.clientWidth + 2);
|
|
title.style.height = ui.pixpos(title.clientHeight + 2);
|
|
return title;
|
|
};
|
|
|
|
/**
|
|
* Return an element representing the title of a component.
|
|
*/
|
|
graph.comptitle = function(comp) {
|
|
return memo(comp, 'title', function() {
|
|
var ct = graph.title(comp);
|
|
var pt = graph.propertytitle(comp);
|
|
if (ct == '' && pt == '')
|
|
return null;
|
|
return graph.mktitle((ct != '' && pt != '')? ct + ' ' + pt : ct + pt, graph.titlex, graph.titley);
|
|
});
|
|
};
|
|
|
|
/**
|
|
* Return the width of the title of a component.
|
|
*/
|
|
graph.comptitlewidth = function(comp) {
|
|
var title = graph.comptitle(comp);
|
|
if (isNull(title))
|
|
return 0;
|
|
return title.clientWidth;
|
|
};
|
|
|
|
/**
|
|
* Draw a component shape selection.
|
|
*/
|
|
graph.compselect = function(g, s, atitle, cvalue, ccopy, cdelete) {
|
|
if (isNull(g) || !s) {
|
|
cvalue.value = '';
|
|
cvalue.readOnly = true;
|
|
cvalue.style.display = 'none';
|
|
atitle.style.display = 'block';
|
|
ccopy.disabled = true;
|
|
cdelete.disabled = true;
|
|
if (isNull(g))
|
|
return true;
|
|
g.shape.strokeStyle = null;
|
|
g.shape.lineWidth = null;
|
|
graph.drawshape(g.shape);
|
|
return true;
|
|
}
|
|
|
|
cvalue.value = graph.hasproperty(g.comp)? graph.property(g.comp) : g.id;
|
|
cvalue.readOnly = false || !editable;
|
|
cvalue.style.display = 'block';
|
|
atitle.style.display = 'none';
|
|
ccopy.disabled = false || !editable;
|
|
cdelete.disabled = false || !editable;
|
|
|
|
g.shape.strokeStyle = graph.colors.link;
|
|
g.shape.lineWidth = 2;
|
|
graph.drawshape(g.shape);
|
|
g.parentNode.appendChild(g);
|
|
return true;
|
|
};
|
|
|
|
/**
|
|
* Draw a palette shape selection.
|
|
*/
|
|
graph.paletteselect = function(g, s) {
|
|
if (isNull(g))
|
|
return true;
|
|
if (!s) {
|
|
g.shape.strokeStyle = null;
|
|
g.shape.lineWidth = null;
|
|
graph.drawshape(g.shape);
|
|
return true;
|
|
}
|
|
|
|
g.shape.strokeStyle = graph.colors.link;
|
|
g.shape.lineWidth = 2;
|
|
graph.drawshape(g.shape);
|
|
g.parentNode.appendChild(g);
|
|
return true;
|
|
};
|
|
|
|
/**
|
|
* Return a node representing a component.
|
|
*/
|
|
graph.compnode = function(comp, cassoc, pos) {
|
|
//debug('compnode', graph.title(comp));
|
|
|
|
// Create the component shape
|
|
var shape = document.createElement('canvas');
|
|
shape.className = 'path';
|
|
shape.fillStyle = graph.color(comp);
|
|
shape.path = graph.comppath(comp, cassoc);
|
|
graph.drawshape(shape);
|
|
|
|
// Make the component title element
|
|
var title = graph.comptitle(comp);
|
|
|
|
// Create a span group element and add the shape and title to it
|
|
var g = document.createElement('span');
|
|
g.className = 'g';
|
|
g.comp = comp;
|
|
g.id = scdl.name(comp);
|
|
graph.translate(g, pos.x, pos.y);
|
|
g.pos = pos.clone();
|
|
g.appendChild(shape);
|
|
g.shape = shape;
|
|
g.style.width = ui.pixpos(shape.width);
|
|
g.style.height = ui.pixpos(shape.height);
|
|
if (!isNull(title)) {
|
|
title.style.left = ui.pixpos(shape.path.xmin * -1);
|
|
g.appendChild(title);
|
|
}
|
|
|
|
// Store the positions of the services and references
|
|
g.refpos = reverse(shape.path.refpos);
|
|
g.svcpos = reverse(shape.path.svcpos);
|
|
|
|
return g;
|
|
};
|
|
|
|
/**
|
|
* Find the node representing a component.
|
|
*/
|
|
graph.findcompnode = function(name, nodes) {
|
|
if (isNull(nodes))
|
|
return null;
|
|
if (isNull(car(nodes).comp))
|
|
return graph.findcompnode(name, cdr(nodes));
|
|
if (name == scdl.name(car(nodes).comp))
|
|
return car(nodes);
|
|
var node = graph.findcompnode(name, nodeList(car(nodes).childNodes));
|
|
if (!isNull(node))
|
|
return node;
|
|
return graph.findcompnode(name, cdr(nodes));
|
|
}
|
|
|
|
/**
|
|
* Return a graphical group.
|
|
*/
|
|
graph.mkgroup = function(pos) {
|
|
var g = document.createElement('div');
|
|
g.className = 'g';
|
|
graph.translate(g, pos.x, pos.y);
|
|
g.pos = pos.clone();
|
|
return g;
|
|
};
|
|
|
|
/**
|
|
* Return a node representing a button.
|
|
*/
|
|
graph.mkbutton = function(t, pos) {
|
|
|
|
// Create the main button shape
|
|
var shape = document.createElement('canvas');
|
|
shape.className = 'path';
|
|
shape.fillStyle = graph.colors.lightgray1;
|
|
shape.path = graph.buttonpath();
|
|
graph.drawshape(shape);
|
|
|
|
// Make the button title
|
|
var title = graph.mktitle(t, graph.titlex, graph.titley);
|
|
|
|
// Create a group and add the button shape and title to it
|
|
var g = document.createElement('span');
|
|
g.className = 'g';
|
|
graph.translate(g, pos.x, pos.y);
|
|
g.pos = pos.clone();
|
|
g.appendChild(shape);
|
|
g.appendChild(title);
|
|
|
|
// Store the button shape in the group
|
|
g.shape = shape;
|
|
|
|
return g;
|
|
};
|
|
|
|
/**
|
|
* Return the position of a node relative to its parent.
|
|
*/
|
|
graph.relpos = function(g) {
|
|
var curX = g.ctmx? g.ctmx : 0;
|
|
var curY = g.ctmy? g.ctmy : 0;
|
|
return graph.mkpath().pos(curX, curY);
|
|
};
|
|
|
|
/**
|
|
* Move a node.
|
|
*/
|
|
graph.move = function(g, pos) {
|
|
g.pos = pos.clone();
|
|
graph.translate(g, g.pos.x, g.pos.y);
|
|
return g;
|
|
};
|
|
|
|
/**
|
|
* Return the absolute position of a component node.
|
|
*/
|
|
graph.abspos = function(e) {
|
|
if (isNull(e) || e == graphdiv)
|
|
return graph.mkpath();
|
|
var gpos = graph.relpos(e);
|
|
var pgpos = graph.abspos(e.parentNode);
|
|
return graph.mkpath().pos(gpos.x + pgpos.x, gpos.y + pgpos.y);
|
|
};
|
|
|
|
/**
|
|
* Bring a component node to the top.
|
|
*/
|
|
graph.bringtotop = function(n, g) {
|
|
if (n == g)
|
|
return null;
|
|
graph.move(n, graph.abspos(n));
|
|
g.appendChild(n);
|
|
}
|
|
|
|
/**
|
|
* Return the title of a SCDL element.
|
|
*/
|
|
graph.title = function(e) {
|
|
var t = scdl.title(e);
|
|
if (t != null) {
|
|
if (t == 'gt')
|
|
return '>'
|
|
if (t == 'lt')
|
|
return '<';
|
|
if (t.indexOf('{propval}') != -1)
|
|
return '';
|
|
if (t.indexOf('{compname}') == -1)
|
|
return t;
|
|
return t.replace('{compname}', scdl.name(e));
|
|
}
|
|
return scdl.name(e);
|
|
};
|
|
|
|
/**
|
|
* Return the property value of a SCDL component.
|
|
*/
|
|
graph.property = function(e) {
|
|
var p = scdl.properties(e);
|
|
if (isNull(p))
|
|
return '';
|
|
if (scdl.visible(car(p)) == 'false')
|
|
return '';
|
|
var pv = scdl.propertyValue(car(p));
|
|
return pv;
|
|
};
|
|
|
|
/**
|
|
* Return the title of a property of a SCDL component.
|
|
*/
|
|
graph.propertytitle = function(e) {
|
|
var pv = graph.property(e);
|
|
var t = scdl.title(e);
|
|
if (t.indexOf('{propval}') == -1)
|
|
return pv;
|
|
return t[0] == ' '? t.substr(1).replace('{propval}', pv) : t.replace('{propval}', pv);
|
|
};
|
|
|
|
/**
|
|
* Return true if a SCDL component has a property.
|
|
*/
|
|
graph.hasproperty = function(e) {
|
|
var p = scdl.properties(e);
|
|
if (isNull(p))
|
|
return false;
|
|
if (scdl.visible(car(p)) == 'false')
|
|
return false;
|
|
return true;
|
|
};
|
|
|
|
/**
|
|
* Change the property value of a SCDL component.
|
|
*/
|
|
graph.setproperty = function(e, value) {
|
|
var p = scdl.properties(e);
|
|
if (isNull(p))
|
|
return '';
|
|
if (scdl.visible(car(p)) == 'false')
|
|
return '';
|
|
var name = scdl.name(car(p));
|
|
setElement(car(p), mklist(element, "'property", mklist(attribute, "'name", name != null? name : "property"), value));
|
|
return value;
|
|
};
|
|
|
|
/**
|
|
* Return the color of a SCDL component.
|
|
*/
|
|
graph.color = function(comp) {
|
|
return memo(comp, 'color', function() {
|
|
var c = scdl.color(comp);
|
|
return c == null? graph.colors.blue1 : graph.colors[c];
|
|
});
|
|
};
|
|
|
|
/**
|
|
* Return the services on the left side of a component.
|
|
*/
|
|
graph.lsvcs = function(comp) {
|
|
return memo(comp, 'lsvcs', function() {
|
|
var svcs = scdl.services(comp);
|
|
if (isNull(svcs))
|
|
return mklist(mklist("'element","'service","'attribute","'name",scdl.name(comp)));
|
|
var l = filter(function(s) {
|
|
var a = scdl.align(s);
|
|
var v = scdl.visible(s);
|
|
return (a == null || a == 'left') && v != 'false';
|
|
}, svcs);
|
|
if (isNull(l))
|
|
return mklist();
|
|
return mklist(car(l));
|
|
});
|
|
};
|
|
|
|
/**
|
|
* Return the references on the right side of a component.
|
|
*/
|
|
graph.rrefs = function(comp) {
|
|
return memo(comp, 'rrefs', function() {
|
|
return filter(function(r) {
|
|
var a = scdl.align(r);
|
|
var v = scdl.visible(r);
|
|
return (a == null || a == 'right') && v != 'false';
|
|
}, scdl.references(comp));
|
|
});
|
|
};
|
|
|
|
/**
|
|
* Return the height of a reference on the right side of a component.
|
|
*/
|
|
graph.rrefheight = function(ref, cassoc) {
|
|
return memo(ref, 'rheight', function() {
|
|
var target = assoc(scdl.target(ref), cassoc);
|
|
if (isNull(target))
|
|
return graph.tabsz * 8;
|
|
return graph.compclosureheight(cadr(target), cassoc);
|
|
});
|
|
};
|
|
|
|
/**
|
|
* Return the total height of the references on the right side of a component.
|
|
*/
|
|
graph.rrefsheight = function(refs, cassoc) {
|
|
if (isNull(refs))
|
|
return 0;
|
|
return graph.rrefheight(car(refs), cassoc) + graph.rrefsheight(cdr(refs), cassoc);
|
|
};
|
|
|
|
/**
|
|
* Return the height of a component node.
|
|
*/
|
|
graph.compheight = function(comp, cassoc) {
|
|
return memo(comp, 'height', function() {
|
|
var lsvcs = graph.lsvcs(comp);
|
|
var lsvcsh = Math.max(1, length(lsvcs)) * (graph.tabsz * 8) + (graph.tabsz * 4);
|
|
var rrefs = graph.rrefs(comp);
|
|
var rrefsh = graph.rrefsheight(rrefs, cassoc) + (graph.tabsz * 2);
|
|
return Math.max(lsvcsh, rrefsh);
|
|
});
|
|
};
|
|
|
|
/**
|
|
* Return the height of a component and the components wired to its bottom side.
|
|
*/
|
|
graph.compclosureheight = function(comp, cassoc) {
|
|
return memo(comp, 'closureheight', function() {
|
|
return graph.compheight(comp, cassoc);
|
|
});
|
|
};
|
|
|
|
/**
|
|
* Return the max width of the references on the right side of a component.
|
|
*/
|
|
graph.rrefswidth = function(refs, cassoc) {
|
|
if (isNull(refs))
|
|
return 0;
|
|
return Math.max(graph.rrefwidth(car(refs), cassoc), graph.rrefswidth(cdr(refs), cassoc));
|
|
};
|
|
|
|
/**
|
|
* Return the width of a component.
|
|
*/
|
|
graph.compwidth = function(comp, cassoc) {
|
|
return memo(comp, 'width', function() {
|
|
var ctw = graph.comptitlewidth(comp);
|
|
var rrefsw = (isNull(graph.rrefs(comp))? 0 : (graph.tabsz * 4));
|
|
var twidth = (graph.titlex * 2) + ctw + rrefsw;
|
|
var width = Math.max(twidth, (graph.tabsz * 8) + (graph.tabsz * 4));
|
|
return width;
|
|
});
|
|
};
|
|
|
|
/**
|
|
* Return a path representing a reference positioned to the right of a component.
|
|
*/
|
|
graph.rrefpath = function(ref, cassoc, path, maxheight) {
|
|
var height = graph.rrefheight(ref, cassoc);
|
|
|
|
// Record reference position in the path
|
|
var xpos = path.x;
|
|
var ypos = path.y;
|
|
path.refpos = cons(mklist(ref, graph.mkpath().pos(xpos, ypos + (graph.tabsz * 5))), path.refpos);
|
|
|
|
// Compute the reference path
|
|
return path.rline(0,graph.tabsz * 2).rcurve(0,graph.tabsz,-graph.tabsz,0).rcurve(-graph.tabsz,0,0,-graph.tabsz/2.0).rcurve(0,-graph.tabsz/2.0,-graph.tabsz,0).rcurve(-graph.tabsz,0,0,graph.tabsz/2.0).rline(0,graph.tabsz * 3).rcurve(0,graph.tabsz/2.0,graph.tabsz,0).rcurve(graph.tabsz,0,0,-graph.tabsz/2.0).rcurve(0,-graph.tabsz/2.0,graph.tabsz,0).rcurve(graph.tabsz,0,0,graph.tabsz).line(path.x, Math.min(ypos + height, maxheight));
|
|
};
|
|
|
|
/**
|
|
* Return a path representing a service positioned to the left of a component.
|
|
*/
|
|
graph.lsvcpath = function(svc, cassoc, path, minheight) {
|
|
var height = graph.tabsz * 8;
|
|
|
|
// Record service position in the path
|
|
var xpos = path.x;
|
|
var ypos = path.y;
|
|
path.svcpos = cons(mklist(svc, graph.mkpath().pos(xpos, ypos - (graph.tabsz * 6))), path.svcpos);
|
|
|
|
// Compute the service path
|
|
return path.rline(0, -(graph.tabsz * 2)).rcurve(0,-graph.tabsz,-graph.tabsz,0).rcurve(-graph.tabsz,0,0,graph.tabsz/2.0).rcurve(0,graph.tabsz/2.0,-graph.tabsz,0).rcurve(-graph.tabsz,0,0,-graph.tabsz/2.0).rline(0,-(graph.tabsz * 3)).rcurve(0,-graph.tabsz/2.0,graph.tabsz,0).rcurve(graph.tabsz,0,0,graph.tabsz/2.0).rcurve(0,graph.tabsz/2.0,graph.tabsz,0).rcurve(graph.tabsz,0,0,-graph.tabsz).line(path.x, Math.max(ypos - height, minheight));
|
|
};
|
|
|
|
/**
|
|
* Return a path representing a component node.
|
|
*/
|
|
graph.comppath = function(comp, cassoc) {
|
|
|
|
// Calculate the width and height of the component node
|
|
var width = graph.compwidth(comp, cassoc);
|
|
var height = graph.compheight(comp, cassoc);
|
|
|
|
/**
|
|
* Apply a path rendering function to a list of services or references.
|
|
*/
|
|
function renderpath(x, f, cassoc, path, height) {
|
|
if (isNull(x))
|
|
return path;
|
|
return renderpath(cdr(x), f, cassoc, f(car(x), cassoc, path, height), height);
|
|
}
|
|
|
|
var path = graph.mkpath().move(graph.curvsz,0);
|
|
|
|
// Store the positions of services and references in the path
|
|
path.refpos = mklist();
|
|
path.svcpos = mklist();
|
|
|
|
// Render the references on the right side of the component
|
|
var rrefs = graph.rrefs(comp);
|
|
path = path.line(width - graph.curvsz,path.y).rcurve(graph.curvsz,0,0,graph.curvsz);
|
|
path = renderpath(rrefs, graph.rrefpath, cassoc, path, height - graph.curvsz);
|
|
|
|
// Render the references on the bottom side of the component
|
|
var boffset = graph.curvsz;
|
|
path = path.line(path.x,height - graph.curvsz).rcurve(0,graph.curvsz,graph.curvsz * -1,0).line(boffset, path.y);
|
|
|
|
// Render the services on the left side of the component
|
|
var lsvcs = graph.lsvcs(comp);
|
|
var loffset = graph.curvsz + (length(lsvcs) * (graph.tabsz * 8));
|
|
path = path.line(graph.curvsz,path.y).rcurve(graph.curvsz * -1,0,0,graph.curvsz * -1).line(path.x, loffset);
|
|
path = renderpath(lsvcs, graph.lsvcpath, cassoc, path, graph.curvsz);
|
|
|
|
// Close the component node path
|
|
path = path.line(0,graph.curvsz).rcurve(0,graph.curvsz * -1,graph.curvsz,0);
|
|
|
|
return path.end();
|
|
};
|
|
|
|
/**
|
|
* Return the position of a component.
|
|
*/
|
|
graph.comppos = function(comp, pos) {
|
|
var x = scdl.x(comp);
|
|
var y = scdl.y(comp);
|
|
return graph.mkpath().pos(x != null? Number(x) + graph.palcx : pos.x, y != null? Number(y) : pos.y);
|
|
};
|
|
|
|
/**
|
|
* Return a path representing a button node.
|
|
*/
|
|
graph.buttonpath = function(t) {
|
|
var path = graph.mkpath().move(graph.curvsz,0);
|
|
path = path.line(graph.buttoncx - graph.curvsz,path.y).rcurve(graph.curvsz,0,0,graph.curvsz);
|
|
path = path.line(path.x,graph.buttoncy - graph.curvsz).rcurve(0,graph.curvsz,-graph.curvsz,0).line(graph.curvsz, path.y);
|
|
path = path.line(graph.curvsz,path.y).rcurve(-graph.curvsz,0,0,-graph.curvsz).line(path.x, graph.curvsz);
|
|
path = path.line(0,graph.curvsz).rcurve(0,-graph.curvsz,graph.curvsz,0);
|
|
return path.end();
|
|
};
|
|
|
|
/**
|
|
* Render a SCDL composite into a list of component nodes.
|
|
*/
|
|
graph.composite = function(compos, pos, aspalette) {
|
|
var name = scdl.name(scdl.composite(compos));
|
|
var comps = scdl.components(compos);
|
|
var cassoc = scdl.nameToElementAssoc(comps);
|
|
var proms = scdl.promotions(compos);
|
|
|
|
// Unmemoize any memoized info about components and their references.
|
|
map(function(c) {
|
|
unmemo(c);
|
|
map(function(r) {
|
|
unmemo(r);
|
|
}, scdl.references(c));
|
|
}, comps);
|
|
|
|
/**
|
|
* Render a component.
|
|
*/
|
|
function rendercomp(comp, cassoc, pos) {
|
|
|
|
/**
|
|
* Render the references on the right side of a component.
|
|
*/
|
|
function renderrrefs(refs, cassoc, pos, gcomp) {
|
|
|
|
/**
|
|
* Render a reference on the right side of a component.
|
|
*/
|
|
function renderrref(ref, cassoc, pos, gcomp) {
|
|
var target = assoc(scdl.target(ref), cassoc);
|
|
if (isNull(target))
|
|
return null;
|
|
|
|
// Render the component target of the reference
|
|
return rendercomp(cadr(target), cassoc, pos);
|
|
}
|
|
|
|
/**
|
|
* Move the rendering cursor down below a reference.
|
|
*/
|
|
function rendermove(ref, cassoc, pos) {
|
|
return pos.clone().rmove(0, graph.rrefheight(ref, cassoc));
|
|
}
|
|
|
|
if (isNull(refs))
|
|
return mklist();
|
|
|
|
// Return list of (ref, comp rendering) pairs
|
|
var grefcomp = renderrref(car(refs), cassoc, pos, gcomp);
|
|
return cons(mklist(car(refs), grefcomp), renderrrefs(cdr(refs), cassoc, rendermove(car(refs), cassoc, pos), gcomp));
|
|
}
|
|
|
|
// Compute the component shape
|
|
var gcomp = graph.compnode(comp, cassoc, pos);
|
|
|
|
// Render the components wired to the component references
|
|
var rrefs = graph.rrefs(comp);
|
|
var rpos = graph.mkpath().rmove(graph.compwidth(comp, cassoc), 0);
|
|
var grrefs = renderrrefs(rrefs, cassoc, rpos, gcomp);
|
|
|
|
// Store list of (ref, pos, component rendering) triplets in the component
|
|
function refposgcomp(refpos, grefs) {
|
|
if (isNull(refpos))
|
|
return mklist();
|
|
|
|
// Append component rendering to component
|
|
var gref = cadr(car(grefs));
|
|
if (gref != null)
|
|
gcomp.appendChild(gref);
|
|
return cons(mklist(car(car(refpos)), cadr(car(refpos)), gref), refposgcomp(cdr(refpos), cdr(grefs)));
|
|
}
|
|
|
|
gcomp.refpos = refposgcomp(gcomp.refpos, grrefs);
|
|
|
|
return gcomp;
|
|
}
|
|
|
|
/**
|
|
* Render a list of promoted service components.
|
|
*/
|
|
function renderproms(svcs, cassoc, pos) {
|
|
|
|
/**
|
|
* Return the component promoted by a service.
|
|
*/
|
|
function promcomp(svc, cassoc) {
|
|
var c = assoc(scdl.promote(svc), cassoc);
|
|
if (isNull(c))
|
|
return mklist();
|
|
return cadr(c);
|
|
}
|
|
|
|
/**
|
|
* Move the rendering cursor down below a component.
|
|
*/
|
|
function rendermove(comp, cassoc, pos) {
|
|
return pos.clone().rmove(0, graph.compclosureheight(comp, cassoc) + Math.max((graph.tabsz * 2), 8));
|
|
}
|
|
|
|
if (isNull(svcs))
|
|
return mklist();
|
|
|
|
// Render the first promoted component in the list
|
|
// then recurse to render the rest of the list
|
|
var comp = promcomp(car(svcs), cassoc);
|
|
if (isNull(comp))
|
|
return renderproms(cdr(svcs), cassoc, rendermove(car(svcs), cassoc, pos));
|
|
|
|
var cpos = graph.comppos(comp, pos);
|
|
return cons(rendercomp(comp, cassoc, cpos), renderproms(cdr(svcs), cassoc, rendermove(comp, cassoc, cpos)));
|
|
}
|
|
|
|
// Render the promoted service components
|
|
var rproms = renderproms(proms, cassoc, pos.clone().rmove(graph.tabsz * 4, graph.tabsz * 4));
|
|
|
|
if (aspalette) {
|
|
|
|
// Prefix ids of palette component elements with 'palette:' and
|
|
// move them to the palette area
|
|
return map(function(r) {
|
|
r.id = 'palette:' + r.id;
|
|
var gpos = r.pos;
|
|
graph.move(r, graph.mkpath().pos(gpos.x - graph.palcx, gpos.y));
|
|
return r;
|
|
}, rproms);
|
|
|
|
} else {
|
|
|
|
// Link app component elements to the containing composite
|
|
return map(function(r) { r.compos = compos; return r; }, rproms);
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Return a component unique id.
|
|
*/
|
|
graph.ucid = function(prefix, compos1, compos2, clone) {
|
|
|
|
// Build an assoc list keyed by component name
|
|
var comps = map(function(c) { return mklist(scdl.name(c), c); }, append(namedElementChildren("'component", compos1), namedElementChildren("'component", compos2)));
|
|
|
|
if (!clone && isNull(assoc(prefix, comps)))
|
|
return prefix;
|
|
|
|
/**
|
|
* Find a free component id.
|
|
*/
|
|
function ucid(p, id) {
|
|
if (isNull(assoc(p + id, comps)))
|
|
return p + id;
|
|
return ucid(p, id + 1);
|
|
}
|
|
|
|
/**
|
|
* Remove trailing digits from a prefix.
|
|
*/
|
|
function untrail(p) {
|
|
if (p.length < 2 || p[p.length - 1] < '0' || p[p.length - 1] > '9')
|
|
return p;
|
|
return untrail(p.substring(0, p.length - 1));
|
|
}
|
|
|
|
return ucid(prefix == ''? 'comp' : (clone? untrail(prefix) : prefix), 1);
|
|
};
|
|
|
|
/**
|
|
* Clone a palette component node.
|
|
*/
|
|
graph.clonepalette = function(e, compos) {
|
|
|
|
// Clone the SCDL component and give it a unique name
|
|
var wcomp = append(mklist(element, "'component", mklist(attribute, "'name", graph.ucid(scdl.name(e.comp), compos, compos, true))),
|
|
filter(function(c) { return !(isAttribute(c) && attributeName(c) == "'name")}, elementChildren(e.comp)));
|
|
var x = '<composite>' + writeXML(mklist(wcomp), false) + '</composite>';
|
|
var rcompos = scdl.composite(readXML(mklist(x)));
|
|
var comp = car(scdl.components(mklist(rcompos)));
|
|
|
|
// Update component position
|
|
setElement(comp, graph.movecomp(comp, graph.abspos(e).rmove(graph.palcx, 0)));
|
|
|
|
return comp;
|
|
};
|
|
|
|
/**
|
|
* Move a SCDL component to the given position.
|
|
*/
|
|
graph.movecomp = function(comp, pos) {
|
|
if (isNull(pos))
|
|
return append(mklist(element, "'component"),
|
|
filter(function(e) { return !(isAttribute(e) && (attributeName(e) == "'x" || attributeName(e) == "'y")); }, elementChildren(comp)));
|
|
return append(mklist(element, "'component", mklist(attribute, "'x", '' + (pos.x - graph.palcx)), mklist(attribute, "'y", '' + pos.y)),
|
|
filter(function(e) { return !(isAttribute(e) && (attributeName(e) == "'x" || attributeName(e) == "'y")); }, elementChildren(comp)));
|
|
};
|
|
|
|
/**
|
|
* Align a pos along a 10pixel grid.
|
|
*/
|
|
graph.gridsnap = function(x) {
|
|
return Math.round(x / 10) * 10;
|
|
}
|
|
|
|
/**
|
|
* Clone a component node and all the components it references.
|
|
*/
|
|
graph.clonecomp = function(e, compos) {
|
|
|
|
// Write the component and the components it references to XML
|
|
function collectcomp(e) {
|
|
function collectrefs(refpos) {
|
|
if (isNull(refpos))
|
|
return mklist();
|
|
var r = car(refpos);
|
|
var n = caddr(r);
|
|
if (isNull(n))
|
|
return collectrefs(cdr(refpos));
|
|
return append(collectcomp(n), collectrefs(cdr(refpos)));
|
|
}
|
|
|
|
return cons(e.comp, collectrefs(e.refpos));
|
|
}
|
|
|
|
var allcomps = collectcomp(e);
|
|
var ls = map(function(e) { return writeXML(mklist(e), false); }, allcomps);
|
|
var x = '<composite>' + writeStrings(ls) + '</composite>';
|
|
|
|
// Read them back from XML to clone them
|
|
var rcompos = scdl.composite(readXML(mklist(x)));
|
|
var comps = scdl.components(mklist(rcompos));
|
|
|
|
// Give them new unique names
|
|
map(function(e) {
|
|
|
|
// Rename each component
|
|
var oname = scdl.name(e);
|
|
var name = graph.ucid(oname, compos, rcompos, true);
|
|
setElement(e, append(mklist(element, "'component", mklist(attribute, "'name", name)),
|
|
filter(function(c) { return !(isAttribute(c) && attributeName(c) == "'name")}, elementChildren(e))));
|
|
|
|
// Refactor references to the component
|
|
map(function(c) { return graph.refactorrefs(scdl.references(c), oname, name); }, comps);
|
|
}, comps);
|
|
|
|
// Update the top component position
|
|
var comp = car(comps);
|
|
setElement(comp, graph.movecomp(comp, graph.abspos(e).rmove(10, 10)));
|
|
|
|
return comps;
|
|
};
|
|
|
|
/**
|
|
* Sort elements of a composite.
|
|
*/
|
|
graph.sortcompos = function(compos) {
|
|
return append(mklist(element, "'composite"), elementChildren(compos).sort(function(a, b) {
|
|
|
|
// Sort attributes, place them at the top
|
|
var aa = isAttribute(a);
|
|
var ba = isAttribute(b);
|
|
if (aa && !ba) return -1;
|
|
if (!aa && ba) return 1;
|
|
if (aa && ba) {
|
|
var aan = attributeName(a);
|
|
var ban = attributeName(b);
|
|
if (aan < ban) return -1;
|
|
if (aan > ban) return 1;
|
|
return 0;
|
|
}
|
|
|
|
// Sort elements, place services before components
|
|
var aen = elementName(a);
|
|
var ben = elementName(b);
|
|
if (aen == "'service" && ben == "'component") return -1;
|
|
if (aen == "'component" && ben == "'service") return 1;
|
|
var an = scdl.name(a);
|
|
var bn = scdl.name(b);
|
|
if (an < bn) return -1;
|
|
if (an > bn) return 1;
|
|
return 0;
|
|
}));
|
|
}
|
|
|
|
/**
|
|
* Add a list of components to a SCDL composite. The first
|
|
* component in the list is a promoted component.
|
|
*/
|
|
graph.addcomps = function(comps, compos) {
|
|
var comp = car(comps);
|
|
var name = scdl.name(comp);
|
|
var prom = mklist(element, "'service", mklist(attribute, "'name", name), mklist(attribute, "'promote", name));
|
|
return append(mklist(element, "'composite"), append(elementChildren(compos), cons(prom, comps)));
|
|
};
|
|
|
|
/**
|
|
* Remove a component from a SCDL composite.
|
|
*/
|
|
graph.removecomp = function(comp, compos) {
|
|
var name = scdl.name(comp);
|
|
return append(mklist(element, "'composite"),
|
|
filter(function(c) { return !(isElement(c) && scdl.name(c) == name); }, elementChildren(compos)));
|
|
};
|
|
|
|
/**
|
|
* Garbage collect components not referenced or promoted.
|
|
*/
|
|
graph.gcollect = function(compos) {
|
|
|
|
// List the promoted components
|
|
var proms = map(function(s) { return mklist(scdl.promote(s), true); }, scdl.promotions(mklist(compos)));
|
|
|
|
// List the referenced components
|
|
var refs = reduce(function(a, comp) {
|
|
return append(a,
|
|
map(function(ref) { return mklist(scdl.target(ref), true); }, filter(function(ref) { return scdl.target(ref) != null; }, scdl.references(comp))));
|
|
}, mklist(), scdl.components(mklist(compos)));
|
|
|
|
// Filter out the unused components
|
|
var used = append(proms, refs);
|
|
return append(mklist(element, "'composite"),
|
|
filter(function(c) { return !(isElement(c) && elementName(c) == "'component" && isNull(assoc(scdl.name(c), used))); }, elementChildren(compos)));
|
|
}
|
|
|
|
/**
|
|
* Clone and cleanup clonable references.
|
|
*/
|
|
graph.clonerefs = function(compos) {
|
|
return append(mklist(element, "'composite"),
|
|
map(function(c) {
|
|
if (elementName(c) != "'component")
|
|
return c;
|
|
|
|
// If the references are clonable
|
|
var refs = scdl.references(c);
|
|
if (isNull(refs))
|
|
return c;
|
|
if (scdl.clonable(car(refs)) != 'true')
|
|
return c;
|
|
|
|
// Filter out the unwired references and add a fresh unwired
|
|
// reference at the end of the list
|
|
var cc = append(
|
|
filter(function(e) { return !(elementName(e) == "'reference" && scdl.target(e) == null); }, elementChildren(c)),
|
|
mklist(mklist(element, "'reference", mklist(attribute, "'name", scdl.name(car(refs))), mklist(attribute, "'clonable", "true"))));
|
|
return append(mklist(element, "'component"), cc);
|
|
|
|
}, elementChildren(compos)));
|
|
}
|
|
|
|
/**
|
|
* Refactor references to a component.
|
|
*/
|
|
graph.refactorrefs = function(refs, oname, nname) {
|
|
if (isNull(refs))
|
|
return true;
|
|
var ref = car(refs);
|
|
if (scdl.target(ref) != oname)
|
|
return graph.refactorrefs(cdr(refs), oname, nname);
|
|
|
|
// Change the reference's target attribute
|
|
setElement(ref, append(mklist(element, "'reference"),
|
|
append(filter(function(e) { return !(isAttribute(e) && attributeName(e) == "'target"); }, elementChildren(ref)),
|
|
mklist(mklist(attribute, "'target", nname)))));
|
|
|
|
return graph.refactorrefs(cdr(refs), oname, nname);
|
|
};
|
|
|
|
/**
|
|
* Rename a component.
|
|
*/
|
|
graph.renamecomp = function(comp, compos, name) {
|
|
|
|
// Refactor all the references to the renamed component
|
|
var oname = scdl.name(comp);
|
|
map(function(c) { return graph.refactorrefs(scdl.references(c), oname, name); }, namedElementChildren("'component", compos));
|
|
|
|
// Rename the SCDL promoted service and component
|
|
var proms = filter(function(s) { return scdl.name(s) == oname }, scdl.services(compos));
|
|
if (!isNull(proms))
|
|
setElement(car(proms), mklist(element, "'service", mklist(attribute, "'name", name), mklist(attribute, "'promote", name)));
|
|
setElement(comp, append(mklist(element, "'component"),
|
|
cons(mklist(attribute, "'name", name),
|
|
filter(function(e) { return !(isAttribute(e) && attributeName(e) == "'name"); }, elementChildren(comp)))));
|
|
|
|
return append(mklist(element, "'composite"), elementChildren(compos));
|
|
};
|
|
|
|
/**
|
|
* Cut the wire to a component node and make that node a
|
|
* top level component node.
|
|
*/
|
|
graph.cutwire = function(node, compos, g) {
|
|
|
|
/**
|
|
* Find the reference wired to a node and cut its wire.
|
|
*/
|
|
function cutref(refs, node) {
|
|
if (isNull(refs))
|
|
return true;
|
|
var ref = car(refs);
|
|
if (caddr(ref) == node) {
|
|
setlist(ref, mklist(car(ref), cadr(ref), null));
|
|
setElement(car(ref),
|
|
append(mklist(element, "'reference"),
|
|
filter(function(e) { return !(isAttribute(e) && attributeName(e) == "'target"); }, elementChildren(car(ref)))));
|
|
}
|
|
return cutref(cdr(refs), node);
|
|
}
|
|
|
|
// Cut any reference wire, if found
|
|
cutref(node.parentNode.refpos, node);
|
|
|
|
// Make the component node a top level node.
|
|
node.compos = g.compos;
|
|
|
|
// Update the SCDL composite, add a promote element for
|
|
// that component
|
|
var comp = node.comp;
|
|
var name = scdl.name(comp);
|
|
var prom = mklist(element, "'service", mklist(attribute, "'name", name), mklist(attribute, "'promote", name));
|
|
return append(mklist(element, "'composite"),
|
|
append(mklist(prom), filter(function(c) { return !(isElement(c) && elementName(c) == "'service" && scdl.name(c) == name); }, elementChildren(compos))));
|
|
}
|
|
|
|
/**
|
|
* Wire a component to the closest neighbor reference.
|
|
*/
|
|
graph.wire = function(n, compos, g) {
|
|
|
|
// Compute position of the component's service node
|
|
var spos = cadr(car(n.svcpos));
|
|
var aspos = graph.abspos(n).rmove(spos.x, spos.y);
|
|
|
|
/**
|
|
* Find closest unwired reference node among all the references
|
|
* of all the components.
|
|
*/
|
|
function closecomprefs(nodes, spos, cref) {
|
|
|
|
/**
|
|
* Find the closest unwired reference node among all the
|
|
* references of a node.
|
|
*/
|
|
function closerefs(npos, refs, spos, cref) {
|
|
if (isNull(refs))
|
|
return cref;
|
|
var fdist = cadddr(cref);
|
|
var ref = car(refs);
|
|
|
|
// Skip wired reference
|
|
if (!isNull(filter(function(n) { return isAttribute(n) && attributeName(n) == "'target"; }, car(ref))))
|
|
return closerefs(npos, cdr(refs), spos, cref);
|
|
|
|
// Compute distance between service node and reference node
|
|
var rpos = cadr(ref).clone().rmove(npos.x, npos.y);
|
|
var dx = Math.pow(rpos.x - spos.x, 2);
|
|
var dy = Math.pow(rpos.y - spos.y, 2);
|
|
|
|
// Check for proximity threshold
|
|
var rdist = (dx < (graph.proxcx * graph.proxcx) && dy < (graph.proxcy * graph.proxcy))? Math.sqrt(dx + dy) : 25000000;
|
|
|
|
// Go through all the references in the component
|
|
return closerefs(npos, cdr(refs), spos, fdist < rdist? cref : mklist(car(ref), cadr(ref), caddr(ref), rdist));
|
|
}
|
|
|
|
if (isNull(nodes))
|
|
return cref;
|
|
|
|
// Skip non-component nodes
|
|
var node = car(nodes);
|
|
if (isNull(node.comp))
|
|
return closecomprefs(cdr(nodes), spos, cref);
|
|
|
|
// Compute the component absolute position
|
|
var npos = graph.abspos(node);
|
|
|
|
// Go through all the components and their references
|
|
return closecomprefs(append(nodeList(node.childNodes), cdr(nodes)), spos, closerefs(npos, node.refpos, spos, cref));
|
|
}
|
|
|
|
// Find closest reference node
|
|
var cref = closecomprefs(nodeList(g.childNodes), aspos, mklist(null, graph.mkpath(), null, 25000000));
|
|
if (car(cref) == null)
|
|
return compos;
|
|
if (cadddr(cref) == 25000000)
|
|
return compos;
|
|
|
|
// Wire component to that reference, un-promote it, and
|
|
// update the SCDL reference and composite
|
|
setElement(n.comp, graph.movecomp(graph.dragging.comp, null));
|
|
n.compos = null;
|
|
setElement(car(cref), append(mklist(element, "'reference", mklist(attribute, "'target", scdl.name(n.comp))), elementChildren(car(cref))));
|
|
var name = scdl.name(n.comp);
|
|
return append(mklist(element, "'composite"),
|
|
filter(function(c) { return !(isElement(c) && elementName(c) == "'service" && scdl.name(c) == name); }, elementChildren(compos)));
|
|
}
|
|
|
|
/**
|
|
* Display a list of graphical nodes.
|
|
*/
|
|
graph.display = function(nodes, g) {
|
|
|
|
// Append the nodes to the graphical canvas
|
|
appendNodes(nodes, g);
|
|
return nodes;
|
|
};
|
|
|
|
/**
|
|
* Hide a graph.
|
|
*/
|
|
graph.hide = function(g) {
|
|
|
|
// Remove nodes from the graph
|
|
map(function(n) { if (!isNull(n.comp) && n.id.substr(0, 8) != 'palette:') { g.removeChild(n); } return n; }, nodeList(g.childNodes));
|
|
return g;
|
|
};
|
|
|
|
/**
|
|
* Refresh a graph.
|
|
*/
|
|
graph.refresh = function(g) {
|
|
//debug('refresh');
|
|
|
|
// Remove existing nodes from the graph
|
|
map(function(n) { if (!isNull(n.comp) && n.id.substr(0, 8) != 'palette:') { g.removeChild(n); } return n; }, nodeList(g.childNodes));
|
|
|
|
// Redisplay the composite associated with the graph
|
|
var nodes = graph.composite(g.compos, graph.mkpath().pos(graph.palcx,0), false);
|
|
appendNodes(nodes, g);
|
|
return nodes;
|
|
};
|
|
|
|
/**
|
|
* Display and enable editing of a composite and the graphical
|
|
* nodes that represent it.
|
|
*/
|
|
graph.edit = function(appname, compos, nodes, g) {
|
|
|
|
// Store the composite elements, and sort them to allow for change detection later
|
|
g.compos = compos;
|
|
var scompos = scdl.composite(g.compos);
|
|
setElement(scompos, graph.sortcompos(scompos));
|
|
|
|
// Remove existing nodes from the graph
|
|
map(function(n) { if (!isNull(n.comp) && n.id.substr(0, 8) != 'palette:') { g.removeChild(n); } return n; }, nodeList(g.childNodes));
|
|
|
|
// Display the composite nodes
|
|
appendNodes(nodes, g);
|
|
return nodes;
|
|
};
|
|
|
|
/**
|
|
* Track the composition graph, whether it's visible or not and the selected component.
|
|
*/
|
|
var gvisible = true;
|
|
var gcomp = null;
|
|
var cdiv = $('contentdiv');
|
|
var pdiv = $('playdiv');
|
|
var graphdiv = $('graphdiv');
|
|
|
|
/**
|
|
* Track the palettes.
|
|
*/
|
|
var gpalettes = new Array();
|
|
var spalette = 'control';
|
|
var bpalette = null;
|
|
|
|
/**
|
|
* Get and display an application composite.
|
|
*/
|
|
function getapp(name, g) {
|
|
if (isNull(name))
|
|
return false;
|
|
workingstatus(true);
|
|
showstatus('Loading');
|
|
|
|
return composites.get(name, function(doc) {
|
|
|
|
// Stop now if we didn't get a composite
|
|
if (doc == null) {
|
|
errorstatus('Couldn\'t get the app info');
|
|
workingstatus(false);
|
|
return false;
|
|
}
|
|
|
|
// Get the composite from the ATOM entry
|
|
var composentry = car(atom.readATOMEntry(mklist(doc)));
|
|
var content = namedElementChild("'content", composentry);
|
|
composite = isNull(content)? mklist() : elementChildren(content);
|
|
if (isNull(composite)) {
|
|
|
|
// 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>';
|
|
composite = readXML(mklist(x));
|
|
}
|
|
|
|
// Display the composite
|
|
graph.edit(name, composite, graph.composite(composite, graph.mkpath().move(graph.palcx,0), false, g), g);
|
|
|
|
// Track the saved composite XML
|
|
savedcomposxml = car(writeXML(composite, false));
|
|
|
|
// Enable author to edit the composite
|
|
author = elementValue(namedElementChild("'author", composentry));
|
|
editable = author == username;
|
|
cadd.disabled = !editable;
|
|
if (editable)
|
|
onlinestatus();
|
|
else
|
|
showstatus('Read only');
|
|
|
|
workingstatus(false);
|
|
return true;
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Display a palette. Get it from the server if needed.
|
|
*/
|
|
function displaypalette(name, g, palette, gpalettes) {
|
|
if (isNull(name))
|
|
return;
|
|
if (isNull(gpalettes[name])) {
|
|
|
|
// Get the palette from the server
|
|
palettes.get(name, function(doc) {
|
|
var entry = car(atom.readATOMEntry(mklist(doc)));
|
|
var content = namedElementChild("'content", entry);
|
|
var compos = isNull(content)? mklist() : elementChildren(content);
|
|
gpalettes[name] = graph.composite(compos, graph.mkpath().move(2580,0), true, g);
|
|
graph.display(gpalettes[name], g);
|
|
});
|
|
return true;
|
|
}
|
|
graph.display(gpalettes[name], g);
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Install a palette, including a button to select the palette, and
|
|
* the palette content.
|
|
*/
|
|
function installpalette(name, pos, g, bg, palette, gpalettes) {
|
|
var b = graph.mkbutton(name, pos);
|
|
graph.display(mklist(b), g);
|
|
b.onclick = function(e) {
|
|
|
|
// Swap the selected palette
|
|
graph.paletteselect(bpalette, false);
|
|
displaypalette(spalette, bg, palette, gpalettes);
|
|
bpalette = b;
|
|
graph.paletteselect(b, true);
|
|
spalette = name;
|
|
return displaypalette(spalette, g, palette, gpalettes);
|
|
};
|
|
|
|
if (name != spalette) {
|
|
|
|
// Will get the palette from the server later if needed
|
|
gpalettes[name] = null;
|
|
return true;
|
|
}
|
|
|
|
// Display the selected palette
|
|
graph.paletteselect(b, true);
|
|
displaypalette(name, g, palette, gpalettes);
|
|
|
|
return b;
|
|
}
|
|
|
|
/**
|
|
* Save the current composite.
|
|
*/
|
|
function save(savexml) {
|
|
workingstatus(true);
|
|
showstatus('Saving');
|
|
|
|
savedcomposxml = savexml;
|
|
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">' + savedcomposxml + '</content></entry>';
|
|
composites.put(appname, entry, function(e) {
|
|
if (e) {
|
|
showstatus('Local copy');
|
|
workingstatus(false);
|
|
return false;
|
|
}
|
|
showstatus('Saved');
|
|
workingstatus(false);
|
|
return false;
|
|
});
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Handle a composite change event.
|
|
*/
|
|
function oncomposchange(prop) {
|
|
if (!editable)
|
|
return false;
|
|
|
|
var newxml = car(writeXML(composite, false));
|
|
if (savedcomposxml == newxml)
|
|
return false;
|
|
showstatus('Modified');
|
|
|
|
// Save property changes right away
|
|
if (prop)
|
|
return save(newxml);
|
|
|
|
// Autosave other changes after 1 second
|
|
showstatus('Modified');
|
|
ui.delay(function autosave() {
|
|
if (savedcomposxml == newxml) {
|
|
showstatus('Saved');
|
|
return false;
|
|
}
|
|
return save(newxml);
|
|
}, 1000);
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Return the link to a component.
|
|
*/
|
|
function complink(appname, cname) {
|
|
if (cname == '' || isNull(cname))
|
|
return '';
|
|
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 + '/c/' + cname;
|
|
return link;
|
|
}
|
|
|
|
/**
|
|
* Handle a component select event.
|
|
*/
|
|
function oncompselect(gsel) {
|
|
if (gsel == gcomp)
|
|
return true;
|
|
gcomp = gsel;
|
|
|
|
cdelete.disabled = isNull(gsel) || !editable;
|
|
ccopy.disabled = isNull(gsel) || !editable;
|
|
cplay.disabled = isNull(gsel);
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Show the result data of a component.
|
|
*/
|
|
function showdata(gcomp) {
|
|
if (!gvisible)
|
|
return true;
|
|
if (isNull(gcomp))
|
|
return true;
|
|
cvalue.value = complink(appname, gcomp.id);
|
|
cplay.value = '<';
|
|
gvisible = false;
|
|
pdiv.innerHTML = '';
|
|
pdiv.style.display = 'block';
|
|
|
|
// Get the component result data
|
|
var comp = sca.component(gcomp.id, appname);
|
|
comp.get('', function(doc) {
|
|
function displaydata(t, w) {
|
|
pdiv.style.width = w;
|
|
pdiv.innerHTML = t;
|
|
return true;
|
|
}
|
|
|
|
// Stop now if we didn't get the doc
|
|
if (doc == null)
|
|
return displaydata('No content', '2500px');
|
|
|
|
// Format data table
|
|
if (json.isJSON(mklist(doc)))
|
|
return displaydata(ui.datatable(json.readJSON(mklist(doc))), '2500px');
|
|
|
|
if (atom.isATOMEntry(mklist(doc)))
|
|
return displaydata(ui.datatable(atom.readATOMEntry(mklist(doc))), '2500px');
|
|
|
|
if (atom.isATOMFeed(mklist(doc)))
|
|
return display(ui.datatable(atom.readATOMFeed(mklist(doc))), '2500px');
|
|
|
|
// Insert the doc as is in an iframe
|
|
var t = '<table class="datatable" style="width: 100%;">' +
|
|
'<tr><td class="datatdltop">' + 'value' + '</td>' + '<td class="datatdr">' +
|
|
'<iframe style="width: 100%; height: 5000px;" scrolling="no" frameborder="0" src="' + clink + '"/>' +
|
|
'</td></tr></table>'
|
|
return displaydata(t, '100%');
|
|
});
|
|
|
|
ui.delay(function hidegraphdiv() {
|
|
graphdiv.style.display = 'none'
|
|
});
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Show the composition graph.
|
|
*/
|
|
function showgraph(gcomp) {
|
|
if (gvisible)
|
|
return true;
|
|
cplay.value = '>';
|
|
graphdiv.style.display = 'block'
|
|
gvisible = true;
|
|
graph.compselect(gcomp, true, atitle, cvalue, ccopy, cdelete);
|
|
ui.delay(function hideplaydiv() {
|
|
pdiv.style.display = 'none';
|
|
pdiv.innerHTML = '';
|
|
});
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Handle play component button event.
|
|
*/
|
|
cplay.onclick = function() {
|
|
if (gcomp == null)
|
|
return false;
|
|
if (!gvisible)
|
|
return showgraph(gcomp);
|
|
return showdata(gcomp);
|
|
}
|
|
|
|
/**
|
|
* Create editor graph area.
|
|
*/
|
|
graph.mkedit(graphdiv, graph.mkpath().move(-2500,0), atitle, cvalue, cadd, ccopy, cdelete, oncomposchange, oncompselect);
|
|
|
|
/**
|
|
* Install the palettes.
|
|
*/
|
|
var bg = graph.mkgroup(graph.mkpath());
|
|
var pos = graph.mkpath().move(0, 0);
|
|
bpalette = installpalette('control', pos.rmove(5,2), graphdiv, bg, spalette, gpalettes);
|
|
installpalette('values', pos.rmove(0,28), graphdiv, bg, spalette, gpalettes);
|
|
installpalette('lists', pos.rmove(0, 28), graphdiv, bg, spalette, gpalettes);
|
|
installpalette('transform', pos.rmove(0, 28), graphdiv, bg, spalette, gpalettes);
|
|
installpalette('text', pos.rmove(0, 28), graphdiv, bg, spalette, gpalettes);
|
|
installpalette('http', pos.rmove(0, 28), graphdiv, bg, spalette, gpalettes);
|
|
installpalette('animation', pos.rmove(0, 28), graphdiv, bg, spalette, gpalettes);
|
|
installpalette('talk', pos.rmove(0, 28), graphdiv, bg, spalette, gpalettes);
|
|
installpalette('social', pos.rmove(0, 28), graphdiv, bg, spalette, gpalettes);
|
|
installpalette('search', pos.rmove(0, 28), graphdiv, bg, spalette, gpalettes);
|
|
installpalette('database', pos.rmove(0, 28), graphdiv, bg, spalette, gpalettes);
|
|
installpalette('logic', pos.rmove(0, 28), graphdiv, bg, spalette, gpalettes);
|
|
installpalette('math', pos.rmove(0, 28), graphdiv, bg, spalette, gpalettes);
|
|
installpalette('python', pos.rmove(0, 28), graphdiv, bg, spalette, gpalettes);
|
|
|
|
/**
|
|
* Get and display the current app.
|
|
*/
|
|
getapp(appname, graphdiv);
|
|
|
|
})();
|
|
</script>
|
|
|
|
</div>
|