webgme-codeeditor
Version:
Code Editor Visualizer for webgme using CodeMirror
1,292 lines (1,168 loc) • 64 kB
JavaScript
/*globals define, WebGMEGlobal*/
/*jshint browser: true*/
/**
* Generated by VisualizerGenerator 0.1.0 from webgme on Sat Apr 16 2016 08:51:41 GMT-0700 (PDT).
*/
// STORES NODES INTO TREE WITH UNIQUE IDs AS THE WEBGME PATH
define([
'js/Utils/ComponentSettings',
'js/Loader/LoaderCircles',
'js/Constants',
'client/logger',
'./Dialog/Dialog',
'webgme-ot',
'./constants',
'common/Constants',
'./CLIENT_COLORS',
// HTML
'text!./CodeEditor.html',
'text!./ThemeSelector.html',
'text!./KeybindingSelector.html',
'text!./LineWrappingToggle.html',
// showdown
'./bower_components/showdown/dist/showdown.min',
// handlebars
'./bower_components/handlebars/handlebars.min',
// fancy tree
//'fancytree/jquery.fancytree-all.min',
// Codemirror
'./bower_components/codemirror/lib/codemirror',
// Scrolling
'./bower_components/codemirror/addon/scroll/simplescrollbars',
// merge
'./bower_components/codemirror/addon/merge/merge',
// Syntax highlighting
'./bower_components/codemirror/mode/clike/clike',
'./bower_components/codemirror/mode/markdown/markdown',
'./bower_components/codemirror/mode/javascript/javascript',
'./bower_components/codemirror/mode/python/python',
'./bower_components/codemirror/mode/diff/diff',
'./bower_components/codemirror/mode/cmake/cmake',
'./bower_components/codemirror/mode/coffeescript/coffeescript',
'./bower_components/codemirror/mode/css/css',
'./bower_components/codemirror/mode/dockerfile/dockerfile',
'./bower_components/codemirror/mode/erlang/erlang',
'./bower_components/codemirror/mode/fortran/fortran',
'./bower_components/codemirror/mode/gfm/gfm',
'./bower_components/codemirror/mode/go/go',
'./bower_components/codemirror/mode/handlebars/handlebars',
'./bower_components/codemirror/mode/haskell/haskell',
'./bower_components/codemirror/mode/htmlembedded/htmlembedded',
'./bower_components/codemirror/mode/htmlmixed/htmlmixed',
'./bower_components/codemirror/mode/idl/idl',
'./bower_components/codemirror/mode/lua/lua',
'./bower_components/codemirror/mode/mathematica/mathematica',
'./bower_components/codemirror/mode/modelica/modelica',
'./bower_components/codemirror/mode/nginx/nginx',
'./bower_components/codemirror/mode/pascal/pascal',
'./bower_components/codemirror/mode/perl/perl',
'./bower_components/codemirror/mode/php/php',
'./bower_components/codemirror/mode/protobuf/protobuf',
'./bower_components/codemirror/mode/r/r',
'./bower_components/codemirror/mode/rst/rst',
'./bower_components/codemirror/mode/ruby/ruby',
'./bower_components/codemirror/mode/shell/shell',
'./bower_components/codemirror/mode/spreadsheet/spreadsheet',
'./bower_components/codemirror/mode/tcl/tcl',
'./bower_components/codemirror/mode/stex/stex',
'./bower_components/codemirror/mode/textile/textile',
'./bower_components/codemirror/mode/vhdl/vhdl',
'./bower_components/codemirror/mode/verilog/verilog',
'./bower_components/codemirror/mode/xml/xml',
'./bower_components/codemirror/mode/yaml/yaml',
'./bower_components/codemirror/mode/yaml-frontmatter/yaml-frontmatter',
// Keymaps
'./bower_components/codemirror/keymap/emacs',
'./bower_components/codemirror/keymap/sublime',
'./bower_components/codemirror/keymap/vim',
// Addons
'./bower_components/codemirror/addon/lint/lint',
'./bower_components/codemirror/addon/lint/json-lint',
'./bower_components/jsonlint/web/jsonlint',
'./bower_components/codemirror/addon/lint/javascript-lint',
'./bower_components/codemirror/addon/lint/coffeescript-lint',
'./bower_components/codemirror/addon/lint/yaml-lint',
'./bower_components/codemirror/addon/hint/show-hint',
'./bower_components/codemirror/addon/hint/anyword-hint',
'./bower_components/codemirror/addon/hint/css-hint',
'./bower_components/codemirror/addon/hint/html-hint',
'./bower_components/codemirror/addon/hint/javascript-hint',
'./bower_components/codemirror/addon/hint/sql-hint',
'./bower_components/codemirror/addon/hint/xml-hint',
'./bower_components/codemirror/addon/search/search',
'./bower_components/codemirror/addon/search/searchcursor',
'./bower_components/codemirror/addon/search/matchesonscrollbar',
'./bower_components/codemirror/addon/search/match-highlighter',
'./bower_components/codemirror/addon/search/jump-to-line',
'./bower_components/codemirror/addon/scroll/annotatescrollbar',
'./bower_components/codemirror/addon/dialog/dialog',
'./bower_components/codemirror/addon/display/fullscreen',
'./bower_components/codemirror/addon/fold/foldcode',
'./bower_components/codemirror/addon/fold/foldgutter',
'./bower_components/codemirror/addon/fold/brace-fold',
'./bower_components/codemirror/addon/fold/xml-fold',
'./bower_components/codemirror/addon/fold/markdown-fold',
'./bower_components/codemirror/addon/fold/comment-fold',
// CSS
// codeEditorWidget
'css!./styles/CodeEditorWidget.css',
'css!./bower_components/codemirror/addon/scroll/simplescrollbars.css',
'css!./bower_components/codemirror/lib/codemirror.css',
'css!./bower_components/codemirror/addon/lint/lint.css',
'css!./bower_components/codemirror/addon/hint/show-hint.css',
'css!./bower_components/codemirror/addon/search/matchesonscrollbar.css',
'css!./bower_components/codemirror/addon/dialog/dialog.css',
'css!./bower_components/codemirror/addon/display/fullscreen.css',
'css!./bower_components/codemirror/theme/night.css',
'css!./bower_components/codemirror/theme/3024-day.css',
'css!./bower_components/codemirror/theme/3024-night.css',
'css!./bower_components/codemirror/theme/abcdef.css',
'css!./bower_components/codemirror/theme/ambiance.css',
'css!./bower_components/codemirror/theme/base16-dark.css',
'css!./bower_components/codemirror/theme/bespin.css',
'css!./bower_components/codemirror/theme/base16-light.css',
'css!./bower_components/codemirror/theme/blackboard.css',
'css!./bower_components/codemirror/theme/cobalt.css',
'css!./bower_components/codemirror/theme/colorforth.css',
'css!./bower_components/codemirror/theme/dracula.css',
'css!./bower_components/codemirror/theme/eclipse.css',
'css!./bower_components/codemirror/theme/elegant.css',
'css!./bower_components/codemirror/theme/erlang-dark.css',
'css!./bower_components/codemirror/theme/hopscotch.css',
'css!./bower_components/codemirror/theme/icecoder.css',
'css!./bower_components/codemirror/theme/isotope.css',
'css!./bower_components/codemirror/theme/lesser-dark.css',
//'css!./bower_components/codemirror/theme/liquibyte.css',
'css!./bower_components/codemirror/theme/material.css',
'css!./bower_components/codemirror/theme/mbo.css',
'css!./bower_components/codemirror/theme/mdn-like.css',
'css!./bower_components/codemirror/theme/midnight.css',
'css!./bower_components/codemirror/theme/monokai.css',
'css!./bower_components/codemirror/theme/neat.css',
'css!./bower_components/codemirror/theme/neo.css',
'css!./bower_components/codemirror/theme/night.css',
'css!./bower_components/codemirror/theme/paraiso-dark.css',
'css!./bower_components/codemirror/theme/paraiso-light.css',
'css!./bower_components/codemirror/theme/pastel-on-dark.css',
'css!./bower_components/codemirror/theme/railscasts.css',
'css!./bower_components/codemirror/theme/rubyblue.css',
'css!./bower_components/codemirror/theme/seti.css',
'css!./bower_components/codemirror/theme/solarized.css',
'css!./bower_components/codemirror/theme/the-matrix.css',
'css!./bower_components/codemirror/theme/tomorrow-night-bright.css',
'css!./bower_components/codemirror/theme/tomorrow-night-eighties.css',
'css!./bower_components/codemirror/theme/ttcn.css',
'css!./bower_components/codemirror/theme/twilight.css',
'css!./bower_components/codemirror/theme/vibrant-ink.css',
'css!./bower_components/codemirror/theme/xq-dark.css',
'css!./bower_components/codemirror/theme/xq-light.css',
'css!./bower_components/codemirror/theme/yeti.css',
'css!./bower_components/codemirror/theme/zenburn.css',
'css!./bower_components/codemirror/addon/fold/foldgutter'
], function (
ComponentSettings,
LoaderCircles,
WebGME_CONSTANTS,
Logger,
Dialog,
ot,
CONSTANTS,
COMMON,
CLIENT_COLORS,
// html
CodeEditorHtml,
ThemeSelectorHtml,
KeybindingSelectorHtml,
LineWrappingToggleHtml,
// showdown
showdown,
// handlebars
handlebars,
// fancytree
// fancytree,
// CodeMirror
CodeMirror,
// Scrolling
CodeMirrorScrolling,
// merge
CodeMirrorMerge,
// Syntax Highlighting
CodeMirrorModeClike,
CodeMirrorModeMarkdown,
CodeMirrorModeJavascript,
CodeMirrorModePython,
CodeMirrorModediff,
CodeMirrorModecmake,
CodeMirrorModecoffeescript,
CodeMirrorModecss,
CodeMirrorModedockerfile,
CodeMirrorModeerlang,
CodeMirrorModefortran,
CodeMirrorModegfm,
CodeMirrorModego,
CodeMirrorModehandlebars,
CodeMirrorModehaskell,
CodeMirrorModehtmlembedded,
CodeMirrorModehtmlmixed,
CodeMirrorModeidl,
CodeMirrorModelua,
CodeMirrorModemathematica,
CodeMirrorModemodelica,
CodeMirrorModenginx,
CodeMirrorModepascal,
CodeMirrorModeperl,
CodeMirrorModephp,
CodeMirrorModeprotobuf,
CodeMirrorModer,
CodeMirrorModerst,
CodeMirrorModeruby,
CodeMirrorModeshell,
CodeMirrorModespreadsheet,
CodeMirrorModetcl,
CodeMirrorModestex,
CodeMirrorModetextile,
CodeMirrorModevhdl,
CodeMirrorModeverilog,
CodeMirrorModexml,
CodeMirrorModeyaml,
CodeMirrorModeyamlfrontmatter,
// Keymaps
CodeMirrorEmacsKeymap,
CodeMirrorSublimeKeymap,
CodeMirrorVimKeymap,
// Addons
CodeMirrorLint,
CodeMirrorJSONLint,
jsonlint,
CodeMirrorJSLint,
CodeMirrorCSLint,
CodeMirrorYAMLLint,
CodeMirrorShowHint,
CodeMirrorAnywordHint,
CodeMirrorCSSHint,
CodeMirrorHTMLHint,
CodeMirrorJSHint,
CodeMirrorSQLHint,
CodeMirrorXMLHint,
CodeMirrorSearch,
CodeMirrorSearchCursor,
CodeMirrorMatchesOnScrollbar,
CodeMirrorMatchHighlighter,
CodeMirrorJumpToLine,
CodeMirrorAnnotateScrollbar,
CodeMirrorDialog,
CodeMirrorFullScreen,
CodeMirrorFoldCode,
CodeMirrorFoldGutter,
CodeMirrorBraceFold,
CodeMirrorXMLFold,
CodeMirrorMarkdownFold,
CodeMirrorCommentFold) {
'use strict';
var CodeEditorWidget,
WIDGET_CLASS = 'code-editor';
CodeEditorWidget = function (logger, container, client) {
this._logger = logger.fork('Widget');
this._client = client;
this._el = container;
this._initialize();
this._logger.debug('ctor finished');
};
CodeEditorWidget.getName = function () {
return 'CodeEditor';
};
CodeEditorWidget.getVersion = function () {
return '0.1.0';
};
CodeEditorWidget.getDefaultConfig = function () {
return {
'theme': 'default',
'enableThemeSelection': true,
'keyBinding': 'sublime',
'enableKeybindingSelection': true,
'lineWrapping': false,
'enableLineWrappingToggle': true,
'defaultSyntax': 'cpp',
'syntaxToModeMap': {},
'rootTypes': [],
'excludeTypes': [],
'loadDepth': 5,
'autoSaveInterval': 2000,
'attrToSyntaxMap': {},
'defaultAttributeMap': {},
'attrToInfoMap': {},
'nameTemplateMap': {}
};
};
CodeEditorWidget.getComponentId = function () {
return 'CodeEditor';
};
CodeEditorWidget.prototype._initialize = function () {
this.branchChanged = false;
$(this._el).css({
'padding': '0'
});
this.nodes = {};
this.docs = {};
this.waitingNodes = {};
var width = this._el.width(),
height = this._el.height(),
self = this;
this._config = CodeEditorWidget.getDefaultConfig();
var compId = CodeEditorWidget.getComponentId();
if (typeof WebGMEGlobal !== 'undefined') {
var deploymentSettings = WebGMEGlobal.componentSettings &&
WebGMEGlobal.componentSettings[ compId ];
var userSettings = WebGMEGlobal.userInfo && WebGMEGlobal.userInfo.settings &&
WebGMEGlobal.userInfo.settings[ compId ];
this._config = ComponentSettings.resolveSettings( this._config, deploymentSettings );
// update what is saved for the user
if (this._config.enableThemeSelection) {
this._config.theme = userSettings && userSettings.theme;
}
if (this._config.enableKeybindingSelection) {
this._config.keyBinding = userSettings && userSettings.keyBinding;
}
if (this._config.enableLineWrappingToggle) {
this._config.lineWrapping = userSettings && userSettings.lineWrapping;
}
//ComponentSettings.resolveWithWebGMEGlobal(this._config, CodeEditorWidget.getComponentId());
}
// set widget class
//this._el.addClass(WIDGET_CLASS);
this._activeInfo = {};
// make sure any resizing gets properly propagated
this._el.resize(this.onWidgetContainerResize);
// Create the CodeEditor and options
this._readOnly = this._client.isReadOnly();
this._fullscreen = false;
this._el.append(CodeEditorHtml);
this._saveBtn = this._el.find('.btn-save');
this._cancelBtn = this._el.find('.btn-cancel');
this._compareBtn = this._el.find('.btn-compare');
this._compareContainer = this._el.find('.compare-container');
this._compareEl = this._el.find('.codemirror-compare');
this._compareTitles = this._el.find('.title-container');
//this._containerTag = '.ui-layout-pane-center';
this._containerTag = '#CODE_EDITOR_DIV';
this._container = this._el.find(this._containerTag).first();
this._codearea = this._el.find('#codearea').first();
// Tree browswer widget
this._treeContainer = this._el.find('#codeEditorScroll');
this._treeBrowser = this._el.find('#codeTree');
this._treeBrowser.fancytree({
'checkbox': false,
'activeVisible': true,
'autoScroll': true,
'scrollParent': self._treeContainer,
'clickFolderMode': 4, // expand,
'focusOnSelect': true,
'icon': true, // make function returning icons,
'imagePath': null, // store icons here for use,
'selectMode': 1, // single select mode,
'keyboard': true, // disable keyboard for now
'source': []
});
this._fancyTree = this._el.find('#codeTree').fancytree('getTree');
this._fancyTree.render();
this._stopWatching = null;
self._debouncedShow = _.debounce( self.show.bind(self), 350 );
this._treeBrowser.on('fancytreeactivate', self.onFancyTreeActivate.bind(self));
// code view button handling
self.oked = false;
this._saveBtn.on('click', function (event) {
event.preventDefault();
event.stopPropagation();
self.save();
});
this._cancelBtn.on('click', function (event) {
self.oked = false;
event.preventDefault();
event.stopPropagation();
self.revert();
});
self.comparing = false;
self._compareTitles.hide();
this._compareBtn.on('click', function (event) {
event.preventDefault();
event.stopPropagation();
self.comparing = !self.comparing;
self.onCompareUpdate();
});
// Split view resizing
this._handle = this._el.find('#codeEditorHandle');
this._left = this._el.find('#codeEditorLeft');
this._right = this._el.find('#codeEditorRight');
this._left.css('width', '19.5%');
this._right.css('width', '80%');
this.isDragging = false;
this._fullScreen = false;
this._handle.mousedown(function(e) {
self.isDragging = true;
e.preventDefault();
});
this._container.mouseup(function() {
self.isDragging = false;
//self.editor.refresh();
}).mousemove(function(e) {
if (self.isDragging) {
var selector = $(self._el).find(self._containerTag);
var mousePosX = e.pageX;
if (self._fullScreen) {
// now we're at the top of the document :)
selector = $(document).find(self._containerTag).first();
}
else {
// convert x position as needed
// get offset from split panel
mousePosX -= $(self._el).find(self._containerTag).parents('.panel-base-wh').parent().position().left;
// get offset from west panel
mousePosX -= $('.ui-layout-pane-center').position().left;
//var selector = self._fullScreen ? self._containerTag : '.ui-layout-pane-center';
}
var maxWidth = selector.width();
var handlePercent = 0.5;
var minX = 0;
var maxX = selector.width() + minX;
var leftWidth = mousePosX - minX;
var leftPercent = Math.max(10, (leftWidth / maxWidth) * 100);
var rightPercent = Math.max(10, 100 - leftPercent - handlePercent);
leftPercent = 100 - rightPercent - handlePercent;
self._left.css('width', leftPercent + '%');
self._right.css('width', rightPercent + '%');
}
});
this._controls = $(this._el).find('#codeEditorControls').first();
// THEME SELECT
if (this._config.enableThemeSelection) {
this._controls.append( ThemeSelectorHtml );
this.theme_select = this._el.find("#theme_select").first();
$(this.theme_select).val(this._config.theme);
this.theme_select.on('change', this.selectTheme.bind(this));
}
// KEY MAP SELECTION
if (this._config.enableKeybindingSelection) {
this._controls.append( KeybindingSelectorHtml );
this.kb_select = this._el.find("#kb_select").first();
$(this.kb_select).val(this._config.keyBinding);
this.kb_select.on('change', this.selectKeyBinding.bind(this));
}
// LINE WRAPPING TOGGLE
if (this._config.enableLineWrappingToggle) {
this._controls.append( LineWrappingToggleHtml );
this.lineWrap_toggle = this._el.find("#cbLineWrapping").first();
$(this.lineWrap_toggle).prop('checked', this._config.lineWrapping);
this.lineWrap_toggle.on('click', this.toggleLineWrapping.bind(this));
}
this._attachClientEventListeners();
};
/* * * * * * * * Display Functions * * * * * * * */
function makeDocString(obj) {
var docStr = obj
if (Array.isArray(docStr)) {
docStr = docStr.join('\n');
}
return docStr;
}
function makeDocSummary(docStr, summary) {
return '<details class="code-editor-details"><summary class="code-editor-summary">' + summary + '</summary><p>\n' + docStr + '\n</p></details>';
}
CodeEditorWidget.prototype.updateDisplay = function() {
var self = this;
if (self._readOnly) {
self.comparing = false;
self.onCompareUpdate();
self._saveBtn.hide();
self._compareBtn.hide();
self._cancelBtn.hide();
} else {
self._saveBtn.show();
self._compareBtn.show();
self._cancelBtn.show();
}
};
CodeEditorWidget.prototype._getDocumentation = function(activeInfo) {
var self = this;
var markdown = null;
var numParents = 0;
var p = null;
if (activeInfo.attribute) {
// we've clicked on an editable code attribute
var parentInfoMap = self._config.attrToInfoMap &&
self._config.attrToInfoMap[activeInfo.parentNode.data.type];
if (parentInfoMap) {
var attrInfo = makeDocString(parentInfoMap.attributes[activeInfo.attribute]);
var parentInfo = makeDocString(parentInfoMap.docstring);
var parentSummary = activeInfo.parentNode.data.type + ' Information';
numParents = parentInfoMap.ancestorDepth || 0;
p = activeInfo.parentNode;
markdown = [attrInfo, makeDocSummary( parentInfo, parentSummary ) ];
}
}
else {
// we've clicked on a folder
var folderInfoMap = self._config.attrToInfoMap &&
self._config.attrToInfoMap[activeInfo.activeNode.data.type];
if (folderInfoMap) {
var folderInfo = makeDocString(folderInfoMap.docstring);
numParents = folderInfoMap.ancestorDepth || 0;
p = activeInfo.activeNode;
markdown = [ folderInfo ];
}
}
for (var i=0; i<numParents; i++) {
p = p.getParent();
if (!p) break;
var parentInfoMap = self._config.attrToInfoMap &&
self._config.attrToInfoMap[p.data.type];
if (parentInfoMap) {
var parentInfo = makeDocString(parentInfoMap.docstring);
var parentSummary = p.data.type + ' Information';
markdown.push(makeDocSummary( parentInfo, parentSummary ) );
}
}
markdown = makeDocString( markdown );
return markdown;
};
CodeEditorWidget.prototype._addSplitPanelToolbarBtns = function(toolbarEl) {
var self = this;
// BUTTON EVENT HANDLERS
var infoEl = [
'<span id="information" class="split-panel-toolbar-btn fa fa-info-circle">',
'</span>',
].join('\n');
toolbarEl.append(infoEl);
toolbarEl.find('#information').on('click', function(){
var d = new Dialog();
var activeInfo = self.getActiveInfo();
var markdown = self._getDocumentation( activeInfo );
if (markdown) {
var converter = new showdown.Converter();
d.initialize(converter.makeHtml(markdown));
d.show();
}
});
};
CodeEditorWidget.prototype.fullScreen = function(toFullScreen) {
if (this._fullScreen == toFullScreen)
return;
if (toFullScreen) {
var container = $(this._el).find(this._containerTag).first();
$(container).css({
position: 'fixed',
top: '0',
left: '0',
width: '100%',
height: '100%'
});
//$(container).zIndex(9999);
$(container).css('zIndex',9999);
$(container).prependTo(document.body);
//this.editor.focus();
this._fullScreen = true;
}
else {
var container = $(document).find(this._containerTag).first();
$(container).css({
position: '',
top: '',
left: '',
width: '100%',
height: '100%'
});
//$(container).zIndex('auto');
$(container).css('zIndex','auto');
$(container).appendTo(this._el);
//this.editor.focus();
this._fullScreen = false;
}
this.editor.refresh();
this.compareView.refresh();
};
// CODE EDITOR WIDGET
CodeEditorWidget.prototype.growl = function(msg, level, delay) {
$.notify({
icon: level === 'danger' ? 'fa fa-exclamation-triangle' : '',
message: msg
}, {
delay: delay,
hideDuration: 0,
type: level,
offset: {
x: 20,
y: 37
}
});
};
CodeEditorWidget.prototype.hasDifferentValue = function() {
var self = this;
return self.editor && !self._readOnly && self.compareView.getValue() !== self.editor.getValue();
}
CodeEditorWidget.prototype.save = function() {
return new Promise((resolve, reject) => {
var self = this,
client = self._client;
var newValue;
if (!self._activeAttributeName || !self.editor || self._readOnly || self.hasDifferentValue() === false) {
return;
}
client.startTransaction();
newValue = self.editor.getValue();
self._activeSelection.forEach(function (id) {
client.setAttribute(id, self._activeAttributeName, newValue);
});
client.completeTransaction('', (err, result) => {
if (err) {
reject(err);
} else {
resolve();
}
});
});
};
CodeEditorWidget.prototype.revert = function() {
var self = this;
var newValue;
if (!self._activeAttributeName || !self.editor || self._readOnly || self.hasDifferentValue() === false) {
return;
}
self.editor.setValue(self.compareView.getValue());
if (self.comparing) {
self.diffView.forceUpdate();
}
};
CodeEditorWidget.prototype.show = function (params) {
var self = this,
otherClients = {},
territory = {},
nodeName,
cmCompare,
cmEditor,
cmSaved,
diffView,
otWrapper,
uiId,
intervalId,
logger,
client,
activeObjectId,
watcherId,
docId;
this.editor = null;
this.compareView = null;
this._activeAttributeName = params.name;
this._storedValue = params.value || '';
function nodeEventHandler(events) {
var newAttr,
i,
nodeObj;
for (i = 0; i < events.length; i += 1) {
if (events[i].etype === 'load') {
nodeObj = client.getNode(events[i].eid);
nodeName = nodeObj.getAttribute('name');
} else if (events[i].etype === 'update') {
nodeObj = client.getNode(events[i].eid);
newAttr = nodeObj.getAttribute(params.name) || '';
if (self._readOnly) {
self.editor.setValue(newAttr);
self.editor.refresh();
self.compareView.setValue(newAttr);
self.compareView.refresh();
} else if (self._storedValue !== newAttr) {
self.growl('Stored value was updated', 'info', 1000);
self._storedValue = newAttr;
cmSaved.setValue(newAttr);
if (self.comparing) {
self.diffView.forceUpdate();
}
}
} else if (events[i].etype === 'unload') {
self.growl('Node was deleted! Make sure to copy your text to preserve changes.', 'danger', 10000);
} else {
// "Technical events" not used.
}
}
}
function isConnected(status) {
return status === COMMON.STORAGE.CONNECTED || status === COMMON.STORAGE.RECONNECTED;
}
function newNetworkStatus(c, status) {
var disconnectedAt,
disconnectTimeout;
if (status === COMMON.STORAGE.DISCONNECTED) {
disconnectedAt = Date.now();
disconnectTimeout = client.gmeConfig.documentEditing.disconnectTimeout;
Object.keys(otherClients).forEach(function (id) {
if (otherClients[id].selection) {
otherClients[id].selection.clear();
}
});
intervalId = setInterval(function () {
var timeLeft = Math.ceil((disconnectTimeout - (Date.now() - disconnectedAt)) / 1000),
msg = 'Connection was lost. If not reconnected within ' + timeLeft +
' seconds, the channel could be closed. You can save your changes and wait for reconnection.';
if (timeLeft >= 0) {
self.growl(msg, 'danger', 3000);
} else {
clearInterval(intervalId);
}
}, disconnectTimeout / 10);
} else if (isConnected(status)) {
clearInterval(intervalId);
self.growl('Reconnected - all is fine.', 'success', 3000);
} else {
clearInterval(intervalId);
self.growl('There were connection issues - the page needs to be refreshed. ' +
'Make sure to copy any text you would like to preserve.', 'danger', 30000);
}
}
client = params.client || WebGMEGlobal.Client;
logger = Logger.createWithGmeConfig('gme:Dialogs:CodeEditorDialog', client.gmeConfig);
activeObjectId = params.activeObject || WebGMEGlobal.State.getActiveObject();
this._activeSelection = params.activeSelection || WebGMEGlobal.State.getActiveSelection();
if (!this._activeSelection || this._activeSelection.length === 0) {
this._activeSelection = [activeObjectId];
}
if (typeof params.okLabel === 'string') {
$(this._okBtn).text(params.okLabel);
}
if (typeof params.cancelLabel === 'string') {
$(this._cancelBtn).text(params.cancelLabel);
}
var mac = CodeMirror.keyMap.default == CodeMirror.keyMap.macDefault;
CodeMirror.keyMap.default[(mac ? "Cmd" : "Ctrl") + "-Space"] = "autocomplete";
CodeMirror.keyMap.sublime[(mac ? "Cmd" : "Ctrl") + "-Space"] = "autocomplete";
CodeMirror.keyMap.emacs[(mac ? "Cmd" : "Ctrl") + "-Space"] = "autocomplete";
CodeMirror.keyMap.vim[(mac ? "Cmd" : "Ctrl") + "-Space"] = "autocomplete";
var CodeMirrorEditorOptions = {
value: params.value || '',
readOnly: self._readOnly,
origRight: params.value || '',
showDifferences: false,
revertButtons: true,
lineNumbers: true,
matchBrackets: true,
scrollbarStyle: "simple",
lint: false,
//viewPortMargin: Infinity,
lineWrapping: this._config.lineWrapping || false,
keyMap: this._config.keyBinding || 'sublime',
path: './bower_components/codemirror/lib/',
theme: this._config.theme || 'default',
fullscreen: false,
foldGutter: true,
gutters: ["CodeMirror-linenumbers", "CodeMirror-foldgutter", "CodeMirror-lint-markers"]
};
self._compareEl.empty();
cmCompare = CodeMirror.MergeView(self._compareEl.get(0), CodeMirrorEditorOptions);
cmEditor = cmCompare.edit; // The cm instance for editing.
cmSaved = cmCompare.right.orig; // The cm instance displaying original value.
diffView = cmCompare.right; // Diff view controller (used to update diff).
otWrapper = new ot.CodeMirrorAdapter(cmEditor); // Ot wrapper for obtaining/applying operations.
cmEditor.setOption('mode', params.mode);
cmSaved.setOption('mode', params.mode);
this.editor = cmEditor;
this.compareView = cmSaved;
this.diffView = diffView;
/*
cmEditor.on(
'change',
_.debounce(this.saveChanges.bind(this), +this._config.autoSaveInterval || 1.0)
);
*/
cmEditor.setOption("extraKeys", {
'F11': function(cm) {
self.fullScreen(!self._fullScreen);
},
'Esc': function(cm) {
self.fullScreen(false);
},
});
cmSaved.setOption("extraKeys", {
'F11': function(cm) {
self.fullScreen(!self._fullScreen);
},
'Esc': function(cm) {
self.fullScreen(false);
},
});
if (COMMON.ATTRIBUTE_MULTILINE_TYPES.hasOwnProperty(params.multilineType)) {
if (params.multilineType !== COMMON.ATTRIBUTE_MULTILINE_TYPES.plaintext) {
cmEditor.setOption('mode', CONSTANTS.MODE[params.multilineType]);
cmSaved.setOption('mode', CONSTANTS.MODE[params.multilineType]);
}
}
var watcherData = {};
var stopWatching = function() {
var doSave = false;
function close() {
if (doSave) {
save();
}
if (uiId) {
client.removeUI(uiId);
}
if (docId && watcherId) {
client.unwatchDocument({docId: docId, watcherId: watcherId}, function (err) {
if (err) {
logger.error(err);
}
});
client.removeEventListener(client.CONSTANTS.NETWORK_STATUS_CHANGED, newNetworkStatus);
}
}
if (typeof self.oked === 'boolean' || self._readOnly || self.hasDifferentValue() === false) {
doSave = self.oked;
close();
} else {
self.growl('You made changes without saving - you cannot exit without deciding whether to save or not',
'warning', 4000);
//e.preventDefault();
//e.stopImmediatePropagation();
return false;
}
};
//cmEditor.focus();
cmEditor.refresh();
this._loader = new LoaderCircles({containerElement: this._right});
if (self._readOnly) {
this._saveBtn.hide();
this._compareBtn.hide();
this._cancelBtn.hide();
this.editor.refresh();
cmEditor.refresh();
/*
if (isConnected(client.getNetworkStatus()) && this._activeSelection.length === 1) {
self._loader.start();
watcherData = {
projectId: client.getActiveProjectId(),
branchName: client.getActiveBranchName(),
nodeId: this._activeSelection[0],
attrName: params.name,
attrValue: params.value
};
client.watchDocument(
watcherData,
function atOperation(operation) {
console.log('got operation');
console.log(operation);
},
function atSelection(eData) {},
function cb(err, initData) {
if (err) {
logger.error(err);
self.growl(err.message, 'danger', 5000);
return;
}
docId = initData.docId;
watcherId = initData.watcherId;
self.editor.setValue(initData.document);
self._loader.stop();
client.addEventListener(client.CONSTANTS.NETWORK_STATUS_CHANGED,
newNetworkStatus);
});
}
*/
if (this._activeSelection.length === 1) {
territory[this._activeSelection[0]] = {children: 0};
uiId = client.addUI(null, nodeEventHandler);
client.updateTerritory(uiId, territory);
} else {
this._compareBtn.hide();
}
} else {
if (client.gmeConfig.documentEditing.enable === true &&
isConnected(client.getNetworkStatus()) && this._activeSelection.length === 1) {
self._loader.start();
watcherData = {
projectId: client.getActiveProjectId(),
branchName: client.getActiveBranchName(),
nodeId: this._activeSelection[0],
attrName: params.name,
attrValue: params.value
};
client.watchDocument(
watcherData,
function atOperation(operation) {
otWrapper.applyOperation(operation);
if (self.comparing) {
self.diffView.forceUpdate();
}
},
function atSelection(eData) {
var colorIndex;
if (otherClients.hasOwnProperty(eData.socketId) === false) {
colorIndex = Object.keys(otherClients).length % CLIENT_COLORS.length;
otherClients[eData.socketId] = {
userId: eData.userId,
selection: null,
color: CLIENT_COLORS[colorIndex]
};
}
// Clear the current selection for that user...
if (otherClients[eData.socketId].selection) {
otherClients[eData.socketId].selection.clear();
}
// .. and if there is a new selection, set it in the editor.
if (eData.selection) {
otherClients[eData.socketId].selection = otWrapper.setOtherSelection(eData.selection,
otherClients[eData.socketId].color, otherClients[eData.socketId].userId);
}
},
function (err, initData) {
if (err) {
logger.error(err);
self.growl(err.message, 'danger', 5000);
return;
}
docId = initData.docId;
watcherId = initData.watcherId;
cmEditor.setValue(initData.document);
if (self.comparing) {
self.diffView.forceUpdate();
}
otWrapper.registerCallbacks({
'change': function (operation) {
client.sendDocumentOperation({
docId: docId,
watcherId: watcherId,
operation: operation,
selection: otWrapper.getSelection()
});
if (self.comparing) {
self.diffView.forceUpdate();
}
},
'selectionChange': function () {
client.sendDocumentSelection({
docId: docId,
watcherId: watcherId,
selection: otWrapper.getSelection()
});
}
});
self._loader.stop();
/*
growl('A channel for close collaboration is open. Changes still have to be ' +
'persisted by saving.', 'success', 5000);
*/
client.addEventListener(client.CONSTANTS.NETWORK_STATUS_CHANGED, newNetworkStatus);
});
}
if (this._activeSelection.length === 1) {
territory[this._activeSelection[0]] = {children: 0};
uiId = client.addUI(null, nodeEventHandler);
client.updateTerritory(uiId, territory);
} else {
this._compareBtn.hide();
}
}
self._stopWatching = stopWatching;
};
// CODE EDITOR WIDGET
CodeEditorWidget.prototype.attributeToTab = function(nodeId, attrName) {
var self = this;
var node = self.nodes[ nodeId ];
var codeAttributes = node && Object.keys(node.codeAttributes).sort();
return codeAttributes && codeAttributes.indexOf( attrName );
};
CodeEditorWidget.prototype.tabToAttribute = function(nodeId, tabId) {
var self = this;
var node = self.nodes[ nodeId ];
var codeAttributes = node && Object.keys(node.codeAttributes).sort();
return codeAttributes && codeAttributes[ tabId ];
};
CodeEditorWidget.prototype.getDefaultAttribute = function(nodeId) {
var self = this;
var node = self.nodes[ nodeId ];
return node && node.type && self._config.defaultAttributeMap[ node.type ];
};
CodeEditorWidget.prototype.getDefaultTab = function(nodeId) {
var self = this;
var node = self.nodes[ nodeId ];
var codeAttributes = node && Object.keys(node.codeAttributes).sort();
var index = codeAttributes && codeAttributes.indexOf( self.getDefaultAttribute( nodeId ));
return (index > -1) ? index : null;
};
CodeEditorWidget.prototype.getTreeSelection = function() {
var self = this;
var selection = {};
var selectedTreeNodes = self._fancyTree.getSelectedNodes();
if (selectedTreeNodes.length) {
var selId = null,
tabId = -1,
selNode = selectedTreeNodes[0]; // should just be one
if (selNode.isFolder()) { // not a code attribute but an actual webGME node
selId = selNode.data.id;
}
else { // code attribute, need to get parent for real webGME node
selId = selNode.getParent().data.id;
tabId = selNode.getIndex();
}
selection.nodeId = selId;
selection.tabId = tabId;
}
return selection;
};
CodeEditorWidget.prototype.setGMESelection = function(nodeId, tabId) {
var self = this;
if (nodeId && tabId) {
WebGMEGlobal.State.registerActiveSelection([nodeId], {invoker: self});
WebGMEGlobal.State.registerActiveTab(tabId, {invoker: self})
}
};
CodeEditorWidget.prototype.getActiveInfo = function() {
var self = this;
var retData = {};
var selectedNodes = self._fancyTree.getSelectedNodes(); // should just return one
if (selectedNodes.length) {
if (!selectedNodes[0].isFolder()) {
var selectedAttribute = selectedNodes[0];
var selectedNode = selectedAttribute.getParent();
retData.activeNode = selectedAttribute;
retData.parentNode = selectedNode;
retData.gmeId = selectedNode.data.id;
retData.attribute = selectedAttribute.title;
}
else {
var selectedNode = selectedNodes[0];
var selectedParent = selectedNode.getParent();
retData.activeNode = selectedNode;
retData.parentNode = selectedParent;
retData.gmeId = selectedNode.data.id;
retData.attribute = null;
}
}
return retData;
};
CodeEditorWidget.prototype.getNodeInfo = function(gmeId) {
var self = this;
var retData = {};
var parentNode = self._fancyTree.getNodeByKey(gmeId);
if (parentNode) {
retData.node = parentNode;
retData.gmeId = gmeId;
retData.attributes = {};
var children = parentNode.getChildren();
if (children) {
children.map(function(child) {
retData.attributes[child.title] = {
'node': child
};
});
}
}
return retData;
};
CodeEditorWidget.prototype.saveConfig = function() {
var self=this;
ComponentSettings.overwriteComponentSettings(
CodeEditorWidget.getComponentId(), this._config,
function (err) {
if (err) {
self._logger.error(err);
}
else
WebGMEGlobal.userInfo.settings[CodeEditorWidget.getComponentId()] = self._config;
});
};
CodeEditorWidget.prototype.selectTheme = function(event) {
var self=this;
var theme_select = event.target;
var theme = theme_select.options[theme_select.selectedIndex].textContent;
this._config.theme = theme;
this.saveConfig();
if (this.editor)
this.editor.setOption("theme", theme);
if (this.compareView)
this.compareView.setOption("theme", theme);
};
CodeEditorWidget.prototype.selectKeyBinding = function(event) {
var self=this;
var kb_select = event.target;
var binding = kb_select.options[kb_select.selectedIndex].textContent;
this._config.keyBinding = binding;
this.saveConfig();
if (this.editor)
this.editor.setOption("keyMap", binding);
if (this.compareView)
this.compareView.setOption("keyMap", binding);
};
CodeEditorWidget.prototype.toggleLineWrapping = function(event) {
var self=this;
//var lineWrapping = $(self.lineWrap_toggle).prop('checked');
var lineWrap_toggle = event.target;
var lineWrap = lineWrap_toggle.checked;
this._config.lineWrapping = lineWrap;
this.saveConfig();
if (this.editor)
this.editor.setOption("lineWrapping", lineWrap);
if (this.compareView)
this.compareView.setOption("lineWrapping", lineWrap);
};
CodeEditorWidget.prototype.onWidgetContainerResize = function (width, height) {
if (this.editor)
this.editor.refresh();
if (this.compareView)
this.compareView.refresh();
//console.log('Widget is resizing...');
};
CodeEditorWidget.prototype.isRootType = function(type) {
var self = this;
var isRoot = self._config.rootTypes.indexOf(type) > -1;
return isRoot;
};
CodeEditorWidget.prototype.checkDependencies = function(desc) {
// only dependency is the node's parent, which must be in the tree already
var self = this;
var depsMet = false;
depsMet = self.isRootType(desc.type) || self.nodes[desc.parentId];
return depsMet;
};
CodeEditorWidget.prototype.updateDependencies = function() {
// a new node has been created, figure out what other nodes can be made now
var self = this;
Object.keys(self.waitingNodes).map(function(gmeId) {
var desc = self.waitingNodes[gmeId];
if (desc) {
if (self.checkDependencies(desc)) {
self.createNode(desc);
}
}
});
};
CodeEditorWidget.prototype.getNodeName = function(desc) {
var self = this;
var name = desc.name;
var template = self._config.nameTemplateMap && self._config.nameTemplateMap[desc.type];
if (template) {
name = handlebars.compile( template )( desc.attributes ) || name;
}
return name;
};
CodeEditorWidget.prototype.createNode = function(desc) {
// simple function to make a node; dependencies have been met
var self = this;
if (!desc || !desc.name || !desc.type || !desc.id) {
// need to make sure we don't make invalid nodes!
return;
}
var parentNode = self._fancyTree.getRootNode();
if (self.nodes[desc.parentId] && !self.isRootType(desc.type))
parentNode = self._fancyTree.getNodeByKey(desc.parentId);
if (!parentNode) // shouldn't happen!
return;
var newChild = parentNode.addChildren({
'title': self.getNodeName(desc),
'tooltip': desc.type,
'folder': true,
'icon': desc.iconPath || 'glyphicon glyphicon-folder-open',
'data': desc,
'key': desc.id
});
self.nodes[desc.id] = desc;
self.docs[desc.id] = {};
self.waitingNodes[desc.id] = undefined;
var attributeNames = Object.keys(desc.codeAttributes).sort();
if (attributeNames.length > 0) {
attributeNames.map(function(attrib