UNPKG

webgme-codeeditor

Version:

Code Editor Visualizer for webgme using CodeMirror

1,292 lines (1,168 loc) 64 kB
/*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