<commit_before>(function($) {

	var evaluatePath, applyViewFn, updateViewBindEl, getFormElVal;

	// Backbone.View Mixins
	// --------------------

	_.extend(Backbone.View.prototype, {

		// Collection of model event bindings.
		//   [{model,event,fn}, ...]
		_modelBindings: [],

		// Unbind the model bindings that are referenced in `this._modelBindings`.
		unstickModel: function() {
			_.each(this._modelBindings, function(binding) {
				binding.model.off(binding.event, binding.fn);
			});
			this._modelBindings = [];
		},

		// Using `this.bindings` configuration or the `optionalBindingsConfig`, binds `this.model`
		// or the `optionalModel` to elements in the view.
		stickit: function(optionalModel, optionalBindingsConfig) {
			var self = this, observeModelEvent,
				model = optionalModel || this.model,
				bindings = optionalBindingsConfig || this.bindings || {};

			this.events || (this.events = {});

			// Setup a model event binding with the given function, and track the event in this._modelBindings.
			observeModelEvent = function(event, fn) {
				model.on(event, fn, self);
				self._modelBindings.push({model:model, event:event, fn:fn});
			};

			// Iterate through the selectors in the bindings configuration and configure
			// the various options for each field.
			_.each(_.keys(bindings), function(selector) {
				var getVal, modelEvents, formElEvent,
					config = bindings[selector] || {},
					$el = self.$(selector),
					format = config.format,
					modelAttr = config.modelAttr,
					attributes = config.attributes || [];

				// Fail fast if the selector didn't match an element.
				if (!$el.length) return false;

				// Returns the given `field`'s value from the model, escaping and formatting if necessary.
				getVal = function(field) {
					var val = config.escape ? model.escape(field) : model.get(field);
					if (_.isUndefined(val)) val = '';
					return format ? applyViewFn(self, format, val) : val;
				};

				// Setup the attributes configuration.
				_.each(attributes, function(attrConfig) {
					var lastClass = '',
						observed = attrConfig.observe || modelAttr,
						updateAttr = function() {
							var val;
							if (attrConfig.format) val = applyViewFn(self, attrConfig.format, model.get(observed));
							else val = model.get(observed);
							// If it is a class then we need to remove the last value and add the new.
							if (attrConfig.name == 'class') {
								$el.removeClass(lastClass).addClass(val);
								lastClass = val;
							}
							else $el.attr(attrConfig.name, val);
						};
					observeModelEvent('bind:' + observed, updateAttr);
					updateAttr();
				});

				if (modelAttr) {
					// If the bind element is a form element, then configure `this.events` bindings
					// so that the model stays in sync with user input/changes.
					if ($el.is('input[type=radio]') || $el.is('input[type=checkbox]') || $el.is('select'))
						formElEvent = 'change';
					else if ($el.is('input') || $el.is('textarea'))
						formElEvent = 'keyup';
					if (formElEvent)
						self.events[formElEvent+' '+selector] = function(e) {
							var options = _.extend({bind:false}, config.setOptions || {});
							model.set(modelAttr, getFormElVal($el), options);
						};

					// Setup a `bind:modelAttr` observer for the model to keep the view element in sync.
					observeModelEvent('bind:'+modelAttr, function() {
						updateViewBindEl(self, $el, config, getVal(modelAttr));
					});

					updateViewBindEl(self, $el, config, getVal(modelAttr), true);
				}
			});

			// We added to `this.events` so we need to re-delegate.
			this.delegateEvents();

			// Wrap remove so that we can remove model events when this view is removed.
			this.remove = _.wrap(this.remove, function(oldRemove) {
				self.unstickModel();
				oldRemove && oldRemove.call(self);
			});
		}
	});

	// Backbone.Model.set Wrapper
	// --------------------------

	// Wrap set and fire a `bind:[attribute_name]` for each changed attribute,
	// unless there is a {bind:false} option.
	Backbone.Model.prototype.set = _.wrap(Backbone.Model.prototype.set, function(oldSet, key, value, oldOptions) {
		var attrs, attr, val, ret,
			options = oldOptions && _.clone(oldOptions) || {},
			now = this.attributes;

		// Use the parameters to get the `attrs` and `options` objects.
		if (_.isObject(key) || key == null) {
			attrs = key;
			options = value;
		} else {
			attrs = {};
			attrs[key] = value;
		}
		options || (options = {});

		// Delegating to Backbone's model.set().
		ret = oldSet.call(this, attrs, options);

		// Iterate through the attributes that were just set.
		_.each(_.keys(attrs), _.bind(function(attr) {
			// Trigger a custom "bind" event for each attribute that has changed, unless {bind:false} option.
			if (options.bind !== false && (!_.isEqual(now[attr], val) || (options.unset && _.has(now, attr))))
				this.trigger('bind:' + attr, attrs[attr]);
		}, this));

		return ret;
	});

	// Utilities
	// ---------

	// Evaluates the given `path` (in object/dot-notation) relative to the given `obj`.
	evaluatePath = function(obj, path) {
		var pathParts = (path || '').split('.');
		return _.reduce(pathParts, function(memo, i) { return memo[i]; }, obj) || obj;
	};

	// If the given `fn` is a string, then view[fn] is called, otherwise it is a function
	// that should be executed.
	applyViewFn = function(view, fn) {
		if (fn) return (_.isString(fn) ? view[fn] : fn).apply(view, _.toArray(arguments).slice(2));
	};

	// Gets the value from the given form element.
	getFormElVal = function($el) {
		var val;
		if ($el.is('input[type=checkbox]')) val = $el.prop('checked');
		else if ($el.is('select')) val = $el.find('option:selected').data('stickit_bind_val');
		else if ($el.is('input[type=number]')) val = $el.asNumber();
		else val = $el.val();
		return val;
	};

	// Update the value of `$el` in `view` using the given configuration.
	updateViewBindEl = function(view, $el, config, val, isInitializing) {
		var markReadonly, originalVal, tempSelection,
			modelAttr = config.modelAttr,
			readonly = config.readonly,
			afterUpdate = config.afterUpdate,
			selectConfig = config.selectOptions,
			updateMethod = config.updateMethod || 'text';

		// Sets the readonly property of the bind element based on the truthiness of the `readonly`
		// configuration, or the result of its execution in the case that it is a function.
		markReadonly = function() {
			if (readonly) {
				if (_.isBoolean(readonly)) $el.prop('readonly', readonly);
				else $el.prop('readonly', view[readonly].call(view, modelAttr));
			} else
				$el.prop('readonly', false);
		};

		if ($el.is('input[type=radio]')) {
			tempSelection = $el.filter('[value='+val+']');
			originalVal = tempSelection.prop('checked');
			tempSelection.prop('checked', true);
			markReadonly();
		} else if ($el.is('input[type=checkbox]')) {
			originalVal = $el.prop('checked');
			$el.prop('checked', val === true);
			markReadonly();
		} else if ($el.is('input') || $el.is('textarea')) {
			originalVal = $el.val();
			if (originalVal != val) $el.val(val);
			markReadonly();
		} else if ($el.is('select')) {
			var optList, list = selectConfig.collection, fieldVal = model.get(modelAttr);

			// Get the current selected option value if the select options exist.
			if ($el[0].options && $el[0].selectedIndex)
				originalVal = $($el[0].options[$el[0].selectedIndex]).data('stickit_bind_val');

			$el.html('');

			// The `list` configuration is a function that returns the options list or a string
			// which represents the path to the list relative to `window`.
			optList = _.isFunction(list) ? list.call(view) : evaluatePath(window, list);

			// Add an empty default option if the current model attribute isn't defined.
			if (fieldVal == null)
				$el.append('<option/>').find('option').prop('selected', true).data('stickit_bind_val', fieldVal);

			_.each(optList, function(obj) {
				var option = $('<option/>'), optionVal = obj;

				// If the list contains a null/undefined value, then an empty option should
				// be appended in the list; otherwise, fill the option with text and value.
				if (obj != null) {
					option.text(obj[selectConfig.labelPath]);
					optionVal = evaluatePath(obj, selectConfig.valuePath);
				}

				// Save the option value so that we can reference it later.
				option.data('stickit_bind_val', optionVal);

				// Determine if this option is selected.
				if (optionVal != null && fieldVal != null && optionVal == fieldVal || (_.isObject(fieldVal) && _.isEqual(optionVal, fieldVal)))
					option.prop('selected', true);

				$el.append(option);
			});
			markReadonly();
		} else {
			originalVal = $el[updateMethod]();
			$el[updateMethod](val);
		}

		// Execute the `afterUpdate` callback from the `bindings` config.
		if (!isInitializing) applyViewFn(view, afterUpdate, $el, val, originalVal);
	};

})(window.jQuery || window.Zepto);<commit_msg>Removed usage of .asNumber. Use Number() instead.<commit_after>(function($) {

	var evaluatePath, applyViewFn, updateViewBindEl, getFormElVal;

	// Backbone.View Mixins
	// --------------------

	_.extend(Backbone.View.prototype, {

		// Collection of model event bindings.
		//   [{model,event,fn}, ...]
		_modelBindings: [],

		// Unbind the model bindings that are referenced in `this._modelBindings`.
		unstickModel: function() {
			_.each(this._modelBindings, function(binding) {
				binding.model.off(binding.event, binding.fn);
			});
			this._modelBindings = [];
		},

		// Using `this.bindings` configuration or the `optionalBindingsConfig`, binds `this.model`
		// or the `optionalModel` to elements in the view.
		stickit: function(optionalModel, optionalBindingsConfig) {
			var self = this, observeModelEvent,
				model = optionalModel || this.model,
				bindings = optionalBindingsConfig || this.bindings || {};

			this.events || (this.events = {});

			// Setup a model event binding with the given function, and track the event in this._modelBindings.
			observeModelEvent = function(event, fn) {
				model.on(event, fn, self);
				self._modelBindings.push({model:model, event:event, fn:fn});
			};

			// Iterate through the selectors in the bindings configuration and configure
			// the various options for each field.
			_.each(_.keys(bindings), function(selector) {
				var getVal, modelEvents, formElEvent,
					config = bindings[selector] || {},
					$el = self.$(selector),
					format = config.format,
					modelAttr = config.modelAttr,
					attributes = config.attributes || [];

				// Fail fast if the selector didn't match an element.
				if (!$el.length) return false;

				// Returns the given `field`'s value from the model, escaping and formatting if necessary.
				getVal = function(field) {
					var val = config.escape ? model.escape(field) : model.get(field);
					if (_.isUndefined(val)) val = '';
					return format ? applyViewFn(self, format, val) : val;
				};

				// Setup the attributes configuration.
				_.each(attributes, function(attrConfig) {
					var lastClass = '',
						observed = attrConfig.observe || modelAttr,
						updateAttr = function() {
							var val;
							if (attrConfig.format) val = applyViewFn(self, attrConfig.format, model.get(observed));
							else val = model.get(observed);
							// If it is a class then we need to remove the last value and add the new.
							if (attrConfig.name == 'class') {
								$el.removeClass(lastClass).addClass(val);
								lastClass = val;
							}
							else $el.attr(attrConfig.name, val);
						};
					observeModelEvent('bind:' + observed, updateAttr);
					updateAttr();
				});

				if (modelAttr) {
					// If the bind element is a form element, then configure `this.events` bindings
					// so that the model stays in sync with user input/changes.
					if ($el.is('input[type=radio]') || $el.is('input[type=checkbox]') || $el.is('select'))
						formElEvent = 'change';
					else if ($el.is('input') || $el.is('textarea'))
						formElEvent = 'keyup';
					if (formElEvent)
						self.events[formElEvent+' '+selector] = function(e) {
							var options = _.extend({bind:false}, config.setOptions || {});
							model.set(modelAttr, getFormElVal($el), options);
						};

					// Setup a `bind:modelAttr` observer for the model to keep the view element in sync.
					observeModelEvent('bind:'+modelAttr, function() {
						updateViewBindEl(self, $el, config, getVal(modelAttr));
					});

					updateViewBindEl(self, $el, config, getVal(modelAttr), true);
				}
			});

			// We added to `this.events` so we need to re-delegate.
			this.delegateEvents();

			// Wrap remove so that we can remove model events when this view is removed.
			this.remove = _.wrap(this.remove, function(oldRemove) {
				self.unstickModel();
				oldRemove && oldRemove.call(self);
			});
		}
	});

	// Backbone.Model.set Wrapper
	// --------------------------

	// Wrap set and fire a `bind:[attribute_name]` for each changed attribute,
	// unless there is a {bind:false} option.
	Backbone.Model.prototype.set = _.wrap(Backbone.Model.prototype.set, function(oldSet, key, value, oldOptions) {
		var attrs, attr, val, ret,
			options = oldOptions && _.clone(oldOptions) || {},
			now = this.attributes;

		// Use the parameters to get the `attrs` and `options` objects.
		if (_.isObject(key) || key == null) {
			attrs = key;
			options = value;
		} else {
			attrs = {};
			attrs[key] = value;
		}
		options || (options = {});

		// Delegating to Backbone's model.set().
		ret = oldSet.call(this, attrs, options);

		// Iterate through the attributes that were just set.
		_.each(_.keys(attrs), _.bind(function(attr) {
			// Trigger a custom "bind" event for each attribute that has changed, unless {bind:false} option.
			if (options.bind !== false && (!_.isEqual(now[attr], val) || (options.unset && _.has(now, attr))))
				this.trigger('bind:' + attr, attrs[attr]);
		}, this));

		return ret;
	});

	// Utilities
	// ---------

	// Evaluates the given `path` (in object/dot-notation) relative to the given `obj`.
	evaluatePath = function(obj, path) {
		var pathParts = (path || '').split('.');
		return _.reduce(pathParts, function(memo, i) { return memo[i]; }, obj) || obj;
	};

	// If the given `fn` is a string, then view[fn] is called, otherwise it is a function
	// that should be executed.
	applyViewFn = function(view, fn) {
		if (fn) return (_.isString(fn) ? view[fn] : fn).apply(view, _.toArray(arguments).slice(2));
	};

	// Gets the value from the given form element.
	getFormElVal = function($el) {
		var val;
		if ($el.is('input[type=checkbox]')) val = $el.prop('checked');
		else if ($el.is('select')) val = $el.find('option:selected').data('stickit_bind_val');
		else if ($el.is('input[type=number]')) val = Number($el.val());
		else val = $el.val();
		return val;
	};

	// Update the value of `$el` in `view` using the given configuration.
	updateViewBindEl = function(view, $el, config, val, isInitializing) {
		var markReadonly, originalVal, tempSelection,
			modelAttr = config.modelAttr,
			readonly = config.readonly,
			afterUpdate = config.afterUpdate,
			selectConfig = config.selectOptions,
			updateMethod = config.updateMethod || 'text';

		// Sets the readonly property of the bind element based on the truthiness of the `readonly`
		// configuration, or the result of its execution in the case that it is a function.
		markReadonly = function() {
			if (readonly) {
				if (_.isBoolean(readonly)) $el.prop('readonly', readonly);
				else $el.prop('readonly', view[readonly].call(view, modelAttr));
			} else
				$el.prop('readonly', false);
		};

		if ($el.is('input[type=radio]')) {
			tempSelection = $el.filter('[value='+val+']');
			originalVal = tempSelection.prop('checked');
			tempSelection.prop('checked', true);
			markReadonly();
		} else if ($el.is('input[type=checkbox]')) {
			originalVal = $el.prop('checked');
			$el.prop('checked', val === true);
			markReadonly();
		} else if ($el.is('input') || $el.is('textarea')) {
			originalVal = $el.val();
			if (originalVal != val) $el.val(val);
			markReadonly();
		} else if ($el.is('select')) {
			var optList, list = selectConfig.collection, fieldVal = model.get(modelAttr);

			// Get the current selected option value if the select options exist.
			if ($el[0].options && $el[0].selectedIndex)
				originalVal = $($el[0].options[$el[0].selectedIndex]).data('stickit_bind_val');

			$el.html('');

			// The `list` configuration is a function that returns the options list or a string
			// which represents the path to the list relative to `window`.
			optList = _.isFunction(list) ? list.call(view) : evaluatePath(window, list);

			// Add an empty default option if the current model attribute isn't defined.
			if (fieldVal == null)
				$el.append('<option/>').find('option').prop('selected', true).data('stickit_bind_val', fieldVal);

			_.each(optList, function(obj) {
				var option = $('<option/>'), optionVal = obj;

				// If the list contains a null/undefined value, then an empty option should
				// be appended in the list; otherwise, fill the option with text and value.
				if (obj != null) {
					option.text(obj[selectConfig.labelPath]);
					optionVal = evaluatePath(obj, selectConfig.valuePath);
				}

				// Save the option value so that we can reference it later.
				option.data('stickit_bind_val', optionVal);

				// Determine if this option is selected.
				if (optionVal != null && fieldVal != null && optionVal == fieldVal || (_.isObject(fieldVal) && _.isEqual(optionVal, fieldVal)))
					option.prop('selected', true);

				$el.append(option);
			});
			markReadonly();
		} else {
			originalVal = $el[updateMethod]();
			$el[updateMethod](val);
		}

		// Execute the `afterUpdate` callback from the `bindings` config.
		if (!isInitializing) applyViewFn(view, afterUpdate, $el, val, originalVal);
	};

})(window.jQuery || window.Zepto);<|endoftext|>


<commit_before>/**
 *
 * taskContainer.js
 *
 */

zkTaskContainer = {};

zkTaskContainer.getDD = function(cmp) {
    zkTask.getDD(cmp);
};

zkTaskContainer.relocateAfterAdding = function(cmp) {
    zkTask.relocateAfterAdding (cmp);
};

zkTaskContainer.cleanup = function(cmp){
    zkTask.cleanup(cmp);
};

zkTaskContainer.init = function(cmp) {
    zkTask.init(cmp);
};

zkTaskContainer.xMouse;
zkTaskContainer.yMouse;

// Listen to mousemove events
YAHOO.util.Event.on(document.body, 'mousemove', function(e) {
	var arrPos = YAHOO.util.Event.getXY(e);
	zkTaskContainer.xMouse = arrPos[0];
	zkTaskContainer.yMouse = arrPos[1];
});

zkTaskContainer.setAttr = function(cmp, nm, val) {
    zkTask.setAttr(cmp, nm, val);
};

zkTaskContainer.addDependency = function(cmp) {
    zkTask.addDependency(cmp);
};

zkTaskContainer.setAttr = function(cmp, name, value) {
    zkTask.setAttr(cmp, name, value);
};

zkTaskContainer.addRelatedDependency = function(cmp, dependency) {
    zkTask.addRelatedDependency(cmp, dependency);
};

/* Dependencies with origin in a task container will be redrawn with a
 * different algorithm */
zkTaskContainer.createArrow = function(cmp) {
    zkTask.createArrow(cmp)
};

zkTaskContainer.isOverTask = function(cmp, arrow) {
    zkTask.isOverTask(cmp, arrow);
};

zkTaskContainer.getElementsByAttribute = function(oElm, strTagName, strAttributeName,
    strAttributeValue) {
    zkTask.getElementsByAttribute(oElm, strTagName, strAttributeName, strAttributeValue);
}

YAHOO.example.DDRegion = function(id, sGroup, config) {
    this.cont = config.cont;
    YAHOO.example.DDRegion.superclass.constructor.apply(this, arguments);
};

var myDom = YAHOO.util.Dom, myEvent = YAHOO.util.Event

YAHOO.extend(YAHOO.example.DDRegion, YAHOO.util.DD, {
    cont : null,
    init : function() {
        //Call the parent's init method
        YAHOO.example.DDRegion.superclass.init.apply(this, arguments);
        this.initConstraints();

        myEvent.on(window, 'resize', function() {
            this.initConstraints();
        }, this, true);
    },
    initConstraints : function() {

        //Get the top, right, bottom and left positions
        var region = myDom.getRegion(this.cont);

        // Get the element we are working on
        var el = this.getEl();

        // Get the xy position of it
        var xy = myDom.getXY(el);

        // Get the width and height
        var width = parseInt(myDom.getStyle(el, 'width'), 10);
        var height = parseInt(myDom.getStyle(el, 'height'), 10);

        // Set left to x minus left
        var left = xy[0] - region.left;

        // Set right to right minus x minus width
        var right = region.right - xy[0] - width;

        // Set top to y minus top
        var top = xy[1] - region.top;

        // Set bottom to bottom minus y minus height
        var bottom = region.bottom - xy[1] - height;

        // Set the constraints based on the above calculations
        this.setXConstraint(left, right);
        this.setYConstraint(top, bottom);
    }
});<commit_msg> ItEr15S12RFComportamentoGraficoPlanificadorItEr14S13: TaskContainers resize and move operations have been disabled while its behaviour it is not clear<commit_after>/**
 *
 * taskContainer.js
 *
 */

zkTaskContainer = {};

/* We will not allow taskcontainer move or resize untill its behaviour its
 * clearly specified
zkTaskContainer.getDD = function(cmp) {
    zkTask.getDD(cmp);
}; */

zkTaskContainer.relocateAfterAdding = function(cmp) {
    zkTask.relocateAfterAdding (cmp);
};

zkTaskContainer.cleanup = function(cmp){
    zkTask.cleanup(cmp);
};

zkTaskContainer.init = function(cmp) {
/* We will not allow taskcontainer move or resize untill its behaviour its
 * clearly specified
    zkTask.init(cmp); */
};

zkTaskContainer.xMouse;
zkTaskContainer.yMouse;

// Listen to mousemove events
YAHOO.util.Event.on(document.body, 'mousemove', function(e) {
	var arrPos = YAHOO.util.Event.getXY(e);
	zkTaskContainer.xMouse = arrPos[0];
	zkTaskContainer.yMouse = arrPos[1];
});

zkTaskContainer.setAttr = function(cmp, nm, val) {
    zkTask.setAttr(cmp, nm, val);
};

zkTaskContainer.addDependency = function(cmp) {
    zkTask.addDependency(cmp);
};

zkTaskContainer.setAttr = function(cmp, name, value) {
    zkTask.setAttr(cmp, name, value);
};

zkTaskContainer.addRelatedDependency = function(cmp, dependency) {
    zkTask.addRelatedDependency(cmp, dependency);
};

/* Dependencies with origin in a task container will be redrawn with a
 * different algorithm */
zkTaskContainer.createArrow = function(cmp) {
    zkTask.createArrow(cmp)
};

zkTaskContainer.isOverTask = function(cmp, arrow) {
    zkTask.isOverTask(cmp, arrow);
};

zkTaskContainer.getElementsByAttribute = function(oElm, strTagName, strAttributeName,
    strAttributeValue) {
    zkTask.getElementsByAttribute(oElm, strTagName, strAttributeName, strAttributeValue);
}

/* We will not allow taskcontainer move or resize untill its behaviour its
 * clearly specified

YAHOO.example.DDRegion = function(id, sGroup, config) {
    this.cont = config.cont;
    YAHOO.example.DDRegion.superclass.constructor.apply(this, arguments);
};

var myDom = YAHOO.util.Dom, myEvent = YAHOO.util.Event

YAHOO.extend(YAHOO.example.DDRegion, YAHOO.util.DD, {
    cont : null,
    init : function() {
        //Call the parent's init method
        YAHOO.example.DDRegion.superclass.init.apply(this, arguments);
        this.initConstraints();

        myEvent.on(window, 'resize', function() {
            this.initConstraints();
        }, this, true);
    },
    initConstraints : function() {

        //Get the top, right, bottom and left positions
        var region = myDom.getRegion(this.cont);

        // Get the element we are working on
        var el = this.getEl();

        // Get the xy position of it
        var xy = myDom.getXY(el);

        // Get the width and height
        var width = parseInt(myDom.getStyle(el, 'width'), 10);
        var height = parseInt(myDom.getStyle(el, 'height'), 10);

        // Set left to x minus left
        var left = xy[0] - region.left;

        // Set right to right minus x minus width
        var right = region.right - xy[0] - width;

        // Set top to y minus top
        var top = xy[1] - region.top;

        // Set bottom to bottom minus y minus height
        var bottom = region.bottom - xy[1] - height;

        // Set the constraints based on the above calculations
        this.setXConstraint(left, right);
        this.setYConstraint(top, bottom);
    }
}); */<|endoftext|>


<commit_before>/*
 *  Waltz
 * Copyright (c) David Watkins. All rights reserved.
 * The use and distribution terms for this software are covered by the
 * Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
 * which can be found in the file epl-v10.html at the root of this distribution.
 * By using this software in any fashion, you are agreeing to be bound by
 * the terms of this license.
 * You must not remove this notice, or any other, from this software.
 *
 */
import _ from "lodash";
import {loadDataFlows, loadDataTypes, loadAppAuthSources, loadOrgUnitAuthSources} from "./registration-utils";
import {prepareSlopeGraph} from "./directives/slope-graph/slope-graph-utils";

function calculateWorkingTypes(flows = [], predicate) {
    return _.chain(flows)
        .filter(predicate)
        .map('dataType')
        .value();
}

function vetoMove(isDirty) {
    if (isDirty) {
        alert('Unsaved changes, either apply them or cancel');
        return true;
    }
    return false;
}

function addApplication(app, appGroup) {
    if (!app) return;
    if (_.some(appGroup, a => a.id === app.id)) return;
    appGroup.push(app);
}

function loadDataTypeUsages(dataTypeUsageStore, appId, vm) {
    dataTypeUsageStore
        .findForEntity('APPLICATION', appId)
        .then(usages => vm.dataTypeUsages = usages);
}

const initialState = {
    app: null,
    currentDataTypes: [],
    dataTypes: [],
    dataTypeUsages: [],
    flows: [],
    graphOptions: {
        data: {
            incoming: [],
            outgoing: [],
            sources: [],
            targets: [],
            types: []
        },
        tweakers: {}
    },
    isDirty: false,
    mode: '',
    selectedApp: null,
    selectedDirection: null,
    selectedUsages: []
};

function controller($q,
                    $scope,
                    $state,
                    application,
                    authSourceStore,
                    dataFlowStore,
                    dataTypeStore,
                    dataTypeUsageStore,
                    displayNameService,
                    notification) {

    const primaryAppId = application.id;
    const ouId = application.organisationalUnitId;

    const vm = _.defaultsDeep(this, initialState);
    vm.app = application;

    const reload = () => {
        loadDataFlows(dataFlowStore, primaryAppId, vm)
            .then(() => prepareData());

        loadDataTypeUsages(dataTypeUsageStore, primaryAppId, vm);
        vm.cancel();
    };

    const selectSource = (app) => {
        if (!app) return;
        if (vetoMove(vm.isDirty)) {
            return;
        }

        vm.selectedApp = app;
        vm.selectedDirection = 'source';

        vm.currentDataTypes = calculateWorkingTypes(
            vm.flows,
            { source: { id: app.id }});

        vm.setMode('editCounterpart');

    };

    const selectTarget = (app) => {
        if (vetoMove(vm.isDirty)) {
            return;
        }
        vm.selectedApp = app;
        vm.selectedDirection = 'target';

        vm.currentDataTypes = calculateWorkingTypes(
            vm.flows,
            { target: { id: app.id }});

        vm.setMode('editCounterpart');

    };

    const selectType = (type) => {
        vm.setMode('editDataTypeUsage');
        vm.selectedDataType = type;
        vm.selectedUsages = _.chain(vm.dataTypeUsages)
            .filter({ dataTypeCode: type.code })
            .map('usage')
            .value()
    };

    const promises = [
        loadDataFlows(dataFlowStore, primaryAppId, vm),
        loadAppAuthSources(authSourceStore, primaryAppId, vm),
        loadOrgUnitAuthSources(authSourceStore, ouId, vm),
        loadDataTypes(dataTypeStore, vm),
        loadDataTypeUsages(dataTypeUsageStore, primaryAppId, vm)
    ];

    const prepareData = () => {
        const dataTypes = _.chain(vm.flows)
            .map('dataType')
            .uniq()
            .value();

        const graphOptions = prepareSlopeGraph(
            primaryAppId,
            vm.flows,
            dataTypes,
            vm.appAuthSources,
            vm.ouAuthSources,
            displayNameService,
            $state);

        graphOptions.tweakers.target = {
            enter: selection => selection.on('click', app => $scope.$evalAsync(() => selectTarget(app)))
        };

        graphOptions.tweakers.source = {
            enter: selection => selection.on('click', app => $scope.$evalAsync(() => selectSource(app)))
        };

        graphOptions.tweakers.type = {
            enter: selection => selection
                .classed('clickable', true)
                .on('click', type => $scope.$evalAsync(() => selectType(type)))
        };

        vm.graphOptions = graphOptions;
    };

    $q.all(promises)
        .then(() => prepareData());

    vm.cancel = () => {
        vm.selectedApp = null;
        vm.selectedDirection = null;
        vm.isDirty = false;
        vm.setMode('');
    };

    vm.saveFlows = (command) => {
        dataFlowStore.create(command)
            .then(() => reload())
            .then(() => notification.success('Logical flows updated'));
    };

    vm.saveUsages = (usages = []) => {
        const ref = { id: vm.app.id, kind: 'APPLICATION' };
        const dataTypeCode = vm.selectedDataType.code;
        dataTypeUsageStore.save(ref, dataTypeCode, usages)
            .then(() => reload())
            .then(() => notification.success('Data usage updated'));
    };

    vm.addSource = (app) => {
        if(vm.app.id == app.id) {
            notification.warning("An application may not link to itself.");
            return;
        }

        selectSource(app);
        addApplication(app, vm.graphOptions.data.sources);
    };

    vm.addTarget = (app) => {
        if(vm.app.id == app.id) {
            notification.warning("An application may not link to itself.")
            return;
        }
        selectTarget(app);
        addApplication(app, vm.graphOptions.data.targets);
    };

    vm.setDirtyChange = (dirty) => vm.isDirty = dirty;

    vm.setMode = (mode) => {
        if (vetoMove(vm.isDirty)) {
            return;
        }
        vm.mode = mode;
    };
}

controller.$inject = [
    '$q',
    '$scope',
    '$state',
    'application',
    'AuthSourcesStore',
    'DataFlowDataStore',
    'DataTypesDataService',
    'DataTypeUsageStore',
    'WaltzDisplayNameService',
    'Notification'
];

export default {
    template: require('./data-flow-edit.html'),
    controller,
    controllerAs: 'ctrl'
};
<commit_msg>pull out check and notify into common function<commit_after>/*
 *  Waltz
 * Copyright (c) David Watkins. All rights reserved.
 * The use and distribution terms for this software are covered by the
 * Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
 * which can be found in the file epl-v10.html at the root of this distribution.
 * By using this software in any fashion, you are agreeing to be bound by
 * the terms of this license.
 * You must not remove this notice, or any other, from this software.
 *
 */
import _ from "lodash";
import {loadDataFlows, loadDataTypes, loadAppAuthSources, loadOrgUnitAuthSources} from "./registration-utils";
import {prepareSlopeGraph} from "./directives/slope-graph/slope-graph-utils";

function calculateWorkingTypes(flows = [], predicate) {
    return _.chain(flows)
        .filter(predicate)
        .map('dataType')
        .value();
}

function vetoMove(isDirty) {
    if (isDirty) {
        alert('Unsaved changes, either apply them or cancel');
        return true;
    }
    return false;
}

function addApplication(app, appGroup) {
    if (!app) return;
    if (_.some(appGroup, a => a.id === app.id)) return;
    appGroup.push(app);
}

function loadDataTypeUsages(dataTypeUsageStore, appId, vm) {
    dataTypeUsageStore
        .findForEntity('APPLICATION', appId)
        .then(usages => vm.dataTypeUsages = usages);
}

function notifyIllegalFlow(notification, targetApp, flowApp) {
    if(targetApp.id == flowApp.id) {
        notification.warning("An application may not link to itself.");
        return true;
    }
    return false;
}

const initialState = {
    app: null,
    currentDataTypes: [],
    dataTypes: [],
    dataTypeUsages: [],
    flows: [],
    graphOptions: {
        data: {
            incoming: [],
            outgoing: [],
            sources: [],
            targets: [],
            types: []
        },
        tweakers: {}
    },
    isDirty: false,
    mode: '',
    selectedApp: null,
    selectedDirection: null,
    selectedUsages: []
};

function controller($q,
                    $scope,
                    $state,
                    application,
                    authSourceStore,
                    dataFlowStore,
                    dataTypeStore,
                    dataTypeUsageStore,
                    displayNameService,
                    notification) {

    const primaryAppId = application.id;
    const ouId = application.organisationalUnitId;

    const vm = _.defaultsDeep(this, initialState);
    vm.app = application;

    const reload = () => {
        loadDataFlows(dataFlowStore, primaryAppId, vm)
            .then(() => prepareData());

        loadDataTypeUsages(dataTypeUsageStore, primaryAppId, vm);
        vm.cancel();
    };

    const selectSource = (app) => {
        if (!app) return;
        if (vetoMove(vm.isDirty)) {
            return;
        }

        vm.selectedApp = app;
        vm.selectedDirection = 'source';

        vm.currentDataTypes = calculateWorkingTypes(
            vm.flows,
            { source: { id: app.id }});

        vm.setMode('editCounterpart');

    };

    const selectTarget = (app) => {
        if (vetoMove(vm.isDirty)) {
            return;
        }
        vm.selectedApp = app;
        vm.selectedDirection = 'target';

        vm.currentDataTypes = calculateWorkingTypes(
            vm.flows,
            { target: { id: app.id }});

        vm.setMode('editCounterpart');

    };

    const selectType = (type) => {
        vm.setMode('editDataTypeUsage');
        vm.selectedDataType = type;
        vm.selectedUsages = _.chain(vm.dataTypeUsages)
            .filter({ dataTypeCode: type.code })
            .map('usage')
            .value()
    };

    const promises = [
        loadDataFlows(dataFlowStore, primaryAppId, vm),
        loadAppAuthSources(authSourceStore, primaryAppId, vm),
        loadOrgUnitAuthSources(authSourceStore, ouId, vm),
        loadDataTypes(dataTypeStore, vm),
        loadDataTypeUsages(dataTypeUsageStore, primaryAppId, vm)
    ];

    const prepareData = () => {
        const dataTypes = _.chain(vm.flows)
            .map('dataType')
            .uniq()
            .value();

        const graphOptions = prepareSlopeGraph(
            primaryAppId,
            vm.flows,
            dataTypes,
            vm.appAuthSources,
            vm.ouAuthSources,
            displayNameService,
            $state);

        graphOptions.tweakers.target = {
            enter: selection => selection.on('click', app => $scope.$evalAsync(() => selectTarget(app)))
        };

        graphOptions.tweakers.source = {
            enter: selection => selection.on('click', app => $scope.$evalAsync(() => selectSource(app)))
        };

        graphOptions.tweakers.type = {
            enter: selection => selection
                .classed('clickable', true)
                .on('click', type => $scope.$evalAsync(() => selectType(type)))
        };

        vm.graphOptions = graphOptions;
    };

    $q.all(promises)
        .then(() => prepareData());

    vm.cancel = () => {
        vm.selectedApp = null;
        vm.selectedDirection = null;
        vm.isDirty = false;
        vm.setMode('');
    };

    vm.saveFlows = (command) => {
        dataFlowStore.create(command)
            .then(() => reload())
            .then(() => notification.success('Logical flows updated'));
    };

    vm.saveUsages = (usages = []) => {
        const ref = { id: vm.app.id, kind: 'APPLICATION' };
        const dataTypeCode = vm.selectedDataType.code;
        dataTypeUsageStore.save(ref, dataTypeCode, usages)
            .then(() => reload())
            .then(() => notification.success('Data usage updated'));
    };

    vm.addSource = (app) => {
        if(notifyIllegalFlow(notification, vm.app, app)) return;
        selectSource(app);
        addApplication(app, vm.graphOptions.data.sources);
    };

    vm.addTarget = (app) => {
        if(notifyIllegalFlow(notification, vm.app, app)) return;
        selectTarget(app);
        addApplication(app, vm.graphOptions.data.targets);
    };

    vm.setDirtyChange = (dirty) => vm.isDirty = dirty;

    vm.setMode = (mode) => {
        if (vetoMove(vm.isDirty)) {
            return;
        }
        vm.mode = mode;
    };
}

controller.$inject = [
    '$q',
    '$scope',
    '$state',
    'application',
    'AuthSourcesStore',
    'DataFlowDataStore',
    'DataTypesDataService',
    'DataTypeUsageStore',
    'WaltzDisplayNameService',
    'Notification'
];

export default {
    template: require('./data-flow-edit.html'),
    controller,
    controllerAs: 'ctrl'
};
<|endoftext|>


<commit_before>/**
* Copyright 2012-2018, Plotly, Inc.
* All rights reserved.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/

'use strict';

var createMatrix = require('regl-scattermatrix');
var arrayRange = require('array-range');

var Lib = require('../../lib');
var AxisIDs = require('../../plots/cartesian/axis_ids');

var subTypes = require('../scatter/subtypes');
var calcMarkerSize = require('../scatter/calc').calcMarkerSize;
var calcAxisExpansion = require('../scatter/calc').calcAxisExpansion;
var calcColorscales = require('../scatter/colorscale_calc');
var convertMarkerSelection = require('../scattergl/convert').convertMarkerSelection;
var convertMarkerStyle = require('../scattergl/convert').convertMarkerStyle;
var calcHover = require('../scattergl').calcHover;

var BADNUM = require('../../constants/numerical').BADNUM;
var TOO_MANY_POINTS = require('../scattergl/constants').TOO_MANY_POINTS;

function calc(gd, trace) {
    var stash = {};
    var opts = {};
    var i, xa, ya, dim;

    var commonLength = trace._commonLength;
    var activeLength = trace._activeLength;
    var matrix = opts.data = [];

    for(i = 0; i < activeLength; i++) {
        dim = trace.dimensions[i];

        if(dim.visible) {
            // using xa or ya should make no difference here
            xa = AxisIDs.getFromId(gd, trace.xaxes[i]);
            matrix.push(makeCalcdata(xa, trace, dim));
        }
    }

    calcColorscales(trace);
    Lib.extendFlat(opts, convertMarkerStyle(trace));

    var visibleLength = matrix.length;
    var hasTooManyPoints = (visibleLength * commonLength) > TOO_MANY_POINTS;
    var k = 0;

    for(i = 0; i < activeLength; i++) {
        dim = trace.dimensions[i];

        if(dim.visible) {
            xa = AxisIDs.getFromId(gd, trace.xaxes[i]);
            ya = AxisIDs.getFromId(gd, trace.yaxes[i]);

            // Re-use SVG scatter axis expansion routine except
            // for graph with very large number of points where it
            // performs poorly.
            // In big data case, fake Axes.expand outputs with data bounds,
            // and an average size for array marker.size inputs.
            var ppad;
            if(hasTooManyPoints) {
                ppad = 2 * (opts.sizeAvg || Math.max(opts.size, 3));
            } else {
                ppad = calcMarkerSize(trace, commonLength);
            }
            calcAxisExpansion(gd, trace, xa, ya, matrix[k], matrix[k], ppad);
            k++;
        }
    }

    var scene = stash._scene = sceneUpdate(gd, stash);
    if(!scene.matrix) scene.matrix = true;
    scene.matrixOptions = opts;

    scene.selectedOptions = convertMarkerSelection(trace, trace.selected);
    scene.unselectedOptions = convertMarkerSelection(trace, trace.unselected);

    return [{x: false, y: false, t: stash, trace: trace}];
}

function makeCalcdata(ax, trace, dim) {
    var i;

    var cdata = ax.makeCalcdata({
        v: dim.values,
        vcalendar: trace.calendar
    }, 'v');

    for(i = 0; i < cdata.length; i++) {
        cdata[i] = cdata[i] === BADNUM ? NaN : cdata[i];
    }

    if(ax.type === 'log') {
        for(i = 0; i < cdata.length; i++) {
            cdata[i] = ax.c2l(cdata[i]);
        }
    }

    return cdata;
}

// TODO do we need this?
function sceneUpdate(gd, stash) {
    var scene = stash._scene;

    var reset = {
        dirty: true
    };

    var first = {
        selectBatch: null,
        unselectBatch: null,
        matrix: false,
        select: null
    };

    if(!scene) {
        scene = stash._scene = Lib.extendFlat({}, reset, first);

        scene.draw = function draw() {
            // draw traces in selection mode
            if(scene.matrix && scene.selectBatch) {
                scene.matrix.draw(scene.unselectBatch, scene.selectBatch);
            }

            else if(scene.matrix) {
                scene.matrix.draw();
            }

            scene.dirty = false;
        };

        // remove scene resources
        scene.destroy = function destroy() {
            if(scene.matrix) scene.matrix.destroy();

            scene.matrixOptions = null;
            scene.selectBatch = null;
            scene.unselectBatch = null;

            stash._scene = null;
        };
    }

    // In case if we have scene from the last calc - reset data
    if(!scene.dirty) {
        Lib.extendFlat(scene, reset);
    }

    return scene;
}

function plot(gd, _, cdata) {
    if(!cdata.length) return;

    for(var i = 0; i < cdata.length; i++) {
        plotOne(gd, cdata[i][0]);
    }
}

function plotOne(gd, cd0) {
    var fullLayout = gd._fullLayout;
    var gs = fullLayout._size;
    var trace = cd0.trace;
    var stash = cd0.t;
    var scene = stash._scene;
    var matrixData = scene.matrixOptions.data;
    var regl = fullLayout._glcanvas.data()[0].regl;
    var dragmode = fullLayout.dragmode;
    var xa, ya;

    if(matrixData.length === 0) return;

    var k = 0, i;
    var activeLength = trace._activeLength;
    var visibleLength = matrixData.length;
    var dataLength = matrixData[0].length;
    var viewOpts = {
        ranges: new Array(visibleLength),
        domains: new Array(visibleLength)
    };

    for(i = 0; i < activeLength; i++) {
        if(!trace.dimensions[i].visible) continue;

        xa = AxisIDs.getFromId(gd, trace.xaxes[i]);
        ya = AxisIDs.getFromId(gd, trace.yaxes[i]);
        viewOpts.ranges[k] = [xa.range[0], ya.range[0], xa.range[1], ya.range[1]];
        viewOpts.domains[k] = [xa.domain[0], ya.domain[0], xa.domain[1], ya.domain[1]];
        k++;
    }

    viewOpts.viewport = [gs.l, gs.b, gs.w + gs.l, gs.h + gs.b];

    if(scene.matrix === true) {
        scene.matrix = createMatrix(regl);
    }

    var selectMode = dragmode === 'lasso' || dragmode === 'select' || !!trace.selectedpoints;
    scene.selectBatch = null;
    scene.unselectBatch = null;

    if(selectMode) {
        if(!scene.selectBatch) {
            scene.selectBatch = [];
            scene.unselectBatch = [];
        }

        // regenerate scene batch, if traces number changed during selection
        if(trace.selectedpoints) {
            scene.selectBatch = trace.selectedpoints;

            var selPts = trace.selectedpoints;
            var selDict = {};
            for(i = 0; i < selPts.length; i++) {
                selDict[selPts[i]] = true;
            }
            var unselPts = [];
            for(i = 0; i < dataLength; i++) {
                if(!selDict[i]) unselPts.push(i);
            }
            scene.unselectBatch = unselPts;
        }

        // precalculate px coords since we are not going to pan during select
        var xpx = new Array(visibleLength);
        var ypx = new Array(visibleLength);
        var data;
        for(k = 0; k < visibleLength; k++) {
            xa = AxisIDs.getFromId(gd, trace.xaxes[k]);
            ya = AxisIDs.getFromId(gd, trace.yaxes[k]);

            xpx[k] = new Array(dataLength);
            ypx[k] = new Array(dataLength);

            data = matrixData[k];

            for(i = 0; i < dataLength; i++) {
                xpx[k][i] = xa.c2p(data[i]);
                ypx[k][i] = ya.c2p(data[i]);
            }
        }
        stash.xpx = xpx;
        stash.ypx = ypx;

        if(scene.selectBatch) {
            scene.matrix.update(scene.matrixOptions, scene.matrixOptions);
            scene.matrix.update(scene.unselectedOptions, scene.selectedOptions);
            scene.matrix.update(viewOpts, viewOpts);
        }
        else {
            // delete selection pass
            scene.matrix.update(viewOpts, null);
        }
    }
    else {
        scene.matrix.update(scene.matrixOptions);
        scene.matrix.update(viewOpts);
        stash.xpx = stash.ypx = null;
    }

    scene.draw();
}

// TODO splom 'needs' the grid component, register it here?

function hoverPoints(pointData, xval, yval) {
    var cd = pointData.cd;
    var trace = cd[0].trace;
    var xa = pointData.xa;
    var ya = pointData.ya;
    var xpx = xa.c2p(xval);
    var ypx = ya.c2p(yval);
    var maxDistance = pointData.distance;
    var dimensions = trace.dimensions;

    var xi, yi, i;
    for(i = 0; i < dimensions.length; i++) {
        if(trace.xaxes[i] === xa._id) xi = i;
        if(trace.yaxes[i] === ya._id) yi = i;
    }

    var x = dimensions[xi].values || [];
    var y = dimensions[yi].values || [];

    var id, ptx, pty, dx, dy, dist, dxy;
    var minDist = maxDistance;

    for(i = 0; i < x.length; i++) {
        ptx = x[i];
        pty = y[i];
        dx = xa.c2p(ptx) - xpx;
        dy = ya.c2p(pty) - ypx;

        dist = Math.sqrt(dx * dx + dy * dy);
        if(dist < minDist) {
            minDist = dxy = dist;
            id = i;
        }
    }

    pointData.index = id;
    pointData.distance = minDist;
    pointData.dxy = dxy;

    if(id === undefined) return [pointData];

    calcHover(pointData, x, y, trace);

    return [pointData];
}

function selectPoints(searchInfo, polygon) {
    var cd = searchInfo.cd;
    var selection = [];
    var trace = cd[0].trace;
    var stash = cd[0].t;
    var scene = stash._scene;
    var xa = searchInfo.xaxis;
    var ya = searchInfo.yaxis;
    var matrixData = scene.matrixOptions.data;

    if(!scene) return selection;

    var hasOnlyLines = (!subTypes.hasMarkers(trace) && !subTypes.hasText(trace));
    if(trace.visible !== true || hasOnlyLines) return selection;

    var xi, yi, i;
    for(i = 0; i < trace.dimensions.length; i++) {
        if(trace.xaxes[i] === xa._id) xi = i;
        if(trace.yaxes[i] === ya._id) yi = i;
    }

    var xpx = stash.xpx[xi];
    var ypx = stash.ypx[yi];
    var x = matrixData[xi];
    var y = matrixData[yi];

    // degenerate polygon does not enable selection
    // filter out points by visible scatter ones
    var els = null;
    var unels = null;
    if(polygon !== false && !polygon.degenerate) {
        els = [], unels = [];
        for(i = 0; i < x.length; i++) {
            if(polygon.contains([xpx[i], ypx[i]])) {
                els.push(i);
                selection.push({
                    pointNumber: i,
                    x: x[i],
                    y: y[i]
                });
            }
            else {
                unels.push(i);
            }
        }
    } else {
        unels = arrayRange(stash.count);
    }

    // make sure selectBatch is created
    if(!scene.selectBatch) {
        scene.selectBatch = [];
        scene.unselectBatch = [];
    }

    if(!scene.selectBatch) {
        // enter every trace select mode
        for(i = 0; i < scene.count; i++) {
            scene.selectBatch = [];
            scene.unselectBatch = [];
        }
        // we should turn scatter2d into unselected once we have any points selected
        scene.matrix.update(scene.unselectedOptions, scene.selectedOptions);
    }

    scene.selectBatch = els;
    scene.unselectBatch = unels;

    scene.matrix.regl.clear({ color: true });

    return selection;
}

function style(gd, cd) {
    if(cd) {
        var stash = cd[0].t;
        var scene = stash._scene;
        scene.draw();
    }
}

module.exports = {
    moduleType: 'trace',
    name: 'splom',

    basePlotModule: require('./base_plot'),
    categories: ['gl', 'regl', 'cartesian', 'symbols', 'markerColorscale', 'showLegend', 'scatter-like'],

    attributes: require('./attributes'),
    supplyDefaults: require('./defaults'),

    calc: calc,
    plot: plot,
    hoverPoints: hoverPoints,
    selectPoints: selectPoints,
    style: style,

    meta: {
        description: [
            'SPLOM !!!'
        ].join(' ')
    }
};
<commit_msg>Handle upper/lowe halves<commit_after>/**
* Copyright 2012-2018, Plotly, Inc.
* All rights reserved.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/

'use strict';

var createMatrix = require('regl-scattermatrix');
var arrayRange = require('array-range');

var Lib = require('../../lib');
var AxisIDs = require('../../plots/cartesian/axis_ids');

var subTypes = require('../scatter/subtypes');
var calcMarkerSize = require('../scatter/calc').calcMarkerSize;
var calcAxisExpansion = require('../scatter/calc').calcAxisExpansion;
var calcColorscales = require('../scatter/colorscale_calc');
var convertMarkerSelection = require('../scattergl/convert').convertMarkerSelection;
var convertMarkerStyle = require('../scattergl/convert').convertMarkerStyle;
var calcHover = require('../scattergl').calcHover;

var BADNUM = require('../../constants/numerical').BADNUM;
var TOO_MANY_POINTS = require('../scattergl/constants').TOO_MANY_POINTS;

function calc(gd, trace) {
    var stash = {};
    var opts = {};
    var i, xa, ya, dim;

    var commonLength = trace._commonLength;
    var activeLength = trace._activeLength;
    var matrix = opts.data = [];

    for(i = 0; i < activeLength; i++) {
        dim = trace.dimensions[i];

        if(dim.visible) {
            // using xa or ya should make no difference here
            xa = AxisIDs.getFromId(gd, trace.xaxes[i]);
            matrix.push(makeCalcdata(xa, trace, dim));
        }
    }

    calcColorscales(trace);
    Lib.extendFlat(opts, convertMarkerStyle(trace));

    var visibleLength = matrix.length;
    var hasTooManyPoints = (visibleLength * commonLength) > TOO_MANY_POINTS;
    var k = 0;

    for(i = 0; i < activeLength; i++) {
        dim = trace.dimensions[i];

        if(dim.visible) {
            xa = AxisIDs.getFromId(gd, trace.xaxes[i]);
            ya = AxisIDs.getFromId(gd, trace.yaxes[i]);

            // Re-use SVG scatter axis expansion routine except
            // for graph with very large number of points where it
            // performs poorly.
            // In big data case, fake Axes.expand outputs with data bounds,
            // and an average size for array marker.size inputs.
            var ppad;
            if(hasTooManyPoints) {
                ppad = 2 * (opts.sizeAvg || Math.max(opts.size, 3));
            } else {
                ppad = calcMarkerSize(trace, commonLength);
            }
            calcAxisExpansion(gd, trace, xa, ya, matrix[k], matrix[k], ppad);
            k++;
        }
    }

    // augment options with proper upper/lower halves
    if(!trace.showupperhalf) opts.upper = false;
    if(!trace.showlowerhalf) opts.lower = false;
    if(!trace.diagonal.visible) opts.diagonal = false;

    var scene = stash._scene = sceneUpdate(gd, stash);
    if(!scene.matrix) scene.matrix = true;
    scene.matrixOptions = opts;

    scene.selectedOptions = convertMarkerSelection(trace, trace.selected);
    scene.unselectedOptions = convertMarkerSelection(trace, trace.unselected);

    return [{x: false, y: false, t: stash, trace: trace}];
}

function makeCalcdata(ax, trace, dim) {
    var i;

    var cdata = ax.makeCalcdata({
        v: dim.values,
        vcalendar: trace.calendar
    }, 'v');

    for(i = 0; i < cdata.length; i++) {
        cdata[i] = cdata[i] === BADNUM ? NaN : cdata[i];
    }

    if(ax.type === 'log') {
        for(i = 0; i < cdata.length; i++) {
            cdata[i] = ax.c2l(cdata[i]);
        }
    }

    return cdata;
}

// TODO do we need this?
function sceneUpdate(gd, stash) {
    var scene = stash._scene;

    var reset = {
        dirty: true
    };

    var first = {
        selectBatch: null,
        unselectBatch: null,
        matrix: false,
        select: null
    };

    if(!scene) {
        scene = stash._scene = Lib.extendFlat({}, reset, first);

        scene.draw = function draw() {
            // draw traces in selection mode
            if(scene.matrix && scene.selectBatch) {
                scene.matrix.draw(scene.unselectBatch, scene.selectBatch);
            }

            else if(scene.matrix) {
                scene.matrix.draw();
            }

            scene.dirty = false;
        };

        // remove scene resources
        scene.destroy = function destroy() {
            if(scene.matrix) scene.matrix.destroy();

            scene.matrixOptions = null;
            scene.selectBatch = null;
            scene.unselectBatch = null;

            stash._scene = null;
        };
    }

    // In case if we have scene from the last calc - reset data
    if(!scene.dirty) {
        Lib.extendFlat(scene, reset);
    }

    return scene;
}

function plot(gd, _, cdata) {
    if(!cdata.length) return;

    for(var i = 0; i < cdata.length; i++) {
        plotOne(gd, cdata[i][0]);
    }
}

function plotOne(gd, cd0) {
    var fullLayout = gd._fullLayout;
    var gs = fullLayout._size;
    var trace = cd0.trace;
    var stash = cd0.t;
    var scene = stash._scene;
    var matrixData = scene.matrixOptions.data;
    var regl = fullLayout._glcanvas.data()[0].regl;
    var dragmode = fullLayout.dragmode;
    var xa, ya;

    if(matrixData.length === 0) return;

    var k = 0, i;
    var activeLength = trace._activeLength;
    var visibleLength = matrixData.length;
    var dataLength = matrixData[0].length;
    var viewOpts = {
        ranges: new Array(visibleLength),
        domains: new Array(visibleLength)
    };

    for(i = 0; i < activeLength; i++) {
        if(!trace.dimensions[i].visible) continue;

        xa = AxisIDs.getFromId(gd, trace.xaxes[i]);
        ya = AxisIDs.getFromId(gd, trace.yaxes[i]);
        viewOpts.ranges[k] = [xa.range[0], ya.range[0], xa.range[1], ya.range[1]];
        viewOpts.domains[k] = [xa.domain[0], ya.domain[0], xa.domain[1], ya.domain[1]];
        k++;
    }

    viewOpts.viewport = [gs.l, gs.b, gs.w + gs.l, gs.h + gs.b];

    if(scene.matrix === true) {
        scene.matrix = createMatrix(regl);
    }

    var selectMode = dragmode === 'lasso' || dragmode === 'select' || !!trace.selectedpoints;
    scene.selectBatch = null;
    scene.unselectBatch = null;

    if(selectMode) {
        if(!scene.selectBatch) {
            scene.selectBatch = [];
            scene.unselectBatch = [];
        }

        // regenerate scene batch, if traces number changed during selection
        if(trace.selectedpoints) {
            scene.selectBatch = trace.selectedpoints;

            var selPts = trace.selectedpoints;
            var selDict = {};
            for(i = 0; i < selPts.length; i++) {
                selDict[selPts[i]] = true;
            }
            var unselPts = [];
            for(i = 0; i < dataLength; i++) {
                if(!selDict[i]) unselPts.push(i);
            }
            scene.unselectBatch = unselPts;
        }

        // precalculate px coords since we are not going to pan during select
        var xpx = new Array(visibleLength);
        var ypx = new Array(visibleLength);
        var data;
        for(k = 0; k < visibleLength; k++) {
            xa = AxisIDs.getFromId(gd, trace.xaxes[k]);
            ya = AxisIDs.getFromId(gd, trace.yaxes[k]);

            xpx[k] = new Array(dataLength);
            ypx[k] = new Array(dataLength);

            data = matrixData[k];

            for(i = 0; i < dataLength; i++) {
                xpx[k][i] = xa.c2p(data[i]);
                ypx[k][i] = ya.c2p(data[i]);
            }
        }
        stash.xpx = xpx;
        stash.ypx = ypx;

        if(scene.selectBatch) {
            scene.matrix.update(scene.matrixOptions, scene.matrixOptions);
            scene.matrix.update(scene.unselectedOptions, scene.selectedOptions);
            scene.matrix.update(viewOpts, viewOpts);
        }
        else {
            // delete selection pass
            scene.matrix.update(viewOpts, null);
        }
    }
    else {
        scene.matrix.update(scene.matrixOptions);
        scene.matrix.update(viewOpts);
        stash.xpx = stash.ypx = null;
    }

    scene.draw();
}

// TODO splom 'needs' the grid component, register it here?

function hoverPoints(pointData, xval, yval) {
    var cd = pointData.cd;
    var trace = cd[0].trace;
    var xa = pointData.xa;
    var ya = pointData.ya;
    var xpx = xa.c2p(xval);
    var ypx = ya.c2p(yval);
    var maxDistance = pointData.distance;
    var dimensions = trace.dimensions;

    var xi, yi, i;
    for(i = 0; i < dimensions.length; i++) {
        if(trace.xaxes[i] === xa._id) xi = i;
        if(trace.yaxes[i] === ya._id) yi = i;
    }

    var x = dimensions[xi].values || [];
    var y = dimensions[yi].values || [];

    var id, ptx, pty, dx, dy, dist, dxy;
    var minDist = maxDistance;

    for(i = 0; i < x.length; i++) {
        ptx = x[i];
        pty = y[i];
        dx = xa.c2p(ptx) - xpx;
        dy = ya.c2p(pty) - ypx;

        dist = Math.sqrt(dx * dx + dy * dy);
        if(dist < minDist) {
            minDist = dxy = dist;
            id = i;
        }
    }

    pointData.index = id;
    pointData.distance = minDist;
    pointData.dxy = dxy;

    if(id === undefined) return [pointData];

    calcHover(pointData, x, y, trace);

    return [pointData];
}

function selectPoints(searchInfo, polygon) {
    var cd = searchInfo.cd;
    var selection = [];
    var trace = cd[0].trace;
    var stash = cd[0].t;
    var scene = stash._scene;
    var xa = searchInfo.xaxis;
    var ya = searchInfo.yaxis;
    var matrixData = scene.matrixOptions.data;

    if(!scene) return selection;

    var hasOnlyLines = (!subTypes.hasMarkers(trace) && !subTypes.hasText(trace));
    if(trace.visible !== true || hasOnlyLines) return selection;

    var xi, yi, i;
    for(i = 0; i < trace.dimensions.length; i++) {
        if(trace.xaxes[i] === xa._id) xi = i;
        if(trace.yaxes[i] === ya._id) yi = i;
    }

    var xpx = stash.xpx[xi];
    var ypx = stash.ypx[yi];
    var x = matrixData[xi];
    var y = matrixData[yi];

    // degenerate polygon does not enable selection
    // filter out points by visible scatter ones
    var els = null;
    var unels = null;
    if(polygon !== false && !polygon.degenerate) {
        els = [], unels = [];
        for(i = 0; i < x.length; i++) {
            if(polygon.contains([xpx[i], ypx[i]])) {
                els.push(i);
                selection.push({
                    pointNumber: i,
                    x: x[i],
                    y: y[i]
                });
            }
            else {
                unels.push(i);
            }
        }
    } else {
        unels = arrayRange(stash.count);
    }

    // make sure selectBatch is created
    if(!scene.selectBatch) {
        scene.selectBatch = [];
        scene.unselectBatch = [];
    }

    if(!scene.selectBatch) {
        // enter every trace select mode
        for(i = 0; i < scene.count; i++) {
            scene.selectBatch = [];
            scene.unselectBatch = [];
        }
        // we should turn scatter2d into unselected once we have any points selected
        scene.matrix.update(scene.unselectedOptions, scene.selectedOptions);
    }

    scene.selectBatch = els;
    scene.unselectBatch = unels;

    scene.matrix.regl.clear({ color: true });

    return selection;
}

function style(gd, cd) {
    if(cd) {
        var stash = cd[0].t;
        var scene = stash._scene;
        scene.draw();
    }
}

module.exports = {
    moduleType: 'trace',
    name: 'splom',

    basePlotModule: require('./base_plot'),
    categories: ['gl', 'regl', 'cartesian', 'symbols', 'markerColorscale', 'showLegend', 'scatter-like'],

    attributes: require('./attributes'),
    supplyDefaults: require('./defaults'),

    calc: calc,
    plot: plot,
    hoverPoints: hoverPoints,
    selectPoints: selectPoints,
    style: style,

    meta: {
        description: [
            'SPLOM !!!'
        ].join(' ')
    }
};
<|endoftext|>


<commit_before>hqDefine('hqwebapp/js/main', [
    "jquery",
    "knockout",
    "underscore",
    "hqwebapp/js/initial_page_data",
    "hqwebapp/js/alert_user",
    "analytix/js/google",
    "hqwebapp/js/hq_extensions.jquery",
    "jquery.cookie/jquery.cookie",
], function (
    $,
    ko,
    _,
    initialPageData,
    alertUser,
    googleAnalytics
) {
    var eventize = function (that) {
        'use strict';
        var events = {};
        that.on = function (tag, callback) {
            if (events[tag] === undefined) {
                events[tag] = [];
            }
            events[tag].push(callback);
            return that;
        };
        that.fire = function (tag, e) {
            var i;
            if (events[tag] !== undefined) {
                for (i = 0; i < events[tag].length; i += 1) {
                    events[tag][i].apply(that, [e]);
                }
            }
            return that;
        };
        return that;
    };

    var makeHqHelp = function (opts, wrap) {
        'use strict';
        wrap = wrap === undefined ? true : wrap;
        var el = $(
            '
' +
                '' +
                    '
'
        );
        _.each(['content', 'title', 'html', 'placement', 'container'], function (attr) {
            $('a', el).data(attr, opts[attr]);
        });
        if (wrap) {
            el.hqHelp();
        }
        return el;
    };

    var transformHelpTemplate = function ($template, wrap) {
        'use strict';
        if ($template.data()) {
            var $help = makeHqHelp($template.data(), wrap);
            $help.insertAfter($template);
            $template.remove();
        }
    };

    ko.bindingHandlers.makeHqHelp = {
        update: function (element, valueAccessor) {
            var opts = valueAccessor(),
                name = ko.utils.unwrapObservable(opts.name || $(element).data('title')),
                description = ko.utils.unwrapObservable(opts.description || $(element).data('content')),
                placement = ko.utils.unwrapObservable(opts.placement || $(element).data('placement')),
                format = ko.utils.unwrapObservable(opts.format);
            $(element).find('.hq-help').remove();
            makeHqHelp({
                title: name,
                content: description,
                html: format === 'html',
                placement: placement || 'right',
            }).appendTo(element);
        },
    };

    ko.bindingHandlers.runOnInit = {
        // suggestion from https://github.com/knockout/knockout/issues/2446 to use
        // instead of an anonymous template
        init: function(elem, valueAccessor) {
            valueAccessor();
        }
    };
    ko.virtualElements.allowedBindings.runOnInit = true;

    ko.bindingHandlers.allowDescendantBindings = {
        // fixes an ssue where we try to apply bindings to a parent element
        // that has a child element with existing bindings.
        // see: https://github.com/knockout/knockout/issues/1922
        init: function(elem, valueAccessor) {
            // Let bindings proceed as normal *only if* my value is false
            var shouldAllowBindings = ko.unwrap(valueAccessor());
            return { controlsDescendantBindings: !shouldAllowBindings };
        }
    };
    ko.virtualElements.allowedBindings.allowDescendantBindings = true;

    var initBlock = function ($elem) {
        'use strict';

        $('.submit').click(function (e) {
            var $form = $(this).closest('.form, form'),
                data = $form.find('[name]').serialize(),
                action = $form.attr('action') || $form.data('action');

            e.preventDefault();
            $.postGo(action, $.unparam(data));
        });

        $(".button", $elem).button().wrap('');
        $("input[type='submit']", $elem).button();
        $("input[type='text'], input[type='password'], textarea", $elem);
        $('.container', $elem).addClass('ui-widget ui-widget-content');
        $('.config', $elem).wrap('
').parent().addClass('container block ui-corner-all');

        $('.hq-help-template').each(function () {
            transformHelpTemplate($(this), true);
        });
    };

    var updateDOM = function (update) {
        'use strict';
        var key;
        for (key in update) {
            if (update.hasOwnProperty(key)) {
                $(key).text(update[key]).val(update[key]);
            }
        }
    };

    var makeSaveButton = function (messageStrings, cssClass, barClass) {
        'use strict';
        var BAR_STATE = {
            SAVE: 'savebtn-bar-save',
            SAVING: 'savebtn-bar-saving',
            SAVED: 'savebtn-bar-saved',
            RETRY: 'savebtn-bar-retry',
        };
        barClass = barClass || '';
        var SaveButton = {
            /*
             options: {
             save: "Function to call when the user clicks Save",
             unsavedMessage: "Message to display when there are unsaved changes and the user leaves the page"
             }
             */
            init: function (options) {
                var button = {
                    $save: $('
').text(SaveButton.message.SAVE).click(function () {
                        button.fire('save');
                    }).addClass(cssClass),
                    $retry: $('
').text(SaveButton.message.RETRY).click(function () {
                        button.fire('save');
                    }).addClass(cssClass),
                    $saving: $('
').text(SaveButton.message.SAVING).addClass('btn btn-primary disabled'),
                    $saved: $('
').text(SaveButton.message.SAVED).addClass('btn btn-primary disabled'),
                    ui: $('
').addClass('pull-right savebtn-bar ' + barClass),
                    setStateWhenReady: function (state) {
                        if (this.state === 'saving') {
                            this.nextState = state;
                        } else {
                            this.setState(state);
                        }
                    },
                    setState: function (state) {
                        if (this.state === state) {
                            return;
                        }
                        this.state = state;
                        this.$save.detach();
                        this.$saving.detach();
                        this.$saved.detach();
                        this.$retry.detach();
                        var buttonUi = this.ui;
                        _.each(BAR_STATE, function (v, k) {
                            buttonUi.removeClass(v);
                        });
                        if (state === 'save') {
                            this.ui.addClass(BAR_STATE.SAVE);
                            this.ui.append(this.$save);
                        } else if (state === 'saving') {
                            this.ui.addClass(BAR_STATE.SAVING);
                            this.ui.append(this.$saving);
                        } else if (state === 'saved') {
                            this.ui.addClass(BAR_STATE.SAVED);
                            this.ui.append(this.$saved);
                        } else if (state === 'retry') {
                            this.ui.addClass(BAR_STATE.RETRY);
                            this.ui.append(this.$retry);
                        }
                    },
                    ajax: function (options) {
                        var beforeSend = options.beforeSend || function () {},
                            success = options.success || function () {},
                            error = options.error || function () {},
                            that = this;
                        options.beforeSend = function (jqXHR, settings) {
                            that.setState('saving');
                            that.nextState = 'saved';
                            $.ajaxSettings.beforeSend(jqXHR, settings);
                            beforeSend.apply(this, arguments);
                        };
                        options.success = function (data) {
                            that.setState(that.nextState);
                            success.apply(this, arguments);
                        };
                        options.error = function (data) {
                            that.nextState = null;
                            that.setState('retry');
                            var customError = ((data.responseJSON && data.responseJSON.message) ? data.responseJSON.message : data.responseText);
                            if (customError.indexOf('') > -1) {
                                // this is sending back a full html page, likely login, so no error message.
                                customError = null;
                            }
                            alertUser.alert_user(customError || SaveButton.message.ERROR_SAVING, 'danger');
                            error.apply(this, arguments);
                        };
                        var jqXHR = $.ajax(options);
                        if (!jqXHR) {
                            // request was aborted
                            that.setState('save');
                        }
                    },
                };
                eventize(button);
                button.setState('saved');
                button.on('change', function () {
                    this.setStateWhenReady('save');
                });
                if (options.save) {
                    button.on('save', options.save);
                }
                $(window).on('beforeunload', function () {
                    var lastParent = button.ui.parents()[button.ui.parents().length - 1];
                    if (lastParent) {
                        var stillAttached = lastParent.tagName.toLowerCase() == 'html';
                        if (button.state !== 'saved' && stillAttached) {
                            if ($('.js-unhide-on-unsaved').length > 0) $('.js-unhide-on-unsaved').removeClass('hide');
                            return options.unsavedMessage || "";
                        }
                    }
                });
                return button;
            },
            initForm: function ($form, options) {
                var url = $form.attr('action'),
                    button = SaveButton.init({
                        unsavedMessage: options.unsavedMessage,
                        save: function () {
                            this.ajax({
                                url: url,
                                type: 'POST',
                                dataType: 'json',
                                data: $form.serialize(),
                                success: options.success,
                            });
                        },
                    }),
                    fireChange = function () {
                        button.fire('change');
                    };
                _.defer(function () {
                    $form.find('*').change(fireChange);
                    $form.find('input, textarea').on('textchange', fireChange);
                });
                return button;
            },
            message: messageStrings,
        };

        return SaveButton;
    };

    var SaveButton = makeSaveButton({
        SAVE: django.gettext("Save"),
        SAVING: django.gettext("Saving..."),
        SAVED: django.gettext("Saved"),
        RETRY: django.gettext("Try Again"),
        ERROR_SAVING: django.gettext("There was an error saving"),
    }, 'btn btn-primary');

    var DeleteButton = makeSaveButton({
        SAVE: django.gettext("Delete"),
        SAVING: django.gettext("Deleting..."),
        SAVED: django.gettext("Deleted"),
        RETRY: django.gettext("Try Again"),
        ERROR_SAVING: django.gettext("There was an error deleting"),
    }, 'btn btn-danger', 'savebtn-bar-danger');

    ko.bindingHandlers.saveButton = {
        init: function (element, getSaveButton) {
            getSaveButton().ui.appendTo(element);
        },
    };

    ko.bindingHandlers.saveButton2 = {
        init: function (element, valueAccessor, allBindingsAccessor) {
            var saveOptions = allBindingsAccessor().saveOptions,
                state = valueAccessor(),
                saveButton;

            saveButton = SaveButton.init({
                save: function () {
                    saveButton.ajax(saveOptions());
                },
            });
            $(element).css('vertical-align', 'top').css('display', 'inline-block');

            saveButton.ui.appendTo(element);
            element.saveButton = saveButton;
            saveButton.on('state:change', function () {
                state(saveButton.state);
            });
        },
        update: function (element, valueAccessor) {
            var state = ko.utils.unwrapObservable(valueAccessor());
            element.saveButton.setStateWhenReady(state);
        },
    };

    ko.bindingHandlers.deleteButton = {
        init: function (element, valueAccessor, allBindingsAccessor) {
            var saveOptions = allBindingsAccessor().saveOptions,
                state = valueAccessor(),
                deleteButton;

            deleteButton = DeleteButton.init({
                save: function () {
                    deleteButton.ajax(saveOptions());
                },
            });
            $(element).css('vertical-align', 'top').css('display', 'inline-block');
            deleteButton.ui.appendTo(element);
            element.deleteButton = deleteButton;
            deleteButton.on('state:change', function () {
                state(deleteButton.state);
            });
        },
        update: function (element, valueAccessor) {
            var state = ko.utils.unwrapObservable(valueAccessor());
            element.deleteButton.setStateWhenReady(state);
        },
    };

    var beforeUnload = [];
    var bindBeforeUnload = function (callback) {
        beforeUnload.push(callback);
    };
    var beforeUnloadCallback = function () {
        for (var i = 0; i < beforeUnload.length; i++) {
            var message = beforeUnload[i]();
            if (message !== null && message !== undefined) {
                return message;
            }
        }
    };

    $(function () {
        'use strict';
        $(window).on('beforeunload', beforeUnloadCallback);
        initBlock($("body"));

        $('#modalTrial30Day').modal('show');

        $(document).on('click', '.track-usage-link', function (e) {
            var $link = $(e.currentTarget),
                data = $link.data();
            googleAnalytics.track.click($link, data.category, data.action, data.label, data.value);
        });

        $(document).on('click', '.mainmenu-tab a', function (e) {
            var data = $(e.currentTarget).closest(".mainmenu-tab").data();
            if (data.category && data.action) {
                googleAnalytics.track.event(data.category, data.action, data.label);
            }
        });

        $(document).on('click', '.post-link', function (e) {
            e.preventDefault();
            $.postGo($(this).attr('href'), {});
        });

        // Maintenance alerts
        var $maintenance = $(".alert-maintenance");
        if ($maintenance.length) {
            var id = $maintenance.data("id"),
                alertCookie = "alert_maintenance";
            if ($.cookie(alertCookie) != id) {  // eslint-disable-line eqeqeq
                $maintenance.removeClass('hide');
                $maintenance.on('click', '.close', function () {
                    $.cookie(alertCookie, id, { expires: 7, path: '/' });
                });
            }
        }

        // EULA modal
        var eulaCookie = "gdpr_rollout";
        if (!$.cookie(eulaCookie)) {
            var $modal = $("#eulaModal");
            if ($modal.length) {
                $("body").addClass("has-eula");
                $("#eula-agree").click(function () {
                    $(this).disableButton();
                    $.ajax({
                        url: initialPageData.reverse("agree_to_eula"),
                        method: "POST",
                        success: function () {
                            $("#eulaModal").modal('hide');
                            $("body").removeClass("has-eula");
                        },
                        error: function (xhr) {
                            // if we got a 403 it may be due to two-factor settings.
                            // force a page reload
                            // https://dimagi-dev.atlassian.net/browse/SAAS-10785
                            if (xhr.status === 403) {
                                location.reload();
                            } else {
                                // do nothing, user will get the popup again on next page load
                                $("body").removeClass("has-eula");
                            }
                        },
                    });
                });
                $modal.modal({
                    keyboard: false,
                    backdrop: 'static',
                });
            }
        }

        // CDA modal
        _.each($(".remote-modal"), function (modal) {
            var $modal = $(modal);
            $modal.on("show show.bs.modal", function () {
                $(this).find(".fetched-data").load($(this).data("url"));
            });
            if ($modal.data("showOnPageLoad")) {
                $modal.modal('show');
            }
        });
    });

    var capitalize = function (string) {
        return string.charAt(0).toUpperCase() + string.substring(1).toLowerCase();
    };

    return {
        beforeUnloadCallback: beforeUnloadCallback,
        eventize: eventize,
        initBlock: initBlock,
        initDeleteButton: DeleteButton.init,
        initSaveButton: SaveButton.init,
        makeSaveButton: makeSaveButton,
        SaveButton: SaveButton,
        initSaveButtonForm: SaveButton.initForm,
        makeHqHelp: makeHqHelp,
        transformHelpTemplate: transformHelpTemplate,
        updateDOM: updateDOM,
        capitalize: capitalize,
    };
});
<commit_msg>fix typo<commit_after>hqDefine('hqwebapp/js/main', [
    "jquery",
    "knockout",
    "underscore",
    "hqwebapp/js/initial_page_data",
    "hqwebapp/js/alert_user",
    "analytix/js/google",
    "hqwebapp/js/hq_extensions.jquery",
    "jquery.cookie/jquery.cookie",
], function (
    $,
    ko,
    _,
    initialPageData,
    alertUser,
    googleAnalytics
) {
    var eventize = function (that) {
        'use strict';
        var events = {};
        that.on = function (tag, callback) {
            if (events[tag] === undefined) {
                events[tag] = [];
            }
            events[tag].push(callback);
            return that;
        };
        that.fire = function (tag, e) {
            var i;
            if (events[tag] !== undefined) {
                for (i = 0; i < events[tag].length; i += 1) {
                    events[tag][i].apply(that, [e]);
                }
            }
            return that;
        };
        return that;
    };

    var makeHqHelp = function (opts, wrap) {
        'use strict';
        wrap = wrap === undefined ? true : wrap;
        var el = $(
            '
' +
                '' +
                    '
'
        );
        _.each(['content', 'title', 'html', 'placement', 'container'], function (attr) {
            $('a', el).data(attr, opts[attr]);
        });
        if (wrap) {
            el.hqHelp();
        }
        return el;
    };

    var transformHelpTemplate = function ($template, wrap) {
        'use strict';
        if ($template.data()) {
            var $help = makeHqHelp($template.data(), wrap);
            $help.insertAfter($template);
            $template.remove();
        }
    };

    ko.bindingHandlers.makeHqHelp = {
        update: function (element, valueAccessor) {
            var opts = valueAccessor(),
                name = ko.utils.unwrapObservable(opts.name || $(element).data('title')),
                description = ko.utils.unwrapObservable(opts.description || $(element).data('content')),
                placement = ko.utils.unwrapObservable(opts.placement || $(element).data('placement')),
                format = ko.utils.unwrapObservable(opts.format);
            $(element).find('.hq-help').remove();
            makeHqHelp({
                title: name,
                content: description,
                html: format === 'html',
                placement: placement || 'right',
            }).appendTo(element);
        },
    };

    ko.bindingHandlers.runOnInit = {
        // suggestion from https://github.com/knockout/knockout/issues/2446 to use
        // instead of an anonymous template
        init: function(elem, valueAccessor) {
            valueAccessor();
        }
    };
    ko.virtualElements.allowedBindings.runOnInit = true;

    ko.bindingHandlers.allowDescendantBindings = {
        // fixes an issue where we try to apply bindings to a parent element
        // that has a child element with existing bindings.
        // see: https://github.com/knockout/knockout/issues/1922
        init: function(elem, valueAccessor) {
            // Let bindings proceed as normal *only if* my value is false
            var shouldAllowBindings = ko.unwrap(valueAccessor());
            return { controlsDescendantBindings: !shouldAllowBindings };
        }
    };
    ko.virtualElements.allowedBindings.allowDescendantBindings = true;

    var initBlock = function ($elem) {
        'use strict';

        $('.submit').click(function (e) {
            var $form = $(this).closest('.form, form'),
                data = $form.find('[name]').serialize(),
                action = $form.attr('action') || $form.data('action');

            e.preventDefault();
            $.postGo(action, $.unparam(data));
        });

        $(".button", $elem).button().wrap('');
        $("input[type='submit']", $elem).button();
        $("input[type='text'], input[type='password'], textarea", $elem);
        $('.container', $elem).addClass('ui-widget ui-widget-content');
        $('.config', $elem).wrap('
').parent().addClass('container block ui-corner-all');

        $('.hq-help-template').each(function () {
            transformHelpTemplate($(this), true);
        });
    };

    var updateDOM = function (update) {
        'use strict';
        var key;
        for (key in update) {
            if (update.hasOwnProperty(key)) {
                $(key).text(update[key]).val(update[key]);
            }
        }
    };

    var makeSaveButton = function (messageStrings, cssClass, barClass) {
        'use strict';
        var BAR_STATE = {
            SAVE: 'savebtn-bar-save',
            SAVING: 'savebtn-bar-saving',
            SAVED: 'savebtn-bar-saved',
            RETRY: 'savebtn-bar-retry',
        };
        barClass = barClass || '';
        var SaveButton = {
            /*
             options: {
             save: "Function to call when the user clicks Save",
             unsavedMessage: "Message to display when there are unsaved changes and the user leaves the page"
             }
             */
            init: function (options) {
                var button = {
                    $save: $('
').text(SaveButton.message.SAVE).click(function () {
                        button.fire('save');
                    }).addClass(cssClass),
                    $retry: $('
').text(SaveButton.message.RETRY).click(function () {
                        button.fire('save');
                    }).addClass(cssClass),
                    $saving: $('
').text(SaveButton.message.SAVING).addClass('btn btn-primary disabled'),
                    $saved: $('
').text(SaveButton.message.SAVED).addClass('btn btn-primary disabled'),
                    ui: $('
').addClass('pull-right savebtn-bar ' + barClass),
                    setStateWhenReady: function (state) {
                        if (this.state === 'saving') {
                            this.nextState = state;
                        } else {
                            this.setState(state);
                        }
                    },
                    setState: function (state) {
                        if (this.state === state) {
                            return;
                        }
                        this.state = state;
                        this.$save.detach();
                        this.$saving.detach();
                        this.$saved.detach();
                        this.$retry.detach();
                        var buttonUi = this.ui;
                        _.each(BAR_STATE, function (v, k) {
                            buttonUi.removeClass(v);
                        });
                        if (state === 'save') {
                            this.ui.addClass(BAR_STATE.SAVE);
                            this.ui.append(this.$save);
                        } else if (state === 'saving') {
                            this.ui.addClass(BAR_STATE.SAVING);
                            this.ui.append(this.$saving);
                        } else if (state === 'saved') {
                            this.ui.addClass(BAR_STATE.SAVED);
                            this.ui.append(this.$saved);
                        } else if (state === 'retry') {
                            this.ui.addClass(BAR_STATE.RETRY);
                            this.ui.append(this.$retry);
                        }
                    },
                    ajax: function (options) {
                        var beforeSend = options.beforeSend || function () {},
                            success = options.success || function () {},
                            error = options.error || function () {},
                            that = this;
                        options.beforeSend = function (jqXHR, settings) {
                            that.setState('saving');
                            that.nextState = 'saved';
                            $.ajaxSettings.beforeSend(jqXHR, settings);
                            beforeSend.apply(this, arguments);
                        };
                        options.success = function (data) {
                            that.setState(that.nextState);
                            success.apply(this, arguments);
                        };
                        options.error = function (data) {
                            that.nextState = null;
                            that.setState('retry');
                            var customError = ((data.responseJSON && data.responseJSON.message) ? data.responseJSON.message : data.responseText);
                            if (customError.indexOf('') > -1) {
                                // this is sending back a full html page, likely login, so no error message.
                                customError = null;
                            }
                            alertUser.alert_user(customError || SaveButton.message.ERROR_SAVING, 'danger');
                            error.apply(this, arguments);
                        };
                        var jqXHR = $.ajax(options);
                        if (!jqXHR) {
                            // request was aborted
                            that.setState('save');
                        }
                    },
                };
                eventize(button);
                button.setState('saved');
                button.on('change', function () {
                    this.setStateWhenReady('save');
                });
                if (options.save) {
                    button.on('save', options.save);
                }
                $(window).on('beforeunload', function () {
                    var lastParent = button.ui.parents()[button.ui.parents().length - 1];
                    if (lastParent) {
                        var stillAttached = lastParent.tagName.toLowerCase() == 'html';
                        if (button.state !== 'saved' && stillAttached) {
                            if ($('.js-unhide-on-unsaved').length > 0) $('.js-unhide-on-unsaved').removeClass('hide');
                            return options.unsavedMessage || "";
                        }
                    }
                });
                return button;
            },
            initForm: function ($form, options) {
                var url = $form.attr('action'),
                    button = SaveButton.init({
                        unsavedMessage: options.unsavedMessage,
                        save: function () {
                            this.ajax({
                                url: url,
                                type: 'POST',
                                dataType: 'json',
                                data: $form.serialize(),
                                success: options.success,
                            });
                        },
                    }),
                    fireChange = function () {
                        button.fire('change');
                    };
                _.defer(function () {
                    $form.find('*').change(fireChange);
                    $form.find('input, textarea').on('textchange', fireChange);
                });
                return button;
            },
            message: messageStrings,
        };

        return SaveButton;
    };

    var SaveButton = makeSaveButton({
        SAVE: django.gettext("Save"),
        SAVING: django.gettext("Saving..."),
        SAVED: django.gettext("Saved"),
        RETRY: django.gettext("Try Again"),
        ERROR_SAVING: django.gettext("There was an error saving"),
    }, 'btn btn-primary');

    var DeleteButton = makeSaveButton({
        SAVE: django.gettext("Delete"),
        SAVING: django.gettext("Deleting..."),
        SAVED: django.gettext("Deleted"),
        RETRY: django.gettext("Try Again"),
        ERROR_SAVING: django.gettext("There was an error deleting"),
    }, 'btn btn-danger', 'savebtn-bar-danger');

    ko.bindingHandlers.saveButton = {
        init: function (element, getSaveButton) {
            getSaveButton().ui.appendTo(element);
        },
    };

    ko.bindingHandlers.saveButton2 = {
        init: function (element, valueAccessor, allBindingsAccessor) {
            var saveOptions = allBindingsAccessor().saveOptions,
                state = valueAccessor(),
                saveButton;

            saveButton = SaveButton.init({
                save: function () {
                    saveButton.ajax(saveOptions());
                },
            });
            $(element).css('vertical-align', 'top').css('display', 'inline-block');

            saveButton.ui.appendTo(element);
            element.saveButton = saveButton;
            saveButton.on('state:change', function () {
                state(saveButton.state);
            });
        },
        update: function (element, valueAccessor) {
            var state = ko.utils.unwrapObservable(valueAccessor());
            element.saveButton.setStateWhenReady(state);
        },
    };

    ko.bindingHandlers.deleteButton = {
        init: function (element, valueAccessor, allBindingsAccessor) {
            var saveOptions = allBindingsAccessor().saveOptions,
                state = valueAccessor(),
                deleteButton;

            deleteButton = DeleteButton.init({
                save: function () {
                    deleteButton.ajax(saveOptions());
                },
            });
            $(element).css('vertical-align', 'top').css('display', 'inline-block');
            deleteButton.ui.appendTo(element);
            element.deleteButton = deleteButton;
            deleteButton.on('state:change', function () {
                state(deleteButton.state);
            });
        },
        update: function (element, valueAccessor) {
            var state = ko.utils.unwrapObservable(valueAccessor());
            element.deleteButton.setStateWhenReady(state);
        },
    };

    var beforeUnload = [];
    var bindBeforeUnload = function (callback) {
        beforeUnload.push(callback);
    };
    var beforeUnloadCallback = function () {
        for (var i = 0; i < beforeUnload.length; i++) {
            var message = beforeUnload[i]();
            if (message !== null && message !== undefined) {
                return message;
            }
        }
    };

    $(function () {
        'use strict';
        $(window).on('beforeunload', beforeUnloadCallback);
        initBlock($("body"));

        $('#modalTrial30Day').modal('show');

        $(document).on('click', '.track-usage-link', function (e) {
            var $link = $(e.currentTarget),
                data = $link.data();
            googleAnalytics.track.click($link, data.category, data.action, data.label, data.value);
        });

        $(document).on('click', '.mainmenu-tab a', function (e) {
            var data = $(e.currentTarget).closest(".mainmenu-tab").data();
            if (data.category && data.action) {
                googleAnalytics.track.event(data.category, data.action, data.label);
            }
        });

        $(document).on('click', '.post-link', function (e) {
            e.preventDefault();
            $.postGo($(this).attr('href'), {});
        });

        // Maintenance alerts
        var $maintenance = $(".alert-maintenance");
        if ($maintenance.length) {
            var id = $maintenance.data("id"),
                alertCookie = "alert_maintenance";
            if ($.cookie(alertCookie) != id) {  // eslint-disable-line eqeqeq
                $maintenance.removeClass('hide');
                $maintenance.on('click', '.close', function () {
                    $.cookie(alertCookie, id, { expires: 7, path: '/' });
                });
            }
        }

        // EULA modal
        var eulaCookie = "gdpr_rollout";
        if (!$.cookie(eulaCookie)) {
            var $modal = $("#eulaModal");
            if ($modal.length) {
                $("body").addClass("has-eula");
                $("#eula-agree").click(function () {
                    $(this).disableButton();
                    $.ajax({
                        url: initialPageData.reverse("agree_to_eula"),
                        method: "POST",
                        success: function () {
                            $("#eulaModal").modal('hide');
                            $("body").removeClass("has-eula");
                        },
                        error: function (xhr) {
                            // if we got a 403 it may be due to two-factor settings.
                            // force a page reload
                            // https://dimagi-dev.atlassian.net/browse/SAAS-10785
                            if (xhr.status === 403) {
                                location.reload();
                            } else {
                                // do nothing, user will get the popup again on next page load
                                $("body").removeClass("has-eula");
                            }
                        },
                    });
                });
                $modal.modal({
                    keyboard: false,
                    backdrop: 'static',
                });
            }
        }

        // CDA modal
        _.each($(".remote-modal"), function (modal) {
            var $modal = $(modal);
            $modal.on("show show.bs.modal", function () {
                $(this).find(".fetched-data").load($(this).data("url"));
            });
            if ($modal.data("showOnPageLoad")) {
                $modal.modal('show');
            }
        });
    });

    var capitalize = function (string) {
        return string.charAt(0).toUpperCase() + string.substring(1).toLowerCase();
    };

    return {
        beforeUnloadCallback: beforeUnloadCallback,
        eventize: eventize,
        initBlock: initBlock,
        initDeleteButton: DeleteButton.init,
        initSaveButton: SaveButton.init,
        makeSaveButton: makeSaveButton,
        SaveButton: SaveButton,
        initSaveButtonForm: SaveButton.initForm,
        makeHqHelp: makeHqHelp,
        transformHelpTemplate: transformHelpTemplate,
        updateDOM: updateDOM,
        capitalize: capitalize,
    };
});
<|endoftext|>