search
close
@@ -494,17 +471,17 @@ export class RightClick {
$('#customContextMenu').append(searchbar)
$('#customContextMenu').append('')
- var paletteList = RightClick.createHtmlEdgeDragList(data)
+ const paletteList = RightClick.createHtmlEdgeDragList(data)
$('#rightClickPaletteList').append(paletteList)
Eagle.selectedRightClickLocation(Eagle.FileType.Graph)
$('#rightClickSearchBar').focus()
RightClick.initiateQuickSelect()
}else{
- var message = 'Lacking graph editing permissions'
+ const message = 'Lacking graph editing permissions'
$('#customContextMenu').append(message)
}
- }else if(targetClass.includes('rightClick_paletteComponent')){
+ }else if(passedObjectClass === 'rightClick_paletteComponent'){
Eagle.selectedRightClickLocation(Eagle.FileType.Palette)
if(Setting.findValue(Setting.ALLOW_PALETTE_EDITING)){
@@ -513,7 +490,7 @@ export class RightClick {
$('#customContextMenu').append('Delete')
$('#customContextMenu').append('
Add to another palette')
}
- }else if(targetClass.includes('rightClick_hierarchyNode')){
+ }else if(passedObjectClass === 'rightClick_hierarchyNode'){
Eagle.selectedRightClickLocation(Eagle.FileType.Graph)
$('#customContextMenu').append(RightClick.getNodeDescriptionDropdown())
@@ -532,6 +509,7 @@ export class RightClick {
$('#customContextMenu').append('
Delete')
if (data.isConstruct()){
$('#customContextMenu').append('
Delete All')
+ $('#customContextMenu').append('
Center Around Children')
}
if(Setting.findValue(Setting.ALLOW_PALETTE_EDITING)){
$('#customContextMenu').append('
Add to palette')
@@ -541,7 +519,7 @@ export class RightClick {
}else if(passedObjectClass === 'rightClick_graphEdge'){
$('#customContextMenu').append('
Delete')
- }else if(targetClass.includes('rightClick_paletteHeader')){
+ }else if(passedObjectClass === 'rightClick_paletteHeader'){
if(!data.fileInfo().builtIn){
$('#customContextMenu').append('
Remove Palette')
@@ -566,7 +544,7 @@ export class RightClick {
}
}
// adding a listener to function options that closes the menu if an option is clicked
- $('#customContextMenu a').on('click',function(){if($(event.target).parents('.searchBarContainer').length){return};RightClick.closeCustomContextMenu(true)})
+ $('#customContextMenu a').on('click',function(){if($(event.target).parents('.searchBarContainer').length){return}RightClick.closeCustomContextMenu(true)})
}
}
diff --git a/src/Setting.ts b/src/Setting.ts
index 6017de045..fadda1467 100644
--- a/src/Setting.ts
+++ b/src/Setting.ts
@@ -1,8 +1,10 @@
import * as ko from "knockout";
+import { ActionMessage } from "./Action";
import {Eagle} from './Eagle';
import {Utils} from './Utils';
import {UiMode, UiModeSystem, SettingData} from './UiModes';
+
export class SettingsGroup {
private name : string;
private displayFunc : (eagle: Eagle) => boolean;
@@ -201,12 +203,29 @@ export class Setting {
}
resetDefault = () : void => {
- let value = this.graphDefaultValue
- if(UiModeSystem.getActiveUiMode().getName()==='Minimal'){
- value = this.minimalDefaultValue
- }else if(UiModeSystem.getActiveUiMode().getName()==='Expert'){
- value = this.expertDefaultValue
+ const activeUIModeName: string = UiModeSystem.getActiveUiMode().getName();
+ let value: any = this.graphDefaultValue;
+
+ switch (activeUIModeName){
+ case "Student":
+ value = this.studentDefaultValue;
+ break;
+ case "Minimal":
+ value = this.minimalDefaultValue;
+ break;
+ case "Graph":
+ value = this.graphDefaultValue;
+ break;
+ case "Component":
+ value = this.componentDefaultValue;
+ break;
+ case "Expert":
+ value = this.expertDefaultValue;
+ break;
+ default:
+ console.warn("Unknown active UI mode name:", activeUIModeName, ", using default value for ", this.name, " setting");
}
+
this.value(value);
}
@@ -227,19 +246,7 @@ export class Setting {
}
static showInspectorErrorsWarnings = () : boolean => {
- const eagle = Eagle.getInstance();
-
- switch (Setting.findValue(Setting.SHOW_INSPECTOR_WARNINGS)){
- case Setting.ShowErrorsMode.Warnings:
- return eagle.selectedNode().getErrorsWarnings(eagle).errors.length + eagle.selectedNode().getErrorsWarnings(eagle).warnings.length > 0;
- break;
- case Setting.ShowErrorsMode.Errors:
- return eagle.selectedNode().getErrorsWarnings(eagle).errors.length > 0;
- break;
- case Setting.ShowErrorsMode.None:
- default:
- return false;
- }
+ return Setting.findValue(Setting.SHOW_INSPECTOR_WARNINGS) != Setting.ShowErrorsMode.None;
}
static readonly GITHUB_ACCESS_TOKEN_KEY: string = "GitHubAccessToken";
@@ -250,7 +257,7 @@ export class Setting {
static readonly ACTION_CONFIRMATIONS : string = "ActionConfirmations";
static readonly CONFIRM_DISCARD_CHANGES : string = "ConfirmDiscardChanges";
static readonly CONFIRM_NODE_CATEGORY_CHANGES : string = "ConfirmNodeCategoryChanges";
- static readonly CONFIRM_REMOVE_REPOSITORES : string = "ConfirmRemoveRepositories";
+ static readonly CONFIRM_REMOVE_REPOSITORIES : string = "ConfirmRemoveRepositories";
static readonly CONFIRM_RELOAD_PALETTES : string = "ConfirmReloadPalettes";
static readonly CONFIRM_DELETE_OBJECTS : string = "ConfirmDeleteObjects";
@@ -340,10 +347,9 @@ const settings : SettingsGroup[] = [
new Setting(true, "Reset Action Confirmations", Setting.ACTION_CONFIRMATIONS, "Enable all action confirmation prompts",false, Setting.Type.Button, '', '','','','',[],'$root.resetActionConfirmations()'),
new Setting(false, "Confirm Discard Changes", Setting.CONFIRM_DISCARD_CHANGES, "Prompt user to confirm that unsaved changes to the current file should be discarded when opening a new file, or when navigating away from EAGLE.",false, Setting.Type.Boolean, true, true,true,true,true),
new Setting(false, "Confirm Node Category Changes", Setting.CONFIRM_NODE_CATEGORY_CHANGES, "Prompt user to confirm that changing the node category may break the node.",false, Setting.Type.Boolean, true, true,true,true,true),
- new Setting(false, "Confirm Remove Repositories", Setting.CONFIRM_REMOVE_REPOSITORES, "Prompt user to confirm removing a repository from the list of known repositories.",false , Setting.Type.Boolean, true,true,true,true,true),
+ new Setting(false, "Confirm Remove Repositories", Setting.CONFIRM_REMOVE_REPOSITORIES, "Prompt user to confirm removing a repository from the list of known repositories.",false , Setting.Type.Boolean, true,true,true,true,true),
new Setting(false, "Confirm Reload Palettes", Setting.CONFIRM_RELOAD_PALETTES, "Prompt user to confirm when loading a palette that is already loaded.",false , Setting.Type.Boolean,true,true,true,true,true),
new Setting(false, "Confirm Delete", Setting.CONFIRM_DELETE_OBJECTS, "Prompt user to confirm when deleting node(s) or edge(s) from a graph.",false , Setting.Type.Boolean, true,true,true,true,true),
- new Setting(false, "Confirm Delete", Setting.CONFIRM_DELETE_OBJECTS, "Prompt user to confirm when deleting node(s) or edge(s) from a graph.",false , Setting.Type.Boolean, true,true,true,true,true),
new Setting(true, "Open Default Palette on Startup", Setting.OPEN_DEFAULT_PALETTE, "Open a default palette on startup. The palette contains an example of all known node categories", false, Setting.Type.Boolean, false,false,true,true,true),
new Setting(true, "Disable JSON Validation", Setting.DISABLE_JSON_VALIDATION, "Allow EAGLE to load/save/send-to-translator graphs and palettes that would normally fail validation against schema.", false, Setting.Type.Boolean, false,false,false,false,false),
new Setting(true, "Overwrite Existing Translator Tab", Setting.OVERWRITE_TRANSLATION_TAB, "When translating a graph, overwrite an existing translator tab", false, Setting.Type.Boolean, true,true,true,true,true),
@@ -356,10 +362,10 @@ const settings : SettingsGroup[] = [
new Setting(true, "Show non key parameters", Setting.SHOW_NON_KEY_PARAMETERS, "Show additional parameters that are not marked as key parameters for the current graph",false, Setting.Type.Boolean, false,true,true,true,true),
new Setting(true, "Display Node Keys", Setting.DISPLAY_NODE_KEYS, "Display Node Keys", false, Setting.Type.Boolean,false,false,false,true,true),
new Setting(false, "Show Developer Tab", Setting.SHOW_DEVELOPER_TAB, "Reveals the developer tab in the settings menu", false, Setting.Type.Boolean, false,false,false,false,true),
- new Setting(true, "Translator Mode", Setting.USER_TRANSLATOR_MODE, "Configue the translator mode", false, Setting.Type.Select, Setting.TranslatorMode.Minimal,Setting.TranslatorMode.Minimal,Setting.TranslatorMode.Normal,Setting.TranslatorMode.Normal,Setting.TranslatorMode.Expert, Object.values(Setting.TranslatorMode)),
+ new Setting(true, "Translator Mode", Setting.USER_TRANSLATOR_MODE, "Configure the translator mode", false, Setting.Type.Select, Setting.TranslatorMode.Minimal,Setting.TranslatorMode.Minimal,Setting.TranslatorMode.Normal,Setting.TranslatorMode.Normal,Setting.TranslatorMode.Expert, Object.values(Setting.TranslatorMode)),
new Setting(true, "Graph Zoom Divisor", Setting.GRAPH_ZOOM_DIVISOR, "The number by which zoom inputs are divided before being applied. Larger divisors reduce the amount of zoom.", false, Setting.Type.Number,1000,1000,1000,1000,1000),
new Setting(false, "Snap To Grid", Setting.SNAP_TO_GRID, "Align positions of nodes in graph to a grid", false, Setting.Type.Boolean,false,false,false,false,false),
- new Setting(true, "Snap To Grid Size", Setting.SNAP_TO_GRID_SIZE, "Size of grid used when aligning positions of nodes in graph (pixels)", false, Setting.Type.Number, 50, 50, 50, 50, 50),
+ new Setting(false, "Snap To Grid Size", Setting.SNAP_TO_GRID_SIZE, "Size of grid used when aligning positions of nodes in graph (pixels)", false, Setting.Type.Number, 50, 50, 50, 50, 50),
new Setting(true, "Show edge/node errors/warnings in inspector", Setting.SHOW_INSPECTOR_WARNINGS, "Show the errors/warnings found for the selected node/edge in the inspector", false, Setting.Type.Select, Setting.ShowErrorsMode.None, Setting.ShowErrorsMode.None, Setting.ShowErrorsMode.Errors, Setting.ShowErrorsMode.Errors,Setting.ShowErrorsMode.Errors, Object.values(Setting.ShowErrorsMode)),
new Setting(false, "Right Window Width", Setting.RIGHT_WINDOW_WIDTH_KEY, "saving the width of the right window", true, Setting.Type.Number,400,400,400,400,400),
new Setting(false, "Left Window Width", Setting.LEFT_WINDOW_WIDTH_KEY, "saving the width of the left window", true, Setting.Type.Number, 310, 310, 310, 310, 310),
@@ -369,14 +375,14 @@ const settings : SettingsGroup[] = [
"Advanced Editing",
() => {return true;},
[
- new Setting(true,"Allow Invalid edges", Setting.ALLOW_INVALID_EDGES, "Allow the user to create edges even if they would normally be determined invalid.", false, Setting.Type.Boolean, false, false, false, false, true),
+ new Setting(true, "Allow Invalid edges", Setting.ALLOW_INVALID_EDGES, "Allow the user to create edges even if they would normally be determined invalid.", false, Setting.Type.Boolean, false, false, false, false, true),
new Setting(true, "Allow Component Editing", Setting.ALLOW_COMPONENT_EDITING, "Allow the user to add/remove ports and parameters from components.",false, Setting.Type.Boolean,false, false, false, true,true),
new Setting(true, "Allow Set Key Parameter", Setting.ALLOW_SET_KEY_PARAMETER, "Allow the user to add/remove key parameter flags from parameters.", false, Setting.Type.Boolean,false, true, true, true,true),
new Setting(true, "Allow Graph Editing", Setting.ALLOW_GRAPH_EDITING, "Allow the user to edit and create graphs.", false, Setting.Type.Boolean, false, false, true, true, true),
new Setting(true, "Allow Palette Editing", Setting.ALLOW_PALETTE_EDITING, "Allow the user to edit palettes.", false, Setting.Type.Boolean, false, false, false, true, true),
new Setting(true, "Allow Readonly Palette Editing", Setting.ALLOW_READONLY_PALETTE_EDITING, "Allow the user to modify palettes that would otherwise be readonly.", false, Setting.Type.Boolean,false,false,false,false,true),
new Setting(true, "Allow Edge Editing", Setting.ALLOW_EDGE_EDITING, "Allow the user to edit edge attributes.", false, Setting.Type.Boolean, false, false,false, false, true),
- new Setting(true, "Filter Node Suggestions", Setting.FILTER_NODE_SUGGESTIONS, "Filter Node Options When Drawing Edges Into Empty Space", false, Setting.Type.Boolean,true,true,true,true,true),
+ new Setting(true, "Filter Node Suggestions", Setting.FILTER_NODE_SUGGESTIONS, "Filter Node Options When Drawing Edges Into Empty Space", false, Setting.Type.Boolean,true,true,true,true,false),
new Setting(false, "STUDENT_SETTINGS_MODE", Setting.STUDENT_SETTINGS_MODE, "Mode disabling setting editing for students.", false, Setting.Type.Boolean, true, false,false, false, false),
new Setting(true, "Value Editing", Setting.VALUE_EDITING_PERMS, "Set which values are allowed to be edited.", false, Setting.Type.Select, Setting.valueEditingPerms.KeyOnly,Setting.valueEditingPerms.Normal,Setting.valueEditingPerms.Normal,Setting.valueEditingPerms.ReadOnly,Setting.valueEditingPerms.ReadOnly, Object.values(Setting.valueEditingPerms)),
]
diff --git a/src/Translator.ts b/src/Translator.ts
index 16a1e6bef..e2a216540 100644
--- a/src/Translator.ts
+++ b/src/Translator.ts
@@ -167,16 +167,7 @@ export class Translator {
}
// validate json
- if (!Setting.findValue(Setting.DISABLE_JSON_VALIDATION)){
- const jsonObject = JSON.parse(jsonString);
- const validatorResult : {valid: boolean, errors: string} = Utils.validateJSON(jsonObject, format, Eagle.FileType.Graph);
- if (!validatorResult.valid){
- const message = "JSON Output failed validation against internal JSON schema, saving anyway";
- console.error(message, validatorResult.errors);
- Utils.showUserMessage("Error", message + "
" + validatorResult.errors);
- //return;
- }
- }
+ Utils.validateJSON(jsonString, Eagle.FileType.Graph);
const translatorData = {
algo: algorithmName,
diff --git a/src/Tutorial.ts b/src/Tutorial.ts
index da506c8a0..2bb41e86f 100644
--- a/src/Tutorial.ts
+++ b/src/Tutorial.ts
@@ -1,4 +1,3 @@
-import { active } from 'd3';
import {Eagle} from './Eagle';
@@ -94,7 +93,13 @@ export class TutorialSystem {
static initiateFindGraphNodeIdByNodeName = (name:string) : JQuery
=> {
const eagle = Eagle.getInstance()
- let x = $('#'+eagle.logicalGraph().findNodeGraphIdByNodeName(name)+' rect')
+ let x = $('#logicalGraph #'+eagle.logicalGraph().findNodeGraphIdByNodeName(name)+'.container')
+ return x
+ }
+
+ static initiateSimpleFindGraphNodeIdByNodeName = (name:string) : string => {
+ const eagle = Eagle.getInstance()
+ let x = eagle.logicalGraph().findNodeGraphIdByNodeName(name)
return x
}
@@ -335,6 +340,7 @@ export class Tutorial {
}
highlightStepTarget = (target: JQuery): void => {
+ const eagle = Eagle.getInstance()
if(TutorialSystem.activeTutCurrentStep.getAlternateHightlightTargetFunc() != null){
target = TutorialSystem.activeTutCurrentStep.getAlternateHightlightTargetFunc()()
}
@@ -343,10 +349,16 @@ export class Tutorial {
const coords = target.offset()
const docWidth = window.innerWidth
const top_actual = Math.round(coords.top)//distance of the top of the element from the top of the document
- const right = coords.left + $(target).outerWidth()
+ let right = coords.left + $(target).outerWidth()
const left = docWidth - coords.left
- const targetHeight = Math.round($(target).outerHeight())
- const bottom_actual = Math.round(coords.top + targetHeight) //distance from the bottom of the target element to the bottom of the document
+ let targetHeight = Math.round($(target).outerHeight())
+ let bottom_actual = Math.round(coords.top + targetHeight) //distance from the bottom of the target element to the bottom of the document
+
+ if(target.parents('#logicalGraphParent').length){
+ targetHeight = Math.round(targetHeight*eagle.globalScale())
+ right = coords.left+$(target).outerWidth() *eagle.globalScale()
+ bottom_actual = Math.round(coords.top + targetHeight)
+ }
//i am appending these once if they dont exist. they are then adjusted for each step. and finally removed when exiting the tutorial
if ($('.tutorialHighlight').length === 0) {
diff --git a/src/Undo.ts b/src/Undo.ts
index 8e2b80ef6..ab152dee9 100644
--- a/src/Undo.ts
+++ b/src/Undo.ts
@@ -24,6 +24,7 @@
import * as ko from "knockout";
+import { ActionMessage } from "./Action";
import {Config} from './Config';
import {Eagle} from './Eagle';
import {LogicalGraph} from './LogicalGraph';
@@ -122,7 +123,7 @@ export class Undo {
Undo.printTable();
}
- eagle.checkGraph();
+ eagle.graphChecker().check();
}
nextSnapshot = (eagle: Eagle) : void => {
@@ -139,7 +140,7 @@ export class Undo {
Undo.printTable();
}
- eagle.checkGraph();
+ eagle.graphChecker().check();
}
toString = () : string => {
@@ -173,7 +174,6 @@ export class Undo {
}
const dataObject: LogicalGraph = snapshot.data();
-
eagle.logicalGraph(dataObject);
}
diff --git a/src/Utils.ts b/src/Utils.ts
index 5f3ee4017..2f0702b17 100644
--- a/src/Utils.ts
+++ b/src/Utils.ts
@@ -26,13 +26,14 @@ import * as Ajv from "ajv";
import * as Showdown from "showdown";
import * as ko from "knockout";
+import { ActionList } from "./ActionList";
+import { ActionMessage } from "./Action";
import {Category} from './Category';
import {CategoryData} from "./CategoryData";
-import {Config} from './Config';
+import { ComponentUpdater } from "./ComponentUpdater";
import {Daliuge} from './Daliuge';
import {Eagle} from './Eagle';
import {Edge} from './Edge';
-import {Errors} from './Errors';
import {Field} from './Field';
import {KeyboardShortcut} from './KeyboardShortcut';
import {LogicalGraph} from './LogicalGraph';
@@ -42,6 +43,7 @@ import {PaletteInfo} from './PaletteInfo';
import {Repository} from './Repository';
import {Setting} from './Setting';
import {FileInfo} from "./FileInfo";
+import { RepositoryFile } from "./RepositoryFile";
import { UiModeSystem } from "./UiModes";
export class Utils {
@@ -93,6 +95,10 @@ export class Utils {
return now.getFullYear() + "-" + Utils.padStart(now.getMonth() + 1, 2) + "-" + Utils.padStart(now.getDate(), 2) + "-" + Utils.padStart(now.getHours(), 2) + "-" + Utils.padStart(now.getMinutes(), 2) + "-" + Utils.padStart(now.getSeconds(), 2);
}
+ static generateGraphName(): string {
+ return "Diagram-" + Utils.generateDateTimeString() + "." + Utils.getDiagramExtension(Eagle.FileType.Graph);
+ }
+
static findNewKey(usedKeys : number[]): number {
for (let i = -1 ; ; i--){
let found = false;
@@ -142,9 +148,9 @@ export class Utils {
return usedKeys;
}
- static newKey(nodes: Node[]): number {
- const usedKeys = Utils.getUsedKeys(nodes);
- return Utils.findNewKey(usedKeys);
+ static newKey(nodes: Node[],usedKeys:number[] = []): number {
+ const allUsedKeys = Utils.getUsedKeys(nodes).concat(usedKeys);
+ return Utils.findNewKey(allUsedKeys);
}
static setEmbeddedApplicationNodeKeys(lg: LogicalGraph): void {
@@ -438,24 +444,29 @@ export class Utils {
}
}
- static showErrorsModal(title: string){
- const errors: Errors.Issue[] = Errors.getErrors();
- const warnings: Errors.Issue[] = Errors.getWarnings();
+ static showActionListModal(title: string, mode: ActionList.Mode, combinedMessages: {source: string, messages: ActionMessage[]}[]){
+ const flatMessages: ActionMessage[] = [];
+ for (const load of combinedMessages){
+ for (const am of load.messages){
+ flatMessages.push(new ActionMessage(am.level, load.source + ": " + am.message, am.show, am.fix, am.fixDescription));
+ }
+ }
- console.log("showErrorsModal() errors:", errors.length, "warnings:", warnings.length);
+ const eagle: Eagle = Eagle.getInstance();
+ eagle.actionList().mode(mode);
+ eagle.actionList().messages(flatMessages);
- $('#errorsModalTitle').text(title);
+ $('#actionListModalTitle').text(title);
// hide whole errors or warnings sections if none are found
- $('#errorsModalErrorsAccordionItem').toggle(errors.length > 0);
- $('#errorsModalWarningsAccordionItem').toggle(warnings.length > 0);
+ $('#actionListModalMessagesAccordionItem').toggle(flatMessages.length > 0);
- $('#errorsModal').modal("toggle");
+ $('#actionListModal').modal("toggle");
}
static showNotification(title : string, message : string, type : "success" | "info" | "warning" | "danger") : void {
$.notify({
- title:title + ":",
+ title:title + ":
",
message:message
}, {
type: type,
@@ -591,7 +602,6 @@ export class Utils {
}else{
$('#confirmModalDontShowAgain button').text('check_box_outline_blank')
}
- console.log($('#confirmModalDontShowAgain button').text())
})
}
@@ -732,8 +742,8 @@ export class Utils {
$('#shortcutsModal').modal("hide");
}
- static closeErrorsModal() : void {
- $('#errorsModal').modal("hide");
+ static closeCheckGraphModal() : void {
+ $('#checkGraphModal').modal("hide");
}
static showPalettesModal(eagle: Eagle) : void {
@@ -1395,9 +1405,9 @@ export class Utils {
return type0 === type1;
}
- static checkPalette(palette: Palette): Errors.ErrorsWarnings {
+ static checkPalette(palette: Palette): ActionMessage[] {
const eagle: Eagle = Eagle.getInstance();
- const errorsWarnings: Errors.ErrorsWarnings = {warnings: [], errors: []};
+ const errors: ActionMessage[] = [];
// check for duplicate keys
const keys: number[] = [];
@@ -1405,7 +1415,7 @@ export class Utils {
for (const node of palette.getNodes()){
// check existing keys
if (keys.indexOf(node.getKey()) !== -1){
- errorsWarnings.errors.push(Errors.Message("Key " + node.getKey() + " used by multiple components in palette."));
+ errors.push(ActionMessage.Message(ActionMessage.Level.Error, "Key " + node.getKey() + " used by multiple components in palette."));
} else {
keys.push(node.getKey());
}
@@ -1413,77 +1423,47 @@ export class Utils {
// check all nodes are valid
for (const node of palette.getNodes()){
- Node.isValid(eagle, node, Eagle.FileType.Palette, false, false, errorsWarnings);
+ Node.isValid(eagle, node, Eagle.FileType.Palette, false, false, errors);
}
- return errorsWarnings;
+ return errors;
}
- static checkGraph(eagle: Eagle): Errors.ErrorsWarnings {
- const errorsWarnings: Errors.ErrorsWarnings = {warnings: [], errors: []};
+ static checkGraph(eagle: Eagle): ActionMessage[] {
+ const errors: ActionMessage[] = [];
const graph: LogicalGraph = eagle.logicalGraph();
// check all nodes are valid
for (const node of graph.getNodes()){
- Node.isValid(eagle, node, Eagle.FileType.Graph, false, false, errorsWarnings);
+ Node.isValid(eagle, node, Eagle.FileType.Graph, false, false, errors);
}
// check all edges are valid
for (const edge of graph.getEdges()){
- Edge.isValid(eagle, edge.getId(), edge.getSrcNodeKey(), edge.getSrcPortId(), edge.getDestNodeKey(), edge.getDestPortId(), edge.getDataType(), edge.isLoopAware(), edge.isClosesLoop(), false, false, errorsWarnings);
- }
-
- // check that all node, edge, field ids are unique
- {
- const ids : string[] = [];
-
- // loop over graph nodes
- for (const node of graph.getNodes()){
- if (ids.includes(node.getId())){
- const issue: Errors.Issue = Errors.ShowFix(
- "Node (" + node.getName() + ") does not have a unique id",
- function(){Utils.showNode(eagle, Eagle.FileType.Graph, node.getId())},
- function(){Utils.newId(node)},
- "Assign node a new id"
- );
- errorsWarnings.errors.push(issue);
- }
- ids.push(node.getId());
-
- for (const field of node.getFields()){
- if (ids.includes(field.getId())){
- const issue: Errors.Issue = Errors.ShowFix(
- "Field (" + field.getDisplayText() + ") on node (" + node.getName() + ") does not have a unique id",
- function(){Utils.showNode(eagle, Eagle.FileType.Graph, node.getId())},
- function(){Utils.newId(field)},
- "Assign field a new id"
- );
- errorsWarnings.errors.push(issue);
- }
- ids.push(field.getId());
- }
- }
+ Edge.isValid(graph, edge.getId(), edge.getSrcNodeKey(), edge.getSrcPortId(), edge.getDestNodeKey(), edge.getDestPortId(), edge.getDataType(), edge.isLoopAware(), edge.isClosesLoop(), false, false, errors);
+ }
- // loop over graph edges
- for (const edge of graph.getEdges()){
- if (ids.includes(edge.getId())){
- const issue: Errors.Issue = Errors.ShowFix(
- "Edge (" + edge.getId() + ") does not have a unique id",
- function(){Utils.showEdge(eagle, edge.getId())},
- function(){Utils.newId(edge)},
- "Assign edge a new id"
- );
- errorsWarnings.errors.push(issue);
- }
- ids.push(edge.getId());
- }
+ return errors;
+ }
+
+ // validate json
+ static validateJSON(jsonString: string, fileType: Eagle.FileType){
+ // if validation disabled, just return true
+ if (Setting.findValue(Setting.DISABLE_JSON_VALIDATION)){
+ return;
}
- return errorsWarnings;
+ const jsonObject = JSON.parse(jsonString);
+ const validatorResult : {valid: boolean, errors: string} = Utils._validateJSON(jsonObject, Daliuge.SchemaVersion.OJS, fileType);
+ if (!validatorResult.valid){
+ const message = "JSON Output failed validation against internal JSON schema, saving anyway";
+ console.error(message, validatorResult.errors);
+ Utils.showUserMessage("Error", message + "
" + validatorResult.errors);
+ }
}
- static validateJSON(json : object, version : Daliuge.SchemaVersion, fileType : Eagle.FileType) : {valid: boolean, errors: string} {
+ static _validateJSON(json : object, version : Daliuge.SchemaVersion, fileType : Eagle.FileType) : {valid: boolean, errors: string} {
// console.log("validateJSON(): version:", version, " fileType:", fileType);
const ajv = new Ajv();
@@ -1562,32 +1542,13 @@ export class Utils {
link.click();
}
- // https://noonat.github.io/intersect/#aabb-vs-aabb
- static nodesOverlap(n0x: number, n0y: number, n0width: number, n0height: number, n1x: number, n1y: number, n1width: number, n1height: number) : boolean {
- const n0pos = {x:n0x + n0width/2, y:n0y + n0height/2};
- const n1pos = {x:n1x + n1width/2, y:n1y + n1height/2};
- const n0half = {x:n0width/2, y:n0height/2};
- const n1half = {x:n1width/2, y:n1height/2};
-
- //console.log("compare", n0x, n0y, n0width, n0height, n1x, n1y, n1width, n1height);
-
- const dx = n0pos.x - n1pos.x;
- const px = (n0half.x + n1half.x) - Math.abs(dx);
- if (px <= 0) {
- //console.log("compare OK");
- return false;
- }
-
- const dy = n0pos.y - n1pos.y;
- const py = (n0half.y + n1half.y) - Math.abs(dy);
- if (py <= 0) {
- //console.log("compare OK");
- return false;
- }
- //console.log("compares HIT");
+ static nodesOverlap(n0x: number, n0y: number, n0radius: number, n1x: number, n1y: number, n1radius: number) : boolean {
+ const dx = n0x - n1x;
+ const dy = n0y - n1y;
+ const distance = Math.sqrt(dx*dx + dy*dy);
- return true;
+ return distance <= (n0radius + n1radius);
}
static table2CSV(table: any[]) : string {
@@ -1607,16 +1568,15 @@ export class Utils {
return s;
}
- // https://stackoverflow.com/questions/5254838/calculating-distance-between-a-point-and-a-rectangular-box-nearest-point
static positionToNodeDistance(positionX: number, positionY: number, node: Node): number {
- const rectMinX = node.getPosition().x;
- const rectMaxX = node.getPosition().x + node.getWidth();
- const rectMinY = node.getPosition().y;
- const rectMaxY = node.getPosition().y + node.getHeight();
+ // first determine the distance between the position and node center
+ const dx = node.getPosition().x - positionX;
+ const dy = node.getPosition().y - positionY;
+ let distance = Math.sqrt(dx*dx + dy*dy);
- const dx = Math.max(rectMinX - positionX, 0, positionX - rectMaxX);
- const dy = Math.max(rectMinY - positionY, 0, positionY - rectMaxY);
- return Math.sqrt(dx*dx + dy*dy);
+ // then subtract the radius, limit to zero
+ distance = Math.max(distance - node.getRadius(), 0);
+ return distance;
}
@@ -1651,7 +1611,7 @@ export class Utils {
static getShortcutDisplay = () : {description:string, shortcut : string,function:string}[] => {
const displayShortcuts : {description:string, shortcut : string, function : any} []=[];
- const eagle = (window).eagle;
+ const eagle: Eagle = Eagle.getInstance();
for (const object of Eagle.shortcuts){
// skip if shortcut should not be displayed
@@ -1726,9 +1686,7 @@ export class Utils {
return value.toLowerCase() === "true";
}
- static fixEdgeType(eagle: Eagle, edgeId: string, newType: string) : void {
- const edge = eagle.logicalGraph().findEdgeById(edgeId);
-
+ static fixEdgeType(edge: Edge, newType: string) : void {
if (edge === null){
return;
}
@@ -1736,19 +1694,20 @@ export class Utils {
edge.setDataType(newType);
}
- static fixDeleteEdge(eagle: Eagle, edgeId: string): void {
- eagle.logicalGraph().removeEdgeById(edgeId);
+ static fixDeleteEdge(logicalGraph: LogicalGraph, edgeId: string): void {
+ logicalGraph.removeEdgeById(edgeId);
}
- static fixPortType(eagle: Eagle, sourcePort: Field, destinationPort: Field): void {
+ static fixPortType(sourcePort: Field, destinationPort: Field): void {
destinationPort.setType(sourcePort.getType());
}
- static fixNodeAddField(eagle: Eagle, node: Node, field: Field){
+ static fixNodeAddField(node: Node, field: Field){
node.addField(field);
}
- static fixNodeFieldIds(eagle: Eagle, nodeKey: number){
+ static fixNodeFieldIds(nodeKey: number){
+ const eagle: Eagle = Eagle.getInstance();
const node: Node = eagle.logicalGraph().findNodeByKey(nodeKey);
if (node === null){
@@ -1762,12 +1721,13 @@ export class Utils {
}
}
- static fixNodeCategory(eagle: Eagle, node: Node, category: Category){
+ static fixNodeCategory(eagle: Eagle, node: Node, category: Category, categoryType: Category.Type){
node.setCategory(category);
+ node.setCategoryType(categoryType);
}
// NOTE: merges field1 into field0
- static fixNodeMergeFieldsByIndex(eagle: Eagle, node: Node, field0Index: number, field1Index: number){
+ static fixNodeMergeFieldsByIndex(eagle: Eagle, location: Eagle.FileType, node: Node, field0Index: number, field1Index: number){
//console.log("fixNodeMergeFieldsByIndex()", node.getName(), field0Index, field1Index);
// abort if one or more of the fields is not found
@@ -1790,11 +1750,13 @@ export class Utils {
field0.setUsage(newUsage);
// update all edges to use new field
- this._mergeEdges(eagle, field1.getId(), field0.getId());
+ if (location === Eagle.FileType.Graph){
+ this._mergeEdges(eagle.logicalGraph(), field1.getId(), field0.getId());
+ }
}
// NOTE: merges field1 into field0
- static fixNodeMergeFields(eagle: Eagle, node: Node, field0: Field, field1: Field){
+ static fixNodeMergeFields(eagle: Eagle, location: Eagle.FileType, node: Node, field0: Field, field1: Field){
//console.log("fixNodeMergeFieldsById()", node.getName(), field0.getDisplayText(), field1.getDisplayText());
// abort if one or more of the fields is not found
@@ -1817,7 +1779,9 @@ export class Utils {
field0.setUsage(newUsage);
// update all edges to use new field
- this._mergeEdges(eagle, field1.getId(), field0.getId());
+ if (location === Eagle.FileType.Graph){
+ this._mergeEdges(eagle.logicalGraph(), field1.getId(), field0.getId());
+ }
}
static _mergeUsage(usage0: Daliuge.FieldUsage, usage1: Daliuge.FieldUsage) : Daliuge.FieldUsage {
@@ -1839,9 +1803,9 @@ export class Utils {
return result;
}
- static _mergeEdges(eagle: Eagle, oldFieldId: string, newFieldId: string){
+ static _mergeEdges(logicalGraph: LogicalGraph, oldFieldId: string, newFieldId: string){
// update all edges to use new field
- for (const edge of eagle.logicalGraph().getEdges()){
+ for (const edge of logicalGraph.getEdges()){
// update src port
if (edge.getSrcPortId() === oldFieldId){
edge.setSrcPortId(newFieldId);
@@ -1854,11 +1818,11 @@ export class Utils {
}
}
- static fixFieldId(eagle: Eagle, field: Field){
+ static fixFieldId(field: Field){
field.setId(Utils.uuidv4());
}
- static fixFieldValue(eagle: Eagle, node: Node, exampleField: Field, value: string){
+ static fixFieldValue(node: Node, exampleField: Field, value: string){
let field : Field = node.getFieldByDisplayText(exampleField.getDisplayText());
// if a field was not found, clone one from the example and add to node
@@ -1871,7 +1835,7 @@ export class Utils {
field.setValue(value);
}
- static fixFieldDefaultValue(eagle: Eagle, field: Field){
+ static fixFieldDefaultValue(field: Field){
// depends on the type
switch(field.getType()){
case Daliuge.DataType.Boolean:
@@ -1892,7 +1856,7 @@ export class Utils {
}
}
- static fixFieldType(eagle: Eagle, field: Field){
+ static fixFieldType(field: Field){
if (field.getType() === Daliuge.DataType.Unknown){
field.setType(Daliuge.DataType.Object);
return;
@@ -1944,16 +1908,11 @@ export class Utils {
field.setParameterType(newType);
}
- static callFixFunc(eagle: Eagle, fixFunc: () => void){
- fixFunc();
- Utils.postFixFunc(eagle);
- }
-
static postFixFunc(eagle: Eagle){
eagle.selectedObjects.valueHasMutated();
eagle.logicalGraph().fileInfo().modified = true;
- eagle.checkGraph();
+ eagle.graphChecker().check();
eagle.undo().pushSnapshot(eagle, "Fix");
}
@@ -1961,9 +1920,14 @@ export class Utils {
object.setId(Utils.uuidv4());
}
- static showEdge(eagle: Eagle, edgeId: string): void {
+ static showEdge(edgeId: string): void {
+ const eagle: Eagle = Eagle.getInstance();
+
// close errors modal if visible
- $('#errorsModal').modal("hide");
+ $('#checkGraphModal').modal("hide");
+
+ // close action list modal if visible
+ $('#actionListModal').modal("hide");
eagle.setSelection(Eagle.RightWindowMode.Inspector, eagle.logicalGraph().findEdgeById(edgeId), Eagle.FileType.Graph);
}
@@ -1972,7 +1936,10 @@ export class Utils {
console.log("showNode()", location, nodeId);
// close errors modal if visible
- $('#errorsModal').modal("hide");
+ $('#checkGraphModal').modal("hide");
+
+ // close action list modal if visible
+ $('#actionListModal').modal("hide");
// find node from nodeKey
let n: Node = null;
@@ -2000,18 +1967,19 @@ export class Utils {
}
// only update result if it is worse that current result
- static worstEdgeError(errorsWarnings: Errors.ErrorsWarnings) : Eagle.LinkValid {
- if (errorsWarnings === null){
- console.warn("errorsWarnings is null");
+ static worstEdgeError(errors: ActionMessage[]) : Eagle.LinkValid {
+ if (errors === null){
+ console.warn("errors is null");
return Eagle.LinkValid.Valid;
}
- if (errorsWarnings.warnings.length === 0 && errorsWarnings.errors.length === 0){
+ if (errors.length === 0){
return Eagle.LinkValid.Valid;
}
- if (errorsWarnings.errors.length !== 0){
- return Eagle.LinkValid.Invalid;
+ if (errors.length !== 0){
+ // TODO: loop through the errors and find the worst
+ return Eagle.LinkValid.Impossible;
}
return Eagle.LinkValid.Warning;
@@ -2046,12 +2014,12 @@ export class Utils {
"category":node.getCategory(),
"categoryType":node.getCategoryType(),
"expanded":node.getExpanded(),
+ "peek":node.isPeek(),
"x":node.getPosition().x,
"y":node.getPosition().y,
- "realX":node.getRealPosition().x,
- "realY":node.getRealPosition().y,
- "width":node.getWidth(),
- "height":node.getHeight(),
+ // "realX":node.getRealPosition().x,
+ // "realY":node.getRealPosition().y,
+ "radius":node.getRadius(),
"inputAppKey":node.getInputApplication() === null ? null : node.getInputApplication().getKey(),
"inputAppCategory":node.getInputApplication() === null ? null : node.getInputApplication().getCategory(),
"inputAppEmbedKey":node.getInputApplication() === null ? null : node.getInputApplication().getEmbedKey(),
@@ -2219,7 +2187,8 @@ export class Utils {
return result;
}
- static openRemoteFileFromUrl(repositoryService : Eagle.RepositoryService, repositoryName : string, repositoryBranch : string, filePath : string, fileName : string, callback: (error : string, data : string) => void ) : void {
- Utils.httpGet(fileName, callback);
+ static openRemoteFileFromUrl(file: RepositoryFile, callback: (error : string, data : string) => void ) : void {
+ Utils.httpGet(file.path, callback);
}
+
}
diff --git a/src/bindingHandlers/eagleRightClick.ts b/src/bindingHandlers/eagleRightClick.ts
index 26cf541c1..490aa7583 100644
--- a/src/bindingHandlers/eagleRightClick.ts
+++ b/src/bindingHandlers/eagleRightClick.ts
@@ -11,9 +11,10 @@ ko.bindingHandlers.eagleRightClick = {
jQueryElement.on('contextmenu', function(e){
e.preventDefault();
e.stopPropagation();
- const data = ko.unwrap(valueAccessor());
+ const data = ko.unwrap(valueAccessor()).data;
+ const type = ko.unwrap(valueAccessor()).type;
- RightClick.requestCustomContextMenu(data,jQueryElement,'')
+ RightClick.requestCustomContextMenu(data,jQueryElement,type)
})
},
update: function (element, valueAccessor) {
diff --git a/src/bindingHandlers/graphRenderer.ts b/src/bindingHandlers/graphRenderer.ts
deleted file mode 100644
index 4b6666ea7..000000000
--- a/src/bindingHandlers/graphRenderer.ts
+++ /dev/null
@@ -1,3906 +0,0 @@
-/* eslint-enable @typescript-eslint/no-unused-vars */
-
-import * as ko from "knockout";
-import * as d3 from "d3";
-import * as $ from "jquery";
-
-import { Category} from '../Category';
-import { CategoryData} from '../CategoryData';
-import { Config} from '../Config';
-import { Daliuge } from "../Daliuge";
-import { Eagle} from '../Eagle';
-import { Edge} from '../Edge';
-import { Field} from '../Field';
-import { LogicalGraph} from '../LogicalGraph';
-import { Node} from '../Node';
-import { RightClick } from "../RightClick";
-import { Setting} from '../Setting';
-import { Utils} from '../Utils';
-
-
-
-ko.bindingHandlers.graphRenderer = {
- init: function(element, valueAccessor, allBindings, viewModel, bindingContext : ko.BindingContext) {
- //console.log("bindingHandlers.graphRenderer.init()");
- },
- update: function(element, valueAccessor, allBindings, viewModel, bindingContext : ko.BindingContext) {
- //console.log("bindingHandlers.graphRenderer.update()");
-
- const graph : LogicalGraph = ko.unwrap(valueAccessor());
-
- if (graph === null){
- //console.warn("graphRenderer update(): graph is null");
- return;
- }
-
- $(element).empty();
-
- render(graph, element.id, bindingContext.$root);
- }
-};
-
-enum LINK_COLORS {
- DEFAULT = 'dimgrey',
- DEFAULT_SELECTED = 'rgb(47 22 213)',
- WARNING = 'orange',
- WARNING_SELECTED = 'rgb(47 22 213)',
- INVALID = 'red',
- INVALID_SELECTED = 'rgb(47 22 213)',
- VALID = 'limegreen',
- EVENT = 'rgb(128,128,255)',
- EVENT_SELECTED = 'rgb(47 22 213)',
- AUTO_COMPLETE = 'purple',
- CLOSES_LOOP = 'dimgrey',
- CLOSES_LOOP_SELECTED = 'rgb(47 22 213)'
-}
-
-function render(graph: LogicalGraph, elementId : string, eagle : Eagle){
- const startTime: number = performance.now();
- eagle.rendererFrameCountRender = eagle.rendererFrameCountRender + 1;
-
- // sort the nodes array so that groups appear first, this ensures that child nodes are drawn on top of the group their parents
- const nodeData : Node[] = depthFirstTraversalOfNodes(graph, eagle.showDataNodes());
- const linkData : Edge[] = getEdges(graph, eagle.showDataNodes());
-
- let hasDraggedBackground : boolean = false;
- let isDraggingNode : boolean = false;
- let draggingInGraph : boolean = false;
- let isDraggingSelectionRegion : boolean = false;
- let sourcePort: Field | null = null;
- let sourceNode: Node | null = null;
- let sourcePortIsInput : boolean;
- let destinationPort: Field | null = null;
- let destinationNode: Node | null = null;
- let suggestedPort: Field | null = null;
- let suggestedNode: Node | null = null;
- let isDraggingPort : boolean = false;
- let isDraggingPortValid : Eagle.LinkValid = Eagle.LinkValid.Unknown;
- let isDraggingWithAlt : boolean = false;
- let dragEventCount : number = 0;
- let draggingNodeId : string = "";
-
- const mousePosition = {x:0, y:0};
- const selectionRegionStart = {x:0, y:0};
- const selectionRegionEnd = {x:0, y:0};
- const dragStart = {x:0, y:0};
- const headerHeight = 57.78 + 30
-
- const DOUBLE_CLICK_DURATION : number = 200;
-
- const APPS_HEIGHT : number = 28;
- const PORT_HEIGHT : number = 24;
-
- const NODE_STROKE_WIDTH : number = 3;
- const HEADER_INSET : number = NODE_STROKE_WIDTH - 4;
-
- const PORT_OFFSET_X : number = 2;
- const PORT_ICON_HEIGHT : number = 12;
- const PORT_INSET : number = 10;
-
- const RESIZE_CONTROL_SIZE : number = 16;
- const SHRINK_BUTTON_SIZE : number = 16;
-
- const RESIZE_BUTTON_LABEL : string = "\u25F2";
- const SHRINK_BUTTON_LABEL : string = "\u21B9";
-
- const HEADER_TEXT_FONT_SIZE : number = 14;
- const CONTENT_TEXT_FONT_SIZE : number = 14;
- const PORT_LABEL_FONT_SIZE : number = 14;
- const RESIZE_BUTTON_LABEL_FONT_SIZE : number = 24;
- const HEADER_BUTTON_LABEL_FONT_SIZE : number = 12;
-
- const SHRINK_BUTTONS_ENABLED : boolean = true;
-
- const MIN_AUTO_COMPLETE_EDGE_RANGE : number = 150;
-
- const snapToGridSize : number = Setting.findValue(Setting.SNAP_TO_GRID_SIZE);
-
- const svgContainer = d3
- .select("#" + elementId)
- .append("svg");
-
- // add a grid pattern to the svg
- const svgDefs = svgContainer.append("defs");
- const svgDefsPattern = svgDefs
- .append("pattern")
- .attr("id", "grid")
- .attr("width", snapToGridSize)
- .attr("height", snapToGridSize)
- .attr("patternUnits", "userSpaceOnUse");
- svgDefsPattern
- .append("path")
- .attr("d", "M " + snapToGridSize + " 0 L 0 0 0 " + snapToGridSize)
- .attr("fill", "none")
- .attr("stroke", "grey")
- .attr("stroke-width", 0.5);
-
- // add a root node to the SVG, we'll scale this root node
- const rootContainer = svgContainer
- .append("g")
- .attr("class", "root")
- .attr("id", "root")
- .attr("transform", rootScaleTranslation);
-
- // add def for markers
- const defs = rootContainer.append("defs");
-
- //generating defs from colors array
- Object.entries(LINK_COLORS).forEach(function (value, i) {
- const newArrowhead = defs
- .append("marker")
- .attr("id", value[0])
- .attr("viewBox", "0 0 10 10")
- .attr("refX", "7")
- .attr("refY", "5")
- .attr("markerUnits", "strokeWidth")
- .attr("markerWidth","8")
- .attr("markerHeight", "6")
- .attr("orient", "auto");
-
- newArrowhead
- .append("path")
- .attr("d", "M 0 0 L 10 5 L 0 10 z")
- .attr("stroke", "none")
- .attr("fill", value[1]);
- })
-
- // background (and grid if enabled)
- const rectWidth = $('#logicalGraphD3Div').width();
- const rectHeight = $('#logicalGraphD3Div').height();
- const offsetX = -eagle.globalOffsetX / eagle.globalScale;
- const offsetY = -eagle.globalOffsetY / eagle.globalScale;
-
- rootContainer
- .append("rect")
- .attr("class", "background")
- .attr("fill", eagle.snapToGrid() ? "url(#grid)" : "transparent")
- .attr("x", offsetX)
- .attr("y", offsetY)
- .attr("width", rectWidth*10)
- .attr("height", rectHeight*10);
-
- $("#logicalGraphD3Div svg").mousedown(function(e:any){
- if(e.button === 2){
- return
- }
- e.preventDefault()
- hasDraggedBackground = false;
- draggingInGraph = true;
-
- if (e.shiftKey || e.altKey){
- hasDraggedBackground = true;
- isDraggingSelectionRegion = true;
- selectionRegionStart.x = DISPLAY_TO_REAL_POSITION_X(e.originalEvent.x);
- selectionRegionStart.y = DISPLAY_TO_REAL_POSITION_Y(e.originalEvent.y-headerHeight);
- selectionRegionEnd.x = selectionRegionStart.x;
- selectionRegionEnd.y = selectionRegionStart.y;
- }
-
- if (e.altKey){
- isDraggingWithAlt = true;
- } else {
- isDraggingWithAlt = false;
- }
- });
-
- $("#logicalGraphD3Div svg").mousemove(function(e){
-
- e.preventDefault()
- if (!draggingInGraph){
- return
- }
-
- if (isDraggingSelectionRegion){
- selectionRegionEnd.x = DISPLAY_TO_REAL_POSITION_X(e.originalEvent.x);
- selectionRegionEnd.y = DISPLAY_TO_REAL_POSITION_Y(e.originalEvent.y-headerHeight);
- } else {
- // move background
- eagle.globalOffsetX += e.originalEvent.movementX;
- eagle.globalOffsetY += e.originalEvent.movementY;
- hasDraggedBackground = true;
- }
-
- tick();
- })
-
- $("#logicalGraphD3Div svg").mouseup(function(e:any){
- if(e.button != 2){
- finishDragging();
- }
- })
-
- $("#logicalGraphD3Div svg").mouseleave(function(e:any){
- if(draggingInGraph === true){
- finishDragging();
- }
- })
-
- function finishDragging(){
- const hadPreviousSelection: boolean = eagle.selectedObjects().length > 0;
- draggingInGraph = false;
-
- // if we just clicked on a node
- if (!hasDraggedBackground && !isDraggingSelectionRegion){
- eagle.setSelection(eagle.rightWindow().mode(), null, Eagle.FileType.Unknown);
- hasDraggedBackground = false;
- if (hadPreviousSelection){
- eagle.rightWindow().mode(Eagle.RightWindowMode.Hierarchy);
- }
- }
-
- // if we dragged a selection region
- if (isDraggingSelectionRegion){
- const nodes: Node[] = findNodesInRegion(selectionRegionStart.x, selectionRegionEnd.x, selectionRegionStart.y, selectionRegionEnd.y);
-
- const edges: Edge[] = findEdgesContainedByNodes(getEdges(eagle.logicalGraph(), eagle.showDataNodes()), nodes);
- console.log("Found", nodes.length, "nodes and", edges.length, "edges in region");
- const objects: (Node | Edge)[] = [];
-
- // only add those objects which are not already selected
- for (const node of nodes){
- if (!eagle.objectIsSelected(node)){
- objects.push(node);
- }
- }
- for (const edge of edges){
- if (!eagle.objectIsSelected(edge)){
- objects.push(edge);
- }
- }
-
- objects.forEach(function(element){
- eagle.editSelection(Eagle.RightWindowMode.Hierarchy, element, Eagle.FileType.Graph )
- })
-
- if (isDraggingWithAlt){
- for (const node of nodes){
- node.setCollapsed(false);
- }
- }
-
- selectionRegionStart.x = 0;
- selectionRegionStart.y = 0;
- selectionRegionEnd.x = 0;
- selectionRegionEnd.y = 0;
-
- // finish selecting a region
- isDraggingSelectionRegion = false;
-
- // necessary to make uncollapsed nodes show up
- eagle.logicalGraph.valueHasMutated();
- }
- }
-
- $("#logicalGraphD3Div svg").on("wheel", function(e:any){
- e.preventDefault()
- // Somehow only the eagle.globalScale does something...
- const wheelDelta = e.originalEvent.deltaY;
- const zoomDivisor = Setting.findValue(Setting.GRAPH_ZOOM_DIVISOR);
-
- const xs = (e.clientX - eagle.globalOffsetX) / eagle.globalScale
- const ys = (e.clientY - eagle.globalOffsetY) / eagle.globalScale
-
- eagle.globalScale *= (1-(wheelDelta/zoomDivisor));
- if(eagle.globalScale<0){
- eagle.globalScale = Math.abs(eagle.globalScale)
- }
- eagle.globalOffsetX = e.clientX - xs * eagle.globalScale;
- eagle.globalOffsetY = e.clientY - ys * eagle.globalScale;
-
- tick();
- });
-
- let nodes : any = rootContainer
- .selectAll("g.node")
- .data(nodeData)
- .enter()
- .append("g")
- .attr("transform", nodeGetTranslation)
- .attr("class", "node")
- .attr("id", function(node : Node, index : number){return "node" + index;})
- .style("display", getNodeDisplay)
- .on("contextmenu", function (d, i) {
- d3.event.preventDefault();
- d3.event.stopPropagation();
- RightClick.initiateContextMenu(d,d3.event.target)
- });
-
- // rects
- nodes
- .append("rect")
- .attr("width", function(node:Node){return getWidth(node);})
- .attr("height", function(node:Node){return getHeight(node);})
- .style("display", getNodeRectDisplay)
- .style("fill", nodeGetFill)
- .style("stroke", nodeGetStroke)
- .style("stroke-width", NODE_STROKE_WIDTH)
- .attr("stroke-dasharray", nodeGetStrokeDashArray);
-
- // custom-shaped nodes
- nodes
- .append("polygon")
- .attr("points", getNodeCustomShapePoints)
- .style("display", getNodeCustomShapeDisplay)
- .style("fill", nodeGetColor)
- .style("stroke", nodeGetStroke)
- .style("stroke-width", NODE_STROKE_WIDTH)
- .attr("stroke-dasharray", nodeGetStrokeDashArray);
-
- // update the parent of the given node
- // however, if allGraphEditing is false, then don't update
- // always keep track of whether an update would have happened, sp we can warn user
- function _updateNodeParent(node: Node, parentKey: number, updated: {parent: boolean}, allowGraphEditing: boolean){
- if (node.getParentKey() !== parentKey){
- if (allowGraphEditing){
- node.setParentKey(parentKey);
- }
- updated.parent = true;
- }
- }
-
- const nodeDragHandler = d3
- .drag()
- .on("start", function (node : Node) {
- isDraggingNode = false;
- dragEventCount = 0;
- hasDraggedBackground = false;
-
-
- if (d3.event.sourceEvent.shiftKey || d3.event.sourceEvent.altKey){
- hasDraggedBackground = true;
- isDraggingSelectionRegion = true;
- selectionRegionStart.x = DISPLAY_TO_REAL_POSITION_X(d3.event.sourceEvent.x);
- selectionRegionStart.y = DISPLAY_TO_REAL_POSITION_Y(d3.event.sourceEvent.y-headerHeight);
- selectionRegionEnd.x = selectionRegionStart.x;
- selectionRegionEnd.y = selectionRegionStart.y;
- return
- }
-
- if (!eagle.objectIsSelected(node)){
- draggingNodeId = node.getId();
- }
-
- // new click time
- const newTime = Date.now();
- const elapsedTime = newTime - Eagle.lastClickTime;
- Eagle.lastClickTime = newTime;
-
- // check if this is a double click
- if (elapsedTime < DOUBLE_CLICK_DURATION){
- node.toggleCollapsed();
- }
-
- // if node not selected, then select it
- if (!eagle.objectIsSelected(node)){
- selectNode(node, d3.event.sourceEvent.shiftKey);
- }
-
- // reset real
- for (const node of eagle.logicalGraph().getNodes()){
- node.resetReal();
- }
-
- // record drag start position
- dragStart.x = d3.event.sourceEvent.movementX;
- dragStart.y = d3.event.sourceEvent.movementY;
-
- //tick();
- })
- .on("drag", function (node : Node, index : number) {
- dragEventCount += 1;
-
- if (isDraggingSelectionRegion){
- selectionRegionEnd.x = DISPLAY_TO_REAL_POSITION_X(d3.event.sourceEvent.x);
- selectionRegionEnd.y = DISPLAY_TO_REAL_POSITION_Y(d3.event.sourceEvent.y-headerHeight);
- tick();
- return
- }
-
- if (!isDraggingNode){
- isDraggingNode = true;
-
- if (d3.event.sourceEvent.altKey){
- isDraggingWithAlt = true;
- } else {
- isDraggingWithAlt = false;
- }
- }
-
- // get distance the mouse was moved
- let movementX = d3.event.sourceEvent.movementX;
- let movementY = d3.event.sourceEvent.movementY;
-
- // in testcafe, d3.event.sourceEvent.movementX and Y are always zero, use the d3.event.dx and dy instead
- if (movementX === 0 && movementY === 0){
- movementX = d3.event.dx;
- movementY = d3.event.dy;
-
- // avoid drag event 1 all together, it is too prone to huge movements
- if (dragEventCount <=2){
- movementX = 0;
- movementY = 0;
- }
- }
-
- //console.log(d3.event.sourceEvent.target.tagName, "dragEventCount", dragEventCount, "movementSource", movementSource, "movementX", movementX, "movementY", movementY);
-
- // transform change in x,y position using current scale factor
- const dx = DISPLAY_TO_REAL_SCALE(movementX);
- const dy = DISPLAY_TO_REAL_SCALE(movementY);
-
- // count number of nodes in the current selection
- let numSelectedNodes = 0;
- for (const object of eagle.selectedObjects()){
- if (object instanceof Node){
- numSelectedNodes += 1;
- }
- }
-
- // move all selected nodes, skip edges (they just follow nodes anyway)
- for (const object of eagle.selectedObjects()){
- if (object instanceof Node){
- const actualChange = object.changePosition(dx, dy, numSelectedNodes === 1);
-
- if (!isDraggingWithAlt){
- moveChildNodes(object, dx, dy, actualChange.dx, actualChange.dy);
- }
- }
- }
-
- // trigger updates
- eagle.flagActiveFileModified();
- eagle.logicalGraph.valueHasMutated();
- })
- .on("end", function(node : Node){
-
- // if we dragged a selection region
- if (isDraggingSelectionRegion){
- const nodes: Node[] = findNodesInRegion(selectionRegionStart.x, selectionRegionEnd.x, selectionRegionStart.y, selectionRegionEnd.y);
-
- //checking if there was no drag distance, if so we are clicking a single object and we will toggle its seletion
- if(Math.abs(selectionRegionStart.x-selectionRegionEnd.x)+Math.abs(selectionRegionStart.y - selectionRegionEnd.y)<3){
- eagle.editSelection(Eagle.RightWindowMode.Inspector, node,Eagle.FileType.Graph);
- return
- }
-
- const edges: Edge[] = findEdgesContainedByNodes(getEdges(eagle.logicalGraph(), eagle.showDataNodes()), nodes);
- console.log("Found", nodes.length, "nodes and", edges.length, "edges in region");
- const objects: (Node | Edge)[] = [];
-
- // only add those objects which are not already selected
- for (const node of nodes){
- if (!eagle.objectIsSelected(node)){
- objects.push(node);
- }
- }
- for (const edge of edges){
- if (!eagle.objectIsSelected(edge)){
- objects.push(edge);
- }
- }
-
- objects.forEach(function(element){
- eagle.editSelection(Eagle.RightWindowMode.Hierarchy, element, Eagle.FileType.Graph )
- })
-
- if (isDraggingWithAlt){
- for (const node of nodes){
- node.setCollapsed(false);
- }
- }
-
- selectionRegionStart.x = 0;
- selectionRegionStart.y = 0;
- selectionRegionEnd.x = 0;
- selectionRegionEnd.y = 0;
-
- // finish selecting a region
- isDraggingSelectionRegion = false;
-
- // necessary to make uncollapsed nodes show up
- eagle.logicalGraph.valueHasMutated();
- return
- }
-
- // update location (in real node data, not sortedData)
- // guarding this behind 'isDraggingNode' is a hack to get around the fact that d3.event.x and d3.event.y behave strangely
- if (isDraggingNode){
- isDraggingNode = false;
- draggingNodeId = "";
- } else {
- // if node already selected, then deselect it
- if (eagle.objectIsSelected(node) && draggingNodeId !== node.getId()){
- selectNode(node, d3.event.sourceEvent.shiftKey);
- }
- }
-
- // determine the size of the node being moved, based on whether it is collapsed or not
- let posX, posY, width, height = 0;
- if (node.isCollapsed()){
- // find center of node
- const centerX = node.getPosition().x + node.getWidth()/2;
- const centerY = node.getPosition().y + node.getHeight()/2;
-
- // top left corner of icon
- posX = centerX - Node.DATA_COMPONENT_WIDTH/2;
- posY = centerY - Node.DATA_COMPONENT_HEIGHT/2;
- width = Node.DATA_COMPONENT_WIDTH;
- height = Node.DATA_COMPONENT_HEIGHT;
- } else {
- posX = node.getPosition().x;
- posY = node.getPosition().y;
- width = node.getWidth();
- height = node.getHeight();
- }
-
- // check for nodes underneath the node we dropped
- const parent : Node = eagle.logicalGraph().checkForNodeAt(posX, posY, width, height, node.getKey(), true);
-
- // check if new candidate parent is already a descendent of the node, this would cause a circular hierarchy which would be bad
- const ancestorOfParent = isAncestor(parent, node);
-
- // keep track of whether we would update any node parents
- const updated = {parent: false};
- const allowGraphEditing = Setting.findValue(Setting.ALLOW_GRAPH_EDITING);
-
- // if a parent was found, update
- if (parent !== null && node.getParentKey() !== parent.getKey() && node.getKey() !== parent.getKey() && !ancestorOfParent){
- //console.log("set parent", parent.getKey());
- //node.setParentKey(parent.getKey());
- _updateNodeParent(node, parent.getKey(), updated, allowGraphEditing);
- }
-
- // if no parent found, update
- if (parent === null && node.getParentKey() !== null){
- //console.log("set parent", null);
- //node.setParentKey(null);
- _updateNodeParent(node, null, updated, allowGraphEditing);
- }
-
- // also check that to see if current children are still in within the group
- if (isDraggingWithAlt && node.isGroup() && !node.isCollapsed()){
- // loop through all nodes, check if node is a child
- // if so, run checkForNodeAt and make sure result is parent
- for (let i = 0; i < nodeData.length ; i++){
- const child : Node = nodeData[i];
-
- if (child.getParentKey() === node.getKey()){
- const parent : Node = eagle.logicalGraph().checkForNodeAt(child.getPosition().x, child.getPosition().y, child.getWidth(), child.getHeight(), child.getKey(), true);
-
- // un-parent the child if no longer contained within the node we are dragging
- if (parent === null || parent.getKey() !== node.getKey()){
- //child.setParentKey(null);
- _updateNodeParent(child, null, updated, allowGraphEditing);
- }
- }
- }
- }
-
- eagle.undo().pushSnapshot(eagle, "Move node " + node.getName());
-
- if (!allowGraphEditing && updated.parent){
- Utils.showNotification("Node Parent not Changed", "Graph Editing is disabled", "danger");
- }
-
- //tick();
- });
-
- nodeDragHandler(rootContainer.selectAll("g.node"));
-
- const customTriangle = d3.symbol().type(d3.symbolTriangle)
-
- // add a header background to each node
- nodes
- .append("rect")
- .attr("class", "header-background")
- .attr("width", function(node:Node){return getHeaderBackgroundWidth(node);})
- .attr("height", function(node:Node){return getHeaderBackgroundHeight(node);})
- .attr("x", HEADER_INSET)
- .attr("y", HEADER_INSET)
- .style("fill", nodeGetColor)
- .style("stroke", "grey")
- .style("display", getHeaderBackgroundDisplay);
-
- // add a text header to each node
- nodes
- .append("foreignObject")
- .attr("class", "header-icon")
- .style("width", "40px")
- .style("height", "40px")
- .style("pointer-events", "none")
- .style("display", "inline")
- .style("font-size", '20px')
- .style("color", "white")
- .attr("x", "5px")
- .attr("y", "2px")
- .append("xhtml:span")
- .attr("class", function(node:Node){
- if (node.isGroup()){
- return node.getIcon()
- }else{
- return ""
- }
- })
-
- // add a text header to each node
- nodes
- .append("text")
- .attr("class", "header")
- .attr("x", function(node:Node){return getHeaderPositionX(node);})
- .attr("y", function(node:Node){return getHeaderPositionY(node);})
- .attr("eagle-wrap-width", getWrapWidth)
- .style("fill", getHeaderFill)
- .style("font-size", HEADER_TEXT_FONT_SIZE + "px")
- .style("font-weight", getHeaderFontWeight)
- .style("display", getHeaderDisplay)
- .text(getHeaderText)
- .call(wrap, false);
-
- // add a app names background to each node
- nodes
- .append("rect")
- .attr("class", "apps-background")
- .attr("width", function(node:Node){return getAppsBackgroundWidth(node);})
- .attr("height", function(node:Node){return getAppsBackgroundHeight(node);})
- .attr("x", HEADER_INSET)
- .attr("y", function(node:Node){return HEADER_INSET + getHeaderBackgroundHeight(node);})
- .style("fill", nodeGetColor)
- .style("stroke", "grey")
- .style("display", getAppsBackgroundDisplay);
-
- // add the input name text
- nodes
- .append("text")
- .attr("class", "inputAppName")
- .attr("x", function(node:Node){return getInputAppPositionX(node);})
- .attr("y", function(node:Node){return getInputAppPositionY(node);})
- .style("fill", getHeaderFill)
- .style("font-size", HEADER_TEXT_FONT_SIZE + "px")
- .style("display", getAppsBackgroundDisplay)
- .text(getInputAppText)
- .on("contextmenu", function (d:Node, i:number) {
- d3.event.preventDefault();
- // d3.event.stopPropagation();
- RightClick.initiateContextMenu(d.getInputApplication(),d3.event.target)
- });
-
- // add the output name text
- nodes
- .append("text")
- .attr("class", "outputAppName")
- .attr("x", function(node:Node){return getOutputAppPositionX(node);})
- .attr("y", function(node:Node){return getOutputAppPositionY(node);})
- .style("fill", getHeaderFill)
- .style("font-size", HEADER_TEXT_FONT_SIZE + "px")
- .style("display", getAppsBackgroundDisplay)
- .text(getOutputAppText)
- .on("contextmenu", function (d:Node, i:number) {
- d3.event.preventDefault();
- // d3.event.stopPropagation();
- RightClick.initiateContextMenu(d.getOutputApplication(),d3.event.target)
- });
-
- // add the content text
- nodes
- .append("text")
- .attr("class", "content")
- .attr("x", function(node:Node){return getContentPositionX(node);})
- .attr("y", function(node:Node){return getContentPositionY(node);})
- .attr("eagle-wrap-width", getWrapWidth)
- .style("fill", getContentFill)
- .style("font-size", CONTENT_TEXT_FONT_SIZE + "px")
- .style("display", getContentDisplay)
- .text(getContentText)
- .call(wrap, true);
-
- // add the svg icon
- nodes
- .append('foreignObject')
- .attr("class","nodeIcon")
- .attr("width", Node.DATA_COMPONENT_WIDTH+4)
- .attr("height", Node.DATA_COMPONENT_HEIGHT+4)
- .attr("x", function(node:Node){return getIconLocationX(node);})
- .attr("y", function(node:Node){return getIconLocationY(node);})
- .style("display", getIconDisplay)
- .attr('data-bind','eagleRightClick: $data')
- .append('xhtml:div')
- .attr("style", function(node:Node){if (eagle.objectIsSelected(node) && node.isCollapsed() && !node.isPeek()){return "background-color:lightgrey; border-radius:4px; border:2px solid "+Config.SELECTED_NODE_COLOR+"; padding:2px; transform:scale(.9);line-height: normal;"}else{return "line-height: normal;padding:4px;transform:scale(.9);"}})
- .append('xhtml:span')
- .attr("style", function(node:Node){ return node.getGraphIconAttr()})
- .attr("class", function(node:Node){return node.getIcon();});
-
- // add the resize controls
- nodes
- .append("rect")
- .attr("class", "resize-control")
- .attr("width", RESIZE_CONTROL_SIZE)
- .attr("height", RESIZE_CONTROL_SIZE)
- .attr("x", function(node : Node){return getWidth(node) - RESIZE_CONTROL_SIZE;})
- .attr("y", function(node : Node){return getHeight(node) - RESIZE_CONTROL_SIZE;})
- .style("display", getResizeControlDisplay);
-
- // add the resize labels
- nodes
- .append("text")
- .attr("class", "resize-control-label")
- .attr('x', function(node : Node){return getWidth(node) - RESIZE_CONTROL_SIZE;})
- .attr('y', function(node : Node){return getHeight(node) - 2;})
- .style('font-size', RESIZE_BUTTON_LABEL_FONT_SIZE + 'px')
- .style('display', getResizeControlDisplay)
- .style('user-select', 'none')
- .style('cursor', 'nwse-resize')
- .text(RESIZE_BUTTON_LABEL);
-
- const resizeDragHandler = d3
- .drag()
- .on("start", function (node : Node) {
- selectNode(node, false);
- tick();
- })
- .on("drag", function (node : Node) {
- let newWidth = node.getWidth() + DISPLAY_TO_REAL_SCALE(d3.event.sourceEvent.movementX);
- let newHeight = node.getHeight() + DISPLAY_TO_REAL_SCALE(d3.event.sourceEvent.movementY);
-
- // ensure node are of at least a minimum size
- newWidth = Math.max(newWidth, Node.MINIMUM_WIDTH);
- newHeight = Math.max(newHeight, Node.MINIMUM_HEIGHT);
-
- node.setWidth(newWidth);
- node.setHeight(newHeight);
-
- eagle.logicalGraph.valueHasMutated();
- //tick();
- });
-
- resizeDragHandler(rootContainer.selectAll("g.node rect.resize-control"));
- resizeDragHandler(rootContainer.selectAll("g.node text.resize-control-label"));
-
- const inputAppDragHandler = d3.drag()
- .on("end", function (node: Node){
- // check if node has an input app
- if (node.hasInputApplication()){
- eagle.setSelection(Eagle.RightWindowMode.Inspector, node.getInputApplication(), Eagle.FileType.Graph);
- } else {
- eagle.setNodeInputApplication(node.getKey());
- }
- });
-
- const outputAppDragHandler = d3.drag()
- .on("end", function (node: Node){
- // check if node has an output app
- if (node.hasOutputApplication()){
- eagle.setSelection(Eagle.RightWindowMode.Inspector, node.getOutputApplication(), Eagle.FileType.Graph);
- } else {
- eagle.setNodeOutputApplication(node.getKey());
- }
- });
-
- inputAppDragHandler(rootContainer.selectAll("g.node text.inputAppName"));
- outputAppDragHandler(rootContainer.selectAll("g.node text.outputAppName"));
-
- // add shrink buttons
- nodes
- .append("rect")
- .attr("class", "shrink-button")
- .attr("width", SHRINK_BUTTON_SIZE)
- .attr("height", SHRINK_BUTTON_SIZE)
- .attr("x", function(node : Node){return getWidth(node) - SHRINK_BUTTON_SIZE - HEADER_INSET - 4;})
- .attr("y", HEADER_INSET + 4)
- .style("display", getShrinkControlDisplay)
- .on("click", shrinkOnClick);
-
- // add shrink button labels
- nodes
- .append("text")
- .attr("class", "shrink-button-label")
- .attr('x', function(node : Node){return getWidth(node) - SHRINK_BUTTON_SIZE - HEADER_INSET - 2;})
- .attr('y', HEADER_INSET + 8 + (SHRINK_BUTTON_SIZE/2))
- .style('font-size', HEADER_BUTTON_LABEL_FONT_SIZE + 'px')
- .style('fill', 'black')
- .style('display', getShrinkControlDisplay)
- .style('user-select', 'none')
- .text(SHRINK_BUTTON_LABEL)
- .on("click", shrinkOnClick);
-
- // add the left-side ports (by default, the input ports)
- const inputPortGroups = nodes
- .append("g")
- .attr("class", getInputPortGroupClass)
- .attr("transform", getInputPortGroupTransform)
- .style("display", getPortsDisplay);
-
- // add input ports
- inputPortGroups
- .selectAll("g")
- .data(function(node : Node){return node.hasInputApplication() ? node.getInputApplicationInputPorts() : node.getInputPorts();})
- .enter()
- .append("text")
- .attr("class", getPortClass)
- .attr("x", getInputPortPositionX)
- .attr("y", getInputPortPositionY)
- .style("font-size", PORT_LABEL_FONT_SIZE + "px")
- .text(function (port : Field) {return port.getDisplayText();});
-
- const inputCircles = inputPortGroups
- .selectAll("g")
- .data(function(node : Node){return node.hasInputApplication() ? node.getInputApplicationInputPorts() : node.getInputPorts();})
- .enter()
- .append("circle")
- .attr("data-id", function(port : Field){return port.getId();})
- .attr("class", function(port : Field){return port.getIsEvent() ? "hiddenPortIcon" : ""})
- .attr("cx", getInputPortCirclePositionX)
- .attr("cy", getInputPortCirclePositionY)
- .attr("r", 6)
- .attr("data-node-key", function(port : Field){return port.getNodeKey();})
- .attr("data-usage", "input")
- .on("mouseenter", mouseEnterPort)
- .on("mouseleave", mouseLeavePort);
-
- const inputTriangles = inputPortGroups
- .selectAll("g")
- .data(function(node : Node){return node.hasInputApplication() ? node.getInputApplicationInputPorts() : node.getInputPorts();})
- .enter()
- .append("path")
- .attr("d", customTriangle)
- .attr("data-id", function(port : Field){return port.getId();})
- .attr("class", function(port : Field){return port.getIsEvent() ? "" : "hiddenPortIcon"})
- .attr("style", getInputPortTranslatePosition)
- .attr("data-node-key", function(port : Field){return port.getNodeKey();})
- .attr("data-usage", "input")
- .on("mouseenter", mouseEnterPort)
- .on("mouseleave", mouseLeavePort);
-
- // add the input local ports
- const inputLocalPortGroups = nodes
- .append("g")
- .attr("class", getInputLocalPortGroupClass)
- .attr("transform", getInputLocalPortGroupTransform)
- .style("display", getPortsDisplay);
-
- inputLocalPortGroups
- .selectAll("g")
- .data(function(node : Node){return node.getInputApplicationOutputPorts();})
- .enter()
- .append("text")
- .attr("class", function(port : Field){return port.getIsEvent() ? "event" : ""})
- .attr("x", getInputLocalPortPositionX)
- .attr("y", getInputLocalPortPositionY)
- .style("font-size", PORT_LABEL_FONT_SIZE + "px")
- .text(function (port : Field) {return port.getDisplayText();});
-
- const inputLocalCircles = inputLocalPortGroups
- .selectAll("g")
- .data(function(node : Node){return node.getInputApplicationOutputPorts();})
- .enter()
- .append("circle")
- .attr("data-id", function(port : Field){return port.getId();})
- .attr("class", function(port : Field){return port.getIsEvent() ? "hiddenPortIcon" : ""})
- .attr("cx", getInputLocalPortCirclePositionX)
- .attr("cy", getInputLocalPortCirclePositionY)
- .attr("r", 6)
- .attr("data-node-key", function(port : Field){return port.getNodeKey();})
- .attr("data-usage", "output")
- .on("mouseenter", mouseEnterPort)
- .on("mouseleave", mouseLeavePort);
-
- const inputLocalTriangles = inputLocalPortGroups
- .selectAll("g")
- .data(function(node : Node){return node.getInputApplicationOutputPorts();})
- .enter()
- .append("path")
- .attr("d", customTriangle)
- .attr("data-id", function(port : Field){return port.getId();})
- .attr("class", function(port : Field){return port.getIsEvent() ? "" : "hiddenPortIcon"})
- .attr("style", getInputLocalPortTranslatePosition)
- .attr("data-node-key", function(port : Field){return port.getNodeKey();})
- .attr("data-usage", "output")
- .on("mouseenter", mouseEnterPort)
- .on("mouseleave", mouseLeavePort);
-
- // add the output ports
- const outputPortGroups = nodes
- .append("g")
- .attr("class", getOutputPortGroupClass)
- .attr("transform", getOutputPortGroupTransform)
- .style("display", getPortsDisplay);
-
- outputPortGroups
- .selectAll("g")
- .data(function(node : Node, index : number){return node.hasOutputApplication() ? node.getOutputApplicationOutputPorts() : node.getOutputPorts();})
- .enter()
- .append("text")
- .attr("class", getPortClass)
- .attr("x", getOutputPortPositionX)
- .attr("y", getOutputPortPositionY)
- .style("font-size", PORT_LABEL_FONT_SIZE + "px")
- .text(function (port : Field) {return port.getDisplayText();});
-
- const outputCircles = outputPortGroups
- .selectAll("g")
- .data(function(node : Node){return node.hasOutputApplication() ? node.getOutputApplicationOutputPorts() : node.getOutputPorts();})
- .enter()
- .append("circle")
- .attr("data-id", function(port : Field){return port.getId();})
- .attr("class", function(port : Field){return port.getIsEvent() ? "hiddenPortIcon" : ""})
- .attr("cx", getOutputPortCirclePositionX)
- .attr("cy", getOutputPortCirclePositionY)
- .attr("r", 6)
- .attr("data-node-key", function(port : Field){return port.getNodeKey();})
- .attr("data-usage", "output")
- .on("mouseenter", mouseEnterPort)
- .on("mouseleave", mouseLeavePort);
-
- const outputTriangles = outputPortGroups
- .selectAll("g")
- .data(function(node : Node){return node.hasOutputApplication() ? node.getOutputApplicationOutputPorts() : node.getOutputPorts();})
- .enter()
- .append("path")
- .attr("d", customTriangle)
- .attr("data-id", function(port : Field){return port.getId();})
- .attr("class", function(port : Field){return port.getIsEvent() ? "" : "hiddenPortIcon"})
- .attr("style", getOutputPortTranslatePosition)
- .attr("data-node-key", function(port : Field){return port.getNodeKey();})
- .attr("data-usage", "output")
- .on("mouseenter", mouseEnterPort)
- .on("mouseleave", mouseLeavePort);
-
- // add the output local ports
- const outputLocalPortGroups = nodes
- .append("g")
- .attr("class", getOutputLocalPortGroupClass)
- .attr("transform", getOutputLocalPortGroupTransform)
- .style("display", getPortsDisplay);
-
- outputLocalPortGroups
- .selectAll("g")
- .data(function(node : Node){return node.getOutputApplicationInputPorts();})
- .enter()
- .append("text")
- .attr("class", function(port : Field){return port.getIsEvent() ? "event" : ""})
- .attr("x", getOutputLocalPortPositionX)
- .attr("y", getOutputLocalPortPositionY)
- .style("font-size", PORT_LABEL_FONT_SIZE + "px")
- .text(function (port : Field) {return port.getDisplayText();});
-
- const outputLocalCircles = outputLocalPortGroups
- .selectAll("g")
- .data(function(node : Node){return node.getOutputApplicationInputPorts();})
- .enter()
- .append("circle")
- .attr("data-id", function(port : Field){return port.getId();})
- .attr("class", function(port : Field){return port.getIsEvent() ? "hiddenPortIcon" : ""})
- .attr("cx", getOutputLocalPortCirclePositionX)
- .attr("cy", getOutputLocalPortCirclePositionY)
- .attr("r", 6)
- .attr("data-node-key", function(port : Field){return port.getNodeKey();})
- .attr("data-usage", "input")
- .on("mouseenter", mouseEnterPort)
- .on("mouseleave", mouseLeavePort);
-
- const outputLocalTriangles = outputLocalPortGroups
- .selectAll("g")
- .data(function(node : Node){return node.getOutputApplicationInputPorts();})
- .enter()
- .append("path")
- .attr("d", customTriangle)
- .attr("data-id", function(port : Field){return port.getId();})
- .attr("class", function(port : Field){return port.getIsEvent() ? "" : "hiddenPortIcon"})
- .attr("style", getOutputLocalPortTranslatePosition)
- .attr("data-node-key", function(port : Field){return port.getNodeKey();})
- .attr("data-usage", "input")
- .on("mouseenter", mouseEnterPort)
- .on("mouseleave", mouseLeavePort);
-
- const portDragHandler = d3.drag()
- .on("start", function (port : Field) {
- //console.log("drag start", "nodeKey", port.getNodeKey(), "portId", port.getId(), "portName", port.getDisplayText());
- isDraggingPort = true;
- sourceNode = graph.findNodeByKey(port.getNodeKey());
- sourcePort = port;
- sourcePortIsInput = d3.event.sourceEvent.target.dataset.usage === "input";
- })
- .on("drag", function () {
- //console.log("drag from port", data.Id);
- mousePosition.x = d3.mouse(svgContainer.node())[0];
- mousePosition.y = d3.mouse(svgContainer.node())[1];
-
- // convert mouse position to graph coordinates
- const mouseX = DISPLAY_TO_REAL_POSITION_X(mousePosition.x);
- const mouseY = DISPLAY_TO_REAL_POSITION_Y(mousePosition.y);
-
- // check for nearby nodes
- const nearbyNodes = findNodesInRange(mouseX, mouseY, MIN_AUTO_COMPLETE_EDGE_RANGE, sourceNode.getKey());
-
- // check for nearest matching port in the nearby nodes
- const matchingPort: Field = findNearestMatchingPort(mouseX, mouseY, nearbyNodes, sourceNode, sourcePort, sourcePortIsInput);
-
- if (matchingPort !== null){
- suggestedNode = graph.findNodeByKey(matchingPort.getNodeKey());
- suggestedPort = matchingPort;
- } else {
- suggestedNode = null;
- suggestedPort = null;
- }
-
- // reset all nodes to peek false
- for (const node of nodeData){
- node.setPeek(false);
- }
-
- // peek at the suggestedNode, if one exists
- if (suggestedNode){
- suggestedNode.setPeek(true);
- }
-
- tick();
- })
- .on("end", function(port : Field){
- //console.log("drag end", port.getId());
- isDraggingPort = false;
-
- if (destinationPort !== null || suggestedPort !== null){
- const srcNode = sourceNode;
- const srcPort = sourcePort;
-
- let destNode;
- let destPort;
-
- if (destinationPort !== null){
- destNode = destinationNode;
- destPort = destinationPort;
- } else {
- destNode = suggestedNode;
- destPort = suggestedPort;
- }
-
- // check if edge is back-to-front (input-to-output), if so, swap the source and destination
- //const backToFront : boolean = (srcPortType === "input" || srcPortType === "outputLocal") && (destPortType === "output" || destPortType === "inputLocal");
- const backToFront : boolean = sourcePortIsInput;
- const realSourceNode: Node = backToFront ? destNode : srcNode;
- const realSourcePort: Field = backToFront ? destPort : srcPort;
- const realDestinationNode: Node = backToFront ? srcNode : destNode;
- const realDestinationPort: Field = backToFront ? srcPort : destPort;
-
- // notify user
- if (backToFront){
- Utils.showNotification("Automatically reversed edge direction", "The edge began at an input port and ended at an output port, so the direction was reversed.", "info");
- }
-
- // check if link is valid
- const linkValid : Eagle.LinkValid = Edge.isValid(eagle, null, realSourceNode.getKey(), realSourcePort.getId(), realDestinationNode.getKey(), realDestinationPort.getId(), realSourcePort.getType(), false, false, true, true, {errors:[], warnings:[]});
-
- // abort if edge is invalid
- if (Setting.findValue(Setting.ALLOW_INVALID_EDGES) || linkValid === Eagle.LinkValid.Valid || linkValid === Eagle.LinkValid.Warning){
- if (linkValid === Eagle.LinkValid.Warning){
- addEdge(realSourceNode, realSourcePort, realDestinationNode, realDestinationPort, true, false);
- } else {
- addEdge(realSourceNode, realSourcePort, realDestinationNode, realDestinationPort, false, false);
- }
- } else {
- console.warn("link not valid, result", linkValid);
- }
- } else {
- // no destination, ask user to choose a new node
- const dataEligible = sourceNode.getCategoryType() !== Category.Type.Data;
- //getting matches from both the graph and the palettes list
- const eligibleComponents = Utils.getComponentsWithMatchingPort('palette graph', !sourcePortIsInput, sourcePort.getType(), dataEligible);
-
- // console.log("Found", eligibleComponents.length, "eligible automatically suggested components that have a " + (sourcePortIsInput ? "output" : "input") + " port of type:", sourcePort.getType());
-
- // check we found at least one eligible component
- if (eligibleComponents.length === 0){
- Utils.showNotification("Not Found", "No eligible components found for connection to port of this type (" + sourcePort.getType() + ")", "info");
- } else {
-
- // get list of strings from list of eligible components
- const eligibleComponentNames : Node[] = [];
- for (const c of eligibleComponents){
- eligibleComponentNames.push(c);
- }
-
- // NOTE: create copy in right click ts because we are using the right click menus to handle the node selection
- RightClick.edgeDropSrcNode = sourceNode;
- RightClick.edgeDropSrcPort = sourcePort;
- RightClick.edgeDropSrcIsInput = sourcePortIsInput;
-
- const x = DISPLAY_TO_REAL_POSITION_X(mousePosition.x);
- const y = DISPLAY_TO_REAL_POSITION_Y(mousePosition.y);
-
- Eagle.selectedRightClickPosition = {x:x, y:y};
-
- RightClick.edgeDropCreateNode(eligibleComponentNames,null)
- }
- }
-
- // stop peeking at any nodes
- for (const node of nodeData){
- node.setPeek(false);
- }
-
- clearEdgeVars();
- eagle.logicalGraph.valueHasMutated();
- });
-
- portDragHandler(inputCircles);
- portDragHandler(inputLocalCircles);
- portDragHandler(outputCircles);
- portDragHandler(outputLocalCircles);
- portDragHandler(inputTriangles);
- portDragHandler(inputLocalTriangles);
- portDragHandler(outputTriangles);
- portDragHandler(outputLocalTriangles);
-
- // draw link extras (these a invisble wider links that assist users in selecting the edges)
- // TODO: ideally we would not use the 'any' type here
- const linkExtras : any = rootContainer
- .selectAll("path.linkExtra")
- .data(linkData)
- .enter()
- .append("path")
- .on("contextmenu", function (linkData, i) {
- d3.event.preventDefault();
- d3.event.stopPropagation();
- RightClick.initiateContextMenu(linkData,d3.event.target)
- });
-
-
- linkExtras
- .attr("class", "linkExtra")
- .attr("d", createLink)
- .attr("stroke", "transparent")
- .attr("stroke-width", "10px")
- .attr("fill", "none")
- .attr("style","pointer-events:visible-stroke;")
- .style("display", getEdgeDisplay);
-
- // draw links
- // TODO: ideally we would not use the 'any' type here
- let links : any = rootContainer
- .selectAll("path.link")
- .data(linkData)
- .enter()
- .append("path");
-
-
- links
- .attr("class", "link")
- .attr("d", createLink)
- .attr("stroke", edgeGetStrokeColor)
- .attr("stroke-dasharray", edgeGetStrokeDashArray)
- .attr("fill", "none")
- .attr("marker-end", edgeGetArrowheadUrl)
- .style("display", getEdgeDisplay)
-
-
- const edgeDragHandler = d3
- .drag()
- .on("start", function(edge : Edge){
- selectEdge(edge, d3.event.sourceEvent.shiftKey);
- tick();
- });
-
- edgeDragHandler(rootContainer.selectAll("path.link, path.linkExtra"));
-
-
- // draw comment links
- let commentLinks : any = rootContainer
- .selectAll("path.commentLink")
- .data(nodeData)
- .enter()
- .append("path");
-
- commentLinks
- .attr("class", "commentLink")
- .attr("d", createCommentLink)
- .attr("stroke", LINK_COLORS.DEFAULT)
- .attr("fill", "transparent")
- .attr("marker-end", "url(#DEFAULT)")
- .style("display", getCommentLinkDisplay);
-
- // create one link that is only used during the creation of a new link
- // this new link follows the mouse pointer to indicate the position
- const draggingLink = rootContainer
- .append("line")
- .attr("class", "draggingLink")
- .attr("x1", 0)
- .attr("y1", 0)
- .attr("x2", 0)
- .attr("y2", 0)
- .attr("stroke", draggingEdgeGetStrokeColor);
-
- // create one link that is only used during the creation of a new link
- // this new link suggests to the user the edge suggested by the auto-complete function
- const autoCompleteLink = rootContainer
- .append("line")
- .attr("class", "autoCompleteLink")
- .attr("x1", 0)
- .attr("y1", 0)
- .attr("x2", 0)
- .attr("y2", 0)
- .attr("stroke", LINK_COLORS.AUTO_COMPLETE);
-
- const selectionRegion = rootContainer
- .append("rect")
- .attr("class", "selection-region")
- .attr("width", 0)
- .attr("height", 0)
- .attr("x", 0)
- .attr("y", 0)
- .attr("stroke", "black")
- .attr("fill", "transparent")
- .style("display", "inline");
-
- function determineDirection(source: boolean, node: Node, portIndex: number, portType: Daliuge.FieldUsage): Eagle.Direction {
- if (source){
- if (node.isBranch()){
- if (portIndex === 0){
- return Eagle.Direction.Down;
- }
- if (portIndex === 1){
- return Eagle.Direction.Right;
- }
- }
-
- if (portType === Daliuge.FieldUsage.OutputPort || portType === Daliuge.FieldUsage.InputOutput){
- return node.isFlipPorts() ? Eagle.Direction.Left : Eagle.Direction.Right;
- } else {
- return node.isFlipPorts() ? Eagle.Direction.Right : Eagle.Direction.Left;
- }
- } else {
- if (node.isBranch()){
- if (portIndex === 0){
- return Eagle.Direction.Down;
- }
- if (portIndex === 1){
- return Eagle.Direction.Right;
- }
- }
-
- if (portType === Daliuge.FieldUsage.InputPort || portType === Daliuge.FieldUsage.InputOutput){
- return node.isFlipPorts() ? Eagle.Direction.Left : Eagle.Direction.Right;
- } else {
- return node.isFlipPorts() ? Eagle.Direction.Right : Eagle.Direction.Left;
- }
- }
- }
-
- function createLink(edge : Edge) : string {
- // determine if edge is "forward" or not
- const srcNode : Node = findNodeWithKey(edge.getSrcNodeKey(), nodeData);
- const destNode : Node = findNodeWithKey(edge.getDestNodeKey(), nodeData);
-
- if (srcNode === null || destNode === null){
- console.warn("Can't find srcNode or can't find destNode for edge.");
- return createBezier(0,0,0,0,Eagle.Direction.Down,Eagle.Direction.Down, edge.isClosesLoop());
- }
-
- const srcPort : Field = srcNode.findFieldById(edge.getSrcPortId());
- const destPort : Field = destNode.findFieldById(edge.getDestPortId());
-
- if (srcPort === null){
- console.warn("Can't find srcPort (" + edge.getSrcPortId() + ") on srcNode (" + srcNode.getName() + ") for edge (" + edge.getId() + ").");
- }
-
- if (destPort === null){
- console.warn("Can't find destPort (" + edge.getDestPortId() + ") on destNode (" + destNode.getName() + ") for edge (" + edge.getId() + ").");
- }
-
- if (srcPort === null || destPort === null){
- return createBezier(0,0,0,0,Eagle.Direction.Down,Eagle.Direction.Down, edge.isClosesLoop());
- }
-
- const srcPortType : Daliuge.FieldUsage = srcPort.getUsage();
- const destPortType : Daliuge.FieldUsage = destPort.getUsage();
- const srcPortIndex : number = srcNode.findPortIndexById(edge.getSrcPortId());
- const destPortIndex : number = destNode.findPortIndexById(edge.getDestPortId());
-
- const srcPortPos = findNodePortPosition(srcNode, edge.getSrcPortId(), false, false);
- const destPortPos = findNodePortPosition(destNode, edge.getDestPortId(), true, false);
-
- let x1 = srcPortPos.x;
- let y1 = srcPortPos.y;
- let x2 = destPortPos.x;
- let y2 = destPortPos.y;
-
- console.assert(!isNaN(x1), "Source x-coord of edge cannot be found: " + srcNode.getName() + " -> " + destNode.getName());
- console.assert(!isNaN(y1), "Source y-coord of edge cannot be found: " + srcNode.getName() + " -> " + destNode.getName());
- console.assert(!isNaN(x2), "Destination x-coord of edge cannot be found: " + srcNode.getName() + " -> " + destNode.getName());
- console.assert(!isNaN(y2), "Destination y-coord of edge cannot be found: " + srcNode.getName() + " -> " + destNode.getName());
-
- // if coordinate isNaN, replace with a default, so at least the edge can be drawn
- if (isNaN(x1)) x1 = 0;
- if (isNaN(y1)) y1 = 0;
- if (isNaN(x2)) x2 = 0;
- if (isNaN(y2)) y2 = 0;
-
- const startDirection = determineDirection(true, srcNode, srcPortIndex, srcPortType);
- const endDirection = determineDirection(false, destNode, destPortIndex, destPortType);
- //console.log("edge", edge.getId(), "startDir", startDirection, "endDir", endDirection, "srcPortType", srcPortType, "destPortType", destPortType);
-
- return createBezier(x1, y1, x2, y2, startDirection, endDirection, edge.isClosesLoop());
- }
-
- function getEdges(graph: LogicalGraph, showDataNodes: boolean): Edge[]{
- if (showDataNodes){
- return graph.getEdges();
- } else {
- //return [graph.getEdges()[0]];
- const edges: Edge[] = [];
-
- for (const edge of graph.getEdges()){
- let srcHasConnectedInput: boolean = false;
- let destHasConnectedOutput: boolean = false;
-
- for (const e of graph.getEdges()){
- if (e.getDestNodeKey() === edge.getSrcNodeKey()){
- srcHasConnectedInput = true;
- }
- if (e.getSrcNodeKey() === edge.getDestNodeKey()){
- destHasConnectedOutput = true;
- }
- }
-
- const srcIsDataNode: boolean = findNodeWithKey(edge.getSrcNodeKey(), graph.getNodes()).isData();
- const destIsDataNode: boolean = findNodeWithKey(edge.getDestNodeKey(), graph.getNodes()).isData();
- //console.log("edge", edge.getId(), "srcIsDataNode", srcIsDataNode, "srcHasConnectedInput", srcHasConnectedInput, "destIsDataNode", destIsDataNode, "destHasConnectedOutput", destHasConnectedOutput);
-
- if (destIsDataNode){
- if (!destHasConnectedOutput){
- // draw edge as normal
- edges.push(edge);
- }
- continue;
- }
-
- if (srcIsDataNode){
- if (srcHasConnectedInput){
- // build a new edge
- const newSrc = findInputToDataNode(graph.getEdges(), edge.getSrcNodeKey());
- edges.push(new Edge(newSrc.nodeKey, newSrc.portId, edge.getDestNodeKey(), edge.getDestPortId(), edge.getDataType(), edge.isLoopAware(), edge.isClosesLoop(), false));
- } else {
- // draw edge as normal
- edges.push(edge);
- }
- }
- }
-
- return edges;
- }
- }
-
- function findInputToDataNode(edges: Edge[], nodeKey: number) : {nodeKey:number, portId: string}{
- for (const edge of edges){
- if (edge.getDestNodeKey() === nodeKey){
- return {
- nodeKey: edge.getSrcNodeKey(),
- portId: edge.getSrcPortId()
- };
- }
- }
-
- return null;
- }
-
- function tick(){
- const startTime = performance.now();
- eagle.rendererFrameCountTick = eagle.rendererFrameCountTick + 1;
-
-
- // background (and grid if enabled)
- const rectWidth = $('#logicalGraphD3Div').width();
- const rectHeight = $('#logicalGraphD3Div').height();
- const offsetX = -eagle.globalOffsetX / eagle.globalScale;
- const offsetY = -eagle.globalOffsetY / eagle.globalScale;
-
- rootContainer
- .selectAll("rect.background")
- .attr("fill", eagle.snapToGrid() ? "url(#grid)" : "transparent")
- .attr("x", offsetX)
- .attr("y", offsetY)
- .attr("width", rectWidth*10)
- .attr("height", rectHeight*10);
-
- // scale the root node
- rootContainer
- .attr("transform", rootScaleTranslation);
-
- // enter any new nodes
- rootContainer
- .selectAll("g.node")
- .data(nodeData)
- .enter()
- .insert("g")
- .attr("class", "node")
- .attr("id", function(node : Node, index : number){return "node" + index;});
-
- // exit any old nodes
- rootContainer
- .selectAll("g.node")
- .data(nodeData)
- .exit()
- .remove();
-
- // enter any new links
- rootContainer
- .selectAll("path.linkExtra")
- .data(linkData)
- .enter()
- .insert("path")
- .attr("class", "linkExtra")
- .style("display", getEdgeDisplay)
-
- // exit any old links.
- rootContainer
- .selectAll("path.linkExtra")
- .data(linkData)
- .exit()
- .remove();
-
- // enter any new links
- rootContainer
- .selectAll("path.link")
- .data(linkData)
- .enter()
- .insert("path")
- .attr("class", "link")
- .style("display", getEdgeDisplay)
-
- // exit any old links.
- rootContainer
- .selectAll("path.link")
- .data(linkData)
- .exit()
- .remove();
-
- // enter any new comment links
- rootContainer
- .selectAll("path.commentLink")
- .data(nodeData)
- .enter()
- .insert("path")
- .attr("class", "commentLink")
- .style("display", getCommentLinkDisplay);
-
- // exit any old comment links
- rootContainer
- .selectAll("path.commentLink")
- .data(nodeData)
- .exit()
- .remove();
-
- // make sure we have references to all the objects of each type
- nodes = rootContainer
- .selectAll("g.node")
- .data(nodeData)
- .style("display", getNodeDisplay);
- links = rootContainer
- .selectAll("path.link")
- .data(linkData);
- commentLinks = rootContainer
- .selectAll("path.commentLink")
- .data(nodeData);
-
- // TODO: update attributes of all nodes
- nodes.attr("transform", nodeGetTranslation);
-
- rootContainer
- .selectAll("g.node rect:not(.header-background):not(.apps-background):not(.resize-control):not(.shrink-button)")
- .data(nodeData)
- .attr("width", function(node:Node){return getWidth(node);})
- .attr("height", function(node:Node){return getHeight(node);})
- .style("display", getNodeRectDisplay)
- .style("fill", nodeGetFill)
- .style("stroke", nodeGetStroke)
- .style("stroke-width", NODE_STROKE_WIDTH)
- .attr("stroke-dasharray", nodeGetStrokeDashArray);
-
- rootContainer
- .selectAll("g.node polygon")
- .data(nodeData)
- .attr("points", getNodeCustomShapePoints)
- .style("display", getNodeCustomShapeDisplay)
- .style("fill", nodeGetColor)
- .style("stroke", nodeGetStroke)
- .style("stroke-width", NODE_STROKE_WIDTH)
- .attr("stroke-dasharray", nodeGetStrokeDashArray);
-
- rootContainer
- .selectAll("g.node rect.header-background")
- .data(nodeData)
- .attr("width", function(node:Node){return getHeaderBackgroundWidth(node);})
- .attr("height", function(node:Node){return getHeaderBackgroundHeight(node);})
- .attr("x", HEADER_INSET)
- .attr("y", HEADER_INSET)
- .style("fill", nodeGetColor)
- .style("stroke", "grey")
- .style("display", getHeaderBackgroundDisplay);
-
- rootContainer
- .selectAll("g.node foreignObject.header-icon")
- .data(nodeData)
- .style("width", "40px")
- .style("pointer-events", "none")
- .style("height", "40px")
- .style("display", "inline")
- .style("font-size", '20px')
- .style("color", "white")
- .attr("x", "5px")
- .attr("y", "2px")
-
- rootContainer
- .selectAll("g.node text.header")
- .data(nodeData)
- .attr("x", function(node:Node){return getHeaderPositionX(node);})
- .attr("y", function(node:Node){return getHeaderPositionY(node);})
- .attr("eagle-wrap-width", getWrapWidth)
- .style("fill", getHeaderFill)
- .style("font-size", HEADER_TEXT_FONT_SIZE + "px")
- .style("font-weight", getHeaderFontWeight)
- .style("display", getHeaderDisplay)
- .text(getHeaderText)
- .call(wrap, false);
-
- rootContainer
- .selectAll("g.node rect.apps-background")
- .data(nodeData)
- .attr("width", function(node:Node){return getAppsBackgroundWidth(node);})
- .attr("height", function(node:Node){return getAppsBackgroundHeight(node);})
- .attr("x", HEADER_INSET)
- .attr("y", function(node:Node){return HEADER_INSET + getHeaderBackgroundHeight(node);})
- .style("fill", nodeGetColor)
- .style("stroke", "grey")
- .style("display", getAppsBackgroundDisplay);
-
- rootContainer
- .selectAll("g.node text.inputAppName")
- .data(nodeData)
- .attr("x", function(node:Node){return getInputAppPositionX(node);})
- .attr("y", function(node:Node){return getInputAppPositionY(node);})
- .style("fill", getHeaderFill)
- .style("font-size", HEADER_TEXT_FONT_SIZE + "px")
- .style("display", getAppsBackgroundDisplay)
- .text(getInputAppText)
- .on("contextmenu", function (d:Node, i:number) {
- d3.event.preventDefault();
- d3.event.stopPropagation();
- RightClick.initiateContextMenu(d.getInputApplication(),d3.event.target)
- });
-
- rootContainer
- .selectAll("g.node text.outputAppName")
- .data(nodeData)
- .attr("x", function(node:Node){return getOutputAppPositionX(node);})
- .attr("y", function(node:Node){return getOutputAppPositionY(node);})
- .style("fill", getHeaderFill)
- .style("font-size", HEADER_TEXT_FONT_SIZE + "px")
- .style("display", getAppsBackgroundDisplay)
- .text(getOutputAppText)
- .on("contextmenu", function (d:Node, i:number) {
- d3.event.preventDefault();
- d3.event.stopPropagation();
- RightClick.initiateContextMenu(d.getOutputApplication(),d3.event.target)
- });
-
- rootContainer
- .selectAll("g.node text.content")
- .data(nodeData)
- .attr("x", function(node:Node){return getContentPositionX(node);})
- .attr("y", function(node:Node){return getContentPositionY(node);})
- .attr("eagle-wrap-width", getWrapWidth)
- .style("fill", getContentFill)
- .style("font-size", CONTENT_TEXT_FONT_SIZE + "px")
- .style("display", getContentDisplay)
- .text(getContentText)
- .call(wrap, true);
-
- rootContainer
- .selectAll("g.node foreignObject.nodeIcon")
- .data(nodeData)
- .attr("width", Node.DATA_COMPONENT_HEIGHT+4)
- .attr("height", Node.DATA_COMPONENT_HEIGHT+4)
- .attr("x", function(node:Node){return getIconLocationX(node);})
- .attr("y", function(node:Node){return getIconLocationY(node);})
- .style("display", getIconDisplay)
- .enter()
- .select("xhtml:div")
- .attr("style", function(node:Node){if (eagle.objectIsSelected(node) && node.isCollapsed() && !node.isPeek()){return "background-color:lightgrey; border-radius:4px; border:2px solid "+Config.SELECTED_NODE_COLOR+"; padding:2px; transform:scale(.9);line-height: normal;"}else{return "line-height: normal;padding:4px;transform:scale(.9);"}})
- .enter()
- .select("xhtml:span")
- .attr("style", function(node:Node){ return node.getGraphIconAttr()})
- .attr("class", function(node:Node){return node.getIcon();})
-
- rootContainer
- .selectAll("g.node rect.resize-control")
- .attr("width", RESIZE_CONTROL_SIZE)
- .attr("height", RESIZE_CONTROL_SIZE)
- .attr("x", function(node : Node){return getWidth(node) - RESIZE_CONTROL_SIZE;})
- .attr("y", function(node : Node){return getHeight(node) - RESIZE_CONTROL_SIZE;})
- .style("display", getResizeControlDisplay);
-
- rootContainer
- .selectAll("g.node text.resize-control-label")
- .attr('x', function(node : Node){return getWidth(node) - RESIZE_CONTROL_SIZE;})
- .attr('y', function(node : Node){return getHeight(node) - 2;})
- .style('font-size', RESIZE_BUTTON_LABEL_FONT_SIZE + 'px')
- .style('display', getResizeControlDisplay);
-
- rootContainer
- .selectAll("g.node rect.shrink-button")
- .attr("width", SHRINK_BUTTON_SIZE)
- .attr("height", SHRINK_BUTTON_SIZE)
- .attr("x", function(node : Node){return getWidth(node) - SHRINK_BUTTON_SIZE - HEADER_INSET - 4;})
- .attr("y", HEADER_INSET + 4)
- .style("display", getShrinkControlDisplay);
-
- rootContainer
- .selectAll("text.shrink-button-label")
- .attr('x', function(node : Node){return getWidth(node) - SHRINK_BUTTON_SIZE - HEADER_INSET - 2;})
- .attr('y', HEADER_INSET + 8 + (SHRINK_BUTTON_SIZE/2))
- .style('font-size', HEADER_BUTTON_LABEL_FONT_SIZE + 'px')
- .style('display', getShrinkControlDisplay);
-
- // inputPorts
- nodes
- .selectAll("g.inputPorts")
- .attr("transform", getInputPortGroupTransform)
- .style("display", getPortsDisplay);
-
- nodes
- .selectAll("g.inputPorts text")
- .data(function(node : Node){return node.hasInputApplication() ? node.getInputApplicationInputPorts() : node.getInputPorts();})
- .enter()
- .select("g.inputPorts")
- .insert("text");
-
- nodes
- .selectAll("g.inputPorts text")
- .data(function(node : Node){return node.hasInputApplication() ? node.getInputApplicationInputPorts() : node.getInputPorts();})
- .exit()
- .remove();
-
- nodes
- .selectAll("g.inputPorts text")
- .data(function(node : Node){return node.hasInputApplication() ? node.getInputApplicationInputPorts() : node.getInputPorts();})
- .attr("class", getPortClass)
- .attr("x", getInputPortPositionX)
- .attr("y", getInputPortPositionY)
- .style("font-size", PORT_LABEL_FONT_SIZE + "px")
- .text(function (port : Field) {return port.getDisplayText();});
-
- nodes
- .selectAll("g.inputPorts circle")
- .data(function(node : Node){return node.hasInputApplication() ? node.getInputApplicationInputPorts() : node.getInputPorts();})
- .enter()
- .select("g.inputPorts")
- .insert("circle");
-
- nodes
- .selectAll("g.inputPorts circle")
- .data(function(node : Node){return node.hasInputApplication() ? node.getInputApplicationInputPorts() : node.getInputPorts();})
- .exit()
- .remove();
-
- nodes
- .selectAll("g.inputPorts circle")
- .data(function(node : Node){return node.hasInputApplication() ? node.getInputApplicationInputPorts() : node.getInputPorts();})
- .attr("data-id", function(port : Field){return port.getId();})
- .attr("cx", getInputPortCirclePositionX)
- .attr("cy", getInputPortCirclePositionY)
- .attr("r", 6)
- .attr("data-node-key", function(port : Field){return port.getNodeKey();})
- .attr("data-usage", "input")
- .on("mouseenter", mouseEnterPort)
- .on("mouseleave", mouseLeavePort);
-
- nodes
- .selectAll("g.inputPorts path")
- .data(function(node : Node){return node.hasInputApplication() ? node.getInputApplicationInputPorts() : node.getInputPorts();})
- .enter()
- .select("g.inputPorts")
- .insert("path");
-
- nodes
- .selectAll("g.inputPorts path")
- .data(function(node : Node){return node.hasInputApplication() ? node.getInputApplicationInputPorts() : node.getInputPorts();})
- .exit()
- .remove();
-
- nodes
- .selectAll("g.inputPorts path")
- .data(function(node : Node){return node.hasInputApplication() ? node.getInputApplicationInputPorts() : node.getInputPorts();})
- .attr("d", customTriangle)
- .attr("data-id", function(port : Field){return port.getId();})
- .attr("style", getInputPortTranslatePosition)
- .attr("data-node-key", function(port : Field){return port.getNodeKey();})
- .attr("data-usage", "input")
- .on("mouseenter", mouseEnterPort)
- .on("mouseleave", mouseLeavePort);
-
- // inputLocalPorts
- nodes
- .selectAll("g.inputLocalPorts")
- .attr("transform", getInputLocalPortGroupTransform)
- .style("display", getPortsDisplay);
-
- nodes
- .selectAll("g.inputLocalPorts text")
- .data(function(node : Node){return node.getInputApplicationOutputPorts();})
- .enter()
- .select("g.inputLocalPorts")
- .insert("text");
-
- nodes
- .selectAll("g.inputLocalPorts text")
- .data(function(node : Node){return node.getInputApplicationOutputPorts();})
- .exit()
- .remove();
-
- nodes
- .selectAll("g.inputLocalPorts text")
- .data(function(node : Node){return node.getInputApplicationOutputPorts();})
- .attr("class", function(port : Field){return port.getIsEvent() ? "event" : ""})
- .attr("x", getInputLocalPortPositionX)
- .attr("y", getInputLocalPortPositionY)
- .style("font-size", PORT_LABEL_FONT_SIZE + "px")
- .text(function (port : Field) {return port.getDisplayText();});
-
- nodes
- .selectAll("g.inputLocalPorts circle")
- .data(function(node : Node){return node.getInputApplicationOutputPorts();})
- .enter()
- .select("g.inputLocalPorts")
- .insert("circle");
-
- nodes
- .selectAll("g.inputLocalPorts circle")
- .data(function(node : Node){return node.getInputApplicationOutputPorts();})
- .exit()
- .remove();
-
- nodes
- .selectAll("g.inputLocalPorts circle")
- .data(function(node : Node){return node.getInputApplicationOutputPorts();})
- .attr("data-id", function(port : Field){return port.getId();})
- .attr("cx", getInputLocalPortCirclePositionX)
- .attr("cy", getInputLocalPortCirclePositionY)
- .attr("r", 6)
- .attr("data-node-key", function(port : Field){return port.getNodeKey();})
- .attr("data-usage", "output")
- .on("mouseenter", mouseEnterPort)
- .on("mouseleave", mouseLeavePort);
-
- nodes
- .selectAll("g.inputLocalPorts path")
- .data(function(node : Node){return node.getInputApplicationOutputPorts();})
- .enter()
- .select("g.inputLocalPorts")
- .insert("circle");
-
- nodes
- .selectAll("g.inputLocalPorts path")
- .data(function(node : Node){return node.getInputApplicationOutputPorts();})
- .exit()
- .remove();
-
- nodes
- .selectAll("g.inputLocalPorts path")
- .data(function(node : Node){return node.getInputApplicationOutputPorts();})
- .attr("d", customTriangle)
- .attr("data-id", function(port : Field){return port.getId();})
- .attr("style", getInputLocalPortTranslatePosition)
- .attr("data-node-key", function(port : Field){return port.getNodeKey();})
- .attr("data-usage", "output")
- .on("mouseenter", mouseEnterPort)
- .on("mouseleave", mouseLeavePort);
-
- // outputPorts
- nodes
- .selectAll("g.outputPorts")
- .attr("transform", getOutputPortGroupTransform)
- .style("display", getPortsDisplay);
-
- nodes
- .selectAll("g.outputPorts text")
- .data(function(node : Node){return node.hasOutputApplication() ? node.getOutputApplicationOutputPorts() : node.getOutputPorts();})
- .enter()
- .select("g.outputPorts")
- .insert("text");
-
- nodes
- .selectAll("g.outputPorts text")
- .data(function(node : Node){return node.hasOutputApplication() ? node.getOutputApplicationOutputPorts() : node.getOutputPorts();})
- .exit()
- .remove();
-
- nodes
- .selectAll("g.outputPorts text")
- .data(function(node : Node){return node.hasOutputApplication() ? node.getOutputApplicationOutputPorts() : node.getOutputPorts();})
- .attr("class", getPortClass)
- .attr("x", getOutputPortPositionX)
- .attr("y", getOutputPortPositionY)
- .style("font-size", PORT_LABEL_FONT_SIZE + "px")
- .text(function (port : Field) {return port.getDisplayText()});
-
- nodes
- .selectAll("g.outputPorts circle")
- .data(function(node : Node){return node.hasOutputApplication() ? node.getOutputApplicationOutputPorts() : node.getOutputPorts();})
- .enter()
- .select("g.outputPorts")
- .insert("circle");
-
- nodes
- .selectAll("g.outputPorts circle")
- .data(function(node : Node){return node.hasOutputApplication() ? node.getOutputApplicationOutputPorts() : node.getOutputPorts();})
- .exit()
- .remove();
-
- nodes
- .selectAll("g.outputPorts circle")
- .data(function(node : Node){return node.hasOutputApplication() ? node.getOutputApplicationOutputPorts() : node.getOutputPorts();})
- .attr("data-id", function(port : Field){return port.getId();})
- .attr("cx", getOutputPortCirclePositionX)
- .attr("cy", getOutputPortCirclePositionY)
- .attr("r", 6)
- .attr("data-node-key", function(port : Field){return port.getNodeKey();})
- .attr("data-usage", "output")
- .on("mouseenter", mouseEnterPort)
- .on("mouseleave", mouseLeavePort);
-
- nodes
- .selectAll("g.outputPorts path")
- .data(function(node : Node){return node.hasOutputApplication() ? node.getOutputApplicationOutputPorts() : node.getOutputPorts();})
- .enter()
- .select("g.outputPorts")
- .insert("circle");
-
- nodes
- .selectAll("g.outputPorts path")
- .data(function(node : Node){return node.hasOutputApplication() ? node.getOutputApplicationOutputPorts() : node.getOutputPorts();})
- .exit()
- .remove();
-
- nodes
- .selectAll("g.outputPorts path")
- .data(function(node : Node){return node.hasOutputApplication() ? node.getOutputApplicationOutputPorts() : node.getOutputPorts();})
- .attr("d", customTriangle)
- .attr("data-id", function(port : Field){return port.getId();})
- .attr("style", getOutputPortTranslatePosition)
- .attr("data-node-key", function(port : Field){return port.getNodeKey();})
- .attr("data-usage", "output")
- .on("mouseenter", mouseEnterPort)
- .on("mouseleave", mouseLeavePort);
-
- // outputLocalPorts
- nodes
- .selectAll("g.outputLocalPorts")
- .attr("transform", getOutputLocalPortGroupTransform)
- .style("display", getPortsDisplay);
-
- nodes
- .selectAll("g.outputLocalPorts text")
- .data(function(node : Node){return node.getOutputApplicationInputPorts();})
- .enter()
- .select("g.outputLocalPorts")
- .insert("text");
-
- nodes
- .selectAll("g.outputLocalPorts text")
- .data(function(node : Node){return node.getOutputApplicationInputPorts();})
- .exit()
- .remove();
-
- nodes
- .selectAll("g.outputLocalPorts text")
- .data(function(node : Node){return node.getOutputApplicationInputPorts();})
- .attr("class", function(port : Field){return port.getIsEvent() ? "event" : ""})
- .attr("x", getOutputLocalPortPositionX)
- .attr("y", getOutputLocalPortPositionY)
- .style("font-size", PORT_LABEL_FONT_SIZE + "px")
- .text(function (port : Field) {return port.getDisplayText();});
-
- nodes
- .selectAll("g.outputLocalPorts circle")
- .data(function(node : Node){return node.getOutputApplicationInputPorts();})
- .enter()
- .select("g.outputLocalPorts")
- .insert("circle");
-
- nodes
- .selectAll("g.outputLocalPorts circle")
- .data(function(node : Node){return node.getOutputApplicationInputPorts();})
- .exit()
- .remove();
-
- nodes
- .selectAll("g.outputLocalPorts circle")
- .data(function(node : Node){return node.getOutputApplicationInputPorts();})
- .attr("data-id", function(port : Field){return port.getId();})
- .attr("cx", getOutputLocalPortCirclePositionX)
- .attr("cy", getOutputLocalPortCirclePositionY)
- .attr("r", 6)
- .attr("data-node-key", function(port : Field){return port.getNodeKey();})
- .attr("data-usage", "input")
- .on("mouseenter", mouseEnterPort)
- .on("mouseleave", mouseLeavePort);
-
-
- nodes
- .selectAll("g.outputLocalPorts path")
- .data(function(node : Node){return node.getOutputApplicationInputPorts();})
- .enter()
- .select("g.outputLocalPorts")
- .insert("circle");
-
- nodes
- .selectAll("g.outputLocalPorts path")
- .data(function(node : Node){return node.getOutputApplicationInputPorts();})
- .exit()
- .remove();
-
- nodes
- .selectAll("g.outputLocalPorts path")
- .data(function(node : Node){return node.getOutputApplicationInputPorts();})
- .attr("d", customTriangle)
- .attr("data-id", function(port : Field){return port.getId();})
- .attr("style", getOutputLocalPortTranslatePosition)
- .attr("data-node-key", function(port : Field){return port.getNodeKey();})
- .attr("data-usage", "input")
- .on("mouseenter", mouseEnterPort)
- .on("mouseleave", mouseLeavePort);
-
- // update attributes of all links
- linkExtras
- .attr("class", "linkExtra")
- .attr("d", createLink)
- .attr("fill", "none")
- .attr("stroke", "transparent")
- .attr("style","pointer-events:visible-stroke;")
- .attr("stroke-width", "10px")
- .style("display", getEdgeDisplay);
-
- // update attributes of all links
- links
- .attr("class", "link")
- .attr("d", createLink)
- .attr("stroke", edgeGetStrokeColor)
- .attr("stroke-dasharray", edgeGetStrokeDashArray)
- .attr("fill", "none")
- .attr("marker-end", edgeGetArrowheadUrl)
- .style("display", getEdgeDisplay);
-
- // update attributes of all comment links
- commentLinks
- .attr("class", "commentLink")
- .attr("d", createCommentLink)
- .attr("stroke", LINK_COLORS.DEFAULT)
- .attr("fill", "none")
- .attr("marker-end", "ur(#DEFAULT)")
- .style("display", getCommentLinkDisplay);
-
- // dragging link
- let draggingX1 : number;
- let draggingY1 : number;
- let draggingX2 : number;
- let draggingY2 : number;
-
- if (isDraggingPort){
- const srcPortPos = findNodePortPosition(sourceNode, sourcePort.getId(), sourcePortIsInput, false);
-
- draggingX1 = srcPortPos.x;
- draggingY1 = srcPortPos.y;
- draggingX2 = DISPLAY_TO_REAL_POSITION_X(mousePosition.x);
- draggingY2 = DISPLAY_TO_REAL_POSITION_Y(mousePosition.y);
-
- // offset x2/y2 so that the draggingLink is not right underneath the cursor (interfering with mouseenter/mouseleave events)
- if (draggingX1 > draggingX2)
- draggingX2 += 4;
- else
- draggingX2 -= 4;
- if (draggingY1 > draggingY2)
- draggingY2 += 4;
- else
- draggingY2 -= 4;
-
- // TODO: this is kind of hacky, creating a single-use edge just so that we can determine it's starting position
- draggingLink.attr("x1", draggingX1)
- .attr("y1", draggingY1)
- .attr("x2", draggingX2)
- .attr("y2", draggingY2)
- .attr("stroke", draggingEdgeGetStrokeColor);
- } else {
- draggingLink.attr("x1", 0)
- .attr("y1", 0)
- .attr("x2", 0)
- .attr("y2", 0)
- .attr("stroke", "none");
- }
-
- // autocomplete link
- if (isDraggingPort && suggestedNode !== null){
- const destPortPos = findNodePortPosition(suggestedNode, suggestedPort.getId(), !sourcePortIsInput, false);
- const x2 : number = destPortPos.x;
- const y2 : number = destPortPos.y;
-
- autoCompleteLink.attr("x1", draggingX2)
- .attr("y1", draggingY2)
- .attr("x2", x2)
- .attr("y2", y2)
- .attr("stroke", LINK_COLORS.AUTO_COMPLETE);
- } else {
- autoCompleteLink.attr("x1", 0)
- .attr("y1", 0)
- .attr("x2", 0)
- .attr("y2", 0)
- .attr("stroke", "none");
- }
-
- // selection region
- // make sure to send the lesser of the two coordinates as the top left point
- selectionRegion
- .attr("width", Math.abs(selectionRegionEnd.x - selectionRegionStart.x))
- .attr("height", Math.abs(selectionRegionEnd.y - selectionRegionStart.y))
- .attr("x", selectionRegionStart.x <= selectionRegionEnd.x ? selectionRegionStart.x : selectionRegionEnd.x)
- .attr("y", selectionRegionStart.y <= selectionRegionEnd.y ? selectionRegionStart.y : selectionRegionEnd.y)
- .attr("stroke", "black")
- .attr("fill", "transparent")
- .style("display", "inline");
-
- const elapsedTime = performance.now() - startTime;
- if (elapsedTime > eagle.rendererFrameMax){eagle.rendererFrameMax = elapsedTime;}
- eagle.rendererFrameDisplay("tick " + elapsedTime.toFixed(2) + "ms (max " + eagle.rendererFrameMax.toFixed(2) + "ms) Renders " + eagle.rendererFrameCountRender + " Ticks " + eagle.rendererFrameCountTick);
- }
-
- function selectEdge(edge : Edge, addToSelection: boolean){
- if (edge !== null){
- if (addToSelection){
- eagle.editSelection(Eagle.RightWindowMode.Inspector, edge, Eagle.FileType.Graph);
- } else {
- eagle.setSelection(Eagle.RightWindowMode.Inspector, edge, Eagle.FileType.Graph);
- }
- }
- }
-
- function selectNode(node : Node, addToSelection: boolean){
- if (node !== null){
- if (addToSelection){
- eagle.editSelection(Eagle.RightWindowMode.Inspector, node, Eagle.FileType.Graph);
- } else {
- eagle.setSelection(Eagle.RightWindowMode.Inspector, node, Eagle.FileType.Graph);
- }
- }
- }
-
- function buildTranslation(x : number, y : number) : string {
- return "translate(" + x.toString() + "," + y.toString() + ")";
- }
-
- function getContentText(data : Node) : string {
- return data.getCustomData();
- }
-
- function rootScaleTranslation(data : Node, e : any) : string {
- //console.log("rootScaleTranslation()", eagle.globalOffsetX, eagle.globalOffsetY, eagle.globalScale);
- return "translate(" + eagle.globalOffsetX + "," + eagle.globalOffsetY + ")scale(" + eagle.globalScale + ")";
- }
-
- function nodeGetTranslation(data : Node) : string {
- //return buildTranslation(REAL_TO_DISPLAY_POSITION_X(data.getPosition().x), REAL_TO_DISPLAY_POSITION_Y(data.getPosition().y));
- return buildTranslation(data.getPosition().x, data.getPosition().y);
- }
-
- function getX(node : Node) : number {
- return node.getPosition().x;
- }
-
- function getY(node : Node) : number {
- return node.getPosition().y;
- }
-
- function getWidth(node : Node) : number {
- return node.getDisplayWidth();
- }
-
- function getHeight(node : Node) : number {
- return node.getDisplayHeight();
- }
-
- function getIconLocationX(node : Node) : number {
- return node.getWidth()/2 - Node.DATA_COMPONENT_WIDTH/2;
- }
-
- function getIconLocationY(node : Node) : number {
- return Node.DATA_COMPONENT_HEIGHT/4;
- }
-
- function getHeaderBackgroundDisplay(node : Node) : string {
- // don't show header background for comment, description and ExclusiveForceNode nodes
- if (node.getCategory() === Category.Comment ||
- node.getCategory() === Category.Description ||
- node.getCategory() === Category.ExclusiveForceNode ||
- node.getCategory() === Category.Branch) {
- return "none";
- }
-
- return !node.isGroup() && node.isCollapsed() && !node.isPeek() ? "none" : "inline";
- }
-
- function getHeaderBackgroundWidth(node : Node) : number {
- return getWidth(node) - HEADER_INSET*2;
- }
-
- function getHeaderBackgroundHeight(node : Node) : number {
- if (node.isGroup() && node.isCollapsed()){
- return Node.GROUP_COLLAPSED_HEIGHT - HEADER_INSET*2;
- }
-
- if (!node.isGroup() && node.isCollapsed() && !node.isPeek()){
- return Node.DATA_COMPONENT_HEIGHT;
- }
-
- // default height
- return 8 + (20 * node.getNameNumLines(node.getDisplayWidth()));
- }
-
- function getHeaderDisplay(node : Node) : string {
- // don't show header background for comment and description nodes
- if (node.getCategory() === Category.Comment || node.getCategory() === Category.Description){
- return "none";
- } else {
- return "inline";
- }
- }
-
- function getHeaderText(data : Node) : string {
- return data.getName();
- }
-
- function getHeaderPositionX(node : Node) : number {
-
- if (!node.isGroup() && node.isCollapsed() && !node.isPeek()){
- return node.getWidth()/2;
- }
-
- return getWidth(node) /2;
- }
-
- function getHeaderPositionY(node : Node) : number {
- if (node.isGroup() && node.isCollapsed()){
-
- // decide how many lines this will be and move upwards some amount
- if (node.getNameNumLines(node.getDisplayWidth()) > 1){
- return Node.GROUP_COLLAPSED_HEIGHT / 3;
- }
-
- return Node.GROUP_COLLAPSED_HEIGHT / 2;
- }
-
- if (!node.isCollapsed() || node.isPeek()){
- return CategoryData.getCategoryData(node.getCategory()).expandedHeaderOffsetY;
- } else {
- return CategoryData.getCategoryData(node.getCategory()).collapsedHeaderOffsetY;
- }
- }
-
- function getHeaderFill(node : Node) : string {
- if (eagle.objectIsSelected(node) && node.isCollapsed() && !node.isPeek()){
- return Config.SELECTED_NODE_COLOR;
- }
- if (!node.isGroup() && node.isCollapsed() && !node.isPeek()){
- return "black";
- }
-
- if (node.getCategory() === Category.ExclusiveForceNode){
- return "black";
- }
-
- return "white";
- }
-
- function getHeaderFontWeight(node : Node) : string {
- if (eagle.objectIsSelected(node)){
- return "bold";
- }
-
- return "normal";
- }
-
- function getAppsBackgroundDisplay(node : Node) : string {
- // if node is collapsed, return 'none'
- if (node.isCollapsed()){
- return "none";
- }
-
- // if a service is not showing ports, hide
- if (node.isService() && node.isCollapsed() && !node.isPeek()){
- return "none";
- }
-
- // if node has input or output apps, return 'inline' else 'none'
- if (Node.canHaveInputApp(node) || Node.canHaveOutputApp(node) ){
- return "inline";
- }
-
- return "none";
- }
-
- function getAppsBackgroundWidth(node : Node) : number {
- return getWidth(node) - HEADER_INSET*2;
- }
-
- function getAppsBackgroundHeight(node : Node) : number {
- if (node.isGroup() && node.isCollapsed()){
- return Node.GROUP_COLLAPSED_HEIGHT;
- }
-
- if (!node.isGroup() && node.isCollapsed() && !node.isPeek()){
- return Node.DATA_COMPONENT_HEIGHT;
- }
-
- // default height
- return APPS_HEIGHT;
- }
-
- function getInputAppText(node:Node) : string {
- if (!Node.canHaveInputApp(node)){
- return "";
- }
-
- const inputApplication : Node = node.getInputApplication();
-
- if (typeof inputApplication === "undefined" || inputApplication === null){
- return Node.NO_APP_STRING;
- }
-
- return inputApplication.getName();
- }
-
- function getInputAppPositionX(node : Node) : number {
- return 8;
- }
-
- function getInputAppPositionY(node : Node) : number {
- return getHeaderBackgroundHeight(node) + 20;
- }
-
- function getOutputAppText(node:Node) : string {
- if (!Node.canHaveOutputApp(node)){
- return "";
- }
-
- const outputApplication : Node = node.getOutputApplication();
-
- if (typeof outputApplication === "undefined" || outputApplication === null){
- return Node.NO_APP_STRING;
- }
-
- return outputApplication.getName();
- }
-
- function getOutputAppPositionX(node : Node) : number {
- return node.getWidth() - 8;
- }
-
- function getOutputAppPositionY(node : Node) : number {
- return getHeaderBackgroundHeight(node) + 20;
- }
-
- function getInputPortGroupClass(node : Node) : string {
- if (node.isFlipPorts()){
- return "inputPorts flipped";
- } else {
- return "inputPorts no-flip";
- }
- }
-
- function getOutputPortGroupClass(node : Node) : string {
- if (node.isFlipPorts()){
- return "outputPorts flipped";
- } else {
- return "outputPorts no-flip";
- }
- }
-
- function getInputLocalPortGroupClass(node : Node) : string {
- if (node.isFlipPorts()){
- return "inputLocalPorts flipped";
- } else {
- return "inputLocalPorts no-flip";
- }
- }
-
- function getOutputLocalPortGroupClass(node : Node) : string {
- if (node.isFlipPorts()){
- return "outputLocalPorts flipped";
- } else {
- return "outputLocalPorts no-flip";
- }
- }
-
- function getPortClass(port : Field, index: number): string {
- const node: Node = findNodeWithKey(port.getNodeKey(), nodeData);
- if (node === null){
- console.warn("Unable to find node from port's node key", port.getNodeKey());
- return "";
- }
-
- if (node.isBranch()){
- if (index === 0){
- return port.getIsEvent() ? "event middle" : "middle";
- }
- if (index === 1){
- return port.getIsEvent() ? "event" : "";
- }
- }
-
- return port.getIsEvent() ? "event" : "";
- }
-
- function getInputPortGroupTransform(node : Node) : string {
- if (node.isBranch()){
- return buildTranslation(0, 0);
- }
-
- if (node.isFlipPorts()){
- return getRightSidePortGroupTransform(node);
- } else {
- return getLeftSidePortGroupTransform(node);
- }
- }
-
- function getOutputPortGroupTransform(node : Node) : string {
- if (node.isBranch()){
- return buildTranslation(0, 0);
- }
-
- if (node.isFlipPorts()){
- return getLeftSidePortGroupTransform(node);
- } else {
- return getRightSidePortGroupTransform(node);
- }
- }
-
- function getInputLocalPortGroupTransform(node : Node) : string {
- if (node.isFlipPorts()){
- return getRightSideLocalPortGroupTransform(node);
- } else {
- return getLeftSideLocalPortGroupTransform(node);
- }
- }
-
- function getOutputLocalPortGroupTransform(node : Node) : string {
- if (node.isFlipPorts()){
- return getLeftSideLocalPortGroupTransform(node);
- } else {
- return getRightSideLocalPortGroupTransform(node);
- }
- }
-
- function getLeftSidePortGroupTransform(node : Node) : string {
- if (Node.canHaveInputApp(node) || Node.canHaveOutputApp(node)){
- return buildTranslation(PORT_OFFSET_X, getHeaderBackgroundHeight(node) + APPS_HEIGHT);
- } else {
- return buildTranslation(PORT_OFFSET_X, getHeaderBackgroundHeight(node));
- }
- }
-
- function getRightSidePortGroupTransform(node : Node) : string {
- if (Node.canHaveInputApp(node) || Node.canHaveOutputApp(node)){
- return buildTranslation(getWidth(node)-PORT_OFFSET_X, getHeaderBackgroundHeight(node) + APPS_HEIGHT);
- } else {
- return buildTranslation(getWidth(node)-PORT_OFFSET_X, getHeaderBackgroundHeight(node));
- }
- }
-
- function getLeftSideLocalPortGroupTransform(node : Node) : string {
- if (Node.canHaveInputApp(node) || Node.canHaveOutputApp(node)){
- return buildTranslation(PORT_OFFSET_X, getHeaderBackgroundHeight(node) + APPS_HEIGHT + node.getInputApplicationInputPorts().length * PORT_HEIGHT);
- } else {
- return buildTranslation(PORT_OFFSET_X, getHeaderBackgroundHeight(node) + node.getInputPorts().length * PORT_HEIGHT);
- }
- }
-
- function getRightSideLocalPortGroupTransform(node : Node) : string {
- if (Node.canHaveInputApp(node) || Node.canHaveOutputApp(node)){
- return buildTranslation(getWidth(node)-PORT_OFFSET_X, getHeaderBackgroundHeight(node) + APPS_HEIGHT + (node.getOutputApplicationOutputPorts().length) * PORT_HEIGHT);
- } else {
- return buildTranslation(getWidth(node)-PORT_OFFSET_X, getHeaderBackgroundHeight(node) + node.getOutputPorts().length * PORT_HEIGHT);
- }
- }
-
- // TODO: one level of indirection here (getInput/Output -> getLeft/Right -> position)
- function getInputPortPositionX(port : Field, index : number) : number {
- const node: Node = findNodeWithKey(port.getNodeKey(), nodeData);
-
- if (node === null){
- console.warn("Unable to find node from port's node key", port.getNodeKey());
- return getLeftSidePortPositionX(port, index);
- }
-
- if (node.isBranch()){
- const numPorts = node.getInputPorts().length;
- return 100 - 76 * portIndexRatio(index, numPorts);
- }
-
- if (node.isFlipPorts()){
- return getRightSidePortPositionX(port, index);
- } else {
- return getLeftSidePortPositionX(port, index);
- }
- }
-
- function getInputPortPositionY(port : Field, index : number) : number {
- const node: Node = findNodeWithKey(port.getNodeKey(), nodeData);
-
- if (node === null){
- console.warn("Unable to find node from port's node key", port.getNodeKey());
- return getPortPositionY(port, index);
- }
-
- if (node.isBranch()){
- const numPorts = node.getInputPorts().length;
- return 24 + 30 * portIndexRatio(index, numPorts);
- }
-
- return getPortPositionY(port, index);
- }
-
- function getOutputPortPositionX(port : Field, index : number) : number {
- const node: Node = findNodeWithKey(port.getNodeKey(), nodeData);
-
- if (node === null){
- console.warn("Unable to find node from port's node key", port.getNodeKey());
- return getRightSidePortPositionX(port, index);
- }
-
- if (node.isBranch()){
- if (index === 0){
- return 200 / 2;
- }
- if (index === 1){
- return 200 - 24;
- }
- }
-
- if (node.isFlipPorts()){
- return getLeftSidePortPositionX(port, index);
- } else {
- return getRightSidePortPositionX(port, index);
- }
- }
-
- function getOutputPortPositionY(port : Field, index : number) : number {
- const node: Node = findNodeWithKey(port.getNodeKey(), nodeData);
-
- if (node === null){
- console.warn("Unable to find node from port's node key", port.getNodeKey());
- return getPortPositionY(port, index);
- }
-
- if (node.isBranch()){
- if (index === 0){
- return 100 - 16;
- }
- if (index === 1){
- return 54;
- }
- }
-
- return getPortPositionY(port, index);
- }
-
- function getInputLocalPortPositionX(port : Field, index : number) : number {
- const node: Node = findNodeWithKey(port.getNodeKey(), nodeData);
-
- if (node === null){
- console.warn("Unable to find node from port's node key", port.getNodeKey());
- return getLeftSidePortPositionX(port, index);
- }
-
- if (node.isBranch()){
- if (index === 0){
- return 200 / 2;
- }
- if (index === 1){
- return 200 - 24;
- }
- }
-
- if (node.isFlipPorts()){
- return getRightSidePortPositionX(port, index);
- } else {
- return getLeftSidePortPositionX(port, index);
- }
- }
-
- function getInputLocalPortPositionY(port : Field, index : number) : number {
- return getPortPositionY(port, index);
- }
-
- function getOutputLocalPortPositionX(port : Field, index : number) : number {
- const node: Node = findNodeWithKey(port.getNodeKey(), nodeData);
-
- if (node === null){
- console.warn("Unable to find node from port's node key", port.getNodeKey());
- return getRightSidePortPositionX(port, index);
- }
-
- if (node.isFlipPorts()){
- return getLeftSidePortPositionX(port, index);
- } else {
- return getRightSidePortPositionX(port, index);
- }
- }
-
- function getOutputLocalPortPositionY(port : Field, index : number) : number {
- return getPortPositionY(port, index);
- }
-
- function getExitLocalPortPositionX(port : Field, index : number) : number {
- const node: Node = findNodeWithKey(port.getNodeKey(), nodeData);
-
- if (node === null){
- console.warn("Unable to find node from port's node key", port.getNodeKey());
- return getRightSidePortPositionX(port, index);
- }
-
- if (node.isFlipPorts()){
- return getLeftSidePortPositionX(port, index);
- } else {
- return getRightSidePortPositionX(port, index);
- }
- }
-
- function getExitLocalPortPositionY(port : Field, index : number) : number {
- return getPortPositionY(port, index);
- }
-
- function getLeftSidePortPositionX(port : Field, index : number) : number {
- return 20;
- }
-
- function getPortPositionY(port : Field, index : number) : number {
- return (index + 1) * PORT_HEIGHT;
- }
-
- function getRightSidePortPositionX(port : Field, index : number) : number {
- return -20;
- }
-
- function getInputPortTranslatePosition(port:Field, index:number) : string {
- const posX = getInputPortCirclePositionX(port, index)
- const posY = getInputPortCirclePositionY(port,index)
-
- return "transform: translate("+posX+"px,"+posY+"px) rotate(90deg)"
- }
-
- function getOutputPortTranslatePosition(port:Field, index:number) : string {
- const posX = getOutputPortCirclePositionX(port, index)
- const posY = getOutputPortCirclePositionY(port,index)
-
- return "transform:translate("+posX+"px,"+posY+"px) rotate(270deg)"
- }
-
- function getInputLocalPortTranslatePosition(port:Field, index:number) : string {
- const posX = getInputLocalPortCirclePositionX(port, index)
- const posY = getInputLocalPortCirclePositionY(port,index)
-
- return "transform: translate("+posX+"px,"+posY+"px) rotate(90deg)"
- }
-
- function getOutputLocalPortTranslatePosition(port:Field, index:number) : string {
- const posX = getOutputLocalPortCirclePositionX(port, index)
- const posY = getOutputLocalPortCirclePositionY(port,index)
-
- return "transform:translate("+posX+"px,"+posY+"px) rotate(270deg)"
- }
-
- // port circle positions
- function getInputPortCirclePositionX(port : Field, index : number) : number {
- const node: Node = findNodeWithKey(port.getNodeKey(), nodeData);
-
- if (node === null){
- console.warn("Unable to find node from port's node key", port.getNodeKey());
- return getLeftSidePortCirclePositionX(port, index);
- }
-
- if (node.isBranch()){
- const numPorts = node.getInputPorts().length;
- return 100 - 100 * portIndexRatio(index, numPorts);
- }
-
- if (node.isFlipPorts()){
- return getRightSidePortCirclePositionX(port, index);
- } else {
- return getLeftSidePortCirclePositionX(port, index);
- }
- }
- function getInputPortCirclePositionY(port : Field, index : number) : number {
- const node: Node = findNodeWithKey(port.getNodeKey(), nodeData);
-
- if (node === null){
- console.warn("Unable to find node from port's node key", port.getNodeKey());
- return getPortCirclePositionY(port, index);
- }
-
- if (node.isBranch()){
- const numPorts = node.getInputPorts().length;
- return 50 * portIndexRatio(index, numPorts);
- }
-
- return getPortCirclePositionY(port, index);
- }
- function getOutputPortCirclePositionX(port : Field, index : number) : number {
- const node: Node = findNodeWithKey(port.getNodeKey(), nodeData);
-
- if (node === null){
- console.warn("Unable to find node from port's node key", port.getNodeKey());
- return getRightSidePortCirclePositionX(port, index);
- }
-
- if (node.isBranch()){
- if (index === 0){
- return 200 / 2;
- }
- if (index === 1){
- return 200;
- }
- }
-
- if (node.isFlipPorts()){
- return getLeftSidePortCirclePositionX(port, index);
- } else {
- return getRightSidePortCirclePositionX(port, index);
- }
- }
- function getOutputPortCirclePositionY(port : Field, index : number) : number {
- const node: Node = findNodeWithKey(port.getNodeKey(), nodeData);
-
- if (node === null){
- console.warn("Unable to find node from port's node key", port.getNodeKey());
- return getPortCirclePositionY(port, index);
- }
-
- if (node.isBranch()){
- // TODO: magic number
- if (index === 0){
- return 100;
- }
- if (index === 1){
- return 100 / 2;
- }
- }
-
- return getPortCirclePositionY(port, index);
- }
- function getExitPortCirclePositionX(port : Field, index : number) : number {
- const node: Node = findNodeWithKey(port.getNodeKey(), nodeData);
-
- if (node === null){
- console.warn("Unable to find node from port's node key", port.getNodeKey());
- return getRightSidePortCirclePositionX(port, index);
- }
-
- if (node.isFlipPorts()){
- return getLeftSidePortCirclePositionX(port, index);
- } else {
- return getRightSidePortCirclePositionX(port, index);
- }
- }
- function getExitPortCirclePositionY(port : Field, index : number) : number {
- return getPortCirclePositionY(port, index);
- }
- function getInputLocalPortCirclePositionX(port : Field, index : number) : number {
- const node: Node = findNodeWithKey(port.getNodeKey(), nodeData);
-
- if (node === null){
- console.warn("Unable to find node from port's node key", port.getNodeKey());
- return getLeftSidePortCirclePositionX(port, index);
- }
-
- if (node.isFlipPorts()){
- return getRightSidePortCirclePositionX(port, index);
- } else {
- return getLeftSidePortCirclePositionX(port, index);
- }
- }
- function getInputLocalPortCirclePositionY(port : Field, index : number) : number {
- return getPortCirclePositionY(port, index);
- }
- function getOutputLocalPortCirclePositionX(port : Field, index : number) : number {
- const node: Node = findNodeWithKey(port.getNodeKey(), nodeData);
-
- if (node === null){
- console.warn("Unable to find node from port's node key", port.getNodeKey());
- return getRightSidePortCirclePositionX(port, index);
- }
-
- if (node.isFlipPorts()){
- return getLeftSidePortCirclePositionX(port, index);
- } else {
- return getRightSidePortCirclePositionX(port, index);
- }
- }
- function getOutputLocalPortCirclePositionY(port : Field, index : number) : number {
- return getPortCirclePositionY(port, index);
- }
- function getExitLocalPortCirclePositionX(port : Field, index : number) : number {
- const node: Node = findNodeWithKey(port.getNodeKey(), nodeData);
-
- if (node === null){
- console.warn("Unable to find node from port's node key", port.getNodeKey());
- return getRightSidePortCirclePositionX(port, index);
- }
-
- if (node.isFlipPorts()){
- return getLeftSidePortCirclePositionX(port, index);
- } else {
- return getRightSidePortCirclePositionX(port, index);
- }
- }
- function getExitLocalPortCirclePositionY(port : Field, index : number) : number {
- return getPortCirclePositionY(port, index);
- }
-
- function getLeftSidePortCirclePositionX(port : Field, index : number) : number {
- return 8;
- }
-
- function getPortCirclePositionY(port : Field, index : number) : number {
- return (index + 1) * PORT_HEIGHT - 5;
- }
-
- function getRightSidePortCirclePositionX(port : Field, index : number) : number {
- return -8;
- }
-
- function getContentPositionX(node : Node) : number {
- // left justified
- return 8;
- }
-
- function getContentPositionY(node : Node) : number {
- // top
- return 16;
- }
-
- function getContentFill() : string {
- return "black";
- }
-
- function getContentDisplay(node : Node) : string {
- // only show content for comment and description nodes
- if ((node.getCategory() === Category.Comment || node.getCategory() === Category.Description) && (!node.isCollapsed() || node.isPeek())){
- return "inline";
- } else {
- return "none";
- }
- }
-
- function getIconDisplay(node : Node) : string {
- if (!node.isGroup() && !(!node.isCollapsed() || node.isPeek()) && !node.isBranch()){
- return "inline"
- } else {
- return "none";
- }
- }
-
- function nodeGetColor(node : Node) : string {
- return node.getColor();
- }
-
- function nodeGetFill(node : Node) : string {
- //console.log("nodeGetFill() category", node.getCategory());
-
- if (!node.isGroup() && node.isCollapsed() && !node.isPeek()){
- return "none";
- }
-
- // no fill color for "ExclusiveForceNode" nodes
- if (node.getCategory() === Category.ExclusiveForceNode){
- return "white";
- }
-
- return "rgba(180,180,180,1)";
- }
-
- function nodeGetStroke(node : Node) : string {
- if (!node.isGroup() && node.isCollapsed() && !node.isPeek()){
- return "none";
- }
-
- if (eagle.objectIsSelected(node)){
- return "black";
- }
-
- return "grey";
- }
-
- function nodeGetStrokeDashArray(node: Node) : string {
- if (node.getCategory() === Category.ExclusiveForceNode){
- return "8";
- }
- return "";
- }
-
- function findDepthOfNode(index: number, nodes : Node[]) : number {
- if (index >= nodes.length){
- console.warn("findDepthOfNode() with node index outside range of nodes. index:", index, "nodes.length", nodes.length);
- return 0;
- }
-
- let depth : number = 0;
- let node : Node = nodes[index];
- let nodeKey : number;
- let nodeParentKey : number = node.getParentKey();
- let iterations = 0;
-
- // follow the chain of parents
- while (nodeParentKey != null){
- if (iterations > 10){
- console.error("too many iterations in findDepthOfNode()");
- break;
- }
-
- iterations += 1;
- depth += 1;
- depth += node.getDrawOrderHint() / 10;
- nodeKey = node.getKey();
- nodeParentKey = node.getParentKey();
-
- if (nodeParentKey === null){
- return depth;
- }
-
- node = findNodeWithKey(nodeParentKey, nodes);
-
- if (node === null){
- console.error("Node", nodeKey, "has parentKey", nodeParentKey, "but call to findNodeWithKey(", nodeParentKey, ") returned null");
- return depth;
- }
-
- // if parent is selected, add more depth, so that it will appear on top
- if (eagle.objectIsSelected(node)){
- depth += 10;
- }
- }
-
- depth += node.getDrawOrderHint() / 10;
-
- // if node is selected, add more depth, so that it will appear on top
- if (eagle.objectIsSelected(node)){
- depth += 10;
- }
-
- return depth;
- }
-
- function depthFirstTraversalOfNodes(graph: LogicalGraph, showDataNodes: boolean) : Node[] {
- const indexPlusDepths : {index:number, depth:number}[] = [];
- const result : Node[] = [];
-
- // populate key plus depths
- for (let i = 0 ; i < graph.getNodes().length ; i++){
- let nodeHasConnectedInput: boolean = false;
- let nodeHasConnectedOutput: boolean = false;
- const node = graph.getNodes()[i];
-
- // check if node has connected input and output
- for (const edge of graph.getEdges()){
- if (edge.getDestNodeKey() === node.getKey()){
- nodeHasConnectedInput = true;
- }
-
- if (edge.getSrcNodeKey() === node.getKey()){
- nodeHasConnectedOutput = true;
- }
- }
-
- // skip data nodes, if showDataNodes is false
- if (!showDataNodes && node.isData() && nodeHasConnectedInput && nodeHasConnectedOutput){
- continue;
- }
-
- const depth = findDepthOfNode(i, graph.getNodes());
-
- indexPlusDepths.push({index:i, depth:depth});
- }
-
- // sort nodes in depth ascending
- indexPlusDepths.sort(function(a, b){
- return a.depth - b.depth;
- });
-
- // write nodes to result in sorted order
- for (const indexPlusDepth of indexPlusDepths){
- result.push(graph.getNodes()[indexPlusDepth.index]);
- }
-
- return result;
- }
-
- function findNodeWithKey(key: number, nodes: Node[]) : Node {
- if (key === null){
- return null;
- }
-
- for (const node of nodes){
- if (node.getKey() === key){
- return node;
- }
-
- // check if the node's inputApp has a matching key
- if (node.hasInputApplication()){
- if (node.getInputApplication().getKey() === key){
- return node.getInputApplication();
- }
- }
-
- // check if the node's outputApp has a matching key
- if (node.hasOutputApplication()){
- if (node.getOutputApplication().getKey() === key){
- return node.getOutputApplication();
- }
- }
- }
-
- console.warn("Cannot find node with key", key);
- return null;
- }
-
- function getEdgeDisplay(edge : Edge) : string {
- const srcNode : Node = findNodeWithKey(edge.getSrcNodeKey(), nodeData);
- const destNode : Node = findNodeWithKey(edge.getDestNodeKey(), nodeData);
-
- if (srcNode === null || destNode === null){
- return "none";
- }
-
- if (findAncestorCollapsedNode(srcNode) !== null && findAncestorCollapsedNode(destNode) !== null){
- return "none";
- }
-
- // also collapse if source port is local port of collapsed node
- if (srcNode.hasLocalPortWithId(edge.getSrcPortId()) && srcNode.isCollapsed()){
- return "none";
- }
-
- return "inline";
- }
-
- function branchPortPosition(node: Node, portId: string, input: boolean) : {x: number, y: number}{
- const portIndex = findNodePortIndex(node, portId);
- const sourceIsInput = node.findPortIsInputById(portId);
-
- if (node.isCollapsed()){ // TODO: maybe add && !node.isPeek() here
- if (input){
- if (portIndex === 0){
- return {
- x: node.getPosition().x + node.getWidth()/2,
- y: node.getPosition().y
- };
- } else {
- return {
- x: node.getPosition().x + node.getWidth()*1/4,
- y: node.getPosition().y + node.getHeight()*3/4 - 4
- };
- }
- } else {
- if (portIndex === 0){
- return {
- x: node.getPosition().x + node.getWidth()/2,
- y: node.getPosition().y + node.getHeight()
- };
- } else {
- return {
- x: node.getPosition().x + node.getWidth()*3/4,
- y: node.getPosition().y + node.getHeight()*3/4 - 4
- };
- }
- }
- }
- else { // not collapsed
- if (input){
- // calculate position of edge starting from a branch INPUT port
- if (portIndex === 0){
- return {
- x: node.getPosition().x + node.getWidth()/2,
- y: node.getPosition().y
- };
- } else {
- return {
- x: node.getPosition().x,
- y: node.getPosition().y + 50
- };
- }
- } else {
- if (portIndex === 0){
- return {
- x: node.getPosition().x + node.getWidth()/2,
- y: node.getPosition().y + 100
- };
- } else {
- return {
- x: node.getPosition().x + node.getWidth(),
- y: node.getPosition().y + 50
- };
- }
- }
- }
-
- return {x:0, y:0};
- }
-
- function portIndexRatio(portIndex: number, numPorts: number){
- if (numPorts <= 1){
- return 0;
- }
-
- return portIndex / (numPorts - 1);
- }
-
- function findNodePortPosition(node : Node, portId: string, input: boolean, inset: boolean) : {x: number, y: number} {
- let local : boolean;
- let index : number;
- const flipped : boolean = node.isFlipPorts();
- const position = {x: node.getPosition().x, y: node.getPosition().y};
-
- //console.log("findNodePortPosition()", "portId", portId, "input", input, "inset", inset, "node.isBranch()", node.isBranch(), "node.isEmbedded()", node.isEmbedded());
-
- // check if an ancestor is collapsed, if so, use center of ancestor
- const collapsedAncestor : Node = findAncestorCollapsedNode(node);
- if (collapsedAncestor !== null){
- return {
- x: collapsedAncestor.getPosition().x + Node.GROUP_COLLAPSED_WIDTH,
- y: collapsedAncestor.getPosition().y
- };
- }
-
- // check if node is a branch
- if (node.isBranch()){
- return branchPortPosition(node, portId, input);
- }
-
- // check if node is an embedded app, if so, use position of the construct in which the app is embedded
- if (node.isEmbedded()){
- const containingConstruct : Node = findNodeWithKey(node.getEmbedKey(), nodeData);
- return findNodePortPosition(containingConstruct, portId, input, true);
- }
-
- if (!node.isGroup() && node.isCollapsed() && !node.isPeek()){
- if ((input && !node.isFlipPorts()) || (!input && node.isFlipPorts())){
- return {
- x: node.getPosition().x + getIconLocationX(node),
- y: node.getPosition().y + getIconLocationY(node) + Node.DATA_COMPONENT_HEIGHT/2
- };
- } else {
- return {
- x: node.getPosition().x + getIconLocationX(node) + Node.DATA_COMPONENT_WIDTH,
- y: node.getPosition().y + getIconLocationY(node) + Node.DATA_COMPONENT_HEIGHT/2
- };
- }
- }
-
- // find the port within the node
- if (input){
- for (let i = 0 ; i < node.getInputPorts().length ; i++){
- const port : Field = node.getInputPorts()[i];
- if (port.getId() === portId){
- local = false;
- index = i;
- }
- }
- }
-
- if (!input){
- for (let i = 0 ; i < node.getOutputPorts().length ; i++){
- const port : Field = node.getOutputPorts()[i];
- if (port.getId() === portId){
- local = false;
- index = i;
- }
- }
- }
-
- // check input application ports
- if (input){
- for (let i = 0 ; i < node.getInputApplicationInputPorts().length ; i++){
- const port : Field = node.getInputApplicationInputPorts()[i];
- if (port.getId() === portId){
- local = false;
- index = i;
- }
- }
- }
-
- if (!input){
- for (let i = 0 ; i < node.getInputApplicationOutputPorts().length ; i++){
- const port : Field = node.getInputApplicationOutputPorts()[i];
- if (port.getId() === portId){
- local = true;
- index = i + node.getInputApplicationInputPorts().length;
- }
- }
- }
-
- // check output application ports
- if (input){
- for (let i = 0 ; i < node.getOutputApplicationInputPorts().length ; i++){
- const port : Field = node.getOutputApplicationInputPorts()[i];
- if (port.getId() === portId){
- local = true;
- index = i + node.getOutputApplicationOutputPorts().length;
- }
- }
- }
-
- if (!input){
- for (let i = 0 ; i < node.getOutputApplicationOutputPorts().length ; i++){
- const port : Field = node.getOutputApplicationOutputPorts()[i];
- if (port.getId() === portId){
- local = false;
- index = i;
- }
- }
- }
-
- // determine whether we need to move down an extra amount to clear the apps display title row
- let appsOffset : number = 0;
- if (Node.canHaveInputApp(node) || Node.canHaveOutputApp(node)){
- appsOffset = APPS_HEIGHT;
- }
-
- // translate the three pieces of info into the x,y position
- let drawLeftHandSide: boolean = false;
- if (input){
- drawLeftHandSide = !drawLeftHandSide;
- }
- if (flipped){
- drawLeftHandSide = !drawLeftHandSide;
- }
- if (local){
- drawLeftHandSide = !drawLeftHandSide;
- }
- //console.log("input", input, "flipped", flipped, "local", local, "index", index, "drawLeftHandSide", drawLeftHandSide);
-
- const headerHeight: number = getHeaderBackgroundHeight(node);
-
- // outer if is an XOR
- if (drawLeftHandSide){
- // left hand side
- if (inset){
- position.x += PORT_INSET;
- }
- if (local){
- position.y += headerHeight + appsOffset + (node.getInputPorts().length + index + 1) * PORT_HEIGHT;
- } else {
- position.y += headerHeight + appsOffset + (index + 1) * PORT_HEIGHT;
- }
- } else {
- // right hand side
- if (inset){
- position.x += node.getWidth() - PORT_INSET;
- } else {
- position.x += node.getWidth();
- }
- if (local){
- position.y += headerHeight + appsOffset + (node.getOutputPorts().length + index + 1) * PORT_HEIGHT;
- } else {
- position.y += headerHeight + appsOffset + (index + 1) * PORT_HEIGHT;
- }
- }
-
- //console.log("position", position);
-
- return position;
- }
-
- function findNodePortIndex(node: Node, portId: string){
- // find the port within the node
- for (let i = 0 ; i < node.getInputPorts().length ; i++){
- const port : Field = node.getInputPorts()[i];
- if (port.getId() === portId){
- return i;
- }
- }
-
- for (let i = 0 ; i < node.getOutputPorts().length ; i++){
- const port : Field = node.getOutputPorts()[i];
- if (port.getId() === portId){
- return i;
- }
- }
-
- return -1;
- }
-
- function edgeGetStrokeColor(edge: Edge, index: number) : string {
- let normalColor: string = LINK_COLORS.DEFAULT;
- let selectedColor: string = LINK_COLORS.DEFAULT_SELECTED;
-
- // check if source node is an event, if so, draw in blue
- const srcNode : Node = eagle.logicalGraph().findNodeByKey(edge.getSrcNodeKey());
-
- if (srcNode !== null){
- const srcPort : Field = srcNode.findFieldById(edge.getSrcPortId());
-
- if (srcPort !== null && srcPort.getIsEvent()){
- normalColor = LINK_COLORS.EVENT;
- selectedColor = LINK_COLORS.EVENT_SELECTED;
- }
- }
-
- // check if link has a warning or is invalid
- const linkValid : Eagle.LinkValid = Edge.isValid(eagle, edge.getId(), edge.getSrcNodeKey(), edge.getSrcPortId(), edge.getDestNodeKey(), edge.getDestPortId(), edge.getDataType(), edge.isLoopAware(), edge.isClosesLoop(), false, false, {errors:[], warnings:[]});
-
- if (linkValid === Eagle.LinkValid.Invalid){
- normalColor = LINK_COLORS.INVALID;
- selectedColor = LINK_COLORS.INVALID_SELECTED;
- }
-
- if (linkValid === Eagle.LinkValid.Warning){
- normalColor = LINK_COLORS.WARNING;
- selectedColor = LINK_COLORS.WARNING_SELECTED;
- }
-
- // check if the edge is a "closes loop" edge
- if (edge.isClosesLoop()){
- normalColor = LINK_COLORS.CLOSES_LOOP;
- selectedColor = LINK_COLORS.CLOSES_LOOP_SELECTED;
- }
-
- return eagle.objectIsSelected(edge) ? selectedColor : normalColor;
- }
-
- // TODO: this is inefficient, it calls edgeGetStrokeColor that determines the correct color enum and returns the color
- // then this function uses the color to get back to the enum
- function edgeGetArrowheadUrl(edge: Edge, index: number) {
- const selectedEdgeColor = edgeGetStrokeColor(edge, index);
- const findResult = Object.entries(LINK_COLORS).find(value => value[1] === selectedEdgeColor);
- return "url(#"+findResult[0]+")";
- }
-
- function edgeGetStrokeDashArray(edge: Edge, index: number) : string {
- const srcNode : Node = eagle.logicalGraph().findNodeByKey(edge.getSrcNodeKey());
- const destNode : Node = eagle.logicalGraph().findNodeByKey(edge.getDestNodeKey());
-
- // if we can't find the edge
- if (srcNode === null){
- return "";
- }
- if (destNode === null){
- return "";
- }
-
- if (srcNode.isStreaming() || destNode.isStreaming()){
- return "8";
- }
-
- if (edge.isClosesLoop()){
- return "8 8 2 8"
- }
-
- return "";
- }
-
- function draggingEdgeGetStrokeColor(edge: Edge, index: number) : string {
- switch (isDraggingPortValid){
- case Eagle.LinkValid.Unknown:
- return "black";
- case Eagle.LinkValid.Invalid:
- return LINK_COLORS.INVALID;
- case Eagle.LinkValid.Warning:
- return LINK_COLORS.WARNING;
- case Eagle.LinkValid.Valid:
- return LINK_COLORS.VALID;
- }
- }
-
- function addEdge(srcNode: Node, srcPort: Field, destNode: Node, destPort: Field, loopAware: boolean, closesLoop: boolean) : void {
- if (srcPort.getId() === destPort.getId()){
- console.warn("Abort addLink() from port to itself!");
- return;
- }
-
- eagle.addEdge(srcNode, srcPort, destNode, destPort, loopAware, closesLoop, (edge : Edge) : void => {
- eagle.checkGraph();
- eagle.logicalGraph.valueHasMutated();
- clearEdgeVars();
- });
- }
-
- function clearEdgeVars(){
- sourcePort = null;
- sourceNode = null;
- sourcePortIsInput = false;
- destinationPort = null;
- destinationNode = null;
- suggestedPort = null;
- suggestedNode = null;
- }
-
- function createCommentLink(node : Node){
- // abort if node is not comment
- if (node.getCategory() !== Category.Comment){
- return "";
- }
-
- // abort if comment node has no subject
- if (node.getSubjectKey() === null){
- return "";
- }
-
- // find subject node
- const subjectNode : Node = findNodeWithKey(node.getSubjectKey(), nodeData);
-
- let x1, y1, x2, y2;
-
- if (node.isFlipPorts()){
- x1 = node.getPosition().x;
- y1 = node.getPosition().y;
- } else {
- x1 = node.getPosition().x + node.getWidth();
- y1 = node.getPosition().y;
- }
-
- if (!node.isGroup() && node.isCollapsed() && !node.isPeek()){
- if (node.isFlipPorts()){
- x1 = node.getPosition().x + getIconLocationX(node);
- y1 = node.getPosition().y + getIconLocationY(node);
- } else {
- x1 = node.getPosition().x + getIconLocationX(node) + Node.DATA_COMPONENT_WIDTH;
- y1 = node.getPosition().y + getIconLocationY(node) + Node.DATA_COMPONENT_HEIGHT/2;
- }
- }
-
- if (subjectNode.isFlipPorts()){
- x2 = subjectNode.getPosition().x + subjectNode.getWidth();
- y2 = subjectNode.getPosition().y;
- } else {
- x2 = subjectNode.getPosition().x;
- y2 = subjectNode.getPosition().y;
- }
-
- if (!subjectNode.isGroup() && subjectNode.isCollapsed() && !subjectNode.isPeek()){
- if (subjectNode.isFlipPorts()){
- x2 = subjectNode.getPosition().x + getIconLocationX(subjectNode) + Node.DATA_COMPONENT_WIDTH;
- y2 = subjectNode.getPosition().y + getIconLocationY(subjectNode) + Node.DATA_COMPONENT_HEIGHT/2;
- } else {
- x2 = subjectNode.getPosition().x + getIconLocationX(subjectNode);
- y2 = subjectNode.getPosition().y + getIconLocationY(subjectNode) + Node.DATA_COMPONENT_HEIGHT/2;
- }
- }
-
- if (subjectNode.isBranch()){
- x2 = subjectNode.getPosition().x + subjectNode.getWidth()/2;
- y2 = subjectNode.getPosition().y;
- }
-
- // determine incident directions for start and end of edge
- const startDirection = node.isFlipPorts() ? Eagle.Direction.Left : Eagle.Direction.Right;
- let endDirection = subjectNode.isFlipPorts() ? Eagle.Direction.Left : Eagle.Direction.Right;
-
- if (subjectNode.isBranch()){
- endDirection = Eagle.Direction.Down;
- }
-
- return createBezier(x1, y1, x2, y2, startDirection, endDirection, false);
- }
-
- function getCommentLinkDisplay(node : Node) : string {
- if (node.getCategory() !== Category.Comment){
- return "none";
- }
-
- if (node.getSubjectKey() === null){
- return "none";
- }
-
- return "inline";
- }
-
- function directionOffset(x: boolean, direction: Eagle.Direction){
- if (x){
- switch (direction){
- case Eagle.Direction.Left:
- return -50;
- case Eagle.Direction.Right:
- return 50;
- default:
- return 0;
- }
- } else {
- switch (direction){
- case Eagle.Direction.Up:
- return -50;
- case Eagle.Direction.Down:
- return 50;
- default:
- return 0;
- }
- }
- }
-
- function createBezier(x1: number, y1: number, x2: number, y2: number, startDirection: Eagle.Direction, endDirection: Eagle.Direction, isLoop: boolean) : string {
- if (isLoop){
- // find control points
- const c1x = x1 + 3 * directionOffset(true, startDirection);
- const c1y = y1 + 1.5 * directionOffset(true, startDirection);
- const c2x = x2 - 3 * directionOffset(true, endDirection);
- const c2y = y2 + 1.5 * directionOffset(true, endDirection);
-
- return "M " + x1 + " " + y1 + " C " + c1x + " " + c1y + ", " + c2x + " " + c2y + ", " + x2 + " " + y2;
- } else {
- // find control points
- const c1x = x1 + directionOffset(true, startDirection);
- const c1y = y1 + directionOffset(false, startDirection);
- const c2x = x2 - directionOffset(true, endDirection);
- const c2y = y2 - directionOffset(false, endDirection);
-
- return "M " + x1 + " " + y1 + " C " + c1x + " " + c1y + ", " + c2x + " " + c2y + ", " + x2 + " " + y2;
- }
- }
-
- function shrinkOnClick(node : Node, index : number){
- console.log("shrink node", index);
-
- eagle.logicalGraph().shrinkNode(node);
- eagle.logicalGraph.valueHasMutated();
- }
-
- // realDeltaX - amount the mouse moved in X
- // realDeltaY - amount the mouse moved in Y
- // actualDeltaX - amount the moving object moved in X, may be different from realDeltaX if snap-to-grid is enabled
- // actualDeltaY - amount the moving object moved in Y, may be different from realDeltaY if snap-to-grid is enabled
- function moveChildNodes(node: Node, realDeltaX: number, realDeltaY: number, actualDeltaX: number, actualDeltaY: number) : void {
- // get id of parent node
- const parentKey : number = node.getKey();
-
- // loop through all nodes, if they belong to the parent's group, move them too
- for (const n of nodeData){
- // skip selected nodes, they are handled in the main drag code
- if (eagle.objectIsSelected(n)){
- continue;
- }
-
- if (n.getParentKey() === parentKey){
- moveNode(n, actualDeltaX, actualDeltaY);
- moveChildNodes(n, realDeltaX, realDeltaY, actualDeltaX, actualDeltaY);
- }
- }
- }
-
- function moveNode(node : Node, deltax : number, deltay : number) : void {
- node.setPosition(getX(node) + deltax, getY(node) + deltay, false);
- }
-
- function findAncestorCollapsedNode(node : Node) : Node {
- let n : Node = node;
- let iterations = 0;
-
- while (true){
- if (iterations > 32){
- console.error("too many iterations in findAncestorCollapsedNode()");
- return null;
- }
-
- // debug
- if (n.getKey() === n.getParentKey()){
- console.error("node", n.getKey(), "is own parent! parentKey", n.getParentKey(), n.getName());
- return null;
- }
-
- iterations += 1;
-
- const oldKey : number = n.getKey();
-
- // move up one level (preference using the node's embed key, then the parent key)
- if (n.getEmbedKey() !== null){
- n = findNodeWithKey(n.getEmbedKey(), nodeData);
- } else {
- n = findNodeWithKey(n.getParentKey(), nodeData);
- }
-
- // if node is null, return "inline"
- if (n === null){
- return null;
- }
- else {
- if (n.getKey() === oldKey){
- console.warn("move up did not move, aborting");
- return null;
- }
-
- // if node is non-null, but collapsed, return "none"
- if (n.isCollapsed()){
- return n;
- }
- }
-
- // otherwise continue while loop
- }
- }
-
- function isAncestor(node : Node, possibleAncestor : Node) : boolean {
- let n : Node = node;
- let iterations = 0;
-
- if (n === null){
- return false;
- }
-
- while (true){
- if (iterations > 32){
- console.error("too many iterations in isDescendent()");
- return null;
- }
-
- iterations += 1;
-
- // check if found
- if (n.getKey() === possibleAncestor.getKey()){
- return true;
- }
-
- // otherwise keep traversing upwards
- const newKey = n.getParentKey();
-
- // if we reach a null parent, we are done looking
- if (newKey === null){
- return false;
- }
-
- n = findNodeWithKey(newKey, nodeData);
- }
- }
-
- function getNodeDisplay(node : Node) : string {
- // hide if node has collapsed ancestor
- if (findAncestorCollapsedNode(node) !== null){
- return "none";
- }
-
- return "inline";
- }
-
- function getNodeRectDisplay(node: Node): string {
- if (node.isBranch()){
- return "none";
- }
- return "inline";
- }
-
- function getNodeCustomShapeDisplay(node: Node): string {
- if (node.isBranch()){
- return "inline";
- }
- return "none";
- }
-
- function getNodeCustomShapePoints(node: Node): string {
- switch(node.getCategory()){
- case Category.Branch:
- let half_width = 200 / 2;
- let half_height = 100 / 2;
- let offsetX = 0;
- let offsetY = 0;
-
- // if branch is collapsed, reduce to half size
- if (node.isCollapsed() && !node.isPeek()){
- half_width = 50;
- half_height = 25;
- offsetX = 50;
- offsetY = 25;
- }
-
- return (half_width+offsetX) + ", " + offsetY + " " + ((half_width*2)+offsetX) + ", " + (half_height+offsetY) + " " + (half_width+offsetX) + ", " + ((half_height*2)+offsetY) + " " + offsetX + ", " + (half_height+offsetY);
- default:
- return "";
- }
- }
-
- function getResizeControlDisplay(node : Node) : string {
- if (node.isCollapsed()){
- return "none";
- }
-
- return node.isResizable() ? "inline" : "none";
- }
-
- function getShrinkControlDisplay(node : Node) : string {
- if (SHRINK_BUTTONS_ENABLED){
- if (node.isGroup()){
- return node.isCollapsed() ? "none" : "inline";
- } else {
- return "none";
- }
- } else {
- return "none";
- }
- }
-
- // whether or not an object in the graph should be rendered or not
- function getPortsDisplay(node : Node) : string {
- if (node.isCollapsed() && !node.isPeek()){
- return "none";
- }
-
- if (!node.isGroup() && node.isCollapsed() && !node.isPeek()){
- return "none";
- }
-
- return "inline";
- }
-
- function findNodesInRegion(left: number, right: number, top: number, bottom: number): Node[] {
- const result: Node[] = [];
-
- // re-assign left, right, top, bottom in case selection region was not dragged in the typical NW->SE direction
- const realLeft = left <= right ? left : right;
- const realRight = left <= right ? right : left;
- const realTop = top <= bottom ? top : bottom;
- const realBottom = top <= bottom ? bottom : top;
-
- for (let i = nodeData.length - 1; i >= 0 ; i--){
- const node : Node = nodeData[i];
-
- // use center of node as position
- const centerX : number = node.getPosition().x + node.getWidth()/2;
- const centerY : number = node.getPosition().y + node.getHeight()/2;
-
- if (centerX >= realLeft && realRight >= centerX && centerY >= realTop && realBottom >= centerY){
- result.push(node);
- }
- }
-
- return result;
- }
-
- function findEdgesContainedByNodes(edges: Edge[], nodes: Node[]): Edge[]{
- const result: Edge[] = [];
-
- for (const edge of edges){
- const srcKey = edge.getSrcNodeKey();
- const destKey = edge.getDestNodeKey();
- let srcFound = false;
- let destFound = false;
-
- for (const node of nodes){
- if ((node.getKey() === srcKey) ||
- (node.hasInputApplication() && node.getInputApplication().getKey() === srcKey) ||
- (node.hasOutputApplication() && node.getOutputApplication().getKey() === srcKey)){
- srcFound = true;
- }
-
- if ((node.getKey() === destKey) ||
- (node.hasInputApplication() && node.getInputApplication().getKey() === destKey) ||
- (node.hasOutputApplication() && node.getOutputApplication().getKey() === destKey)){
- destFound = true;
- }
- }
-
- if (srcFound && destFound){
- result.push(edge);
- }
- }
-
- return result;
- }
-
- function findNodesInRange(positionX: number, positionY: number, range: number, sourceNodeKey: number): Node[]{
- const result: Node[] = [];
-
- //console.log("findNodesInRange(): sourceNodeKey", sourceNodeKey);
-
- for (let i = 0; i < nodeData.length; i++){
- // skip the source node
- if (nodeData[i].getKey() === sourceNodeKey){
- continue;
- }
-
- // fetch categoryData for the node
- const categoryData = CategoryData.getCategoryData(nodeData[i].getCategory());
- let possibleInputs = categoryData.maxInputs;
- let possibleOutputs = categoryData.maxOutputs;
-
- // add categoryData for embedded apps (if they exist)
- if (nodeData[i].hasInputApplication()){
- const inputApp = nodeData[i].getInputApplication();
- const inputAppCategoryData = CategoryData.getCategoryData(inputApp.getCategory());
- possibleInputs += inputAppCategoryData.maxInputs;
- possibleOutputs += inputAppCategoryData.maxOutputs;
- }
- if (nodeData[i].hasOutputApplication()){
- const outputApp = nodeData[i].getOutputApplication();
- const outputAppCategoryData = CategoryData.getCategoryData(outputApp.getCategory());
- possibleInputs += outputAppCategoryData.maxInputs;
- possibleOutputs += outputAppCategoryData.maxOutputs;
- }
-
- // skip nodes that can't have inputs or outputs
- if (possibleInputs === 0 && possibleOutputs === 0){
- continue;
- }
-
- // determine distance from position to this node
- const distance = Utils.positionToNodeDistance(positionX, positionY, nodeData[i]);
-
- if (distance <= range){
- //console.log("distance to", nodeData[i].getName(), nodeData[i].getKey(), "=", distance);
- result.push(nodeData[i]);
- }
- }
-
- return result;
- }
-
- function findNearestMatchingPort(positionX: number, positionY: number, nearbyNodes: Node[], sourceNode: Node, sourcePort: Field, sourcePortIsInput: boolean) : Field {
- //console.log("findNearestMatchingPort(), sourcePortIsInput", sourcePortIsInput);
- let minDistance = Number.MAX_SAFE_INTEGER;
- let minPort = null;
-
- for (const node of nearbyNodes){
- let portList: Field[] = [];
-
- // if source node is Data, then no nearby Data nodes can have matching ports
- if (sourceNode.getCategoryType() === Category.Type.Data && node.getCategoryType() === Category.Type.Data){
- continue;
- }
-
- // if sourcePortIsInput, we should search for output ports, and vice versa
- if (sourcePortIsInput){
- portList = portList.concat(node.getOutputPorts());
- } else {
- portList = portList.concat(node.getInputPorts());
- }
-
- // get inputApplication ports
- if (sourcePortIsInput){
- portList = portList.concat(node.getInputApplicationOutputPorts());
- } else {
- portList = portList.concat(node.getInputApplicationInputPorts());
- }
-
- // get outputApplication ports
- if (sourcePortIsInput){
- portList = portList.concat(node.getOutputApplicationOutputPorts());
- } else {
- portList = portList.concat(node.getOutputApplicationInputPorts());
- }
-
- for (const port of portList){
- if (!Utils.portsMatch(port, sourcePort)){
- continue;
- }
-
- // if port has no id (broken) then don't consider it as a auto-complete target
- if (port.getId() === ""){
- continue;
- }
-
- // get position of port
- const portX = node.getPosition().x;
- const portY = node.getPosition().y;
-
- // get distance to port
- const distance = Math.sqrt( Math.pow(portX - positionX, 2) + Math.pow(portY - positionY, 2) );
-
- // remember this port if it the best so far
- if (distance < minDistance){
- minPort = port;
- minDistance = distance;
- }
- }
- }
-
- return minPort;
- }
-
- function mouseEnterPort(port : Field) : void {
- if (!isDraggingPort){
- return;
- }
-
- destinationPort = port;
- destinationNode = graph.findNodeByKey(port.getNodeKey());
-
- isDraggingPortValid = Edge.isValid(eagle, null, sourceNode.getKey(), sourcePort.getId(), destinationNode.getKey(), destinationPort.getId(), sourcePort.getType(), false, false, false, false, {errors:[], warnings:[]});
- }
-
- function mouseLeavePort(port : Field) : void {
- destinationPort = null;
- destinationNode = null;
-
- isDraggingPortValid = Eagle.LinkValid.Unknown;
- }
-
- function REAL_TO_DISPLAY_POSITION_X(x: number) : number {
- return eagle.globalOffsetX + (x * eagle.globalScale);
- }
- function REAL_TO_DISPLAY_POSITION_Y(y: number) : number {
- return eagle.globalOffsetY + (y * eagle.globalScale);
- }
- function REAL_TO_DISPLAY_SCALE(n: number, name: string = null) : number {
-
- if (name != null){
- console.log(name, n, eagle.globalScale, n * eagle.globalScale);
- }
-
- return n * eagle.globalScale;
- }
- function DISPLAY_TO_REAL_POSITION_X(x: number) : number {
- return (x - eagle.globalOffsetX)/eagle.globalScale;
- }
- function DISPLAY_TO_REAL_POSITION_Y(y: number) : number {
- return (y - eagle.globalOffsetY)/eagle.globalScale;
- }
- function DISPLAY_TO_REAL_SCALE(n: number) : number {
- return n / eagle.globalScale;
- }
-
- function getWrapWidth(node: Node) {
- if (node.isData()){
- return Number.POSITIVE_INFINITY;
- }
-
- return node.getDisplayWidth();
- }
-
- function wrap(text: any, padding: boolean) {
- text.each(function() {
- const text = d3.select(this),
- words = text.text().split(/[_ ]+/).reverse(),
- lineHeight = 1.1, // ems
- x = parseInt(text.attr("x"), 10),
- y = parseInt(text.attr("y"), 10),
- //dy = parseFloat(text.attr("dy")),
- dy = 0.0;
- let word;
- let wordWrapWidth = parseInt(text.attr("eagle-wrap-width"), 10);
- let line : string[] = [];
- let tspan = text.text(null).append("tspan").attr("x", x).attr("y", y).attr("dy", dy + "em");
- $(this).attr('transform','translate(0,-3)');
- let lineNumber = 0;
-
- if (padding){
- wordWrapWidth = wordWrapWidth - x - x;
- }
-
- while (word = words.pop()) {
- line.push(word);
- tspan.text(line.join(" "));
- if (tspan.node().getComputedTextLength() > wordWrapWidth) {
- line.pop();
- tspan.text(line.join(" "));
- line = [word];
- tspan = text.append("tspan").attr("x", x).attr("y", y).attr("dy", ++lineNumber * lineHeight + dy + "em").text(word);
- $(this).attr('transform','translate(0,-15)')
- }
- }
- });
- }
-
- // performance
- const elapsedTime = performance.now() - startTime;
- if (elapsedTime > eagle.rendererFrameMax){eagle.rendererFrameMax = elapsedTime;}
- eagle.rendererFrameDisplay("render " + elapsedTime.toFixed(2) + "ms (max " + eagle.rendererFrameMax.toFixed(2) + "ms) Renders " + eagle.rendererFrameCountRender + " Ticks " + eagle.rendererFrameCountTick);
-}
diff --git a/src/graphConfig.ts b/src/graphConfig.ts
new file mode 100644
index 000000000..4112d3bc9
--- /dev/null
+++ b/src/graphConfig.ts
@@ -0,0 +1,121 @@
+const colors: { name: string; color: string; }[] = [
+ {
+ //node colours
+ name: 'bodyBorder',
+ color: '#2e3192'
+ },{
+ name: 'branchBg',
+ color: '#dcdee2'
+ },{
+ name: 'constructBg',
+ color: '#05142912'
+ },{
+ name: 'embeddedApp',
+ color: '#dcdee2'
+ },{
+ name: 'constructIcon',
+ color: '#0000000f'
+ },{
+ name: 'graphText',
+ color: 'black'
+ },{
+ name: 'nodeBg',
+ color: 'white'
+ },{
+ name: 'nodeInputPort',
+ color: '#2bb673'
+ },{
+ name: 'nodeOutputPort',
+ color: '#fbb040'
+ },{
+ name: 'nodeUtilPort',
+ color: '#6fa7f1'
+ },{
+ name: 'selectBackground',
+ color: '#b4d4ff'
+ },{
+ name: 'selectConstructBackground',
+ color: '#85b9ff94'
+ },{
+
+ //edge colours
+ name: 'edgeDefault',
+ color: '#58595b'
+ },{
+ name: 'edgeDefaultSelected',
+ color: '#4247df'
+ },{
+ name: 'commentEdge',
+ color: '#7c7e81'
+ },{
+ name: 'edgeValid',
+ color: '#32cd32'
+ },{
+ name: 'edgeWarning',
+ color: '#ffa500'
+ },{
+ name: 'edgeWarningSelected',
+ color: '#4247df'
+ },{
+ name: 'edgeInvalid',
+ color: '#ff0000'
+ },{
+ name: 'edgeInvalidSelected',
+ color: '#4247df'
+ },{
+ name: 'edgeEvent',
+ color: '#a6a6fe'
+ },{
+ name: 'edgeEventSelected',
+ color: '#4247df'
+ },{
+ name: 'edgeAutoCompleteSuggestion',
+ color: '#dbcfe1'
+ },{
+ name: 'edgeAutoComplete',
+ color: '#9c3bca'
+ },{
+ name: 'edgeClosesLoop',
+ color: '#58595b'
+ },{
+ name: 'edgeClosesLoopSelected',
+ color: '#4247df'
+ }
+]
+
+export class GraphConfig {
+
+ // graph behaviour
+ public static readonly NODE_SUGGESTION_RADIUS = 300
+ public static readonly NODE_SUGGESTION_SNAP_RADIUS = 150
+ public static readonly PORT_MINIMUM_DISTANCE = 14
+
+ //node settings
+ // TODO: could move to CategoryData?
+ public static readonly NORMAL_NODE_RADIUS : number = 25;
+ public static readonly BRANCH_NODE_RADIUS : number = 44;
+ public static readonly CONSTRUCT_NODE_RADIUS: number = 200;
+ public static readonly MINIMUM_CONSTRUCT_RADIUS : number = 44;
+
+ //edge settings
+ public static readonly EDGE_ARROW_SIZE : number = 8;
+ public static readonly EDGE_DISTANCE_ARROW_VISIBILITY : number = 100; //how loong does an edge have to be to show the direction arrows
+ public static readonly SWITCH_TO_STRAIGHT_EDGE_MULTIPLIER : number = 5 //this affect the cutoff distance between nodes required to switch between a straight and curved edge
+
+ // when creating a new construct to enclose a selection, or shrinking a node to enclose its children,
+ // this is the default margin that should be left on each side
+ public static readonly CONSTRUCT_MARGIN: number = 30;
+ public static readonly CONSTRUCT_DRAG_OUT_DISTANCE: number = 200;
+
+ static getColor = (name:string) : string => {
+ let result = 'red'
+ for (const color of colors) {
+ if(color.name === name){
+ result = color.color
+ }else{
+ continue
+ }
+ }
+ return result
+ }
+}
\ No newline at end of file
diff --git a/src/main.ts b/src/main.ts
index 93625d276..0b689812d 100644
--- a/src/main.ts
+++ b/src/main.ts
@@ -28,15 +28,20 @@ import "jqueryMigrate";
import "jqueryui";
import * as bootstrap from 'bootstrap';
-import {UiMode, UiModeSystem, SettingData} from './UiModes';
+import { ActionList } from "./ActionList";
+import { ActionMessage } from "./Action";
import {Category} from './Category';
import {CategoryData} from './CategoryData';
import {Config} from './Config';
import {Daliuge} from './Daliuge';
import {Eagle} from './Eagle';
-import {Errors} from './Errors';
import {GitHub} from './GitHub';
import {GitLab} from './GitLab';
+<<<<<<< HEAD
+import { GraphChecker } from "./GraphChecker";
+=======
+import { GraphRenderer } from "./GraphRenderer";
+>>>>>>> html-graph-renderer
import {Hierarchy} from './Hierarchy';
import {RightClick} from './RightClick';
import {QuickActions} from './QuickActions';
@@ -45,6 +50,7 @@ import {LogicalGraph} from './LogicalGraph';
import {Modals} from './Modals';
import {Palette} from './Palette';
import {Setting} from './Setting';
+import {UiMode, UiModeSystem, SettingData} from './UiModes';
import {Utils} from './Utils';
import {Repositories} from './Repositories';
import {Repository} from './Repository';
@@ -52,6 +58,7 @@ import {RepositoryFile} from './RepositoryFile';
import {ParameterTable} from "./ParameterTable";
import {SideWindow} from "./SideWindow";
import {TutorialSystem} from "./Tutorial";
+import {GraphConfig} from "./graphConfig";
import * as quickStart from './tutorials/quickStart'
import * as graphBuilding from './tutorials/graphBuilding'
@@ -68,11 +75,12 @@ $(function(){
// add eagle to the window object, slightly hacky, but useful for debugging
(window).eagle = eagle;
+ (window).ActionList = ActionList;
(window).Category = Category;
(window).Config = Config;
(window).Daliuge = Daliuge;
(window).Eagle = Eagle;
- (window).Errors = Errors;
+ (window).GraphChecker = GraphChecker;
(window).Hierarchy = Hierarchy;
(window).ParameterTable = ParameterTable;
(window).Repositories = Repositories;
@@ -80,11 +88,17 @@ $(function(){
(window).Setting = Setting;
(window).SideWindow = SideWindow;
(window).TutorialSystem = TutorialSystem;
+<<<<<<< HEAD
+ (window).ActionMessage = ActionMessage;
+=======
+ (window).GraphRenderer = GraphRenderer;
+>>>>>>> html-graph-renderer
(window).UiModeSystem = UiModeSystem;
(window).Utils = Utils;
(window).KeyboardShortcut = KeyboardShortcut;
(window).QuickActions = QuickActions;
(window).Modals = Modals;
+ (window).GraphConfig = GraphConfig;
ko.options.deferUpdates = true;
ko.applyBindings(eagle);
@@ -123,32 +137,67 @@ $(function(){
GitLab.loadRepoList();
}
- // load the default palette
+ // build list of auto-load files
+ const autoLoadFiles: RepositoryFile[] = [];
+
+ // if url contains file to auto-load, add to auto-load files list
+ const urlAutoLoadFile = parseUrlAutoLoad();
+ if (urlAutoLoadFile !== null){
+ autoLoadFiles.push(urlAutoLoadFile);
+ }
+
+ // if 'load default palette' setting is set
if (Setting.findValue(Setting.OPEN_DEFAULT_PALETTE)){
- eagle.loadPalettes([
- {name:"Builtin Components", filename:Daliuge.PALETTE_URL, readonly:true},
- {name:Palette.DYNAMIC_PALETTE_NAME, filename:Daliuge.TEMPLATE_URL, readonly:true}
- ], (errorsWarnings: Errors.ErrorsWarnings, palettes: Palette[]):void => {
- const showErrors: boolean = Setting.findValue(Setting.SHOW_FILE_LOADING_ERRORS);
-
- // display of errors if setting is true
- if (showErrors && (Errors.hasErrors(errorsWarnings) || Errors.hasWarnings(errorsWarnings))){
- // add warnings/errors to the arrays
- eagle.loadingErrors(errorsWarnings.errors);
- eagle.loadingWarnings(errorsWarnings.warnings);
-
- eagle.errorsMode(Setting.ErrorsMode.Loading);
- Utils.showErrorsModal("Loading File");
+ autoLoadFiles.push(new RepositoryFile(new Repository(Eagle.RepositoryService.Url, "", "", false), Daliuge.PALETTE_URL, "Builtin Components"));
+ autoLoadFiles.push(new RepositoryFile(new Repository(Eagle.RepositoryService.Url, "", "", false), Daliuge.TEMPLATE_URL, Palette.DYNAMIC_PALETTE_NAME));
+ }
+
+ // load the default palette
+ eagle.loadFiles(autoLoadFiles, (palettes: {file: RepositoryFile, palette: Palette, errors: ActionMessage[]}[], logicalGraphs: {file: RepositoryFile, logicalGraph: LogicalGraph, errors: ActionMessage[]}[]):void => {
+ const loads : {file: RepositoryFile, errors: ActionMessage[]}[] = [];
+
+ // handle palettes
+ for (const p of palettes){
+ loads.push({file:p.file, errors: p.errors});
+ if (p.palette !== null){
+ p.palette.fileInfo().name = p.file.name;
+ eagle.remotePaletteLoaded(p.file, p.palette);
}
+ }
- for (const palette of palettes){
- if (palette !== null){
- eagle.palettes.push(palette);
- }
+ // handle graphs
+ for (const g of logicalGraphs){
+ loads.push({file:g.file, errors: g.errors});
+ if (g.logicalGraph !== null){
+ eagle.remoteGraphLoaded(g.file, g.logicalGraph);
}
+ }
+
+ // handle errors
+ eagle.handleLoadingErrors(loads);
+
+ // show the left window if palettes were loaded
+ // TODO: is this required? try removing
+ if (palettes.length > 0){
eagle.leftWindow().shown(true);
- });
- }
+ }
+
+ if (logicalGraphs.length > 0){
+ // center graph
+ eagle.centerGraph();
+
+ // check graph
+ eagle.graphChecker().check();
+
+ // HACK: we assume the urlAutoLoadFile is the graph file, may not be the case!
+
+ // push undo snapshot
+ eagle.undo().pushSnapshot(eagle, "Loaded " + urlAutoLoadFile.name);
+
+ // if the fileType is the same as the current mode, update the activeFileInfo with details of the repository the file was loaded from
+ eagle.updateLogicalGraphFileInfo(urlAutoLoadFile);
+ }
+ });
// set other state based on settings values
if (Setting.findValue(Setting.SNAP_TO_GRID)){
@@ -171,15 +220,17 @@ $(function(){
document.onkeydown = KeyboardShortcut.processKey;
document.onkeyup = KeyboardShortcut.processKey;
+<<<<<<< HEAD
// HACK: without this global wheel event handler, d3 does not receive zoom events
// not sure why, this wasn't always the case
document.onwheel = () => {return;};
-
+=======
// auto load the file
autoLoad(eagle);
+>>>>>>> html-graph-renderer
// auto load a tutorial, if specified on the url
- autoTutorial(eagle);
+ parseUrlAutoTutorial();
//hides the dropdown navbar elements when stopping hovering over the element
$(".dropdown-menu").mouseleave(function(){
@@ -190,9 +241,9 @@ $(function(){
$('.modal').on('hidden.bs.modal', function () {
$('.modal-dialog').css({"left":"0px", "top":"0px"})
$("#editFieldModal textarea").attr('style','')
- $("#errorsModalAccordion").parent().parent().attr('style','')
+ $("#checkGraphModalAccordion").parent().parent().attr('style','')
- //reset parameter table selecction
+ //reset parameter table selection
ParameterTable.resetSelection()
});
@@ -274,9 +325,17 @@ $(function(){
break;
}
}
+
+
+ //initiating all the eagle ui when the graph is ready
+ $('#logicalGraph').show(200)
+ $('.leftWindow').show(200)
+ $('.rightWindow').show(200)
+ $('#graphNameWrapper').show(200)
+ $('nav.navbar').show(200).css('display', 'flex');
});
-function autoLoad(eagle: Eagle) {
+function parseUrlAutoLoad(): RepositoryFile {
const service = (window).auto_load_service;
const repository = (window).auto_load_repository;
const branch = (window).auto_load_branch;
@@ -290,30 +349,30 @@ function autoLoad(eagle: Eagle) {
// skip unknown services
if (typeof realService === "undefined" || realService === Eagle.RepositoryService.Unknown){
console.log("No auto load. Service Unknown");
- return;
+ return null;
}
// skip empty strings
if ([Eagle.RepositoryService.GitHub, Eagle.RepositoryService.GitLab].includes(realService) && (repository === "" || branch === "" || filename === "")){
console.log("No auto load. Repository, branch or filename not specified");
- return;
+ return null;
}
// skip url if url is not specified
if (realService === Eagle.RepositoryService.Url && url === ""){
console.log("No auto load. Url not specified");
- return;
+ return null;
}
// load
if (service === Eagle.RepositoryService.Url){
- Repositories.selectFile(new RepositoryFile(new Repository(service, "", "", false), "", url));
+ return new RepositoryFile(new Repository(service, "", "", false), url, "");
} else {
- Repositories.selectFile(new RepositoryFile(new Repository(service, repository, branch, false), path, filename));
+ return new RepositoryFile(new Repository(service, repository, branch, false), path, filename);
}
}
-function autoTutorial(eagle: Eagle){
+function parseUrlAutoTutorial(){
const urlParams = new URLSearchParams(window.location.search);
const tutorialName = urlParams.get('tutorial');
diff --git a/src/require-config.ts b/src/require-config.ts
index aef63f2d0..01da8b77b 100644
--- a/src/require-config.ts
+++ b/src/require-config.ts
@@ -9,12 +9,10 @@ require.config({
"jqueryui": "./static/externals/jquery-ui.min",
"bootstrap": "./static/externals/bootstrap.bundle.min",
"bootstrap-notify": "./static/externals/bootstrap-notify.min",
- "d3": "./static/externals/d3.v5.min",
"ajv": "./static/externals/ajv.min",
"showdown": "./static/externals/showdown.min",
"bindingHandlers/readonly":"./static/built/bindingHandlers/readonly",
"bindingHandlers/disabled":"./static/built/bindingHandlers/disabled",
- "bindingHandlers/graphRenderer":"./static/built/bindingHandlers/graphRenderer",
"bindingHandlers/eagleTooltip":"./static/built/bindingHandlers/eagleTooltip",
"bindingHandlers/eagleRightClick":"./static/built/bindingHandlers/eagleRightClick",
"components":"./static/built/components",
@@ -26,7 +24,9 @@ require.config({
"Eagle": "./static/built/Eagle",
"Utils": "./static/built/Utils",
"Modals": "./static/built/Modals",
+ "GraphChecker": "./static/built/GraphChecker",
"GraphUpdater": "./static/built/GraphUpdater",
+ "GraphRenderer": "./static/built/GraphRenderer",
"Repository": "./static/built/Repository",
"RepositoryFolder": "./static/built/RepositoryFolder",
"RepositoryFile": "./static/built/RepositoryFile",
@@ -56,7 +56,13 @@ require.config({
"Hierarchy": "./static/built/Hierarchy",
"RightClick": "./static/built/RightClick",
"Repositories": "./static/built/Repositories",
- "ParameterTable": "./static/built/ParameterTable"
+ "ParameterTable": "./static/built/ParameterTable",
+<<<<<<< HEAD
+ "Action": "./static/built/Action",
+ "ActionList": "./static/built/ActionList"
+=======
+ "graphConfig": "./static/built/graphConfig"
+>>>>>>> html-graph-renderer
},
shim: {
"bootstrap": {
diff --git a/src/tutorials/graphBuilding.ts b/src/tutorials/graphBuilding.ts
index fab621c9c..3f179ae0c 100644
--- a/src/tutorials/graphBuilding.ts
+++ b/src/tutorials/graphBuilding.ts
@@ -1,3 +1,4 @@
+<<<<<<< HEAD
import {Tutorial, TutorialStep, TutorialSystem} from '../Tutorial';
import {Eagle} from '../Eagle';
@@ -120,15 +121,15 @@ newTut.newTutStep("Connecting nodes", "Click and hold the output Port of the
newTut.newTutStep("Graph Errors and warnings", "Notice that we have a few graph warnings detected. Click here to view them", function(){return $("#checkGraphWarnings")})
.setType(TutorialStep.Type.Press)
-newTut.newTutStep("Graph Errors and warnings", "This modal may aid you in troubleshooting graphs. In this case these errors are all port type errors. Eagle can automatically fix errors such as these for you. To do this you can press 'F' in the graph or click on 'Fix All'", function(){return $("#errorModalFixAll")})
+newTut.newTutStep("Graph Errors and warnings", "This modal may aid you in troubleshooting graphs. In this case these errors are all port type errors. Eagle can automatically fix errors such as these for you. To do this you can press 'F' in the graph or click on 'Fix All'", function(){return $("#checkGraphModalFixAll")})
.setType(TutorialStep.Type.Press)
.setWaitType(TutorialStep.Wait.Modal)
-.setAlternateHighlightTargetFunc(function(){return $("#errorModalFixAll").parent().parent()})
-.setBackPreFunction(function(){$('#errorsModal').modal('show')})
+.setAlternateHighlightTargetFunc(function(){return $("#checkGraphModalFixAll").parent().parent()})
+.setBackPreFunction(function(eagle:Eagle){eagle.openCheckGraphModal()})
newTut.newTutStep("Saving a Graph", "Options to save your graph are available in the graph menu Click on 'Graph' to continue.", function(){return $("#navbarDropdownGraph")})
.setType(TutorialStep.Type.Press)
-.setPreFunction(function(eagle:Eagle){eagle.closeErrorsModal()})
+.setPreFunction(function(eagle:Eagle){eagle.closeCheckGraphModal()})
.setBackPreFunction(function(){$('.forceShow').removeClass('forceShow');$(".dropdown-toggle").removeClass("show");$(".dropdown-menu").removeClass("show")}) //allowing the graph navbar dropdown to hide
newTut.newTutStep("Saving a Graph", "You are able to download the graph in the 'local storage' section, or save the graph into your github repository under 'git storage'", function(){return $("#navbarDropdownGraph").parent().find('.dropdown-menu')})
@@ -137,3 +138,144 @@ newTut.newTutStep("Saving a Graph", "You are able to download the graph in the '
newTut.newTutStep("Well Done!", "You have completed the Hello world graph creation tutorial! Be sure to check our online documentation for additional help and guidance.", function(){return $("#logicalGraphParent")})
.setPreFunction(function(){$('.forceShow').removeClass('forceShow')}) //allowing the graph navbar dropdown to hide
+=======
+import { Eagle } from '../Eagle';
+import { RightClick } from '../RightClick';
+import { TutorialStep, TutorialSystem } from '../Tutorial';
+
+
+const newTut = TutorialSystem.newTutorial('Graph Building', 'An introduction to graph building.')
+
+newTut.newTutStep("Welcome to the Graph Building tutorial!", "You can quit this tutorial anytime using the 'exit' button or ESC key. Please refer to the main documentation for in-depth information.", function(){return $("#logicalGraphParent")})
+
+newTut.newTutStep("Creating a New Graph", "First we are going to create a new graph. Options for creating, loading and saving graphs can be found here. Click on 'Graph' to continue.", function(){return $("#navbarDropdownGraph")})
+.setType(TutorialStep.Type.Press)
+.setBackPreFunction(function(){$('.forceShow').removeClass('forceShow');$('.modal').modal("hide");}) //allowing the graph navbar dropdown to hide
+
+newTut.newTutStep("Creating a New Graph", "Click on 'New'.", function(){return $("#navbarDropdownGraph").parent().find('.dropdown-item').first()})
+.setType(TutorialStep.Type.Press)
+.setPreFunction(function(){TutorialSystem.activeTutCurrentStep.getTargetFunc()().parent().addClass('forceShow')}) //keeping the navbar graph doropdown open
+.setBackPreFunction(function(){$("#navbarDropdownGraph").parent().find('#createNewGraph').removeClass('forceShow')})//allowing the 'new' drop drop down section to close
+.setBackSkip(true)
+
+newTut.newTutStep("Creating a New Graph", "Click on 'Create new graph'", function(){return $("#navbarDropdownGraph").parent().find('#createNewGraph')})
+.setType(TutorialStep.Type.Press)
+.setPreFunction(function(){TutorialSystem.activeTutCurrentStep.getTargetFunc()().parent().addClass('forceShow')})//keeping the 'new' drop drop down section open as well
+.setBackPreFunction(function(){$("#navbarDropdownGraph").parent().find('.dropdown-item').first().parent().addClass('forceShow');TutorialSystem.activeTutCurrentStep.getTargetFunc()().parent().addClass('forceShow')})//force showing both of the navbar graph drop downs
+.setBackSkip(true)
+
+newTut.newTutStep("Creating a new graph", "Then just give it a name and press enter", function(){return $("#inputModalInput")})
+.setWaitType(TutorialStep.Wait.Modal)
+.setType(TutorialStep.Type.Input)
+.setPreFunction(function(){$('.forceShow').removeClass('forceShow')}) //allowing the graph navbar dropdown to hide
+.setBackSkip(true)
+
+newTut.newTutStep("Creating a new graph", "And 'Ok' to save!", function(){return $("#inputModal .affermativeBtn")})
+.setWaitType(TutorialStep.Wait.Modal)
+.setType(TutorialStep.Type.Press)
+.setBackSkip(true)
+
+newTut.newTutStep("Graph Model Data", "This button brings up the 'Graph Modal Data' which allows you to add a description for your graph. Try clicking it now to try it out", function(){return $("#openGraphModelDataModal")})
+.setType(TutorialStep.Type.Press)
+.setBackPreFunction(function(){$('.modal').modal("hide");}) //hiding open modals
+
+newTut.newTutStep("Editing Graph Descriptions", "You are able to enter a simple first glance and a more detailed decription in addition to description nodes in the graph, should you need it.", function(){return $("#modelDataDescription")})
+.setWaitType(TutorialStep.Wait.Modal)
+
+newTut.newTutStep("Other Graph Information", "Most of the other information is automatically filled out when saving a graph, such as the version of EAGLE used for creating it.", function(){return $("#modelDataEagleVersion")})
+.setWaitType(TutorialStep.Wait.Modal)
+
+newTut.newTutStep("Close the Modal", "Press OK to close the modal and continue the Tutorial.", function(){return $("#modelDataModalOKButton")})
+.setWaitType(TutorialStep.Wait.Modal)
+.setType(TutorialStep.Type.Press)
+.setBackPreFunction(function(){$('#modelDataModal').modal('show')})
+
+newTut.newTutStep("Palette Components", "Each of these components in a palette performs a function that can be used in your graph", function(){return $("#palette_0_HelloWorldApp")})
+
+newTut.newTutStep("Adding base components into the graph", "To add one into the graph, simply click on the icon or drag the component into the graph. Click on the icon to continue.", function(){return $("#addPaletteNodeHelloWorldApp")})
+.setType(TutorialStep.Type.Press)
+
+newTut.newTutStep("Graph Nodes", "Once added into your graph, the component is in your own instance. This means you can adjust its parameters and they will be saved with the graph. Click on the node to select it.", function(){return TutorialSystem.initiateFindGraphNodeIdByNodeName('HelloWorldApp')})
+.setType(TutorialStep.Type.Condition)
+.setWaitType(TutorialStep.Wait.Element)
+.setConditionFunction(function(){return TutorialSystem.isRequestedNodeSelected('HelloWorldApp')})
+.setPreFunction(function(eagle:Eagle){eagle.resetEditor()})
+.setBackPreFunction(function(eagle:Eagle){eagle.resetEditor()})
+
+newTut.newTutStep("Editing Components", "The inspector panel provides access to the complete set of specifications of a component. The Component Parameters are settings pertaining to the DALiuGE component wrapper, the Application Arguments are settings exposed by the actual application code.", function(){return $("#rightWindowContainer")})
+.setPreFunction(function(eagle:Eagle){eagle.rightWindow().mode(Eagle.RightWindowMode.Inspector)})
+
+newTut.newTutStep("Click to open", "Click to open the node fields table and continue.", function(){return $("#openNodeFieldsTable")})
+.setWaitType(TutorialStep.Wait.Element)
+.setType(TutorialStep.Type.Press)
+.setBackPreFunction(function(){$('#parameterTableModal').modal('hide')})
+
+newTut.newTutStep("Enter a Name", "In case of this hello world app we can change who we are greeting. Enter a name and press enter to continue.", function(){return $('.tableFieldStringValueInput').first()})
+.setType(TutorialStep.Type.Input)
+.setWaitType(TutorialStep.Wait.Delay)
+.setDelayAmount(700)
+
+newTut.newTutStep("Key Attributes", "You can flag important parameters and attributes of a graph as 'Key Attributes'. These are then all available for editing in one location. Click on the heart to flag this argument as key attribute.", function(){return $('.column_KeyAttr button').first()})
+.setType(TutorialStep.Type.Press)
+.setBackPreFunction(function(){$('#openNodeFieldsTable').click()})
+.setWaitType(TutorialStep.Wait.Delay)
+.setDelayAmount(700)
+
+newTut.newTutStep("Key Attributes", "You can view the key attributes of a graph by opening the key attributes table located here.", function(){return $("#openKeyParameterTable")})
+.setPreFunction(function(){$('#parameterTableModal').modal('hide')})
+
+newTut.newTutStep("Right Click to add nodes", "There are also various right click options available in EAGLE. Right click on the graph to bring up a 'add node' menu", function(){return $("#logicalGraphParent")})
+.setType(TutorialStep.Type.Condition)
+.setConditionFunction(function(){ if($('#customContextMenu').length){return true}else{return false}})
+.setPreFunction(function(){$('.modal').modal("hide");}) //hiding open moddals
+.setBackPreFunction(function(){RightClick.closeCustomContextMenu(true);})
+
+newTut.newTutStep("Graph Context menu", "all of your loaded palettes and their contents will appear here", function(){return $("#rightClickPaletteList")})
+.setPreFunction(function(){$("#customContextMenu").addClass('forceShow')})
+.setWaitType(TutorialStep.Wait.Element)
+.setBackSkip(true)
+
+newTut.newTutStep("Quickly adding nodes", "If you already know what you want you can quickly add it by using the search bar. Search for 'file' now and press enter", function(){return $("#rightClickSearchBar")})
+.setType(TutorialStep.Type.Input)
+.setExpectedInput('file')
+.setBackSkip(true)
+
+newTut.newTutStep("Connecting nodes", "To save the output of the hello world app onto the file we need to draw an edge from the 'Hello World' node's output port to the 'File' node's input port.", function(){return $("#logicalGraphParent")})
+
+newTut.newTutStep("Node Ports", "This is the output port of the Hello world app, Output ports are always shown in orange and are initially on the right side of the node.", function(){return $('#portContainer .' + TutorialSystem.initiateSimpleFindGraphNodeIdByNodeName('HelloWorldApp')+' .outputPort')})
+.setPreFunction(function(eagle:Eagle){eagle.resetEditor()})
+.setBackPreFunction(function(eagle:Eagle){eagle.resetEditor()})
+.setAlternateHighlightTargetFunc(function(){return TutorialSystem.initiateFindGraphNodeIdByNodeName('HelloWorldApp')})
+.setWaitType(TutorialStep.Wait.Element)
+
+newTut.newTutStep("Node Ports", "And this is the input port for the file storage node, Iutput ports are always shown in green and are initially on the left side of the node.", function(){return $('#portContainer .' + TutorialSystem.initiateSimpleFindGraphNodeIdByNodeName('File')+' .inputPort')})
+.setPreFunction(function(eagle:Eagle){eagle.resetEditor()})
+.setBackPreFunction(function(eagle:Eagle){eagle.resetEditor()})
+.setAlternateHighlightTargetFunc(function(){return TutorialSystem.initiateFindGraphNodeIdByNodeName('File')})
+.setWaitType(TutorialStep.Wait.Element)
+
+newTut.newTutStep("Connecting nodes", "Click and hold the output Port of the hello world app and drag over to the file node's input port, then release.", function(){return $('#portContainer .' + TutorialSystem.initiateSimpleFindGraphNodeIdByNodeName('HelloWorldApp')+' .outputPort')})
+.setType(TutorialStep.Type.Condition)
+.setAlternateHighlightTargetFunc(function(){return $("#logicalGraphParent")})
+.setConditionFunction(function(eagle:Eagle){if(eagle.logicalGraph().getEdges().length != 0){return true}else{return false}}) //check if there are any edges present in the graph
+
+newTut.newTutStep("Graph Errors and warnings", "This is the error checking system, it is showing a checkmark, so we did everything correctly. If there are errors in the graph you are able to troubleshoot them by clicking here.", function(){return $("#checkGraphDone")})
+
+// newTut.newTutStep("Graph Errors and warnings", "This modal may aid you in troubleshooting graphs. In this case these errors are all port type errors. Eagle can automatically fix errors such as these for you. To do this you can press 'F' in the graph or click on 'Fix All'", function(){return $("#errorModalFixAll")})
+// .setType(TutorialStep.Type.Press)
+// .setWaitType(TutorialStep.Wait.Modal)
+// .setAlternateHighlightTargetFunc(function(){return $("#errorModalFixAll").parent().parent()})
+// .setBackPreFunction(function(){$('#errorsModal').modal('show')})
+
+newTut.newTutStep("Saving a Graph", "Options to save your graph are available in the graph menu Click on 'Graph' to continue.", function(){return $("#navbarDropdownGraph")})
+.setType(TutorialStep.Type.Press)
+.setPreFunction(function(eagle:Eagle){eagle.closeErrorsModal()})
+.setBackPreFunction(function(){$('.forceShow').removeClass('forceShow');$(".dropdown-toggle").removeClass("show");$(".dropdown-menu").removeClass("show")}) //allowing the graph navbar dropdown to hide
+
+newTut.newTutStep("Saving a Graph", "You are able to download the graph in the 'local storage' section, or save the graph into your github repository under 'git storage'", function(){return $("#navbarDropdownGraph").parent().find('.dropdown-menu')})
+.setPreFunction(function(){TutorialSystem.activeTutCurrentStep.getTargetFunc()().addClass('forceShow')}) //keeping the navbar graph doropdown open
+.setBackSkip(true)
+
+newTut.newTutStep("Well Done!", "You have completed the Hello world graph creation tutorial! Be sure to check our online documentation for additional help and guidance.", function(){return $("#logicalGraphParent")})
+.setPreFunction(function(){$('.forceShow').removeClass('forceShow')}) //allowing the graph navbar dropdown to hide
+>>>>>>> html-graph-renderer
diff --git a/static/base.css b/static/base.css
index 9d95d33a2..d957015b8 100644
--- a/static/base.css
+++ b/static/base.css
@@ -195,13 +195,14 @@ color: #00bb00;}
.rightWindow {
background-color: rgba(214, 219, 239, 0.95);
- z-index: 1;
+ z-index: 10;
position:absolute;
top: 0px;
right: -300px;
height: 100%;
transition: right 0.25s linear;
border-left: 2px solid rgba(0, 36, 74, 0.95);
+ display: none;
}
.rightWindow.show {
@@ -410,7 +411,7 @@ color: #00bb00;}
overflow-y: auto;
}
-#parameterTableModal .modal-dialog .typesInput, #edintFieldModal .modal-dialog .typesInput{
+#parameterTableModal .modal-dialog .typesInput, #editFieldModal .modal-dialog .typesInput{
padding: 0px;
height: 35px;
width: 100%;
@@ -945,6 +946,7 @@ td:first-child input {
height: 100%;
transition: left 0.25s linear;
padding:0px 2px 0px 6px;
+ display: none;
}
/* width */
@@ -1232,29 +1234,29 @@ select.form-control{
border: 1px solid #c2c8dc;
}
-#errorsModalAccordion .accordion-button{
+#checkGraphModalAccordion .accordion-button, #actionListModalAccordion .accordion-button {
color: white;
background-color: #4a6b8f;
}
-#errorsModalAccordion .accordion-button::after{
+#checkGraphModalAccordion .accordion-button::after, #actionListModalAccordion .accordion-button::after {
background-image: url("data:image/svg+xml,") !important;
}
-#errorsModalAccordion .accordion-button:focus{
+#checkGraphModalAccordion .accordion-button:focus, #actionListModalAccordion .accordion-button:focus {
border-color: transparent;
box-shadow: none !important;
}
-#errorsModalAccordion .accordion-item{
+#checkGraphModalAccordion .accordion-item, #actionListModalAccordion .accordion-item {
margin-bottom: 10px;
}
-#errorsModalErrorCount{
+#checkGraphModalErrorCount, #actionListModalErrorCount {
color: #dc3545;
}
-#errorsModalWarningCount{
+#checkGraphModalWarningCount, #actionListModalWarningCount {
color: #ffc107;
}
@@ -1872,20 +1874,13 @@ select.form-control{
height: 100%;
}
-#logicalGraphD3Div {
+#logicalGraph {
position: relative;
width: 100%;
height: 100%;
background-color: white;
}
-#paletteD3Div {
- position: relative;
- width: 100%;
- height: 100%;
- background-color: #dddddd;
-}
-
.navbar-btn i.material-icons.md-18 {
position: relative;
top: 3px;
@@ -2070,6 +2065,7 @@ ul.nav.navbat-nav .btn-outline-secondary:hover{
padding-left:10px;
background: rgb(0,64,133);
background: linear-gradient(90deg, rgba(0,64,133,1) 0%, rgba(0,52,107,1) 20%, rgba(0,35,73,1) 100%);
+ display:none;
}
.navbar a{
@@ -2102,6 +2098,11 @@ ul.nav.navbat-nav .btn-outline-secondary:hover{
white-space: pre;
}
+ #eagleAndVersion sup{
+ font-family: 'EB Garamond', serif;
+ font-size: 1em;
+ }
+
#brandEagleIcon{
margin-left:4px;
}
@@ -2362,7 +2363,7 @@ ul.nav.navbar-nav .dropdown-item:hover{
#graphNameWrapper{
top: 56px;
- display: block;
+ display: none;
width: 100%;
height:26px;
background-color: #002349;
@@ -2661,7 +2662,7 @@ palette-component input.form-control.selected {
}
.tooltip-inner h3 {
- font-size: 1.2rem;
+ font-size: 18px;
font-weight: 600;
margin: 0px;
}
@@ -2692,12 +2693,12 @@ palette-component input.form-control.selected {
transition: 0s visibility;
visibility: visible;
max-height: 400px;
- max-width:300px;
+ max-width: 300px;
overflow: auto;
text-align: left;
}
-#errorsModal ul.list-group .list-group-item span {
+#checkGraphModal ul.list-group .list-group-item span, #actionListModal ul.list-group .list-group-item span {
max-width: 80%;
}
diff --git a/static/components/fix.html b/static/components/fix.html
index 79a0b3014..732e8deb7 100644
--- a/static/components/fix.html
+++ b/static/components/fix.html
@@ -3,7 +3,7 @@
-
+