"use strict";
// Copyright 2012 United States Government, as represented by the Secretary of Defense, Under
// Secretary of Defense (Personnel & Readiness).
//
// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
// in compliance with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software distributed under the License
// is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
// or implied. See the License for the specific language governing permissions and limitations under
// the License.
/// vwf/view/editor creates a view interface for editor functions.
///
/// @module vwf/view/editor
/// @requires version
/// @requires vwf/view
/// @requires vwf/utility
define([
"module",
"version",
"vwf/view",
"vwf/utility",
"vwf/view/lib/ace/ace",
"jquery",
"jquery-ui",
"jquery-encoder-0.1.0"
], function (module, version, view, utility, ace, $) {
return view.load(module, {
// == Module Definition ====================================================================
initialize: function () {
var self = this;
this.ace = window.ace;
this.nodes = {};
this.scenes = {};
this.allScripts = {};
// EDITOR CLOSED --> 0
// HIERARCHY OPEN --> 1
// USER LIST OPEN --> 2
// TIMELINE OPEN --> 3
// ABOUT OPEN --> 4
this.editorView = 0;
this.editorOpen = false;
this.timelineInit = false;
this.aboutInit = false;
this.codeEditorInit = false;
this.modelsInit = false;
this.editingScript = false;
this.topdownName = '#topdown_a';
this.topdownTemp = '#topdown_b';
this.clientList = '#client_list';
this.timeline = '#time_control';
this.about = '#about_tab';
this.codeEditor = '#codeEditor_tab';
this.models = '#model_a';
this.modelsTemp = '#model_b';
this.currentNodeID = '';
this.currentModelID = '';
this.currentModelURL = '';
this.highlightedChild = '';
this.intervalTimer = 0;
this.activeCameraID = undefined;
$(document.head).append('');
document.querySelector('head').innerHTML += '';
// $('body').append('');
$('body').append(
"
");
$('#' + childIDAlpha).click(function (evt) {
drillDown.call(self, $(this).attr("data-nodeID"), nodeIDAttribute);
});
$('#children > div:last').css('border-bottom-width', '3px');
}
if (nodeID === this.kernel.application() && childName === 'camera') {
this.activeCameraID = childID;
}
if (nodeID === this.kernel.application()) {
// document.querySelector('a-scene').classList.add("mdc-toolbar-fixed-adjust");
document.querySelector('body').classList.add("editor-body");
}
},
createdProperty: function (nodeID, propertyName, propertyValue) {
return this.initializedProperty(nodeID, propertyName, propertyValue);
},
initializedProperty: function (nodeID, propertyName, propertyValue) {
var node = this.nodes[nodeID];
if (!node) return; // TODO: patch until full-graph sync is working; drivers should be able to assume that nodeIDs refer to valid objects
var property = node.properties[propertyName] = createProperty.call(this, node, propertyName, propertyValue);
node.properties.push(property);
},
deletedNode: function (nodeID) {
var node = this.nodes[nodeID];
node.parent.children.splice(node.parent.children.indexOf(node), 1);
delete this.nodes[nodeID];
var nodeIDAttribute = $.encoder.encodeForAlphaNumeric(nodeID); // $.encoder.encodeForHTMLAttribute("id", nodeID, true);
$('#' + nodeIDAttribute).remove();
$('#children > div:last').css('border-bottom-width', '3px');
},
//addedChild: [ /* nodeID, childID, childName */ ],
//removedChild: [ /* nodeID, childID */ ],
satProperty: function (nodeID, propertyName, propertyValue) {
var node = this.nodes[nodeID];
if (!node) return; // TODO: patch until full-graph sync is working; drivers should be able to assume that nodeIDs refer to valid objects
// It is possible for a property to have satProperty called for it without ever getting an
// initializedProperty (if that property delegated to itself or another on replication)
// Catch that case here and create the property
if (!node.properties[propertyName]) {
var property = node.properties[propertyName] = createProperty.call(this, node, propertyName, propertyValue);
node.properties.push(property);
}
if (propertyName === "activeCamera") {
if (this.nodes[propertyValue] !== undefined) {
this.activeCameraID = propertyValue;
}
}
try {
propertyValue = utility.transform(propertyValue, utility.transforms.transit);
node.properties[propertyName].value = JSON.stringify(propertyValue);
node.properties[propertyName].rawValue = propertyValue;
} catch (e) {
this.logger.warnx("satProperty", nodeID, propertyName, propertyValue,
"stringify error:", e.message);
node.properties[propertyName].value = propertyValue;
}
if ((this.editorView == 1) && (this.currentNodeID == nodeID)) {
var nodeIDAttribute = $.encoder.encodeForAlphaNumeric(nodeID); // $.encoder.encodeForHTMLAttribute("id", nodeID, true);
var propertyNameAttribute = $.encoder.encodeForAlphaNumeric("id", propertyName, true);
// No need to escape propertyValue, because .val does its own escaping
$('#input-' + nodeIDAttribute + '-' + propertyNameAttribute).val(node.properties[propertyName].getValue());
}
let propCell = document.querySelector("#currentNode #prop-"+propertyName);
if (propCell !== null){
if (propCell._currentNode == nodeID) {
propCell.value = node.properties[propertyName].getValue();
}
}
},
//gotProperty: [ /* nodeID, propertyName, propertyValue */ ],
createdMethod: function (nodeID, methodName, methodParameters, methodBody) {
var node = this.nodes[nodeID];
if (node) {
node.methods[methodName] = methodParameters;
}
},
//calledMethod: function( nodeID, methodName, methodParameters, methodValue ) {
//},
createdEvent: function (nodeID, eventName, eventParameters) {
var node = this.nodes[nodeID];
if (node) {
node.events[eventName] = eventParameters;
}
},
firedEvent: function (nodeID, eventName, eventParameters) {
if (eventName == "pointerHover") {
highlightChildInHierarchy.call(this, nodeID);
}
},
executed: function (nodeID, scriptText, scriptType) {
// var nodeScript = {
// text: scriptText,
// type: scriptType,
// };
// if ( !this.allScripts[ nodeID ] ) {
// var nodeScripts = new Array();
// nodeScripts.push(nodeScript);
// this.allScripts[ nodeID ] = nodeScripts;
// }
// else {
// this.allScripts[ nodeID ].push(nodeScript);
// }
},
//ticked: [ /* time */ ],
});
function createCellWindow(elementID, cellNode, title) {
document.querySelector('#'+elementID).$cell({
$cell: true,
$type: "div",
id: elementID,
class: 'draggable',
$init: function(){
// let draggie = new Draggabilly('.draggable', {
// handle: '.handle',
// containment: true
// });
// get all draggie elements
var draggableElems = document.querySelectorAll('.draggable');
// array of Draggabillies
var draggies = []
// init Draggabillies
for ( var i=0, len = draggableElems.length; i < len; i++ ) {
var draggableElem = draggableElems[i];
var draggie = new Draggabilly( draggableElem, {
handle: '.handle',
containment: true
});
draggies.push( draggie );
}
this.style.visibility = 'hidden';
},
$components: [
{
$cell: true,
$type: "div",
class: "handle",
$components: [
{
$cell: true,
$type: "button",
class: "mdc-button mdc-button--compact",
$text: "X",
onclick: function(e){
//self.currentNodeID = m.ID;
document.querySelector('#'+elementID).style.visibility = 'hidden';
}
},
{
$cell: true,
$type: "span",
class: "mdc-typography--title",
$text: title
}
]
},
cellNode
// { $text: "23423"}
]
}
);
}
// -- getChildByName --------------------------------------------------------------------
function getChildByName(node, childName) {
var childNode = undefined;
for (var i = 0; i < node.children.length && childNode === undefined; i++) {
if (node.children[i].name == childName) {
childNode = node.children[i];
}
}
return childNode;
};
function updateCameraProperties() {
if (this.currentNodeID == this.activeCameraID) {
if (!this.intervalTimer) {
var self = this;
this.intervalTimer = setInterval(function () { updateProperties.call(self, self.activeCameraID) }, 200);
}
}
else {
if (this.intervalTimer) {
clearInterval(this.intervalTimer);
this.intervalTimer = 0;
}
}
}
function updateProperties(nodeName) {
var nodeID = nodeName;
var properties = getProperties.call(this, this.kernel, nodeID);
for (var i in properties) {
try {
var propertyName = properties[i].prop.name;
var propertyValue = JSON.stringify(utility.transform(vwf.getProperty(nodeID, propertyName, []), utility.transforms.transit));
} catch (e) {
this.logger.warnx("satProperty", nodeID, propertyName, propertyValue, "stringify error:", e.message);
}
if (propertyValue) {
var nodeIDAttribute = $.encoder.encodeForAlphaNumeric(nodeID);
var propertyNameAttribute = $.encoder.encodeForHTMLAttribute("id", propertyName, true);
var inputElement$ = $('#input-' + nodeIDAttribute + '-' + propertyNameAttribute);
// Only update if property value input is not in focus
// If in focus, change font style to italic
if (!inputElement$.is(":focus")) {
inputElement$.val(propertyValue);
inputElement$.css("font-style", "normal");
} else {
inputElement$.css("font-style", "italic");
}
}
}
}
// -- openEditor ------------------------------------------------------------------------
function openEditor(eView) // invoke with the view as "this"
{
if (eView == 0) {
closeEditor.call(this);
}
if (this.editorView != eView) {
// Hierarchy
if (eView == 1) {
var topdownName = this.topdownName;
var topdownTemp = this.topdownTemp;
if (!this.currentNodeID) {
this.currentNodeID = vwf_view.kernel.find("", "/")[0];
}
drill.call(this, this.currentNodeID, undefined);
$(this.clientList).hide();
$(this.timeline).hide();
$(this.about).hide();
$(this.codeEditor).hide();
$(this.models).hide();
if (this.editorOpen) {
$(topdownName).hide();
$(topdownTemp).show();
}
else {
$(topdownTemp).show('slide', { direction: 'right' }, 175);
}
this.topdownName = topdownTemp;
this.topdownTemp = topdownName;
}
else if (this.editingScript) {
// Reset width if on script
this.editingScript = false;
$('#editor').animate({ 'left': "-260px" }, 175);
$('.vwf-tree').animate({ 'width': "260px" }, 175);
}
// User List
if (eView == 2) {
$(this.topdownName).hide();
$(this.topdownTemp).hide();
$(this.timeline).hide();
$(this.about).hide();
$(this.codeEditor).hide();
$(this.models).hide();
$(this.modelsTemp).hide();
showUserList.call(this);
}
// Timeline
else if (eView == 3) {
$(this.topdownName).hide();
$(this.topdownTemp).hide();
$(this.clientList).hide();
$(this.about).hide();
$(this.codeEditor).hide();
$(this.models).hide();
$(this.modelsTemp).hide();
showTimeline.call(this);
}
// About
else if (eView == 4) {
$(this.topdownName).hide();
$(this.topdownTemp).hide();
$(this.clientList).hide();
$(this.timeline).hide();
$(this.models).hide();
$(this.modelsTemp).hide();
$(this.codeEditor).hide();
showAboutTab.call(this);
}
// Models
else if (eView == 5) {
var models = this.models;
var modelsTemp = this.modelsTemp;
showModelsTab.call(this, this.currentModelID, this.currentModelURL);
$(this.topdownName).hide();
$(this.topdownTemp).hide();
$(this.clientList).hide();
$(this.timeline).hide();
$(this.about).hide();
$(this.codeEditor).hide();
if (this.editorOpen) {
$(models).hide();
$(modelsTemp).show();
}
else {
$(modelsTemp).show('slide', { direction: 'right' }, 175);
}
this.models = modelsTemp;
this.modelsTemp = models;
}
// Code Editor
else if (eView == 6) {
$(this.topdownName).hide();
$(this.topdownTemp).hide();
$(this.clientList).hide();
$(this.timeline).hide();
$(this.models).hide();
$(this.modelsTemp).hide();
$(this.about).hide();
showCodeEditorTab.call(this);
}
if (this.editorView == 0) {
$('#vwf-root').animate({ 'left': "-=260px" }, 175);
$('#editor').animate({ 'left': "-260px" }, 175);
$('#x').delay(1000).css({ 'display': 'inline' });
}
this.editorView = eView;
this.editorOpen = true;
}
}
// -- closeEditor -----------------------------------------------------------------------
function closeEditor() // invoke with the view as "this"
{
var topdownName = this.topdownName;
if (this.editorOpen && this.editorView == 1) // Hierarchy view open
{
$(topdownName).hide('slide', { direction: 'right' }, 175);
$(topdownName).empty();
$(this.clientList).hide();
$(this.timeline).hide();
$(this.about).hide();
$(this.codeEditor).hide();
$(this.models).hide();
}
else if (this.editorOpen && this.editorView == 2) // Client list open
{
$(this.clientList).hide('slide', { direction: 'right' }, 175);
$(topdownName).hide();
$(this.timeline).hide();
$(this.about).hide();
$(this.codeEditor).hide();
$(this.models).hide();
}
else if (this.editorOpen && this.editorView == 3) // Timeline open
{
$(this.timeline).hide('slide', { direction: 'right' }, 175);
$(topdownName).hide();
$(this.clientList).hide();
$(this.about).hide();
$(this.models).hide();
}
else if (this.editorOpen && this.editorView == 4) // About open
{
$(this.about).hide('slide', { direction: 'right' }, 175);
$(this.codeEditor).hide();
$(topdownName).hide();
$(this.clientList).hide();
$(this.timeline).hide();
$(this.models).hide();
}
else if (this.editorOpen && this.editorView == 5) // Models open
{
$(this.models).hide('slide', { direction: 'right' }, 175);
$(topdownName).hide();
$(this.clientList).hide();
$(this.timeline).hide();
$(this.about).hide();
$(this.codeEditor).hide();
}
else if (this.editorOpen && this.editorView == 6) // Code Editor open
{
$(this.codeEditor).hide('slide', { direction: 'right' }, 175);
$(this.about).hide();
$(topdownName).hide();
$(this.clientList).hide();
$(this.timeline).hide();
$(this.models).hide();
}
$('#vwf-root').animate({ 'left': "+=260px" }, 175);
$('#editor').animate({ 'left': "0px" }, 175);
$('#x').css({ 'display': 'none' });
this.editorView = 0;
this.editorOpen = false;
}
// -- showUserList ----------------------------------------------------------------------
function showUserList() // invoke with the view as "this"
{
var clientList = this.clientList;
viewClients.call(this);
if (!this.editorOpen) {
$(clientList).show('slide', { direction: 'right' }, 175);
}
else {
$(clientList).show();
}
}
// -- viewClients -----------------------------------------------------------------------
function viewClients() {
var self = this;
var app = window.location.pathname;
var pathSplit = app.split('/');
if (pathSplit[0] == "") {
pathSplit.shift();
}
if (pathSplit[pathSplit.length - 1] == "") {
pathSplit.pop();
}
var instIndex = pathSplit.length - 1;
if (pathSplit.length > 2) {
if (pathSplit[pathSplit.length - 2] == "load") {
instIndex = pathSplit.length - 3;
}
}
if (pathSplit.length > 3) {
if (pathSplit[pathSplit.length - 3] == "load") {
instIndex = pathSplit.length - 4;
}
}
var root = "";
for (var i = 0; i < instIndex; i++) {
if (root != "") {
root = root + "/";
}
root = root + pathSplit[i];
}
if (root.indexOf('.vwf') != -1) root = root.substring(0, root.lastIndexOf('/'));
var clients$ = $(this.clientList);
var node = this.nodes["http://vwf.example.com/clients.vwf"];
clients$.html("
Connected Clients
");
// Add node children
clients$.append("");
for (var i = 0; i < node.children.length; i++) {
var nodeChildIDAttribute = $.encoder.encodeForHTMLAttribute("id", node.children[i].ID, true);
var nodeChildIDAlpha = $.encoder.encodeForAlphaNumeric(node.children[i].ID);
var nodeChildNameHTML = $.encoder.encodeForHTML(node.children[i].name);
$('#clientsChildren').append("
" + nodeChildNameHTML + "
");
$('#' + nodeChildIDAlpha).click(function (evt) {
viewClient.call(self, $(this).attr("data-nodeID"));
});
}
// Login Information
clients$.append("");
clients$.append("");
$('#userName').keydown(function (evt) {
evt.stopPropagation();
});
$('#userName').keypress(function (evt) {
evt.stopPropagation();
});
$('#userName').keyup(function (evt) {
evt.stopPropagation();
});
$('#password').keydown(function (evt) {
evt.stopPropagation();
});
$('#password').keypress(function (evt) {
evt.stopPropagation();
});
$('#password').keyup(function (evt) {
evt.stopPropagation();
});
$('#login').click(function (evt) {
// Future call to validate username and password
//login.call(self, $('#userName').val(), $('#password').val());
var moniker = vwf_view.kernel.moniker();
var clients = vwf_view.kernel.findClients("", "/*");
var client = undefined;
for (var i = 0; i < clients.length; i++) {
if (clients[i].indexOf(moniker) != -1) {
client = clients[i];
break;
}
}
// var client = vwf_view.kernel.findClients("", "/" + moniker)[0];
if (client) {
vwf_view.kernel.setProperty(client, "displayName", $('#userName').val());
}
});
// clients$.append("");
// $('#liveeditor').click(function(evt) {
// openLiveEditor.call(self);
// });
// Save / Load
clients$.append("");
$('#fileName').keydown(function (evt) {
evt.stopPropagation();
});
$('#fileName').keypress(function (evt) {
evt.stopPropagation();
});
$('#fileName').keyup(function (evt) {
evt.stopPropagation();
});
$('#save').click(function (evt) {
saveStateAsFile.call(self, $('#fileName').val());
});
clients$.append("");
$('#fileToLoad').append("");
$.getJSON("/" + root + "/listallsaves", function (data) {
$.each(data, function (key, value) {
var applicationName = value['applicationpath'].split("/");
if (applicationName.length > 0) {
applicationName = applicationName[applicationName.length - 1];
}
if (applicationName.length > 0) {
applicationName = applicationName.charAt(0).toUpperCase() + applicationName.slice(1);
}
if (value['latestsave']) {
$('#fileToLoad').append("");
}
else {
$('#fileToLoad').append("");
}
});
});
clients$.append("");
$('#load').click(function (evt) {
loadSavedState.call(self, $('#fileToLoad').val(), $('#fileToLoad').find(':selected').attr('applicationpath'), $('#fileToLoad').find(':selected').attr('revision'));
});
}
// -- editor ---
function openLiveEditor(value) {
//this.liveditor = document.createElement('div');
//this.liveditor.setAttribute('id', "editorlive");
// $('body').append(this.liveditor);
var editor = this.ace.edit("editorlive");
editor.setTheme("ace/theme/monokai");
editor.getSession().setMode("ace/mode/javascript");
}
// -- viewClient ------------------------------------------------------------------------
function viewClient(clientID) {
var self = this;
var clients$ = $(this.clientList);
var node = this.nodes[clientID];
clients$.html("
" + $.encoder.encodeForHTML(node.name) + "
");
$('#back').click(function (evt) {
viewClients.call(self);
});
// Add node properties
clients$.append("");
var displayedProperties = {};
for (var i = 0; i < node.properties.length; i++) {
if (!displayedProperties[node.properties[i].name]) {
displayedProperties[node.properties[i].name] = "instance";
var nodeIDAlpha = $.encoder.encodeForAlphaNumeric(clientID);
var propertyNameAttribute = $.encoder.encodeForHTMLAttribute("id", node.properties[i].name, true);
var propertyNameAlpha = $.encoder.encodeForAlphaNumeric(node.properties[i].name);
var propertyNameHTML = $.encoder.encodeForHTML(node.properties[i].name);
var propertyValueAttribute = $.encoder.encodeForHTMLAttribute("val", node.properties[i].getValue(), true);
$('#clientProperties').append("
" + propertyNameHTML + "
");
}
}
}
// -- drillDown -------------------------------------------------------------------------
function drillDown(nodeID, drillBackID) // invoke with the view as "this"
{
var topdownName = this.topdownName;
var topdownTemp = this.topdownTemp;
drill.call(this, nodeID, drillBackID);
if (nodeID != vwf_view.kernel.find("", "/")[0]) $(topdownName).hide('slide', { direction: 'left' }, 175);
$(topdownTemp).show('slide', { direction: 'right' }, 175);
this.topdownName = topdownTemp;
this.topdownTemp = topdownName;
}
// -- drillUp ---------------------------------------------------------------------------
function drillUp(nodeID) // invoke with the view as "this"
{
var topdownName = this.topdownName;
var topdownTemp = this.topdownTemp;
drill.call(this, nodeID, undefined);
$(topdownName).hide('slide', { direction: 'right' }, 175);
$(topdownTemp).show('slide', { direction: 'left' }, 175);
this.topdownName = topdownTemp;
this.topdownTemp = topdownName;
}
// -- drillBack---------------------------------------------------------------------------
function drillBack(nodeID) // invoke with the view as "this"
{
var topdownName = this.topdownName;
var topdownTemp = this.topdownTemp;
drill.call(this, nodeID, undefined);
// No slide motion, when resizing script window back to normal
$(topdownName).hide();
$(topdownTemp).show();
this.topdownName = topdownTemp;
this.topdownTemp = topdownName;
}
// -- drill -----------------------------------------------------------------------------
function drill(nodeID, drillBackID) // invoke with the view as "this"
{
var node = this.nodes[nodeID];
if (!node) {
this.logger.errorx("drill: Cannot find node '" + nodeID + "'");
return;
}
var self = this;
var topdownName = this.topdownName;
var topdownTemp = this.topdownTemp;
var nodeIDAlpha = $.encoder.encodeForAlphaNumeric(nodeID);
$(topdownName).html(''); // Clear alternate div first to ensure content is added correctly
this.currentNodeID = nodeID;
if (!drillBackID) drillBackID = node.parentID;
if (nodeID == vwf_view.kernel.find("", "/")[0]) {
$(topdownTemp).html("
index
");
}
else {
$(topdownTemp).html("
" + $.encoder.encodeForHTML(node.name) + "
");
$('#' + nodeIDAlpha + '-back').click(function (evt) {
drillUp.call(self, drillBackID);
});
}
// Add node children
$(topdownTemp).append("");
for (var i = 0; i < node.children.length; i++) {
var nodeChildIDAttribute = $.encoder.encodeForHTMLAttribute("id", node.children[i].ID, true);
var nodeChildIDAlpha = $.encoder.encodeForAlphaNumeric(node.children[i].ID);
$('#children').append("