<commit_before>/*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).
 */

define([
    'js/Utils/ComponentSettings',
    // HTML
    'text!./CodeEditor.html',
    // Codemirror
    'rosmod/Libs/cm/lib/codemirror',
    // Syntax highlighting
    'rosmod/Libs/cm/mode/clike/clike',
    'rosmod/Libs/cm/mode/markdown/markdown',
    // Keymaps
    'rosmod/Libs/cm/keymap/emacs',
    'rosmod/Libs/cm/keymap/sublime',
    'rosmod/Libs/cm/keymap/vim',
    // Addons
    'rosmod/Libs/cm/addon/hint/show-hint',
    'rosmod/Libs/cm/addon/search/search',
    'rosmod/Libs/cm/addon/search/searchcursor',
    'rosmod/Libs/cm/addon/search/matchesonscrollbar',
    'rosmod/Libs/cm/addon/search/match-highlighter',
    'rosmod/Libs/cm/addon/search/jump-to-line',
    'rosmod/Libs/cm/addon/scroll/annotatescrollbar',
    'rosmod/Libs/cm/addon/dialog/dialog',
    'rosmod/Libs/cm/addon/display/fullscreen',
    'rosmod/Libs/cm/addon/fold/foldcode',
    'rosmod/Libs/cm/addon/fold/foldgutter',
    'rosmod/Libs/cm/addon/fold/brace-fold',
    'rosmod/Libs/cm/addon/fold/xml-fold',
    'rosmod/Libs/cm/addon/fold/markdown-fold',
    'rosmod/Libs/cm/addon/fold/comment-fold',
    // CSS
    'css!./styles/CodeEditorWidget.css',
    'css!rosmod/Libs/cm/addon/hint/show-hint.css',
    'css!rosmod/Libs/cm/addon/search/matchesonscrollbar.css',
    'css!rosmod/Libs/cm/addon/dialog/dialog.css',
    'css!rosmod/Libs/cm/addon/display/fullscreen.css',
    'css!rosmod/Libs/cm/theme/night.css',
    'css!rosmod/Libs/cm/lib/codemirror.css',
    'css!rosmod/Libs/cm/theme/3024-day.css',
    'css!rosmod/Libs/cm/theme/3024-night.css',
    'css!rosmod/Libs/cm/theme/abcdef.css',
    'css!rosmod/Libs/cm/theme/ambiance.css',
    'css!rosmod/Libs/cm/theme/base16-dark.css',
    'css!rosmod/Libs/cm/theme/bespin.css',
    'css!rosmod/Libs/cm/theme/base16-light.css',
    'css!rosmod/Libs/cm/theme/blackboard.css',
    'css!rosmod/Libs/cm/theme/cobalt.css',
    'css!rosmod/Libs/cm/theme/colorforth.css',
    'css!rosmod/Libs/cm/theme/dracula.css',
    'css!rosmod/Libs/cm/theme/eclipse.css',
    'css!rosmod/Libs/cm/theme/elegant.css',
    'css!rosmod/Libs/cm/theme/erlang-dark.css',
    'css!rosmod/Libs/cm/theme/hopscotch.css',
    'css!rosmod/Libs/cm/theme/icecoder.css',
    'css!rosmod/Libs/cm/theme/isotope.css',
    'css!rosmod/Libs/cm/theme/lesser-dark.css',
    'css!rosmod/Libs/cm/theme/liquibyte.css',
    'css!rosmod/Libs/cm/theme/material.css',
    'css!rosmod/Libs/cm/theme/mbo.css',
    'css!rosmod/Libs/cm/theme/mdn-like.css',
    'css!rosmod/Libs/cm/theme/midnight.css',
    'css!rosmod/Libs/cm/theme/monokai.css',
    'css!rosmod/Libs/cm/theme/neat.css',
    'css!rosmod/Libs/cm/theme/neo.css',
    'css!rosmod/Libs/cm/theme/night.css',
    'css!rosmod/Libs/cm/theme/paraiso-dark.css',
    'css!rosmod/Libs/cm/theme/paraiso-light.css',
    'css!rosmod/Libs/cm/theme/pastel-on-dark.css',
    'css!rosmod/Libs/cm/theme/railscasts.css',
    'css!rosmod/Libs/cm/theme/rubyblue.css',
    'css!rosmod/Libs/cm/theme/seti.css',
    'css!rosmod/Libs/cm/theme/solarized.css',
    'css!rosmod/Libs/cm/theme/the-matrix.css',
    'css!rosmod/Libs/cm/theme/tomorrow-night-bright.css',
    'css!rosmod/Libs/cm/theme/tomorrow-night-eighties.css',
    'css!rosmod/Libs/cm/theme/ttcn.css',
    'css!rosmod/Libs/cm/theme/twilight.css',
    'css!rosmod/Libs/cm/theme/vibrant-ink.css',
    'css!rosmod/Libs/cm/theme/xq-dark.css',
    'css!rosmod/Libs/cm/theme/xq-light.css',
    'css!rosmod/Libs/cm/theme/yeti.css',
    'css!rosmod/Libs/cm/theme/zenburn.css',
    'css!rosmod/Libs/cm/addon/fold/foldgutter'
], function (
    ComponentSettings,
    CodeEditorHtml,
    CodeMirror,
    // Syntax Highlighting
    CodeMirrorModeClike,
    CodeMirrorModeMarkdown,
    // Keymaps
    CodeMirrorEmacsKeymap,
    CodeMirrorSublimeKeymap,
    CodeMirrorVimKeymap,
    // Addons
    CodeMirrorShowHint,
    CodeMirrorSearch,
    CodeMirrorSearchCursor,
    CodeMirrorMatchesOnScrollbar,
    CodeMirrorMatchHighlighter,
    CodeMirrorJumpToLine,
    CodeMirrorAnnotateScrollbar,
    CodeMirrorDialog,
    CodeMirrorFullScreen,
    CodeMirrorFoldCode,
    CodeMirrorFoldGutter,
    CodeMirrorBraceFold,
    CodeMirrorXMLFold,
    CodeMirrorMarkdownFold,
    CodeMirrorCommentFold) {
    'use strict';

    var CodeEditorWidget,
    WIDGET_CLASS = 'code-editor',
    cmPercent = '94%';

    CodeEditorWidget = function (logger, container, client) {
        this._logger = logger.fork('Widget');
	this._client = client;
        this._el = container;

	$(this._el).css({
	    'padding': '0'
	});

        this.nodes = {};
        this._initialize();

        this._logger.debug('ctor finished');
    };

    CodeEditorWidget.getName = function () {
        return 'CodeEditorWidget';
    };

    CodeEditorWidget.getVersion = function () {
        return '0.1.0';
    };

    CodeEditorWidget.getDefaultConfig = function () {
        return {
	    'theme': 'default',
	    'keyBindings': 'sublime'
	};
    };

    CodeEditorWidget.getComponentId = function () {
        return 'CodeEditorWidget';
    };

    CodeEditorWidget.prototype._initialize = function () {
        var width = this._el.width(),
        height = this._el.height(),
        self = this;

	this._config = CodeEditorWidget.getDefaultConfig();
	ComponentSettings.resolveWithWebGMEGlobal(this._config, CodeEditorWidget.getComponentId());

        // set widget class
        //this._el.addClass(WIDGET_CLASS);

        // Create the CodeEditor and options
	this._readOnly = this._client.isProjectReadOnly();
	this._fullscreen = false;
        this._el.append(CodeEditorHtml);
	this._container = this._el.find('#CODE_EDITOR_DIV').first();
	this._codearea = this._el.find('#codearea').first();
	this._title = this._el.find('#code_editor_title');
	this.selectedAttribute = '';
	this.selectedNode = '';

	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 = {
	    readOnly: this._readOnly,
	    lineNumbers: true,
	    matchBrackets: true,
	    //viewPortMargin: Infinity,
	    keyMap: this._config.keyBindings,
	    path: 'rosmod/Libs/cm/lib/',
	    theme: this._config.theme,
	    fullscreen: false,
	    foldGutter: true,
	    gutters: ["CodeMirror-linenumbers", "CodeMirror-foldgutter"]
	};
	this.editor = new CodeMirror.fromTextArea(
	    this._codearea.get(0),
	    CodeMirrorEditorOptions
	);

	this.editor.on(
	    'changes',
	    _.debounce(this.saveChanges.bind(this), 250) // make sure this doesn't fire more than every 250 ms
	);

	var self=this;
	this.editor.setOption("extraKeys", {
	    'F11': function(cm) {
		//cm.setOption('fullScreen', !cm.getOption('fullScreen'));
		self.fullScreen(!self._fullScreen);
	    },
	    'Esc': function(cm) {
		//cm.setOption('fullScreen', false);
		self.fullScreen(false);
	    },
	    "Ctrl-Q": function(cm){
		cm.foldCode(cm.getCursor());
	    }
	});

	this.editor.foldCode(CodeMirror.Pos(0, 0));
	// THEME SELECT
	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
	this.kb_select = this._el.find("#kb_select").first();
	$(this.kb_select).val(this._config.keyBindings);
	this.kb_select.on('change', this.selectKeyBinding.bind(this));

	this.buffer_select = this._el.find("#buffer_select").first();
	this.buffer_select.on('change', this.selectBuffer.bind(this));

	this.docs = {};
	$('.CodeMirror').css({
	    height: cmPercent
	});
    };

    CodeEditorWidget.prototype.fullScreen = function(toFullScreen) {
	if (toFullScreen) {
	    var container = $(this._el).find('#CODE_EDITOR_DIV').first();
	    $(container).css({
		position: 'fixed',
		top: '0',
		left: '0',
		width: '100%',
		height: '100%'
	    });
	    $(container).zIndex(9999);
	    $(container).prependTo(document.body);
	    this.editor.focus();
	    this._fullScreen = true;
	}
	else {
	    var container = $(document).find('#CODE_EDITOR_DIV').first();
	    $(container).css({
		position: '',
		top: '',
		left: '',
		width: '100%',
		height: '100%'
	    });
	    $(container).zIndex('auto');
	    $(container).appendTo(this._el);
	    this.editor.focus();
	    this._fullScreen = false;
	}
	this.editor.refresh();
    };

    CodeEditorWidget.prototype.saveChanges = function(cm, changes) {
	try {
	    this._client.setAttributes(this.selectedNode, this.selectedAttribute, cm.getValue());
	}
	catch (e) {
	    this._logger.error('Saving META failed!');
	}
    };

    CodeEditorWidget.prototype.selectBuffer = function(event) {
	var buffer_select = event.target;
	var newAttribute = buffer_select.options[buffer_select.selectedIndex].textContent;
	if (newAttribute) {
	    var newDoc = this.docs[newAttribute];
	    this.docs[this.selectedAttribute] = this.editor.swapDoc(newDoc);
	    this.selectedAttribute = newAttribute;
	    this.editor.refresh();
	}
    };

    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();
	this.editor.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.keyBindings = binding;
	this.saveConfig();
	this.editor.setOption("keyMap", binding);
    };

    CodeEditorWidget.prototype.onWidgetContainerResize = function (width, height) {
        console.log('Widget is resizing...');
    };

    // Adding/Removing/Updating items
    CodeEditorWidget.prototype.addNode = function (desc) {
	var self = this;
        if (desc) {
	    $(self._title).text(desc.name);
	    var attributeNames = Object.keys(desc.codeAttributes);
	    if (attributeNames.length > 0) {
		self.nodes[desc.id] = desc;
		self.selectedNode = desc.id;
		attributeNames.map(function(attributeName) {
		    // add the attributes to buffers
		    self.docs[attributeName] = new CodeMirror.Doc(desc.codeAttributes[attributeName].value,
								  desc.codeAttributes[attributeName].mode);
		    $(self.buffer_select).append(new Option(attributeName, attributeName));
		});
		// select the first attribute?
		self.selectedAttribute = attributeNames[0];
		self.editor.swapDoc(self.docs[self.selectedAttribute]);
		self.editor.refresh();
	    }
        }
    };

    CodeEditorWidget.prototype.removeNode = function (gmeId) {
	var self = this;
        var desc = this.nodes[gmeId];
	if(desc) {
	    $(this._el).find('#CODE_EDITOR_DIV').first().detach();
            delete this.nodes[gmeId];
	}
    };

    CodeEditorWidget.prototype.updateNode = function (desc) {
	var self = this;
        if (desc) {
	    var attributeNames = Object.keys(desc.codeAttributes);
	    if (attributeNames.length > 0) {
		self.nodes[desc.id] = desc;
		attributeNames.map(function(attributeName) {
		    var cursor = self.docs[attributeName].getCursor();
		    var lineCount = self.docs[attributeName].lineCount();
		    self._logger.error(cursor);
		    self.docs[attributeName].replaceRange(
			desc.codeAttributes[attributeName].value,
			{line:0, ch: 0},
			{line:lineCount}
		    );
		    self.docs[attributeName].setCursor(cursor);
		});
		self.editor.refresh();
	    }
        }
    };

    /* * * * * * * * Visualizer event handlers * * * * * * * */

    CodeEditorWidget.prototype.onNodeClick = function (id) {
        // This currently changes the active node to the given id and
        // this is overridden in the controller.
    };

    /* * * * * * * * Visualizer life cycle callbacks * * * * * * * */
    CodeEditorWidget.prototype.destroy = function () {
    };

    CodeEditorWidget.prototype.onActivate = function () {
        //console.log('CodeEditorWidget has been activated');
    };

    CodeEditorWidget.prototype.onDeactivate = function () {
        //console.log('CodeEditorWidget has been deactivated');
    };

    return CodeEditorWidget;
});
<commit_msg>updated to only autosave after at least 1 second.<commit_after>/*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).
 */

define([
    'js/Utils/ComponentSettings',
    // HTML
    'text!./CodeEditor.html',
    // Codemirror
    'rosmod/Libs/cm/lib/codemirror',
    // Syntax highlighting
    'rosmod/Libs/cm/mode/clike/clike',
    'rosmod/Libs/cm/mode/markdown/markdown',
    // Keymaps
    'rosmod/Libs/cm/keymap/emacs',
    'rosmod/Libs/cm/keymap/sublime',
    'rosmod/Libs/cm/keymap/vim',
    // Addons
    'rosmod/Libs/cm/addon/hint/show-hint',
    'rosmod/Libs/cm/addon/search/search',
    'rosmod/Libs/cm/addon/search/searchcursor',
    'rosmod/Libs/cm/addon/search/matchesonscrollbar',
    'rosmod/Libs/cm/addon/search/match-highlighter',
    'rosmod/Libs/cm/addon/search/jump-to-line',
    'rosmod/Libs/cm/addon/scroll/annotatescrollbar',
    'rosmod/Libs/cm/addon/dialog/dialog',
    'rosmod/Libs/cm/addon/display/fullscreen',
    'rosmod/Libs/cm/addon/fold/foldcode',
    'rosmod/Libs/cm/addon/fold/foldgutter',
    'rosmod/Libs/cm/addon/fold/brace-fold',
    'rosmod/Libs/cm/addon/fold/xml-fold',
    'rosmod/Libs/cm/addon/fold/markdown-fold',
    'rosmod/Libs/cm/addon/fold/comment-fold',
    // CSS
    'css!./styles/CodeEditorWidget.css',
    'css!rosmod/Libs/cm/addon/hint/show-hint.css',
    'css!rosmod/Libs/cm/addon/search/matchesonscrollbar.css',
    'css!rosmod/Libs/cm/addon/dialog/dialog.css',
    'css!rosmod/Libs/cm/addon/display/fullscreen.css',
    'css!rosmod/Libs/cm/theme/night.css',
    'css!rosmod/Libs/cm/lib/codemirror.css',
    'css!rosmod/Libs/cm/theme/3024-day.css',
    'css!rosmod/Libs/cm/theme/3024-night.css',
    'css!rosmod/Libs/cm/theme/abcdef.css',
    'css!rosmod/Libs/cm/theme/ambiance.css',
    'css!rosmod/Libs/cm/theme/base16-dark.css',
    'css!rosmod/Libs/cm/theme/bespin.css',
    'css!rosmod/Libs/cm/theme/base16-light.css',
    'css!rosmod/Libs/cm/theme/blackboard.css',
    'css!rosmod/Libs/cm/theme/cobalt.css',
    'css!rosmod/Libs/cm/theme/colorforth.css',
    'css!rosmod/Libs/cm/theme/dracula.css',
    'css!rosmod/Libs/cm/theme/eclipse.css',
    'css!rosmod/Libs/cm/theme/elegant.css',
    'css!rosmod/Libs/cm/theme/erlang-dark.css',
    'css!rosmod/Libs/cm/theme/hopscotch.css',
    'css!rosmod/Libs/cm/theme/icecoder.css',
    'css!rosmod/Libs/cm/theme/isotope.css',
    'css!rosmod/Libs/cm/theme/lesser-dark.css',
    'css!rosmod/Libs/cm/theme/liquibyte.css',
    'css!rosmod/Libs/cm/theme/material.css',
    'css!rosmod/Libs/cm/theme/mbo.css',
    'css!rosmod/Libs/cm/theme/mdn-like.css',
    'css!rosmod/Libs/cm/theme/midnight.css',
    'css!rosmod/Libs/cm/theme/monokai.css',
    'css!rosmod/Libs/cm/theme/neat.css',
    'css!rosmod/Libs/cm/theme/neo.css',
    'css!rosmod/Libs/cm/theme/night.css',
    'css!rosmod/Libs/cm/theme/paraiso-dark.css',
    'css!rosmod/Libs/cm/theme/paraiso-light.css',
    'css!rosmod/Libs/cm/theme/pastel-on-dark.css',
    'css!rosmod/Libs/cm/theme/railscasts.css',
    'css!rosmod/Libs/cm/theme/rubyblue.css',
    'css!rosmod/Libs/cm/theme/seti.css',
    'css!rosmod/Libs/cm/theme/solarized.css',
    'css!rosmod/Libs/cm/theme/the-matrix.css',
    'css!rosmod/Libs/cm/theme/tomorrow-night-bright.css',
    'css!rosmod/Libs/cm/theme/tomorrow-night-eighties.css',
    'css!rosmod/Libs/cm/theme/ttcn.css',
    'css!rosmod/Libs/cm/theme/twilight.css',
    'css!rosmod/Libs/cm/theme/vibrant-ink.css',
    'css!rosmod/Libs/cm/theme/xq-dark.css',
    'css!rosmod/Libs/cm/theme/xq-light.css',
    'css!rosmod/Libs/cm/theme/yeti.css',
    'css!rosmod/Libs/cm/theme/zenburn.css',
    'css!rosmod/Libs/cm/addon/fold/foldgutter'
], function (
    ComponentSettings,
    CodeEditorHtml,
    CodeMirror,
    // Syntax Highlighting
    CodeMirrorModeClike,
    CodeMirrorModeMarkdown,
    // Keymaps
    CodeMirrorEmacsKeymap,
    CodeMirrorSublimeKeymap,
    CodeMirrorVimKeymap,
    // Addons
    CodeMirrorShowHint,
    CodeMirrorSearch,
    CodeMirrorSearchCursor,
    CodeMirrorMatchesOnScrollbar,
    CodeMirrorMatchHighlighter,
    CodeMirrorJumpToLine,
    CodeMirrorAnnotateScrollbar,
    CodeMirrorDialog,
    CodeMirrorFullScreen,
    CodeMirrorFoldCode,
    CodeMirrorFoldGutter,
    CodeMirrorBraceFold,
    CodeMirrorXMLFold,
    CodeMirrorMarkdownFold,
    CodeMirrorCommentFold) {
    'use strict';

    var CodeEditorWidget,
    WIDGET_CLASS = 'code-editor',
    cmPercent = '94%';

    CodeEditorWidget = function (logger, container, client) {
        this._logger = logger.fork('Widget');
	this._client = client;
        this._el = container;

	$(this._el).css({
	    'padding': '0'
	});

        this.nodes = {};
        this._initialize();

        this._logger.debug('ctor finished');
    };

    CodeEditorWidget.getName = function () {
        return 'CodeEditorWidget';
    };

    CodeEditorWidget.getVersion = function () {
        return '0.1.0';
    };

    CodeEditorWidget.getDefaultConfig = function () {
        return {
	    'theme': 'default',
	    'keyBindings': 'sublime'
	};
    };

    CodeEditorWidget.getComponentId = function () {
        return 'CodeEditorWidget';
    };

    CodeEditorWidget.prototype._initialize = function () {
        var width = this._el.width(),
        height = this._el.height(),
        self = this;

	this._config = CodeEditorWidget.getDefaultConfig();
	ComponentSettings.resolveWithWebGMEGlobal(this._config, CodeEditorWidget.getComponentId());

        // set widget class
        //this._el.addClass(WIDGET_CLASS);

        // Create the CodeEditor and options
	this._readOnly = this._client.isProjectReadOnly();
	this._fullscreen = false;
        this._el.append(CodeEditorHtml);
	this._container = this._el.find('#CODE_EDITOR_DIV').first();
	this._codearea = this._el.find('#codearea').first();
	this._title = this._el.find('#code_editor_title');
	this.selectedAttribute = '';
	this.selectedNode = '';

	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 = {
	    readOnly: this._readOnly,
	    lineNumbers: true,
	    matchBrackets: true,
	    //viewPortMargin: Infinity,
	    keyMap: this._config.keyBindings,
	    path: 'rosmod/Libs/cm/lib/',
	    theme: this._config.theme,
	    fullscreen: false,
	    foldGutter: true,
	    gutters: ["CodeMirror-linenumbers", "CodeMirror-foldgutter"]
	};
	this.editor = new CodeMirror.fromTextArea(
	    this._codearea.get(0),
	    CodeMirrorEditorOptions
	);

	this.editor.on(
	    'changes',
	    _.debounce(this.saveChanges.bind(this), 1000) // make sure this doesn't fire more than every second
	);

	var self=this;
	this.editor.setOption("extraKeys", {
	    'F11': function(cm) {
		//cm.setOption('fullScreen', !cm.getOption('fullScreen'));
		self.fullScreen(!self._fullScreen);
	    },
	    'Esc': function(cm) {
		//cm.setOption('fullScreen', false);
		self.fullScreen(false);
	    },
	    "Ctrl-Q": function(cm){
		cm.foldCode(cm.getCursor());
	    }
	});

	this.editor.foldCode(CodeMirror.Pos(0, 0));
	// THEME SELECT
	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
	this.kb_select = this._el.find("#kb_select").first();
	$(this.kb_select).val(this._config.keyBindings);
	this.kb_select.on('change', this.selectKeyBinding.bind(this));

	this.buffer_select = this._el.find("#buffer_select").first();
	this.buffer_select.on('change', this.selectBuffer.bind(this));

	this.docs = {};
	$('.CodeMirror').css({
	    height: cmPercent
	});
    };

    CodeEditorWidget.prototype.fullScreen = function(toFullScreen) {
	if (toFullScreen) {
	    var container = $(this._el).find('#CODE_EDITOR_DIV').first();
	    $(container).css({
		position: 'fixed',
		top: '0',
		left: '0',
		width: '100%',
		height: '100%'
	    });
	    $(container).zIndex(9999);
	    $(container).prependTo(document.body);
	    this.editor.focus();
	    this._fullScreen = true;
	}
	else {
	    var container = $(document).find('#CODE_EDITOR_DIV').first();
	    $(container).css({
		position: '',
		top: '',
		left: '',
		width: '100%',
		height: '100%'
	    });
	    $(container).zIndex('auto');
	    $(container).appendTo(this._el);
	    this.editor.focus();
	    this._fullScreen = false;
	}
	this.editor.refresh();
    };

    CodeEditorWidget.prototype.saveChanges = function(cm, changes) {
	try {
	    this._client.setAttributes(this.selectedNode, this.selectedAttribute, cm.getValue());
	}
	catch (e) {
	    this._logger.error('Saving META failed!');
	}
    };

    CodeEditorWidget.prototype.selectBuffer = function(event) {
	var buffer_select = event.target;
	var newAttribute = buffer_select.options[buffer_select.selectedIndex].textContent;
	if (newAttribute) {
	    var newDoc = this.docs[newAttribute];
	    this.docs[this.selectedAttribute] = this.editor.swapDoc(newDoc);
	    this.selectedAttribute = newAttribute;
	    this.editor.refresh();
	}
    };

    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();
	this.editor.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.keyBindings = binding;
	this.saveConfig();
	this.editor.setOption("keyMap", binding);
    };

    CodeEditorWidget.prototype.onWidgetContainerResize = function (width, height) {
        console.log('Widget is resizing...');
    };

    // Adding/Removing/Updating items
    CodeEditorWidget.prototype.addNode = function (desc) {
	var self = this;
        if (desc) {
	    $(self._title).text(desc.name);
	    var attributeNames = Object.keys(desc.codeAttributes);
	    if (attributeNames.length > 0) {
		self.nodes[desc.id] = desc;
		self.selectedNode = desc.id;
		attributeNames.map(function(attributeName) {
		    // add the attributes to buffers
		    self.docs[attributeName] = new CodeMirror.Doc(desc.codeAttributes[attributeName].value,
								  desc.codeAttributes[attributeName].mode);
		    $(self.buffer_select).append(new Option(attributeName, attributeName));
		});
		// select the first attribute?
		self.selectedAttribute = attributeNames[0];
		self.editor.swapDoc(self.docs[self.selectedAttribute]);
		self.editor.refresh();
	    }
        }
    };

    CodeEditorWidget.prototype.removeNode = function (gmeId) {
	var self = this;
        var desc = this.nodes[gmeId];
	if(desc) {
	    $(this._el).find('#CODE_EDITOR_DIV').first().detach();
            delete this.nodes[gmeId];
	}
    };

    CodeEditorWidget.prototype.updateNode = function (desc) {
	var self = this;
        if (desc) {
	    var attributeNames = Object.keys(desc.codeAttributes);
	    if (attributeNames.length > 0) {
		self.nodes[desc.id] = desc;
		attributeNames.map(function(attributeName) {
		    var cursor = self.docs[attributeName].getCursor();
		    var lineCount = self.docs[attributeName].lineCount();
		    self._logger.error(cursor);
		    self.docs[attributeName].replaceRange(
			desc.codeAttributes[attributeName].value,
			{line:0, ch: 0},
			{line:lineCount}
		    );
		    self.docs[attributeName].setCursor(cursor);
		});
		self.editor.refresh();
	    }
        }
    };

    /* * * * * * * * Visualizer event handlers * * * * * * * */

    CodeEditorWidget.prototype.onNodeClick = function (id) {
        // This currently changes the active node to the given id and
        // this is overridden in the controller.
    };

    /* * * * * * * * Visualizer life cycle callbacks * * * * * * * */
    CodeEditorWidget.prototype.destroy = function () {
    };

    CodeEditorWidget.prototype.onActivate = function () {
        //console.log('CodeEditorWidget has been activated');
    };

    CodeEditorWidget.prototype.onDeactivate = function () {
        //console.log('CodeEditorWidget has been deactivated');
    };

    return CodeEditorWidget;
});
<|endoftext|>


<commit_before>requirejs.config({
    paths: {
        'text': '../Scripts/text',
        'durandal': '../Scripts/durandal',
        'plugins': '../Scripts/durandal/plugins',
        'transitions': '../Scripts/durandal/transitions'
    }
});

define('jquery', function() { return jQuery; });
define('knockout', ko);

define(['durandal/system', 'durandal/app', 'durandal/viewLocator'],  function (system, app, viewLocator) {
    //>>excludeStart("build", true);
    system.debug(true);
    //>>excludeEnd("build");

    app.title = 'ZUBINRAJ.COM';

    app.configurePlugins({
        router: true,
        dialog: true,
        widget: true
    });

    app.start().then(function() {
        //Replace 'viewmodels' in the moduleId with 'views' to locate the view.
        //Look for partial views in a 'views' folder in the root.
        viewLocator.useConvention();

        //Show the app by setting the root view model for our application with a transition.
        app.setRoot('viewmodels/shell', 'entrance');
    });
});<commit_msg>Added cache buster for debug mode<commit_after>requirejs.config({
    //urlArgs: "version=0.1",
    urlArgs: "bust=" + (new Date().getTime()),   // cache buster to load new version while debugging. Todo: comment out defore deployment
    paths: {
        'text': '../Scripts/text',
        'durandal': '../Scripts/durandal',
        'plugins': '../Scripts/durandal/plugins',
        'transitions': '../Scripts/durandal/transitions'
    }
});

define('jquery', function() { return jQuery; });
define('knockout', ko);

define(['durandal/system', 'durandal/app', 'durandal/viewLocator'],  function (system, app, viewLocator) {
    //>>excludeStart("build", true);
    system.debug(true);
    //>>excludeEnd("build");

    app.title = 'ZUBINRAJ.COM';

    app.configurePlugins({
        router: true,
        dialog: true,
        widget: true
    });

    app.start().then(function() {
        //Replace 'viewmodels' in the moduleId with 'views' to locate the view.
        //Look for partial views in a 'views' folder in the root.
        viewLocator.useConvention();

        //Show the app by setting the root view model for our application with a transition.
        app.setRoot('viewmodels/shell', 'entrance');
    });
});<|endoftext|>


<commit_before>/*
	InfluxData.com Developer Documentation
	--------------------------------------
	UI Interactions
*/

/* Toggle product switcher from hamburger */
$('#navbar--hamburger').click( function() {
	$(this).toggleClass('open');
	$('#navbar--product-container').toggleClass('open');
});

/* Open product switcher from dropdown */
$('#navbar--dropdown').click( function() {
	$(this).toggleClass('open');
	$('#navbar--product-container').toggleClass('open');
});

/* Close product switcher from "Dismiss Overlay" */
$('#navbar--dropdown-dismiss').click( function() {
	$('#navbar--dropdown').removeClass('open');
	$('#navbar--product-container').removeClass('open');
});
/* Close product switcher by clicking a link */
$('#navbar--product-menu').click( function() {
	$('#navbar--hamburger').removeClass('open');
	$('#navbar--product-container').removeClass('open');
	$('#navbar--dropdown').removeClass('open');
});

/* Open Sidebar */
$('#sidebar--toggle').click( function() {
	$('#sidebar, .sidebar--toggle').toggleClass('open');
});

function getRandomIntInclusive(min, max) {
  return Math.floor(Math.random() * (max - min + 1)) + min;
}

$(function(){
	/* Inject tooltips on load */
	$('.tooltip').each( function(){
		$toolTipText = $('<div/>').addClass('tooltip-text').text($(this).attr('data-tooltip-text'));
		$toolTipElement = $('<div/>').addClass('tooltip-container').append($toolTipText);
		$(this).prepend($toolTipElement);
	});

	/* Set random header image */
	var imagePathString = "/img/header-images/header-image-"+getRandomIntInclusive(1,14)+".jpg";
	$('#page-title').css('background-image','url('+imagePathString+')');
});

/**
 * Behavior for the tabs widget.
 */
$(function() {
	const container = '.tabs-container'
	const tab = '.tabs p a';
	const content = '.tab-content';

	// Add the active class to the first tab, in case it wasn't already set in the markup.
	$(container).each(function () {
		$(tab, this).removeClass('is-active');
		$(tab + ':first', this).addClass('is-active');
	});

	$(tab).on('click', function(e) {
		e.preventDefault();

		// Make sure the tab being clicked is marked as active, and make the rest inactive.
		$(this).addClass('is-active').siblings().removeClass('is-active');

		// Render the correct tab content based on the position of the tab being clicked.
		const activeIndex = $(tab).index(this);
		$(content).each(function(i) {
			if (i === activeIndex) {
				$(this).show();
			} else {
				$(this).hide();
			}
		});
		console.log(activeIndex);
	});

});

// Randomize advertisement content on page load
// Ad content stored in object

$advertContent = [
  {
    ctaText: "Try InfluxCloud",
    ctaLink: "https://cloud.influxdata.com/",
    advertText: "Collect, store, and retrieve time-series data in one minute.",
    style: "support-ad--cloud",
  },
  {
    ctaText: "Try InfluxEnterprise",
    ctaLink: "https://portal.influxdata.com/",
    advertText: "Unlock powerful insights that help you delight your customers.",
    style: "support-ad--enterprise",
  },
]
$(document).on('ready', function(){
  $numAdverts = $advertContent.length;
  $randomizer = Math.floor(Math.random() * $numAdverts);
  $contentToInject = $advertContent[$randomizer];

  $ctaButton = $('<a />')
  	.addClass('sidebar--support-ad--cta')
  	.attr({
  		'href': $contentToInject.ctaLink,
  		'target': '_blank'
  	})
  	.text($contentToInject.ctaText);
  $advertText = $('<p />')
  	.addClass('sidebar--support-ad--desc')
  	.text($contentToInject.advertText);

  $('#support-ad')
  	.addClass($contentToInject.style)
  	.append($advertText,$ctaButton);
});

/*
 * Add class to outbound title links on section landing pages
 * Force external links to open in a new tab/window
 * Handle headings without content
 */

$(document).ready( function(){
	// Add arrow icon if h2 links go to a different doc
  $('h2 a:not([href^="#"])').addClass('off-page');

	// Force links to other sites open in a new tab/window
	$('.article-content a[href^="http"]').attr('target', '_blank');

	// Remove heading border if not followed by a paragraph
	$('h2').each(function() {
		var hasParagraph = $(this).next('p, ol, ul, table, code, pre').length
		if(hasParagraph == 0) {
			$(this).addClass('no-paragraph');
		};
	});
});

/*
 * View in Chronograf button and settings
 */

$(document).ready( function(){

	// Inject the "View in Chronograf" button html on page load
	addChronografBtnHtml()

	// Open Chronograf settings modal
  $('.chronograf-url-settings').click(function() {
		var chronografUrlValue = getChronografUrl()
		openModal('#chronograf-url-modal');
		// Update the value of the chronograf url field using the saved cookie
		$('input#chronograf-url').val(chronografUrlValue)
	})

	// Close the modal by clicking anywhere but the modal
	$('.modal-overlay').click(function() {
		closeModal();
	})

	// Save Chronograf URL
	$('#save-chronograf-url').submit(function(e){
		e.preventDefault();
		var newChronografUrl = $('input#chronograf-url').val()

		// 1. Update cookie
		updateChronografUrl(newChronografUrl);

		// 2. Update html
		updateChronografBtnHtml(newChronografUrl);

		// 3. Button lifecycle
		saveBtnLifecycle('.save-btn', 150)
		setTimeout(function(){
			closeModal();
		}, 1000)

	})

	// Revert to default Chronograf URL
	$('#default-chronograf-url').click(function() {
		revertToDefault();
	})
})

/*
 * Reposition sidebar on vertical scroll
 */

$(document).ready( function() {
	var offsetElement = $("#main-nav")
	element = $("#sidebar")

	$("body").scroll(function() {
		var pos = offsetElement.offset().top + offsetElement.outerHeight(true);
		if ( pos < 0 ) {
			$(element).css("top", "0px");
		} else if ( pos <= 64 ){
			$(element).css("top", pos + "px");
		}
	})
})
<commit_msg>fixed tab toggle so it only toggles content under tabs when multiple tab groups are present<commit_after>/*
	InfluxData.com Developer Documentation
	--------------------------------------
	UI Interactions
*/

/* Toggle product switcher from hamburger */
$('#navbar--hamburger').click( function() {
	$(this).toggleClass('open');
	$('#navbar--product-container').toggleClass('open');
});

/* Open product switcher from dropdown */
$('#navbar--dropdown').click( function() {
	$(this).toggleClass('open');
	$('#navbar--product-container').toggleClass('open');
});

/* Close product switcher from "Dismiss Overlay" */
$('#navbar--dropdown-dismiss').click( function() {
	$('#navbar--dropdown').removeClass('open');
	$('#navbar--product-container').removeClass('open');
});
/* Close product switcher by clicking a link */
$('#navbar--product-menu').click( function() {
	$('#navbar--hamburger').removeClass('open');
	$('#navbar--product-container').removeClass('open');
	$('#navbar--dropdown').removeClass('open');
});

/* Open Sidebar */
$('#sidebar--toggle').click( function() {
	$('#sidebar, .sidebar--toggle').toggleClass('open');
});

function getRandomIntInclusive(min, max) {
  return Math.floor(Math.random() * (max - min + 1)) + min;
}

$(function(){
	/* Inject tooltips on load */
	$('.tooltip').each( function(){
		$toolTipText = $('<div/>').addClass('tooltip-text').text($(this).attr('data-tooltip-text'));
		$toolTipElement = $('<div/>').addClass('tooltip-container').append($toolTipText);
		$(this).prepend($toolTipElement);
	});

	/* Set random header image */
	var imagePathString = "/img/header-images/header-image-"+getRandomIntInclusive(1,14)+".jpg";
	$('#page-title').css('background-image','url('+imagePathString+')');
});

/**
 * Behavior for the tabs widget.
 */
$(function() {
	const container = '.tabs-container'
	const tab = '.tabs p a';
	const content = '.tab-content';

	// Add the active class to the first tab in each tab group,
	// in case it wasn't already set in the markup.
	$(container).each(function () {
		$(tab, this).removeClass('is-active');
		$(tab + ':first', this).addClass('is-active');
	});

	$(tab).on('click', function(e) {
		e.preventDefault();

		// Make sure the tab being clicked is marked as active, and make the rest inactive.
		$(this).addClass('is-active').siblings().removeClass('is-active');

		// Render the correct tab content based on the position of the tab being clicked.
		const activeIndex = $(tab).index(this);
		$(content).each(function(i) {
			if (i === activeIndex) {
				$(this).show();
				$(this).siblings(content).hide();
			}
		});
		console.log(activeIndex);
	});

});

// Randomize advertisement content on page load
// Ad content stored in object

$advertContent = [
  {
    ctaText: "Try InfluxCloud",
    ctaLink: "https://cloud.influxdata.com/",
    advertText: "Collect, store, and retrieve time-series data in one minute.",
    style: "support-ad--cloud",
  },
  {
    ctaText: "Try InfluxEnterprise",
    ctaLink: "https://portal.influxdata.com/",
    advertText: "Unlock powerful insights that help you delight your customers.",
    style: "support-ad--enterprise",
  },
]
$(document).on('ready', function(){
  $numAdverts = $advertContent.length;
  $randomizer = Math.floor(Math.random() * $numAdverts);
  $contentToInject = $advertContent[$randomizer];

  $ctaButton = $('<a />')
  	.addClass('sidebar--support-ad--cta')
  	.attr({
  		'href': $contentToInject.ctaLink,
  		'target': '_blank'
  	})
  	.text($contentToInject.ctaText);
  $advertText = $('<p />')
  	.addClass('sidebar--support-ad--desc')
  	.text($contentToInject.advertText);

  $('#support-ad')
  	.addClass($contentToInject.style)
  	.append($advertText,$ctaButton);
});

/*
 * Add class to outbound title links on section landing pages
 * Force external links to open in a new tab/window
 * Handle headings without content
 */

$(document).ready( function(){
	// Add arrow icon if h2 links go to a different doc
  $('h2 a:not([href^="#"])').addClass('off-page');

	// Force links to other sites open in a new tab/window
	$('.article-content a[href^="http"]').attr('target', '_blank');

	// Remove heading border if not followed by a paragraph
	$('h2').each(function() {
		var hasParagraph = $(this).next('p, ol, ul, table, code, pre').length
		if(hasParagraph == 0) {
			$(this).addClass('no-paragraph');
		};
	});
});

/*
 * View in Chronograf button and settings
 */

$(document).ready( function(){

	// Inject the "View in Chronograf" button html on page load
	addChronografBtnHtml()

	// Open Chronograf settings modal
  $('.chronograf-url-settings').click(function() {
		var chronografUrlValue = getChronografUrl()
		openModal('#chronograf-url-modal');
		// Update the value of the chronograf url field using the saved cookie
		$('input#chronograf-url').val(chronografUrlValue)
	})

	// Close the modal by clicking anywhere but the modal
	$('.modal-overlay').click(function() {
		closeModal();
	})

	// Save Chronograf URL
	$('#save-chronograf-url').submit(function(e){
		e.preventDefault();
		var newChronografUrl = $('input#chronograf-url').val()

		// 1. Update cookie
		updateChronografUrl(newChronografUrl);

		// 2. Update html
		updateChronografBtnHtml(newChronografUrl);

		// 3. Button lifecycle
		saveBtnLifecycle('.save-btn', 150)
		setTimeout(function(){
			closeModal();
		}, 1000)

	})

	// Revert to default Chronograf URL
	$('#default-chronograf-url').click(function() {
		revertToDefault();
	})
})

/*
 * Reposition sidebar on vertical scroll
 */

$(document).ready( function() {
	var offsetElement = $("#main-nav")
	element = $("#sidebar")

	$("body").scroll(function() {
		var pos = offsetElement.offset().top + offsetElement.outerHeight(true);
		if ( pos < 0 ) {
			$(element).css("top", "0px");
		} else if ( pos <= 64 ){
			$(element).css("top", pos + "px");
		}
	})
})
<|endoftext|>


<commit_before>/**
 * Unit tests for checking that a full game works. It's tedious to repeat very
 * long sequence with GUI, but this module makes sure that basics are working.
 */

const expect = require('chai').expect;
const RG = require('../../../client/src/battles');
const RGTest = require('../../roguetest.js');
const Game = require('../../../client/src/game');

const checkXY = RGTest.checkActorXY;
const Actor = RG.Actor.Rogue;

const RGObjects = require('../../../client/data/battles_objects.js');
RG.Effects = require('../../../client/data/effects.js');

const globalParser = new RG.ObjectShell.Parser();
globalParser.parseShellData(RG.Effects);
globalParser.parseShellData(RGObjects);

function checkMap(map, cols, rows) {
    for (let x = 0; x < cols; x++) {
        for (let y = 0; y < rows; y++) {
            // console.log("x :: " + x);
            expect(typeof map.getCell(x, y)).not.to.equal('undefined');
        }
    }
}

function getNewLevel(cols, rows) {
    return RG.FACT.createLevel('arena', cols, rows);
}

describe('Game.Main', () => {
    let game = null;
    beforeEach( () => {
        game = new Game.Main();
    });

    it('Initializes the game and adds player', () => {
        const movSystem = new RG.System.Movement(['Movement']);
        const cols = 50;
        const rows = 30;
        const level = getNewLevel(cols, rows);

        const actor = new Actor('Player'); // player
        actor.setIsPlayer(true);
        actor.setFOVRange(5);
        game.addLevel(level);
        expect(game.addPlayer(actor)).to.equal(true);

        expect(game.shownLevel()).to.equal(level);
        expect(actor.getLevel()).to.equal(level);

        const newMap = level.getMap();
        checkMap(newMap, cols, rows);

        const movComp = new RG.Component.Movement(12, 13, level);
        actor.add('Movement', movComp);
        movSystem.update();
        expect(actor.getX()).to.equal(12);
        expect(actor.getY()).to.equal(13);

        const explCells = level.exploreCells(actor);
        expect(explCells.length).to.equal(145);
    });

    it('can add player to the specified location', () => {
        const fact = new RG.Factory.World();
        const worldConf = {
            name: 'StartPlace',
            nAreas: 1, area: [{name: 'a1', maxX: 2, maxY: 2}]
        };
        const world = fact.createWorld(worldConf);
        const game = new RG.Game.Main();
        game.addPlace(world);
        expect(game.getLevels()).to.have.length(4);

        const player = new RG.Actor.Rogue('PlayerHero');
        player.setIsPlayer(true);
        game.addPlayer(player, {place: 'StartPlace', x: 1, y: 1});

        const playerLevelID = player.getLevel().getID();
        const area = world.getAreas()[0];
        const expectedLevelID = area.getTileXY(1, 1).getLevel().getID();
        expect(playerLevelID).to.equal(expectedLevelID);
    });
});

/* For listening actor killed events.*/
const KillListener = function(actor) {

    const _actor = actor;

    this.isAlive = actor.get('Health').isAlive();

    this.notify = function(evtName, obj) {
        if (evtName === RG.EVT_ACTOR_KILLED) {
            if (obj.actor === _actor) {
                this.isAlive = false;
            }
        }
    };
    RG.POOL.listenEvent(RG.EVT_ACTOR_KILLED, this);
};

describe('How combat should evolve', () => {

    it('Deals damage from attacker to defender', () => {
        const comSystem = new RG.System.Attack(['Attack']);
        const dmgSystem = new RG.System.Damage(['Damage']);

        const cols = 50;
        const rows = 30;
        const level = getNewLevel(cols, rows);

        const attacker = new Actor('Attacker');
        expect(attacker.get('Health').isAlive()).to.equal(true);
        const defender = new Actor('Defender');
        expect(defender.get('Health').isAlive()).to.equal(true);
        attacker.get('Combat').setAttack(10);
        attacker.get('Combat').setDamageDie('1d4');
        defender.get('Health').setHP(1);
        defender.get('Combat').setDefense(0);
        defender.get('Combat').setProtection(0);
        defender.get('Stats').setAgility(0);

        level.addActor(attacker, 1, 1);
        level.addActor(defender, 2, 2);

        const attackComp = new RG.Component.Attack(defender);
        attacker.add('Attack', attackComp);
        comSystem.update();
        expect(defender.has('Damage')).to.equal(true);
        dmgSystem.update();
        expect(attacker.has('Attack')).to.equal(false);
        expect(defender.has('Damage')).to.equal(false);

        expect(defender.get('Health').isAlive()).to.equal(false);

        const def2 = new Actor('defender2');
        level.addActor(def2, 2, 2);

        const attComp2 = new RG.Component.Attack(def2);
        attacker.add('Attack', attComp2);

        def2.get('Health').setHP(20);
        def2.get('Combat').setDefense(0);
        def2.get('Stats').setAgility(0);
        expect(def2.get('Health').isAlive()).to.equal(true);

        comSystem.update();
        expect(def2.has('Damage')).to.equal(true);
        dmgSystem.update();
        expect(def2.has('Damage')).to.equal(false);

        expect(def2.get('Health').getHP() < 20).to.equal(true);

        expect(def2.get('Health').isAlive()).to.equal(true);

        attacker.add('Attack', attComp2);
        comSystem.update();
        dmgSystem.update();

        expect(def2.get('Health').isAlive()).to.equal(true);

        const killListen = new KillListener(def2);
        while (killListen.isAlive) {
            attacker.add('Attack', attComp2);
            comSystem.update();
            dmgSystem.update();
        }
        expect(def2.get('Health').isAlive()).to.equal(false);

    });
});

describe('How AI brain works', () => {
    const cols = 30;
    const rows = 20;
    const level = getNewLevel(cols, rows);
    const mons1 = new Actor('Monster');
    const player = new Actor('Player');
    player.setType('player');
    player.setIsPlayer(true);

    it('Brain should find player cell', () => {
        expect(level.addActor(player, 2, 2)).to.equal(true);
        expect(level.addActor(mons1, 3, 5)).to.equal(true);

        const map = level.getMap();
        expect(map.isPassable(2, 3)).to.equal(true);

        const brain = mons1.getBrain();
        const seenCells = level.getMap().getVisibleCells(mons1);
        expect(seenCells.length).to.not.equal(0);
        const playerCell = brain.findEnemyCell(seenCells);
        expect(playerCell.hasProp('actors')).to.equal(true);

        const pathCells = brain.getShortestPathTo(playerCell);
        expect(pathCells).to.be.an('array');
        expect(pathCells.length).to.not.equal(0);
    });

    it('Moves towards player when seen.', () => {
        const movSystem = new RG.System.Movement(['Movement']);
        expect(level.addActor(player, 2, 2)).to.equal(true);
        expect(level.addActor(mons1, 2, 4)).to.equal(true);
        const action = mons1.nextAction();
        action.doAction();
        movSystem.update();
        checkXY(mons1, 2, 3);
    });

});

describe('How poison item is used, and experience propagates', () => {
    it('Kills an actor after some time', () => {

        const game = new RG.Game.Main();
        const level = RG.FACT.createLevel('arena', 20, 20);
        const assassin = new Actor('assassin');
        const poison = globalParser.createActualObj('items',
            'Potion of frost poison');
        assassin.getInvEq().addItem(poison);

        const victim = new Actor('victim');
        victim.get('Health').setHP(5);

        level.addActor(assassin, 3, 5);
        level.addActor(victim, 6, 6);
        poison.useItem({target: level.getMap().getCell(6, 6)});

        const startExp = assassin.get('Experience').getExp();

        let count = 0;
        while (victim.get('Health').isAlive() && count < 100) {
            game.simulateGame();
            ++count;
        }
        expect(count, 'Victim dies in 100 turns').to.be.below(100);
        expect(victim.get('Health', 'Victim is dead').isAlive()).to.be.false;

        const endExp = assassin.get('Experience').getExp();
        expect(endExp > startExp, 'Exp. points from poison').to.equal(true);

        const curePoison = globalParser.createActualObj('items',
            'Potion of cure poison');
        const frostPoison = globalParser.createActualObj('items',
            'Potion of frost poison');

        assassin.getInvEq().addItem(frostPoison);
        const curedVictim = new Actor('Cured victim');

        expect(curedVictim.has('Expiration')).to.equal(false);
        expect(curedVictim.has('Poison')).to.equal(false);
        level.addActor(curedVictim, 4, 4);

        const poisonTarget = level.getMap().getCell(4, 4);
        expect(frostPoison.useItem({target: poisonTarget})).to.equal(true);
        expect(curedVictim.has('Expiration')).to.equal(true);
        expect(curedVictim.has('Poison')).to.equal(true);

        curedVictim.getInvEq().addItem(curePoison);
        game.simulateGame();

        const cureTarget = level.getMap().getCell(4, 4);
        expect(curePoison.useItem({target: cureTarget})).to.equal(true);
        expect(curedVictim.has('Poison')).to.equal(false);
        expect(curedVictim.get('Health').isAlive()).to.equal(true);
        for (let i = 0; i < 20; i++) {game.simulateGame();}
        expect(curedVictim.has('Expiration')).to.equal(false);

    });
});

describe('Game.WinCondition', () => {
    it('description', () => {
        const winCond = new RG.Game.WinCondition('Kill boss');
        expect(winCond.isTrue(), 'Win condition false').to.be.false;

        const boss = new RG.Actor.Rogue('Huge evil boss');
        winCond.addActorKilled(boss);
        RG.POOL.emitEvent(RG.EVT_ACTOR_KILLED, {actor: boss});
        expect(winCond.isTrue(), 'Win condition true now').to.be.true;

    });
});
<commit_msg>Added beforeEach block.<commit_after>/**
 * Unit tests for checking that a full game works. It's tedious to repeat very
 * long sequence with GUI, but this module makes sure that basics are working.
 */

const expect = require('chai').expect;
const RG = require('../../../client/src/battles');
const RGTest = require('../../roguetest.js');
const Game = require('../../../client/src/game');

const checkXY = RGTest.checkActorXY;
const Actor = RG.Actor.Rogue;

const RGObjects = require('../../../client/data/battles_objects.js');
RG.Effects = require('../../../client/data/effects.js');

const globalParser = new RG.ObjectShell.Parser();
globalParser.parseShellData(RG.Effects);
globalParser.parseShellData(RGObjects);

function checkMap(map, cols, rows) {
    for (let x = 0; x < cols; x++) {
        for (let y = 0; y < rows; y++) {
            // console.log("x :: " + x);
            expect(typeof map.getCell(x, y)).not.to.equal('undefined');
        }
    }
}

function getNewLevel(cols, rows) {
    return RG.FACT.createLevel('arena', cols, rows);
}

describe('Game.Main', () => {
    let game = null;
    beforeEach( () => {
        game = new Game.Main();
    });

    it('Initializes the game and adds player', () => {
        const movSystem = new RG.System.Movement(['Movement']);
        const cols = 50;
        const rows = 30;
        const level = getNewLevel(cols, rows);

        const actor = new Actor('Player'); // player
        actor.setIsPlayer(true);
        actor.setFOVRange(5);
        game.addLevel(level);
        expect(game.addPlayer(actor)).to.equal(true);

        expect(game.shownLevel()).to.equal(level);
        expect(actor.getLevel()).to.equal(level);

        const newMap = level.getMap();
        checkMap(newMap, cols, rows);

        const movComp = new RG.Component.Movement(12, 13, level);
        actor.add('Movement', movComp);
        movSystem.update();
        expect(actor.getX()).to.equal(12);
        expect(actor.getY()).to.equal(13);

        const explCells = level.exploreCells(actor);
        expect(explCells.length).to.equal(145);
    });

    it('can add player to the specified location', () => {
        const fact = new RG.Factory.World();
        const worldConf = {
            name: 'StartPlace',
            nAreas: 1, area: [{name: 'a1', maxX: 2, maxY: 2}]
        };
        const world = fact.createWorld(worldConf);
        const game = new RG.Game.Main();
        game.addPlace(world);
        expect(game.getLevels()).to.have.length(4);

        const player = new RG.Actor.Rogue('PlayerHero');
        player.setIsPlayer(true);
        game.addPlayer(player, {place: 'StartPlace', x: 1, y: 1});

        const playerLevelID = player.getLevel().getID();
        const area = world.getAreas()[0];
        const expectedLevelID = area.getTileXY(1, 1).getLevel().getID();
        expect(playerLevelID).to.equal(expectedLevelID);
    });
});

/* For listening actor killed events.*/
const KillListener = function(actor) {

    const _actor = actor;

    this.isAlive = actor.get('Health').isAlive();

    this.notify = function(evtName, obj) {
        if (evtName === RG.EVT_ACTOR_KILLED) {
            if (obj.actor === _actor) {
                this.isAlive = false;
            }
        }
    };
    RG.POOL.listenEvent(RG.EVT_ACTOR_KILLED, this);
};

describe('How combat should evolve', () => {

    it('Deals damage from attacker to defender', () => {
        const comSystem = new RG.System.Attack(['Attack']);
        const dmgSystem = new RG.System.Damage(['Damage']);

        const cols = 50;
        const rows = 30;
        const level = getNewLevel(cols, rows);

        const attacker = new Actor('Attacker');
        expect(attacker.get('Health').isAlive()).to.equal(true);
        const defender = new Actor('Defender');
        expect(defender.get('Health').isAlive()).to.equal(true);
        attacker.get('Combat').setAttack(10);
        attacker.get('Combat').setDamageDie('1d4');
        defender.get('Health').setHP(1);
        defender.get('Combat').setDefense(0);
        defender.get('Combat').setProtection(0);
        defender.get('Stats').setAgility(0);

        level.addActor(attacker, 1, 1);
        level.addActor(defender, 2, 2);

        const attackComp = new RG.Component.Attack(defender);
        attacker.add('Attack', attackComp);
        comSystem.update();
        expect(defender.has('Damage')).to.equal(true);
        dmgSystem.update();
        expect(attacker.has('Attack')).to.equal(false);
        expect(defender.has('Damage')).to.equal(false);

        expect(defender.get('Health').isAlive()).to.equal(false);

        const def2 = new Actor('defender2');
        level.addActor(def2, 2, 2);

        const attComp2 = new RG.Component.Attack(def2);
        attacker.add('Attack', attComp2);

        def2.get('Health').setHP(20);
        def2.get('Combat').setDefense(0);
        def2.get('Stats').setAgility(0);
        expect(def2.get('Health').isAlive()).to.equal(true);

        comSystem.update();
        expect(def2.has('Damage')).to.equal(true);
        dmgSystem.update();
        expect(def2.has('Damage')).to.equal(false);

        expect(def2.get('Health').getHP() < 20).to.equal(true);

        expect(def2.get('Health').isAlive()).to.equal(true);

        attacker.add('Attack', attComp2);
        comSystem.update();
        dmgSystem.update();

        expect(def2.get('Health').isAlive()).to.equal(true);

        const killListen = new KillListener(def2);
        while (killListen.isAlive) {
            attacker.add('Attack', attComp2);
            comSystem.update();
            dmgSystem.update();
        }
        expect(def2.get('Health').isAlive()).to.equal(false);

    });
});

describe('How AI brain works', () => {

    let level = null;
    let mons1 = null;
    let player = null;

    beforeEach(() => {
        const cols = 30;
        const rows = 20;
        level = getNewLevel(cols, rows);
        mons1 = new Actor('Monster');
        player = new Actor('Player');
        player.setIsPlayer(true);
        mons1.addEnemy(player);
    });

    it('Brain should find player cell', () => {
        expect(level.addActor(player, 2, 2)).to.equal(true);
        expect(level.addActor(mons1, 3, 5)).to.equal(true);

        const map = level.getMap();
        expect(map.isPassable(2, 3)).to.equal(true);

        const brain = mons1.getBrain();
        const seenCells = level.getMap().getVisibleCells(mons1);
        expect(seenCells.length).to.not.equal(0);
        const playerCell = brain.findEnemyCell(seenCells);
        expect(playerCell.hasProp('actors')).to.equal(true);

        const pathCells = brain.getShortestPathTo(playerCell);
        expect(pathCells).to.be.an('array');
        expect(pathCells.length).to.not.equal(0);
    });

    it('Moves towards player when seen.', () => {
        const movSystem = new RG.System.Movement(['Movement']);
        expect(level.addActor(player, 2, 2)).to.equal(true);
        expect(level.addActor(mons1, 2, 4)).to.equal(true);
        const action = mons1.nextAction();
        action.doAction();
        movSystem.update();
        checkXY(mons1, 2, 3);
    });

});

describe('How poison item is used, and experience propagates', () => {
    it('Kills an actor after some time', () => {

        const game = new RG.Game.Main();
        const level = RG.FACT.createLevel('arena', 20, 20);
        const assassin = new Actor('assassin');
        const poison = globalParser.createActualObj('items',
            'Potion of frost poison');
        assassin.getInvEq().addItem(poison);

        const victim = new Actor('victim');
        victim.get('Health').setHP(5);

        level.addActor(assassin, 3, 5);
        level.addActor(victim, 6, 6);
        poison.useItem({target: level.getMap().getCell(6, 6)});

        const startExp = assassin.get('Experience').getExp();

        let count = 0;
        while (victim.get('Health').isAlive() && count < 100) {
            game.simulateGame();
            ++count;
        }
        expect(count, 'Victim dies in 100 turns').to.be.below(100);
        expect(victim.get('Health', 'Victim is dead').isAlive()).to.be.false;

        const endExp = assassin.get('Experience').getExp();
        expect(endExp > startExp, 'Exp. points from poison').to.equal(true);

        const curePoison = globalParser.createActualObj('items',
            'Potion of cure poison');
        const frostPoison = globalParser.createActualObj('items',
            'Potion of frost poison');

        assassin.getInvEq().addItem(frostPoison);
        const curedVictim = new Actor('Cured victim');

        expect(curedVictim.has('Expiration')).to.equal(false);
        expect(curedVictim.has('Poison')).to.equal(false);
        level.addActor(curedVictim, 4, 4);

        const poisonTarget = level.getMap().getCell(4, 4);
        expect(frostPoison.useItem({target: poisonTarget})).to.equal(true);
        expect(curedVictim.has('Expiration')).to.equal(true);
        expect(curedVictim.has('Poison')).to.equal(true);

        curedVictim.getInvEq().addItem(curePoison);
        game.simulateGame();

        const cureTarget = level.getMap().getCell(4, 4);
        expect(curePoison.useItem({target: cureTarget})).to.equal(true);
        expect(curedVictim.has('Poison')).to.equal(false);
        expect(curedVictim.get('Health').isAlive()).to.equal(true);
        for (let i = 0; i < 20; i++) {game.simulateGame();}
        expect(curedVictim.has('Expiration')).to.equal(false);

    });
});

describe('Game.WinCondition', () => {
    it('description', () => {
        const winCond = new RG.Game.WinCondition('Kill boss');
        expect(winCond.isTrue(), 'Win condition false').to.be.false;

        const boss = new RG.Actor.Rogue('Huge evil boss');
        winCond.addActorKilled(boss);
        RG.POOL.emitEvent(RG.EVT_ACTOR_KILLED, {actor: boss});
        expect(winCond.isTrue(), 'Win condition true now').to.be.true;

    });
});
<|endoftext|>


<commit_before>L.Handler.PolylineHandles = L.Handler.extend({

    includes: L.Mixin.Events,

    options: {
        overlapRadius: 15, // pixels
        markerFactory: null
    },

    initialize: function (map) {
        L.Handler.prototype.initialize.call(this, map);

        if (map.almostOver === undefined) {
            throw 'Leaflet.AlmostOver handler is required on this map.';
        }
        this._dragging = false;
        this._attached = [];
        this._marker = null;
        this._snap = null;
        this._layers = [];
    },

    addHooks: function () {
        this._toggleAlmostEvent(true);
        this.on('attach', this._onAttach, this);
        this.on('detach', this._onDetach, this);
    },

    removeHooks: function () {
        this.off('attach', this._onAttach, this);
        this.off('detach', this._onDetach, this);
        this._toggleAlmostEvent(false);
    },

    addGuideLayer: function (l) {
        this._layers.push(l);
    },

    _toggleAlmostEvent: function (state) {
        var method = state ? 'on' : 'off';
        this._map[method]('almost:over', this._onAlmostOver, this);
        this._map[method]('almost:move', this._onAlmostMove, this);
        this._map[method]('almost:out', this._onAlmostOut, this);
    },

    _onAttach: function (e) {
        this._attached.push(e.marker);
    },

    _onDetach: function (e) {
        var idx = this._attached.indexOf(e.marker);
        this._attached.splice(idx, 1);
    },

    _onAlmostOver: function (e) {
        if (this.options.markerFactory) {
            this._marker = this.options.markerFactory(e.latlng);
        }
        else {
            // Default marker and icon
            var handleIcon = L.divIcon({className: 'handle-icon'});
            this._marker = L.marker(e.latlng, {icon: handleIcon});
        }
        this._marker.attached = false;
        this._marker.on('dragstart', this._onDragStart, this);
        this._marker.on('dragend', this._onDragEnd, this);
        this._marker.on('click', this._onClick, this);
    },

    _onAlmostMove: function (e) {
        // Hide current marker when approaching the ones already attached.
        for (var i=0, n=this._attached.length; i<n; i++) {
            var attached = this._attached[i],
                space = L.GeometryUtil.distance(this._map,
                                                attached.getLatLng(),
                                                e.latlng);
            if (space < this.options.overlapRadius) {
                this._map.removeLayer(this._marker);
                return;
            }
        }
        // No attached marker around
        if (i == n) {
            this._map.addLayer(this._marker);
            this._marker.dragging = new L.Handler.MarkerDrag(this._marker);
            this._marker.dragging.enable();
        }
        this._marker.setLatLng(e.latlng);
    },

    _onAlmostOut: function (e) {
        this._marker.off('dragstart', this._onDragStart, this);
        this._marker.off('dragend', this._onDragEnd, this);
        this._marker.off('click', this._onClick, this);
        this._map.removeLayer(this._marker);
        this._marker = null;
    },

    // When marker starts dragging :
    //  - ignore map mouse moves
    //  - detach if was attached
    //  - initialize snapping if new marker
    _onDragStart: function (e) {
        this._toggleAlmostEvent(false);

        var marker = e.target;

        if (!!marker.attached) {
            this._detach(marker);
        }
        else {
            marker.snapediting = new L.Handler.MarkerSnap(this._map, marker, {snapVertices: false});
            for (var i=0, n=this._layers.length; i<n; i++) {
                marker.snapediting.addGuideLayer(this._layers[i]);
            }
        }
        marker.snapediting.enable();
        marker.on('snap', this._onSnap, this);
        marker.on('unsnap', this._onUnsnap, this);
        // Don't listen to click while dragging
        marker.off('click', this._onClick, this);
    },

    _onSnap: function (e) {
        this._snap = e.layer;
    },

    _onUnsnap: function (e) {
        this._snap = null;
    },

    // When marker dragging stops :
    //  - 'attach' and start again if marker was snapped
    //  - 'detach' if marker was previously attached
    _onDragEnd: function (e) {
        var marker = e.target;
        marker.snapediting.disable();
        marker.off('snap', this._onSnap, this);
        marker.off('unsnap', this._onUnsnap, this);

        if (this._snap) {
            this._attach(marker, this._snap);
            // Start over with new marker
            this._onAlmostOver({latlng: marker.getLatLng()});
        }
        else {
            this._map.removeLayer(marker);
        }
        this._toggleAlmostEvent(true);
    },

    _attach: function (marker, layer) {
        marker.attached = true;
        this.fire('attach', {marker: marker, layer: layer});
        if (marker._icon) L.DomUtil.addClass(marker._icon, 'marker-attached');

        // Detach on click
        marker.on('click', function (e) {
            this._detach(marker);
            this._map.removeLayer(e.target);
        }, this);
    },

    _detach: function (marker) {
        marker.attached = false;
        this.fire('detach', {marker: marker});
        if (marker._icon) L.DomUtil.removeClass(marker._icon, 'marker-attached');
    },

    _onClick: function (e) {
        // Simulate drag-snap on click
        var marker = e.target;
        this._onDragStart(e);
        marker.fire('move');
        this._onDragEnd(e);
    },
});

L.Handler.PolylineHandles.singleton = function (map) {
    if (this.__polylineHandles)
        return this.__polylineHandles;
    this.__polylineHandles = new L.Handler.PolylineHandles(map);
    return this.__polylineHandles;
};

L.Polyline.mergeOptions({
    polylineHandles: false
});

L.PolylineHandlesMixin = {
    __onAdd: L.Polyline.prototype.onAdd,
    __onRemove: L.Polyline.prototype.onRemove,

    onAdd: function (map) {
        this.__onAdd.call(this, map);

        this.polylineHandles = L.Handler.PolylineHandles.singleton(map);
        if (this.options.polylineHandles) {
            this.polylineHandles.enable();
        }
    },

    onRemove: function (map) {
        this.polylineHandles.disable();
        this.__onRemove.call(this, map);
    },
};

L.Polyline.include(L.PolylineHandlesMixin);
<commit_msg>Attach on almost click<commit_after>L.Handler.PolylineHandles = L.Handler.extend({

    includes: L.Mixin.Events,

    options: {
        overlapRadius: 15, // pixels
        markerFactory: null
    },

    initialize: function (map) {
        L.Handler.prototype.initialize.call(this, map);

        if (map.almostOver === undefined) {
            throw 'Leaflet.AlmostOver handler is required on this map.';
        }
        this._dragging = false;
        this._attached = [];
        this._marker = null;
        this._snap = null;
        this._layers = [];
    },

    addHooks: function () {
        this._toggleAlmostEvent(true);
        this.on('attach', this._onAttach, this);
        this.on('detach', this._onDetach, this);
    },

    removeHooks: function () {
        this.off('attach', this._onAttach, this);
        this.off('detach', this._onDetach, this);
        this._toggleAlmostEvent(false);
    },

    addGuideLayer: function (l) {
        this._layers.push(l);
    },

    _toggleAlmostEvent: function (state) {
        var method = state ? 'on' : 'off';
        this._map[method]('almost:over', this._onAlmostOver, this);
        this._map[method]('almost:move', this._onAlmostMove, this);
        this._map[method]('almost:out', this._onAlmostOut, this);
        this._map[method]('almost:click', this._onAlmostClick, this);
    },

    _onAttach: function (e) {
        this._attached.push(e.marker);
    },

    _onDetach: function (e) {
        var idx = this._attached.indexOf(e.marker);
        this._attached.splice(idx, 1);
    },

    _onAlmostOver: function (e) {
        if (this.options.markerFactory) {
            this._marker = this.options.markerFactory(e.latlng);
        }
        else {
            // Default marker and icon
            var handleIcon = L.divIcon({className: 'handle-icon'});
            this._marker = L.marker(e.latlng, {icon: handleIcon});
        }
        this._marker.attached = false;
        this._marker.on('dragstart', this._onDragStart, this);
        this._marker.on('dragend', this._onDragEnd, this);
        this._marker.on('click', this._onClick, this);
    },

    _onAlmostMove: function (e) {
        // Hide current marker when approaching the ones already attached.
        for (var i=0, n=this._attached.length; i<n; i++) {
            var attached = this._attached[i],
                space = L.GeometryUtil.distance(this._map,
                                                attached.getLatLng(),
                                                e.latlng);
            if (space < this.options.overlapRadius) {
                this._map.removeLayer(this._marker);
                return;
            }
        }
        // No attached marker around
        if (i == n) {
            this._map.addLayer(this._marker);
            this._marker.dragging = new L.Handler.MarkerDrag(this._marker);
            this._marker.dragging.enable();
        }
        this._marker.setLatLng(e.latlng);
    },

    _onAlmostOut: function (e) {
        this._marker.off('dragstart', this._onDragStart, this);
        this._marker.off('dragend', this._onDragEnd, this);
        this._marker.off('click', this._onClick, this);
        this._map.removeLayer(this._marker);
        this._marker = null;
    },

    _onAlmostClick: function (e) {
        // this._marker should be set in almost:over
        console.assert(this._marker);
        this._marker.fire('click', e);
    },

    // When marker starts dragging :
    //  - ignore map mouse moves
    //  - detach if was attached
    //  - initialize snapping if new marker
    _onDragStart: function (e) {
        this._toggleAlmostEvent(false);

        var marker = e.target;

        if (!!marker.attached) {
            this._detach(marker);
        }
        else {
            marker.snapediting = new L.Handler.MarkerSnap(this._map, marker, {snapVertices: false});
            for (var i=0, n=this._layers.length; i<n; i++) {
                marker.snapediting.addGuideLayer(this._layers[i]);
            }
        }
        marker.snapediting.enable();
        marker.on('snap', this._onSnap, this);
        marker.on('unsnap', this._onUnsnap, this);
        // Don't listen to click while dragging
        marker.off('click', this._onClick, this);
    },

    _onSnap: function (e) {
        this._snap = e.layer;
    },

    _onUnsnap: function (e) {
        this._snap = null;
    },

    // When marker dragging stops :
    //  - 'attach' and start again if marker was snapped
    //  - 'detach' if marker was previously attached
    _onDragEnd: function (e) {
        var marker = e.target;
        marker.snapediting.disable();
        marker.off('snap', this._onSnap, this);
        marker.off('unsnap', this._onUnsnap, this);

        if (this._snap) {
            this._attach(marker, this._snap);
            // Start over with new marker
            this._onAlmostOver({latlng: marker.getLatLng()});
        }
        else {
            this._map.removeLayer(marker);
        }
        this._toggleAlmostEvent(true);
    },

    _attach: function (marker, layer) {
        marker.attached = true;
        this.fire('attach', {marker: marker, layer: layer});
        if (marker._icon) L.DomUtil.addClass(marker._icon, 'marker-attached');

        // Detach on click
        marker.on('click', function (e) {
            this._detach(marker);
            this._map.removeLayer(e.target);
        }, this);
    },

    _detach: function (marker) {
        marker.attached = false;
        this.fire('detach', {marker: marker});
        if (marker._icon) L.DomUtil.removeClass(marker._icon, 'marker-attached');
    },

    _onClick: function (e) {
        // Simulate drag-snap on click
        var marker = e.target;
        this._onDragStart(e);
        marker.fire('move');
        this._onDragEnd(e);
    },
});

L.Handler.PolylineHandles.singleton = function (map) {
    if (this.__polylineHandles)
        return this.__polylineHandles;
    this.__polylineHandles = new L.Handler.PolylineHandles(map);
    return this.__polylineHandles;
};

L.Polyline.mergeOptions({
    polylineHandles: false
});

L.PolylineHandlesMixin = {
    __onAdd: L.Polyline.prototype.onAdd,
    __onRemove: L.Polyline.prototype.onRemove,

    onAdd: function (map) {
        this.__onAdd.call(this, map);

        this.polylineHandles = L.Handler.PolylineHandles.singleton(map);
        if (this.options.polylineHandles) {
            this.polylineHandles.enable();
        }
    },

    onRemove: function (map) {
        this.polylineHandles.disable();
        this.__onRemove.call(this, map);
    },
};

L.Polyline.include(L.PolylineHandlesMixin);
<|endoftext|>


<commit_before>class ST {
  constructor() {
    this.table = [];
  }

  put(key, val) {
    // we want to be able to store values in the table with a key for reference
    let exists = -1;
    this.table.forEach((e, i) => {
      if (e[0] === key)
        exists = i;
      if (e[1] === null)
        this.table = [...this.table.slice(0, i), ...this.table.slice(i + 1)];
    });
    if (exists > -1) {
      this.table[exists][1] = val;
    } else {
      this.table.push([key, val]);
    }
  }

  get(key) {
    // we want to be able to get values from the table by their reference
    let result = null;
    this.table.forEach((e) => {
      if (e[0] === key && e[1] !== null)
        result = e[1];
    })
    return result;
  }

  delete(key) {
    // we want to be able to remove values from the table
    let contains = this.contains(key);
    if (contains > -1)
      this.table[contains][1] = null;
    // this.table.forEach((e, i) => {
    //   if (e[0] === key)
    //     this.table[i][1] = null;
    // });
  }

  contains(key) {
    // we want to confirm the contents of the table
    let exists = -1;
    this.table.forEach((e, i) => {
      if (e[0] === key && e[1] !== null)
        exists = i;
    });
    return exists;
  }

  isEmpty() {
    return this.table.size() === 0;
  }
  size() {
    return this.table.length;
  }
  min(){}
  max(){}
  floor(){}
  ceiling(){}
  rank(){}
  select(){}
  deleteMin(){}
  deleteMax(){}
  size(){}
  keys(){}
  compareTo(){}
  equals(){}

}

const test = new ST();
test.put('a', 'tetest1')
test.put('b', 'tetest2')
test.put('c', 'tetest3')
test.put('d', 'tetest4')
test.put('e', 'test5')
console.log(test.get('e'), test.get('c'));
console.log(test.delete('e'), test.delete('c'));
console.log(test.contains('e'), test.contains('a'));<commit_msg>complete most basic implementation of symbol table and move on to optimizations<commit_after>class ST {
  constructor() {
    this.table = [];
  }

  put(key, val) {
    // we want to be able to store values in the table with a key for reference
    let exists = -1;
    this.table.forEach((e, i) => {
      if (e[0] === key)
        exists = i;
      if (e[1] === null)
        this.table = [...this.table.slice(0, i), ...this.table.slice(i + 1)];
    });
    if (exists > -1) {
      this.table[exists][1] = val;
    } else {
      this.table.push([key, val]);
    }
  }

  get(key) {
    // we want to be able to get values from the table by their reference
    let result = null;
    this.table.forEach((e) => {
      if (e[0] === key && e[1] !== null)
        result = e[1];
    });
    return result;
  }

  delete(key) {
    // we want to be able to remove values from the table
    let contains = this.contains(key);
    if (contains > -1) {
      this.table[contains][1] = null;
    }
  }

  contains(key) {
    // we want to confirm the contents of the table
    let exists = -1;
    this.table.forEach((e, i) => {
      if (e[0] === key && e[1] !== null)
        exists = i;
    });
    return exists;
  }

  isEmpty() {
    return this.table.size() === 0;
  }

  size() {
    return this.table.length;
  }

  min(init=null, comparator=this.compareTo) {
    // we want to be able to find the minimum key
    let result = init;
    this.table.forEach((e, i) => {
      if (result === null) result = e[0];
      else if (comparator(e[0], result) < 0) result = e[0];
    });
    return result;
  }

  max(init=null, comparator=this.compareTo) {
    // we want to be able to find the maximum key

    let result = init;
    this.table.forEach((e, i) => {
      if (result === null) result = e[0];
      else if (comparator(e[0], result) > 0) result = e[0];
    });
    return result;
  }

  floor(key) {
    const result = this.max(key, (a, b) => {
      if (a < key && a > b) return 1;
      if (b === key && a < b) return 1;
    });
    if (result === key) return null;
    return result;
  }

  ceiling(key) {
    const result = this.max(key, (a, b) => {
      if (a > key && a < b) return 1;
      if (b === key && a > b) return 1;
    });
    if (result === key) return null;
    return result;
  }

  rank(key) {
    let count = 0;
    this.table.forEach((e) => {
      if (e[0] < key) count++;
    })
    return count;
  }

  select() {
    // complete on optimized run through to aovid O(n2)
  }

  deleteMin() {
    this.delete(this.min());

  }

  deleteMax() {
    this.delete(this.max());

  }

  size(a, b) {
    let count = 0;
    this.table.forEach((e) => {

    })
  }

  keys(cb) {
  }

  compareTo(a, b) {
    if (a < b) return -1;
    if (a > b) return 1;
    return 0;
  }

  equals() {}

}

const test = new ST();

test.put('a', 1)
test.put('b', 2)
test.put('c', 3)
test.put('d', 4)
test.put('e', 5)

console.log('1', test.get('e'), test.get('c')); // 5 and 3
console.log('2', test.contains('e'), test.contains('a')); // -1 and 0
console.log('3', test.min(), test.max()); // 'a' and 'e'
console.log('4', test.ceiling('b'), test.floor('b')); // 'c' and 'a'

test.delete('e');
test.delete('a');
test.delete('b');
test.delete('c');
test.delete('d');

console.log('5', test.get('e'), test.get('a')); // null and null
console.log('6', test.contains('e'), test.contains('a')); // -1 and -1
console.log('7', test.min(), test.max()); // 'a' and 'e'
console.log('8', test.ceiling('b'), test.floor('b')); // 'c' and 'a'

test.delete('a');
test.delete('b');
test.delete('c');
test.delete('d');
console.log('9', test.get('e'), test.get('a')); // 5 and 1
test.deleteMin();
test.deleteMax();

console.log('10', test.get('e'), test.get('a')); // null and null
console.log('11', test.contains('e'), test.contains('a')); // -1 and -1
console.log('12', test.min(), test.max()); // 'a' and 'e'
console.log('13', test.ceiling('b'), test.floor('b')); // 'c' and 'a'
<|endoftext|>


<commit_before>
/* DOM 3 Event: KeyboardEvent:UIEvent
 * http://www.w3.org/TR/DOM-Level-3-Events/#events-keyboardevents
 */

(function () {
  var keyboardevent_properties,
      isEvent = doodle.Event.isEvent,
      check_boolean_type = doodle.utils.types.check_boolean_type,
      check_number_type = doodle.utils.types.check_number_type,
      check_string_type = doodle.utils.types.check_string_type;

  /* Super constructor
   * @param {String} type
   * @param {Boolean} bubbles
   * @param {Boolean} cancelable
   * @param {DOM Object} view
   * @param {String} keyIdentifier
   * @param {Number} keyLocation
   * @param {String} modifiersList White-space separated list of key modifiers.
   * @param {Boolean} repeat
   *
   * @alternative instantiation
   * @param {Event} initializer event to wrap
   *
   * @return {KeyboardEvent}
   */
  doodle.KeyboardEvent = function (type, bubbles, cancelable, view,
                                   keyIdentifier, keyLocation, modifiersList, repeat) {
    var arg_len = arguments.length,
        initializer, //if passed another event object
        keyboardevent, //this super-object we'll be constructing
        //read-only properties
        ctrlKey = false,
        shiftKey = false,
        altKey = false,
        metaKey = false;

    //check if given an init event to wrap
    if (arg_len === 1 && isEvent(arguments[0])) {
      initializer = arguments[0]; //event object

      //copy event properties to our args that'll be used for initialization
      //initKeyboardEvent() will typecheck these
      type = initializer.type;
      bubbles = initializer.bubbles;
      cancelable = initializer.cancelable;
      view = initializer.view;
      keyIdentifier = initializer.keyIdentifier;
      keyLocation = initializer.keyLocation;
      repeat = initializer.repeat;
      //get modifiers, use defaults to avoid contructing a new modifiers list string
      //initKeyboardEvent() won't touch these
      ctrlKey = initializer.ctrlKey || false;
      shiftKey = initializer.shiftKey || false;
      altKey = initializer.altKey || false;
      metaKey = initializer.metaKey || false;

      //pass on the event arg to init our uievent prototype
      keyboardevent = Object.create(doodle.UIEvent(initializer));

    } else if (arg_len === 0 || arg_len > 8) {
      //check arg count
      throw new SyntaxError("[object KeyboardEvent]: Invalid number of parameters.");
    } else {
      //regular instantiation of our prototype
      bubbles = bubbles === true; //false
      cancelable = cancelable === true;
      view = (view === undefined) ? null : view;

      /*DEBUG*/
      check_string_type(type, '[object KeyboardEvent].constructor', '*type*');
      check_boolean_type(bubbles, '[object KeyboardEvent].constructor', '*bubbles*');
      check_boolean_type(cancelable, '[object KeyboardEvent].constructor', '*cancelable*');
      /*END_DEBUG*/

      keyboardevent = Object.create(doodle.UIEvent(type, bubbles, cancelable, view));
    }

    Object.defineProperties(keyboardevent, keyboardevent_properties);
    Object.defineProperties(keyboardevent, {
      /* PROPERTIES
       */

      'keyIdentifier': {
        enumerable: true,
        configurable: false,
        get: function () { return keyIdentifier; }
      },

      'keyLocation': {
        enumerable: true,
        configurable: false,
        get: function () { return keyLocation; }
      },

      'repeat': {
        enumerable: true,
        configurable: false,
        get: function () { return repeat; }
      },

      'altKey': {
        enumerable: true,
        configurable: false,
        get: function () { return altKey; }
      },

      'ctrlKey': {
        enumerable: true,
        configurable: false,
        get: function () { return ctrlKey; }
      },

      'metaKey': {
        enumerable: true,
        configurable: false,
        get: function () { return metaKey; }
      },

      'shiftKey': {
        enumerable: true,
        configurable: false,
        get: function () { return shiftKey; }
      },

      /* METHODS
       */

      'initKeyboardEvent': {
        value: function (typeArg, canBubbleArg, cancelableArg, viewArg,
                         keyIdentifierArg, keyLocationArg, modifiersListArg, repeatArg) {
          //parameter defaults, assign to outer constructor vars
          type = typeArg;
          bubbles = canBubbleArg === true; //false
          cancelable = cancelableArg === true;
          view = (viewArg === undefined) ? null : viewArg;
          keyIdentifier = (keyIdentifierArg === undefined) ? "" : keyIdentifierArg;
          keyLocation = (keyLocationArg === undefined) ? 0 : keyLocationArg;
          modifiersList = (modifiersListArg === undefined) ? "" : modifiersListArg;
          repeat = repeatArg === true;

          /*DEBUG*/
          check_string_type(type, this+'.initKeyboardEvent', 'type');
          check_boolean_type(bubbles, this+'.initKeyboardEvent', 'bubbles');
          check_boolean_type(cancelable, this+'.initKeyboardEvent', 'cancelable');
          check_string_type(keyIdentifier, this+'.initKeyboardEvent', 'keyIdentifier');
          check_number_type(keyLocation, this+'.initKeyboardEvent', 'keyLocation');
          check_string_type(modifiersList, this+'.initKeyboardEvent', 'modifiersList');
          check_boolean_type(repeat, this+'.initKeyboardEvent', 'repeat');
          /*END_DEBUG*/

          //parse string of white-space separated list of modifier key identifiers
          modifiersList.split(" ").forEach(function (modifier) {
            switch (modifier) {
            case 'Alt':
              altKey = true;
              break;
            case 'Control':
              ctrlKey = true;
              break;
            case 'Meta':
              metaKey = true;
              break;
            case 'Shift':
              shiftKey = true;
              break;
            }
          });

          this.initUIEvent(type, bubbles, cancelable, view);
          return this;
        }
      },

      /* Queries the state of a modifier using a key identifier.
       * @param {String} key A modifier key identifier
       * @return {Boolean} True if it is a modifier key and the modifier is activated, false otherwise.
       * This is an incomplete list of modifiers.
       */
      'getModifierState': {
        value: function (key) {
          check_string_type(key, this+'.getModifierState');
          switch (key) {
          case 'Alt':
            return altKey;
          case 'Control':
            return ctrlKey;
          case 'Meta':
            return metaKey;
          case 'Shift':
            return shiftKey;
          default:
            return false;
          }
        }
      }

    });

    //init event
    keyboardevent.initKeyboardEvent(type, bubbles, cancelable, view,
                                    keyIdentifier, keyLocation, modifiersList, repeat);

    return keyboardevent;
  };

  //static
  keyboardevent_properties = {
    'toString': {
      enumerable: true,
      writable: false,
      configurable: false,
      value: function () {
        return "[object KeyboardEvent]";
      }
    }
  };

}());//end class closure
<commit_msg>Refactored KeyboardEvent.<commit_after>/*globals doodle*/

/* DOM 3 Event: KeyboardEvent:UIEvent
 * http://www.w3.org/TR/DOM-Level-3-Events/#events-keyboardevents
 */

(function () {
  var keyboardevent_static_properties,
      isEvent = doodle.Event.isEvent,
      check_boolean_type = doodle.utils.types.check_boolean_type,
      check_number_type = doodle.utils.types.check_number_type,
      check_string_type = doodle.utils.types.check_string_type;

  /* Super constructor
   * @param {String} type
   * @param {Boolean} bubbles
   * @param {Boolean} cancelable
   * @param {DOM Object} view
   * @param {String} keyIdentifier
   * @param {Number} keyLocation
   * @param {String} modifiersList White-space separated list of key modifiers.
   * @param {Boolean} repeat
   *
   * @alternative instantiation
   * @param {Event} initializer event to wrap
   *
   * @return {KeyboardEvent}
   */
  doodle.KeyboardEvent = function (type, bubbles, cancelable, view,
                                   keyIdentifier, keyLocation, modifiersList, repeat) {
    var keyboardevent,
        arg_len = arguments.length,
        init_obj, //function, event
        copy_keyboardevent_properties; //fn declared per event for private vars

    /*DEBUG*/
    if (arg_len === 0 || arg_len > 8) {
      throw new SyntaxError("[object KeyboardEvent](type, bubbles, cancelable, view, keyIdentifier, keyLocation, modifiersList, repeat): Invalid number of parameters.");
    }
    /*END_DEBUG*/

    //initialize uievent prototype with another event, function, or args
    if (isEvent(arguments[0])) {
      /*DEBUG*/
      if (arg_len > 1) {
        throw new SyntaxError("[object KeyboardEvent](event): Invalid number of parameters.");
      }
      /*END_DEBUG*/
      init_obj = arguments[0];
      type = undefined;
      keyboardevent = Object.create(doodle.UIEvent(init_obj));
    } else if (typeof arguments[0] === 'function') {
      /*DEBUG*/
      if (arg_len > 1) {
        throw new SyntaxError("[object KeyboardEvent](function): Invalid number of parameters.");
      }
      /*END_DEBUG*/
      init_obj = arguments[0];
      type = undefined;
      //use empty event type for now, will check after we call the init function.
      keyboardevent = Object.create(doodle.UIEvent(''));
    } else {
      //parameter defaults
      bubbles = (bubbles === undefined) ? false : bubbles;
      cancelable = (cancelable === undefined) ? false : cancelable;
      view = (view === undefined) ? null : view;
      /*DEBUG*/
      check_string_type(type, '[object KeyboardEvent]', '*type*, bubbles, cancelable, view, keyIdentifier, keyLocation, modifiersList, repeat');
      check_boolean_type(bubbles, '[object KeyboardEvent]', 'type, *bubbles*, cancelable, view, keyIdentifier, keyLocation, modifiersList, repeat');
      check_boolean_type(cancelable, '[object KeyboardEvent]', 'type, bubbles, *cancelable*, view, keyIdentifier, keyLocation, modifiersList, repeat');
      /*END_DEBUG*/
      keyboardevent = Object.create(doodle.UIEvent(type, bubbles, cancelable, view));
    }

    Object.defineProperties(keyboardevent, keyboardevent_static_properties);
    //properties that require privacy
    Object.defineProperties(keyboardevent, (function () {
      var evt_keyIdentifier,
          evt_keyLocation,
          evt_repeat,
          evt_ctrlKey = false,
          evt_altKey = false,
          evt_shiftKey = false,
          evt_metaKey = false;

      copy_keyboardevent_properties = function (evt) {
        //only looking for KeyboardEvent properties
        if (evt.keyIdentifier !== undefined) { evt_keyIdentifier = evt.keyIdentifier; }
        if (evt.keyLocation !== undefined) { evt_keyLocation = evt.keyLocation; }
        if (evt.repeat !== undefined) { evt_repeat = evt.repeat; }
        if (evt.ctrlKey !== undefined) { evt_ctrlKey = evt.ctrlKey; }
        if (evt.altKey !== undefined) { evt_altKey = evt.altKey; }
        if (evt.shiftKey !== undefined) { evt_shiftKey = evt.shiftKey; }
        if (evt.metaKey !== undefined) { evt_metaKey = evt.metaKey; }
      };

      return {
        'keyIdentifier': {
          enumerable: true,
          configurable: false,
          get: function () { return evt_keyIdentifier; }
        },

        'keyLocation': {
          enumerable: true,
          configurable: false,
          get: function () { return evt_keyLocation; }
        },

        'repeat': {
          enumerable: true,
          configurable: false,
          get: function () { return evt_repeat; }
        },

        'ctrlKey': {
          enumerable: true,
          configurable: false,
          get: function () { return evt_ctrlKey; }
        },

        'altKey': {
          enumerable: true,
          configurable: false,
          get: function () { return evt_altKey; }
        },

        'shiftKey': {
          enumerable: true,
          configurable: false,
          get: function () { return evt_shiftKey; }
        },

        'metaKey': {
          enumerable: true,
          configurable: false,
          get: function () { return evt_metaKey; }
        },

        'initKeyboardEvent': {
          value: function (typeArg, canBubbleArg, cancelableArg, viewArg,
                           keyIdentifierArg, keyLocationArg, modifiersListArg, repeatArg) {
            //parameter defaults
            canBubbleArg = (canBubbleArg === undefined) ? false : canBubbleArg;
            cancelableArg = (cancelableArg === undefined) ? false : cancelableArg;
            viewArg = (viewArg === undefined) ? null : viewArg;
            keyIdentifierArg = (keyIdentifierArg === undefined) ? "" : keyIdentifierArg;
            keyLocationArg = (keyLocationArg === undefined) ? 0 : keyLocationArg;
            modifiersListArg = (modifiersListArg === undefined) ? "" : modifiersListArg;
            repeatArg = (repeatArg === undefined) ? false : repeatArg;
            /*DEBUG*/
            check_string_type(typeArg, this+'.initKeyboardEvent', '*type*, bubbles, cancelable, view, keyIdentifier, keyLocation, modifiersList, repeat');
            check_boolean_type(canBubbleArg, this+'.initKeyboardEvent', 'type, *bubbles*, cancelable, view, keyIdentifier, keyLocation, modifiersList, repeat');
            check_boolean_type(cancelableArg, this+'.initKeyboardEvent', 'type, bubbles, *cancelable*, view, keyIdentifier, keyLocation, modifiersList, repeat');
            check_string_type(keyIdentifierArg, this+'.initKeyboardEvent', 'type, bubbles, cancelable, view, *keyIdentifier*, keyLocation, modifiersList, repeat');
            check_number_type(keyLocationArg, this+'.initKeyboardEvent', 'type, bubbles, cancelable, view, keyIdentifier, *keyLocation*, modifiersList, repeat');
            check_string_type(modifiersListArg, this+'.initKeyboardEvent', 'type, bubbles, cancelable, view, keyIdentifier, keyLocation, *modifiersList*, repeat');
            check_boolean_type(repeatArg, this+'.initKeyboardEvent', 'type, bubbles, cancelable, view, keyIdentifier, keyLocation, modifiersList, *repeat*');
            /*END_DEBUG*/
            evt_keyIdentifier = keyIdentifierArg;
            evt_keyLocation = keyLocationArg;
            evt_repeat = repeatArg;

            //parse string of white-space separated list of modifier key identifiers
            modifiersListArg.split(" ").forEach(function (modifier) {
              switch (modifier) {
              case 'Alt':
                evt_altKey = true;
                break;
              case 'Control':
                evt_ctrlKey = true;
                break;
              case 'Meta':
                evt_metaKey = true;
                break;
              case 'Shift':
                evt_shiftKey = true;
                break;
              }
            });

            this.initUIEvent(typeArg, canBubbleArg, cancelableArg, viewArg);
            return this;
          }
        },

        /* Queries the state of a modifier using a key identifier.
         * @param {String} key A modifier key identifier
         * @return {Boolean} True if it is a modifier key and the modifier is activated, false otherwise.
         * This is an incomplete list of modifiers.
         */
        'getModifierState': {
          value: function (key) {
            check_string_type(key, this+'.getModifierState', '*key*');
            switch (key) {
            case 'Alt':
              return evt_altKey;
            case 'Control':
              return evt_ctrlKey;
            case 'Meta':
              return evt_metaKey;
            case 'Shift':
              return evt_shiftKey;
            default:
              return false;
            }
          }
        }
      };
    }()));

    //initialize keyboardevent
    if (init_obj) {
      if (typeof init_obj === 'function') {
        init_obj.call(keyboardevent);
        /*DEBUG*/
        //make sure we've checked our dummy type string
        if (keyboardevent.type === undefined || keyboardevent.type === '' ||
            keyboardevent.bubbles === undefined ||
            keyboardevent.cancelable === undefined ||
            keyboardevent.view === undefined ||
            keyboardevent.keyLocation === undefined ||
            keyboardevent.keyIdentifier === undefined ||
            keyboardevent.repeat === undefined) {
          throw new SyntaxError("[object KeyboardEvent](function): Must call 'this.initKeyboardevent(type, bubbles, cancelable, view, keyIdentifier, keyLocation, modifiersList, repeat)' within the function argument.");
        }
        /*END_DEBUG*/
      } else {
        //passed a doodle or dom event object
        copy_keyboardevent_properties(init_obj);
      }
    } else {
      //standard instantiation
      keyboardevent.initKeyboardEvent(type, bubbles, cancelable, view,
                                      keyIdentifier, keyLocation, modifiersList, repeat);
    }

    return keyboardevent;
  };

  keyboardevent_static_properties = {
    'toString': {
      enumerable: true,
      writable: false,
      configurable: false,
      value: function () {
        return "[object KeyboardEvent]";
      }
    }
  };

}());//end class closure
<|endoftext|>