webgme
Version:
Web-based Generic Modeling Environment
518 lines (452 loc) • 23.3 kB
JavaScript
/*globals define, _, WebGMEGlobal, $, console*/
/*jshint browser: true*/
/**
* @author rkereskenyi / https://github.com/rkereskenyi
* @author nabana / https://github.com/nabana
*/
define([
'js/logger',
'client/client',
'js/Constants',
'js/Panels/MetaEditor/MetaEditorConstants',
'js/Utils/GMEConcepts',
'js/Utils/GMEVisualConcepts',
'js/Utils/StateManager',
'js/Utils/WebGMEUrlManager',
'js/LayoutManager/LayoutManager',
'js/Decorators/DecoratorManager',
'js/KeyboardManager/KeyboardManager',
'js/PanelManager/PanelManager',
'./WebGME.History',
'js/Utils/PreferencesHelper',
'js/Dialogs/Projects/ProjectsDialog',
'js/Utils/InterpreterManager',
'js/Utils/ComponentSettings',
'js/Utils/SvgManager',
'common/storage/util',
'q',
'jquery'
], function (Logger,
Client,
CONSTANTS,
METACONSTANTS,
GMEConcepts,
GMEVisualConcepts,
StateManager,
WebGMEUrlManager,
LayoutManager,
DecoratorManager,
KeyboardManager,
PanelManager,
WebGMEHistory,
PreferencesHelper,
ProjectsDialog,
InterpreterManager,
ComponentSettings,
SvgManager,
StorageUtil,
Q) {
'use strict';
var componentId = 'GenericUIWebGMEStart',
defaultConfig = {
disableProjectsDialog: false,
initialContext: {
project: null,
branch: null,
node: null
},
nodeAtOpen: '',
layout: 'DefaultLayout',
byProjectId: {
nodeAtOpen: {},
layout: {}
},
byProjectKind: {
nodeAtOpen: {},
layout: {}
}
};
function webGMEStart(afterPanelsLoaded) {
var layoutManager,
client,
gmeConfig = WebGMEGlobal.gmeConfig,
logger = Logger.create('gme:WebGME', WebGMEGlobal.gmeConfig.client.log),
initialThingsToDo = WebGMEUrlManager.parseInitialThingsToDoFromUrl(),
projectOpenDialog,
initialProject = true,
config = defaultConfig;
if (typeof gmeConfig.visualization.layout.default === 'string') {
config.layout = gmeConfig.visualization.layout.default;
console.warn('Since v2.11.0 gmeConfig.visualization.layout.default is a component setting of ' +
'GenericUIWebGMEStart.layout and can be configured for projects based on kind, name and ID.' +
'The value in gmeConfig.visualization.layout.default will right now be used for non-specified ' +
'projects.');
}
ComponentSettings.resolveWithWebGMEGlobal(config, componentId);
// URL query has higher priority than the config.
if ((initialThingsToDo.projectToLoad || initialThingsToDo.createNewProject) === false) {
initialThingsToDo.projectToLoad = config.initialContext.project;
initialThingsToDo.branchToLoad = initialThingsToDo.branchToLoad ||
config.initialContext.branch;
initialThingsToDo.objectToLoad = initialThingsToDo.objectToLoad ||
config.initialContext.node || initialThingsToDo.objectToLoad; // i.e. the root-node.
// TODO: add commit to load
}
layoutManager = new LayoutManager();
layoutManager.loadLayout(initialThingsToDo.layoutToLoad || config.layout, function () {
var panels = [],
layoutPanels = layoutManager._currentLayout.panels,
decorators,
len = layoutPanels ? layoutPanels.length : 0,
i;
client = new Client(gmeConfig);
WebGMEGlobal.Client = client;
WebGMEGlobal.InterpreterManager = new InterpreterManager(client, gmeConfig);
WebGMEGlobal.SvgManager = SvgManager;
Object.defineProperty(WebGMEGlobal, 'State', {
value: StateManager.initialize(),
writable: false,
enumerable: true,
configurable: false
}
);
client.registerUIStateGetter(function () {
return WebGMEGlobal.State.toJSON();
});
WebGMEGlobal.State.on('change:' + CONSTANTS.STATE_ACTIVE_OBJECT, function () {
// Currently we only emit events to other users when our active object changed,
// this is up for debate.
client.emitStateNotification();
});
WebGMEGlobal.State.registerLayout(initialThingsToDo.layoutToLoad || config.layout,
{suppressHistoryUpdate: true});
WebGMEHistory.initialize();
GMEConcepts.initialize(client);
GMEVisualConcepts.initialize(client);
PreferencesHelper.initialize(client);
//hook up branch changed to set read-only mode on panels
client.addEventListener(client.CONSTANTS.BRANCH_CHANGED, function (_client, branchName) {
layoutManager.setPanelReadOnly(client.isCommitReadOnly() || client.isProjectReadOnly());
if (branchName) {
WebGMEGlobal.State.registerActiveBranchName(branchName);
} else if (client.getActiveCommitHash()) {
WebGMEGlobal.State.registerActiveCommit(client.getActiveCommitHash());
} else {
WebGMEGlobal.State.registerActiveBranchName(null);
}
});
client.addEventListener(client.CONSTANTS.PROJECT_OPENED, function (_client, projectId) {
var projectKind,
layout,
nodePath;
if (typeof gmeConfig.client.pageTitle !== 'string') {
if (WebGMEGlobal.gmeConfig.authentication.enable &&
StorageUtil.getOwnerFromProjectId(projectId) !== WebGMEGlobal.userInfo._id) {
document.title = WebGMEGlobal.getProjectDisplayedNameFromProjectId(projectId);
} else {
document.title = StorageUtil.getProjectNameFromProjectId(projectId);
}
}
layoutManager.setPanelReadOnly(client.isProjectReadOnly());
WebGMEGlobal.State.registerActiveProjectName(projectId);
if (initialProject === false) {
projectKind = client.getActiveProjectKind();
if (config.byProjectId.nodeAtOpen.hasOwnProperty(projectId)) {
nodePath = config.byProjectId.nodeAtOpen[projectId];
} else if (projectKind && config.byProjectKind.nodeAtOpen.hasOwnProperty([projectKind])) {
nodePath = config.byProjectKind.nodeAtOpen[projectKind];
} else {
nodePath = config.nodeAtOpen || CONSTANTS.PROJECT_ROOT_ID;
}
if (config.byProjectId.layout.hasOwnProperty(projectId)) {
layout = config.byProjectId.layout[projectId];
} else if (projectKind && config.byProjectKind.layout.hasOwnProperty([projectKind])) {
layout = config.byProjectKind.layout[projectKind];
} else {
layout = config.layout;
}
if (layout !== WebGMEGlobal.State.getLayout()) {
document.location.href = window.location.href.split('?')[0] + '?' +
WebGMEUrlManager.getSearchQuery({
projectId: projectId,
nodePath: nodePath,
layout: layout
});
} else {
setActiveNode(nodePath);
}
}
});
//on project close clear the current state
client.addEventListener(client.CONSTANTS.PROJECT_CLOSED, function (/* __project, projectName */) {
var layout = WebGMEGlobal.State.getLayout();
document.title = config.pageTitle;
initialProject = false;
WebGMEGlobal.State.clear();
WebGMEGlobal.State.registerLayout(layout, {suppressHistoryUpdate: true});
});
client.decoratorManager = new DecoratorManager();
decorators = gmeConfig.visualization.decoratorsToPreload || WebGMEGlobal.allDecorators || [];
client.decoratorManager.downloadAll(decorators, function (err) {
if (err) {
logger.error(err);
}
for (i = 0; i < len; i += 1) {
panels.push({
panel: layoutPanels[i].panel,
container: layoutPanels[i].container,
control: layoutPanels[i].control,
params: {client: client}
});
}
//load the panels
loadPanels(panels);
//as of now it's a global variable just to make access to it easier
//TODO: might need to be changed
WebGMEGlobal.KeyboardManager = KeyboardManager;
WebGMEGlobal.KeyboardManager.setEnabled(true);
WebGMEGlobal.PanelManager = new PanelManager(client);
});
});
function loadPanels(panels) {
var p = panels.splice(0, 1)[0];
layoutManager.loadPanel(p, function () {
if (panels.length > 0) {
loadPanels(panels);
} else {
if (_.isFunction(afterPanelsLoaded)) {
afterPanelsLoaded(client);
}
client.connectToDatabase(function (err) {
if (err) {
logger.error('Failed to connect to database', err);
return;
}
if (initialThingsToDo.createNewProject) {
createNewProject();
} else if (initialThingsToDo.projectToLoad) {
loadProject();
} else {
openProjectLoadDialog(false);
}
});
}
});
}
function loadProject() {
Q.nfcall(client.selectProject, initialThingsToDo.projectToLoad, undefined)
.then(function () {
if (!initialThingsToDo.branchToLoad) {
return {};
}
return Q.nfcall(client.getBranches, initialThingsToDo.projectToLoad);
})
.then(function (branches) {
if (initialThingsToDo.commitToLoad) {
return Q.nfcall(client.selectCommit, initialThingsToDo.commitToLoad);
}
if (initialThingsToDo.branchToLoad && branches[initialThingsToDo.branchToLoad]) {
if (initialThingsToDo.branchToLoad !== client.getActiveBranchName()) {
return Q.nfcall(client.selectBranch, initialThingsToDo.branchToLoad, null);
} else {
logger.info('branch was already open', initialThingsToDo.branchToLoad);
}
}
})
.then(function () {
selectObject(initialThingsToDo.objectToLoad, initialThingsToDo.activeSelectionToLoad,
initialThingsToDo.visualizerToLoad, initialThingsToDo.tabToSelect);
})
.catch(function (err) {
logger.error('error during startup', err);
openProjectLoadDialog(false);
return;
});
}
function openProjectLoadDialog(connect) {
initialProject = false;
//if initial project openings failed we show the project opening dialog
logger.info('init-phase false');
logger.info('about to open projectOpenDialog, connect:', connect);
if (connect) {
client.connectToDatabase(function (err) {
if (err) {
logger.error('Failed to connect to database', err);
return;
}
if (config.disableProjectsDialog === false) {
projectOpenDialog = new ProjectsDialog(client);
projectOpenDialog.show();
} else {
showNoProjectModal();
}
});
} else if (config.disableProjectsDialog === false) {
projectOpenDialog = new ProjectsDialog(client);
projectOpenDialog.show();
} else {
showNoProjectModal();
}
}
function selectObject(nodeId, selectionIds, vizualizer, tab) {
var user = {},
userPattern = {},
activeNodeUI,
nodePath = nodeId === 'root' ? CONSTANTS.PROJECT_ROOT_ID : nodeId;
userPattern[nodePath] = {children: 0};
logger.debug('selectObject', nodeId);
logger.debug('activeSelectionToLoad', selectionIds);
if (selectionIds && selectionIds.length > 0) {
userPattern[nodePath] = {children: 1};
} else {
userPattern[nodePath] = {children: 0};
}
//we try to set the visualizer first so we will not change it later with the other settings
//TODO when there will be a new global state element, it has to be added here
function eventHandler(events) {
var i,
activeNode,
updatedState = {},
opts = {},
aspectNames;
logger.debug('events from selectObject', events);
if (events[0].etype !== 'complete') {
logger.warn('partial events only');
return;
}
logger.debug('active "' + nodePath + '" node loaded');
for (i = 0; i < events.length; i += 1) {
//look for the active node
if (events[i].eid === nodePath) {
activeNode = client.getNode(nodePath);
if (activeNode) {
updatedState[CONSTANTS.STATE_ACTIVE_OBJECT] = nodePath;
selectionIds = selectionIds || [];
if (selectionIds.length > 0) {
updatedState[CONSTANTS.STATE_ACTIVE_SELECTION] = selectionIds;
}
if (vizualizer) {
updatedState[CONSTANTS.STATE_ACTIVE_VISUALIZER] = vizualizer;
opts.suppressVisualizerFromNode = true;
tab = parseInt(tab, 10);
if (tab >= 0) {
updatedState[CONSTANTS.STATE_ACTIVE_TAB] = tab;
// For the ModelEditor to work the tab-index and aspect name must be aligned.
if (vizualizer === 'ModelEditor') {
aspectNames = activeNode.getValidAspectNames();
aspectNames.sort(function (a, b) {
var an = a.toLowerCase(),
bn = b.toLowerCase();
return (an < bn) ? -1 : 1;
});
aspectNames.unshift('All');
updatedState[CONSTANTS.STATE_ACTIVE_ASPECT] = aspectNames[tab] || 'All';
}
}
}
WebGMEGlobal.State.set(updatedState, opts);
break;
}
}
}
client.removeUI(activeNodeUI);
}
activeNodeUI = client.addUI(user, eventHandler);
client.updateTerritory(activeNodeUI, userPattern);
}
function createNewProject() {
Q.nfcall(client.getProjects, {})
.then(function (projectArray) {
var projectDeferred = Q.defer(),
projectExisted = false,
userId = WebGMEGlobal.userInfo._id,
newProjectId = userId + client.CONSTANTS.STORAGE.PROJECT_ID_SEP +
initialThingsToDo.projectToLoad,
i;
for (i = 0; i < projectArray.length; i += 1) {
if (projectArray[i]._id === newProjectId) {
projectExisted = true;
break;
}
}
if (projectExisted) {
//fall back to loading
Q.nfcall(client.selectProject, newProjectId, undefined)
.then(function () {
return Q.nfcall(client.getBranches, newProjectId);
})
.then(projectDeferred.resolve)
.catch(projectDeferred.reject);
return projectDeferred.promise;
}
Q.nfcall(client.seedProject, {
type: 'file',
projectName: initialThingsToDo.projectToLoad,
seedName: WebGMEGlobal.gmeConfig.seedProjects.defaultProject
})
.then(function () {
return Q.nfcall(client.selectProject, newProjectId, undefined);
})
.then(function () {
return Q.nfcall(client.getBranches, newProjectId);
})
.then(projectDeferred.resolve)
.catch(projectDeferred.reject);
return projectDeferred.promise;
})
.then(function (branches) {
if (initialThingsToDo.commitToLoad) {
return Q.nfcall(client.selectCommit, initialThingsToDo.commitToLoad);
}
if (initialThingsToDo.branchToLoad && branches[initialThingsToDo.branchToLoad]) {
if (initialThingsToDo.branchToLoad !== client.getActiveBranchName()) {
return Q.nfcall(client.selectBranch, initialThingsToDo.branchToLoad, null);
} else {
logger.info('branch was already open', initialThingsToDo.branchToLoad);
}
}
})
.then(function () {
selectObject(initialThingsToDo.objectToLoad, initialThingsToDo.activeSelectionToLoad,
initialThingsToDo.visualizerToLoad, initialThingsToDo.tabToSelect);
})
.catch(function (err) {
logger.error('error during startup', err);
openProjectLoadDialog(false);
});
}
function showNoProjectModal() {
$('<div class="modal fade" tabindex="-1" role="dialog">' +
'<div class="modal-dialog" style="margin-top: 200px;">' +
'<div class="modal-content" style="background: none; border: none;">' +
'<h1 style="text-align: center;">No project opened ...</h1>' +
'</div></div></div>').modal('show');
}
function setActiveNode(nodePath) {
var userPattern = {},
tempUI;
userPattern[nodePath] = {children: 0};
function eventHandler(events) {
var i,
activeNode;
for (i = 0; i < events.length; i += 1) {
//look for the active node
if (events[i].eid === nodePath) {
activeNode = client.getNode(nodePath);
if (activeNode) {
WebGMEGlobal.State.registerActiveObject(nodePath);
break;
}
}
}
client.removeUI(tempUI);
}
tempUI = client.addUI(null, eventHandler);
client.updateTerritory(tempUI, userPattern);
}
}
return {
start: webGMEStart
};
}
);