<commit_before>define([
	'jquery',
	'underscore',
	'backbone',
	'config',
	'utils',
	'text!templates/map-layer-editor.html',
	'views/panel-view-base',
	'mixins/model-editor-mixin',
	'lib/color-gradient/color-gradient',
], function($, _, Backbone, config, utils, templateHtml, PanelViewBase, ModelEditorMixin, ColorGradient) {

	var MapLayerEditorView = PanelViewBase.extend({

		className: 'panel map-layer-editor',
		
	    events: {
	    	'click .btn.save': 'saveButtonClicked',
	    	'click .btn.undo': 'undoButtonClicked',
	    	'click .btn.destroy': 'destroyButtonClicked',
	    	'click .btn.feature-type': 'featureTypeClicked',
	    	'change .model-input.layer-toggle': 'layerToggled',
	    	'change .model-input, .color-palette input, .color-palette select': 'modelInputChanged',
	    	// does not work well with live preview since val is unchanged
	    	// 'keydown .model-input, .color-palette input, .color-palette select': 'modelInputChanged',
	    	'change .preview': 'previewChanged',
	    	'change .show-advanced': 'showAdvancedChanged',
	    	'click .generate-colors': 'generateColorsClicked',
	    	'click .hide-color-generator': 'hideColorGenerator',
	    	'click .remove-color': 'removeColorClicked',
	    	'click .add-color': 'addColorClicked',
	    	'click .color-schemes .color-scheme a': 'toggleColorScheme',
	    	'click .add-color-scheme a': 'addColorScheme',
	    	'click .delete-color-scheme a': 'deleteColorScheme',
	    	'change .color-scheme-name': 'colorSchemeNameChanged',
	    	'keydown input[type=text]': function(event) {
	    		if (event.which == 13) {
	    			$(event.currentTarget).select();
	    			$(event.currentTarget).trigger('change');
					// prevent button click
	    			return false;
	    		}
	    	}
	    },

	    initialize: function(options) 
	    {
	    	MapLayerEditorView.__super__.initialize.call(this, options);
		    this.template = _.template(templateHtml);	
		    this.listenTo(this.model, 'change', this.updateFromModel);
		    this.listenTo(this.model, 'sync', this.modelSynced);
		    this.listenTo(this.model, 'destroy', this.remove);
		    this.colorSchemeIndex = this.model.getLayerOptions().colorSchemeIndex || 0;
	    },

	    modelSynced: function(model)
	    {
			if (this.model.hasChanged('layerOptions')) {
			    // TODO this will overwrite unsaved changes:
				this.populateFromModel();	
			} 
	    },

	    render: function() 
	    {
	    	var self = this,
	    		featureCollection = this.model.attributes.featureCollection;
			MapLayerEditorView.__super__.render.call(this);

	    	this.colorRowTemplate = this.$('.color-palette tr.element-template').remove()
	    		.clone().removeClass('element-template');
			this.initModelInputs();
			this.initSliders();

			this.$('.dropdown-toggle').dropdown();

			this.$('.has-popover').each(function() {
				var trigger = $(this),
					sel = trigger.attr('data-content-selector');
				if (sel) {
					var el = self.$(sel).remove();
					$(this).popover({
						content: el,
						html: true,
					});
					$(this).on('shown', function(event, item) {
						trigger.addClass('active');
					});
					$(this).on('hidden', function(event, item) {
						trigger.removeClass('active');
					});
					trigger.click(function(event) {
						event.preventDefault();
					});
				}  else {
					$(this).popover({});
				}
			});

			this.populateFromModel();
			this.initColorPicker(this.$('.model-input.color-picker'));

			this.$('.color-palette tbody').sortable({
				handle: '.drag-handle',
				stop: function(event, ui) {
					self.modelInputChanged(event);
				}
			});

			this.initColorPicker(this.$('.model-input.color-picker'));

			this.$('.has-tooltip').tooltip({ delay: 0, animation: false });

			return this;
	    },

	    updateFromModel: function()
	    {
	    	// do not populate form on model change since that breaks
	    	// things like the colorpicker 
			this.$('.model-title').text(this.model.getDisplay('title'));
			this.$('.model-numeric').toggle(this.model.isNumeric());
	    },

	    initSliders: function() 
	    {
	    	var self = this,
	    		or = function(val1, val2) {
	    			return !isNaN(val1) ? val1 : val2
	    		};
			this.$('.slider').each(function() {
				var fieldName = $(this).attr('data-field'),
					min = or(parseFloat($(this).attr('data-min')), 0),
					max = or(parseFloat($(this).attr('data-max')), 1);
				$(this).slider({
					min: min,
					max: max,
					range: "min",
					step: (max == 1 ? .05 : 1),
					slide: function( event, ui ) {
						$(self.modelInputs[fieldName][0]).val(ui.value);
						self.modelInputChanged();
					}
			    });
			    $(self.modelInputs[fieldName][0]).change(function() {
			    	self.updateSliders();
			    });
			});
		},

		updateSliders: function() 
		{
	    	var self = this;
			this.$('.slider').each(function() {
				var fieldName = $(this).attr('data-field');
				$(this).slider('value', $(self.modelInputs[fieldName][0]).val());
			});
		},

		populateFieldInputs: function()
		{
	    	var self = this,
	    		fields = this.model.getFeatureCollectionAttr('fields', []);
			this.$('select.field-names').each(function() {
				var fieldType = $(this).attr('data-type'),
					opts = ['<option>(' + __('none') + ')</option>'];
				_.each(fields, function(field) {
					if (field.name.match(/^properties\./) && (!fieldType || fieldType == field.type)) {
						opts.push('<option value="%(name)s">%(label)s</option>'.format(field));
					}
				})
				$(this).html(opts.join('\n'));
			});
		},

	    populateFromModel: function()
	    {
			this.updateFromModel();
			this.populateFieldInputs();
			this.populateModelInputs();
			this.$('.panel-header .title').text(this.model.get('layerOptions.title'));
			this.populateColorSchemes();
			this.setButtonState(false, false);
			this.updateSliders();
	    },

	    getModelInputValues: function(values)
	    {
	    	var self = this,
	    		schemes = _.deepClone(this.model.get('layerOptions.colorSchemes')) || [{}];
	    	schemes[this.colorSchemeIndex].colors = this.getColorsFromTable();
	    	values['layerOptions.colorSchemes'] = schemes;
	    	values['layerOptions.colorSchemeIndex'] = this.colorSchemeIndex;
	    	return values;
	    },

	    undoButtonClicked: function(event) 
	    {
	    	this.colorSchemeIndex = this.model.getLayerOptions().colorSchemeIndex || 0;
	    	this.model.setColorScheme(this.colorSchemeIndex);
	    	this.model.set(this.savedModelAttributes);
	    	this.populateFromModel();
	    	return false;
	    },

	    saveButtonClicked: function(event) <commit_msg>Improved enter key handling and fixed color scheme bugs<commit_after>define([
	'jquery',
	'underscore',
	'backbone',
	'config',
	'utils',
	'text!templates/map-layer-editor.html',
	'views/panel-view-base',
	'mixins/model-editor-mixin',
	'lib/color-gradient/color-gradient',
], function($, _, Backbone, config, utils, templateHtml, PanelViewBase, ModelEditorMixin, ColorGradient) {

	var MapLayerEditorView = PanelViewBase.extend({

		className: 'panel map-layer-editor',
		
	    events: {
	    	'click .btn.save': 'saveButtonClicked',
	    	'click .btn.undo': 'undoButtonClicked',
	    	'click .btn.destroy': 'destroyButtonClicked',
	    	'click .btn.feature-type': 'featureTypeClicked',
	    	'change .model-input.layer-toggle': 'layerToggled',
	    	'change .model-input, .color-palette input, .color-palette select': 'modelInputChanged',
	    	// does not work well with live preview since val is unchanged
	    	// 'keydown .model-input, .color-palette input, .color-palette select': 'modelInputChanged',
	    	'change .preview': 'previewChanged',
	    	'change .show-advanced': 'showAdvancedChanged',
	    	'click .generate-colors': 'generateColorsClicked',
	    	'click .hide-color-generator': 'hideColorGenerator',
	    	'click .remove-color': 'removeColorClicked',
	    	'click .add-color': 'addColorClicked',
	    	'click .color-schemes .color-scheme a': 'toggleColorScheme',
	    	'click .add-color-scheme a': 'addColorScheme',
	    	'click .delete-color-scheme a': 'deleteColorScheme',
	    	'change .color-scheme-name': 'colorSchemeNameChanged',
	    	'keydown input[type=text]': function(event) {
	    		if (event.which == 13) {
	    			$(event.currentTarget).select();
	    			$(event.currentTarget).trigger('change');
					// prevent button click
	    			return false;
	    		}
	    	}
	    },

	    initialize: function(options) 
	    {
	    	MapLayerEditorView.__super__.initialize.call(this, options);
		    this.template = _.template(templateHtml);	
		    this.listenTo(this.model, 'change', this.updateFromModel);
		    this.listenTo(this.model, 'sync', this.modelSynced);
		    this.listenTo(this.model, 'destroy', this.remove);
		    this.colorSchemeIndex = this.model.getLayerOptions().colorSchemeIndex || 0;
	    },

	    modelSynced: function(model)
	    {
			if (this.model.hasChanged('layerOptions')) {
			    // TODO this will overwrite unsaved changes:
				this.populateFromModel();	
			} 
	    },

	    render: function() 
	    {
	    	var self = this,
	    		featureCollection = this.model.attributes.featureCollection;
			MapLayerEditorView.__super__.render.call(this);

	    	this.colorRowTemplate = this.$('.color-palette tr.element-template').remove()
	    		.clone().removeClass('element-template');
			this.initModelInputs();
			this.initSliders();

			this.$('.dropdown-toggle').dropdown();

			this.$('.has-popover').each(function() {
				var trigger = $(this),
					sel = trigger.attr('data-content-selector');
				if (sel) {
					var el = self.$(sel).remove();
					$(this).popover({
						content: el,
						html: true,
					});
					$(this).on('shown', function(event, item) {
						trigger.addClass('active');
					});
					$(this).on('hidden', function(event, item) {
						trigger.removeClass('active');
					});
					trigger.click(function(event) {
						event.preventDefault();
					});
				}  else {
					$(this).popover({});
				}
			});

			this.populateFromModel();
			this.initColorPicker(this.$('.model-input.color-picker'));

			this.$('.color-palette tbody').sortable({
				handle: '.drag-handle',
				stop: function(event, ui) {
					self.modelInputChanged(event);
				}
			});

			this.initColorPicker(this.$('.model-input.color-picker'));

			this.$('.has-tooltip').tooltip({ delay: 0, animation: false });

			return this;
	    },

	    updateFromModel: function()
	    {
	    	// do not populate form on model change since that breaks
	    	// things like the colorpicker 
			this.$('.model-title').text(this.model.getDisplay('title'));
			this.$('.model-numeric').toggle(this.model.isNumeric());
	    },

	    initSliders: function() 
	    {
	    	var self = this,
	    		or = function(val1, val2) {
	    			return !isNaN(val1) ? val1 : val2
	    		};
			this.$('.slider').each(function() {
				var fieldName = $(this).attr('data-field'),
					min = or(parseFloat($(this).attr('data-min')), 0),
					max = or(parseFloat($(this).attr('data-max')), 1);
				$(this).slider({
					min: min,
					max: max,
					range: "min",
					step: (max == 1 ? .05 : 1),
					slide: function( event, ui ) {
						$(self.modelInputs[fieldName][0]).val(ui.value);
						self.modelInputChanged();
					}
			    });
			    $(self.modelInputs[fieldName][0]).change(function() {
			    	self.updateSliders();
			    });
			});
		},

		updateSliders: function() 
		{
	    	var self = this;
			this.$('.slider').each(function() {
				var fieldName = $(this).attr('data-field');
				$(this).slider('value', $(self.modelInputs[fieldName][0]).val());
			});
		},

		populateFieldInputs: function()
		{
	    	var self = this,
	    		fields = this.model.getFeatureCollectionAttr('fields', []);
			this.$('select.field-names').each(function() {
				var fieldType = $(this).attr('data-type'),
					opts = ['<option>(' + __('none') + ')</option>'];
				_.each(fields, function(field) {
					if (field.name.match(/^properties\./) && (!fieldType || fieldType == field.type)) {
						opts.push('<option value="%(name)s">%(label)s</option>'.format(field));
					}
				})
				$(this).html(opts.join('\n'));
			});
		},

	    populateFromModel: function()
	    {
			this.updateFromModel();
			this.populateFieldInputs();
			this.populateModelInputs();
			this.$('.panel-header .title').text(this.model.get('layerOptions.title'));
			this.populateColorSchemes();
			this.setButtonState(false, false);
			this.updateSliders();
	    },

	    getModelInputValues: function(values)
	    {
	    	var self = this,
	    		schemes = _.deepClone(this.model.get('layerOptions.colorSchemes')) || [{}];
	    	schemes[this.colorSchemeIndex].colors = this.getColorsFromTable();
	    	values['layerOptions.colorSchemes'] = schemes;
	    	values['layerOptions.colorSchemeIndex'] = this.colorSchemeIndex;
	    	return values;
	    },

	    undoButtonClicked: function(event) 
	    {
	    	this.colorSchemeIndex = this.savedModelAttributes.layerOptions.colorSchemeIndex || 0;
	    	this.model.setColorScheme(this.colorSchemeIndex);
	    	this.model.set(this.savedModelAttributes);
	    	this.populateFromModel();
	    	return false;
	    },

	    saveButtonClicked: function(event) 
{'fraction_non_alphanumeric': 0.1733495959749962, 'fraction_numerical': 0.0028967830461960665, 'mean_word_length': 3.067906976744186, 'pattern_counts': {'":': 0, '<': 11, '<?xml version=': 0, '>': 11, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3503302', 'n_tokens_mistral': 4909, 'n_tokens_neox': 4113, 'n_words': 680}