(function() { if (ELA.Models == null) { ELA.Models = {}; } ELA.Models.BaseCalculator = (function() { _.extend(BaseCalculator.prototype, Backbone.Events); BaseCalculator.prototype.xRange = function(func) { return 1.0; }; BaseCalculator.prototype.yRange = function(func) { var curve, group, i, len, maxY, minY, range, result; curve = func; group = _.find(this.curveGroups, function(group) { return _.contains(group, curve); }); minY = null; maxY = null; if (group != null) { for (i = 0, len = group.length; i < len; i++) { func = group[i]; if (!(_.isFunction(this[func]))) { continue; } result = this[func](); maxY = Math.max(result.maxY, maxY); minY = Math.min(result.minY, minY); } } else { result = null; if (_.isFunction(this[func])) { result = this[func](); } else { throw new Error(func + " does not specify a calculator function in " + name); } minY = result.minY; maxY = result.maxY; } range = 0; if (maxY > 0) { range += maxY; } if (minY < 0) { range -= minY; } return range; }; function BaseCalculator(options) { var i, len, model, models, name, ref, required; ref = options.required || []; for (i = 0, len = ref.length; i < len; i++) { required = ref[i]; if (options[required] == null) { throw "Missing `" + required + "` option"; } } models = _.omit(options, 'required'); this._memo = {}; for (name in models) { model = models[name]; this[name] = model; } this.bindEvents(models); } BaseCalculator.prototype.bindEvents = function(models) { var key, obj, results; if (models == null) { models = {}; } this.stopListening(); results = []; for (key in models) { obj = models[key]; results.push((function(_this) { return function() { var modelName; if (obj.on != null) { modelName = _.clone(key); return _this.listenTo(obj, 'change', function(model) { var attribute, ref, results1, value; ref = model.changed; results1 = []; for (attribute in ref) { value = ref[attribute]; results1.push(_this.trigger("change:" + modelName + "." + attribute, model, value)); } return results1; }); } }; })(this)()); } return results; }; BaseCalculator._parseForDeps = function(funcStr) { var ampIndex, dep, i, len, match, ref, results; ref = funcStr.match(/this.([$_a-zA-Z][$_a-zA-Z0-9]*(\.get)?(\([^\)]*\))?)/g) || []; results = []; for (i = 0, len = ref.length; i < len; i++) { dep = ref[i]; dep = dep.slice(5); if (/^[_A-Z][_A-Z0-9]*$/.test(dep)) { continue; } if ((match = dep.match(/([^.]+)\.get\(['"]([^'"]+)['"]\)/))) { results.push(match[1] + "." + match[2]); } else { ampIndex = dep.indexOf('('); if (ampIndex > 0) { results.push([dep.slice(0, ampIndex), this._parseForDeps(dep.slice(ampIndex))]); } else { results.push(dep); } } } return results; }; BaseCalculator.extractDeps = function(func) { var deps; deps = this._parseForDeps(func.toString()); return _.chain(deps).flatten().compact().uniq().value(); }; BaseCalculator.memoize = function(name, func) { var bindDeps, f, parameters, resolveDeps; resolveDeps = function(f) { var dep, depsBefore; depsBefore = ''; while (f.deps.join('') !== depsBefore) { depsBefore = f.deps.join(''); f.deps = (function() { var i, len, ref, results; ref = f.deps; results = []; for (i = 0, len = ref.length; i < len; i++) { dep = ref[i]; if (/([^.]+)\.([^.]+)/.test(dep)) { results.push(dep); } else if (this[dep] != null) { results.push(this[dep].deps || dep); } else { results.push(void 0); } } return results; }).call(this); f.deps = _.chain(f.deps).flatten().compact().uniq().value(); } return f.depsResolved = true; }; bindDeps = function(f, callback) { var dep, i, len, ref; ref = f.deps; for (i = 0, len = ref.length; i < len; i++) { dep = ref[i]; this.on("change:" + dep, (function(_this) { return function() { callback(); return _this.trigger("change:" + name); }; })(this)); } return this._memo[name].eventsBound = true; }; parameters = (function() { var funcStr, match; funcStr = func.toString(); if ((match = funcStr.match(/function ?\(([^\)]+)\)/)) != null) { return match[1].split(','); } else { return []; } })(); f = parameters.length > 0 ? function() { var base, key; (base = this._memo)[name] || (base[name] = { values: {}, eventsBound: false }); key = JSON.stringify(arguments); if (this._memo[name].values[key] == null) { this._memo[name].values[key] = func.apply(this, arguments); } if (!f.depsResolved) { resolveDeps.call(this, f); } if (!this._memo[name].eventsBound) { bindDeps.call(this, f, (function(_this) { return function() { return _this._memo[name].values = {}; }; })(this)); } return this._memo[name].values[key]; } : function() { var base; (base = this._memo)[name] || (base[name] = { eventsBound: false }); if (this._memo[name].value == null) { this._memo[name].value = func.apply(this, arguments); } if (!f.depsResolved) { resolveDeps.call(this, f); } if (!this._memo[name].eventsBound) { bindDeps.call(this, f, (function(_this) { return function() { return _this._memo[name].value = null; }; })(this)); } return this._memo[name].value; }; f.depsResolved = false; f.memoizable = true; f.deps = this.extractDeps(func); return f; }; BaseCalculator.reactive = function(name, func) { var bindDeps, f, parameters, resolveDeps; resolveDeps = function(f) { var dep, depsBefore, resolvedDeps; depsBefore = ''; resolvedDeps = []; while (f.deps.join('') !== depsBefore) { depsBefore = f.deps.join(''); f.deps = (function() { var i, len, ref, results; ref = f.deps; results = []; for (i = 0, len = ref.length; i < len; i++) { dep = ref[i]; if (dep === name) { continue; } if (/([^.]+)\.([^.]+)/.test(dep)) { results.push(dep); } else if ((this[dep] != null) && !_.contains(resolvedDeps, dep)) { resolvedDeps.push(dep); results.push(this[dep].deps || dep); } else { results.push(void 0); } } return results; }).call(this); f.deps = _.chain(f.deps).flatten().compact().uniq().value(); } return f.depsResolved = true; }; bindDeps = function(f, callback) { var dep, i, len, ref; ref = f.deps; for (i = 0, len = ref.length; i < len; i++) { dep = ref[i]; this.on("change:" + dep, (function(_this) { return function() { if (callback != null) { callback(); } return _this.trigger("change:" + name); }; })(this)); } return this._memo[name].eventsBound = true; }; parameters = (function() { var funcStr, match; funcStr = func.toString(); if ((match = funcStr.match(/function \(([^\)]+)\)/)) != null) { return match[1].split(','); } else { return []; } })(); f = function() { var base; (base = this._memo)[name] || (base[name] = { eventsBound: false }); if (!f.depsResolved) { resolveDeps.call(this, f); } if (!this._memo[name].eventsBound) { bindDeps.call(this, f); } return f._func.apply(this, arguments); }; f.depsResolved = false; f.memoizable = false; f.deps = this.extractDeps(func); f._func = func; return f; }; BaseCalculator.prototype.clearMemoizations = function() { var key, memo, ref, results; ref = this._memo; results = []; for (key in ref) { memo = ref[key]; if (_.has(memo, 'value')) { memo.value = null; } if (_.has(memo, 'values')) { results.push(memo.values = {}); } else { results.push(void 0); } } return results; }; BaseCalculator.prototype.pole = function(func, options) { var max, maxPos, middle, middlePos, min, minPos, n, precision, y; if (options == null) { options = {}; } precision = Math.abs(options.precision || 0.001); min = options.min || 0.0; max = options.max || 1.0; middle = (max + min) * 0.5; minPos = this[func](min) > 0; maxPos = this[func](max) > 0; middlePos = this[func](middle) > 0; n = 0; if (minPos === maxPos) { return max; } else { while (middle - min > precision) { y = this[func](middle); if (y === 2e308 || y === -2e308) { return middle; } if (minPos === middlePos) { min = middle; minPos = middlePos; } else { max = middle; maxPos = middlePos; } middle = (max + min) * 0.5; middlePos = this[func](middle) > 0; n += 1; } } return min; }; return BaseCalculator; })(); }).call(this);