3 _parse: function(str, sep)
6 str.split(sep).forEach(function(part) {
7 var item = part.split("=");
8 var value = decodeURIComponent(item[1]);
10 output[item[0]] = value.substr(1, value.length - 2);
12 output[item[0]] = value;
17 parseParameters: function()
19 return this._parse(window.location.search.substr(1), "&");
22 parseArguments: function(str)
24 return this._parse(str, " ");
27 extendObject: function(obj1, obj2)
29 for (var attrname in obj2)
30 obj1[attrname] = obj2[attrname];
34 copyObject: function(obj)
36 return this.extendObject({}, obj);
39 mergeObjects: function(obj1, obj2)
41 return this.extendObject(this.copyObject(obj1), obj2);
44 createClass: function(classConstructor, classMethods)
46 classConstructor.prototype = classMethods;
47 return classConstructor;
50 createSubclass: function(superclass, classConstructor, classMethods)
52 classConstructor.prototype = Object.create(superclass.prototype);
53 classConstructor.prototype.constructor = classConstructor;
55 Utilities.extendObject(classConstructor.prototype, classMethods);
56 return classConstructor;
59 createElement: function(name, attrs, parentElement)
61 var element = document.createElement(name);
63 for (var key in attrs)
64 element.setAttribute(key, attrs[key]);
66 parentElement.appendChild(element);
70 createSVGElement: function(name, attrs, xlinkAttrs, parentElement)
72 const svgNamespace = "http://www.w3.org/2000/svg";
73 const xlinkNamespace = "http://www.w3.org/1999/xlink";
75 var element = document.createElementNS(svgNamespace, name);
77 for (var key in attrs)
78 element.setAttribute(key, attrs[key]);
80 for (var key in xlinkAttrs)
81 element.setAttributeNS(xlinkNamespace, key, xlinkAttrs[key]);
83 parentElement.appendChild(element);
87 browserPrefix: function()
89 // Get the HTML element's CSSStyleDeclaration
90 var styles = window.getComputedStyle(document.documentElement, '');
92 // Convert the styles list to an array
93 var stylesArray = Array.prototype.slice.call(styles);
95 // Concatenate all the styles in one big string
96 var stylesString = stylesArray.join('');
98 // Search the styles string for a known prefix type, settle on Opera if none is found.
99 var prefixes = stylesString.match(/-(moz|webkit|ms)-/) || (styles.OLink === '' && ['', 'o']);
101 // prefixes has two elements; e.g. for webkit it has ['-webkit-', 'webkit'];
102 var prefix = prefixes[1];
104 // Have 'O' before 'Moz' in the string so it is matched first.
105 var dom = ('WebKit|O|Moz|MS').match(new RegExp(prefix, 'i'))[0];
107 // Return all the required prefixes.
111 css: '-' + prefix + '-',
112 js: prefix[0].toUpperCase() + prefix.substr(1)
116 setElementPrefixedProperty: function(element, property, value)
118 element.style[property] = element.style[this.browserPrefix().js + property[0].toUpperCase() + property.substr(1)] = value;
121 progressValue: function(value, min, max)
123 return (value - min) / (max - min);
126 lerp: function(value, min, max)
128 return min + (max - min) * value;
132 Array.prototype.swap = function(i, j)
140 if (!Array.prototype.fill) {
141 Array.prototype.fill = function(value) {
143 throw new TypeError('Array.prototype.fill called on null or undefined');
145 var object = Object(this);
146 var len = parseInt(object.length, 10);
147 var start = arguments[1];
148 var relativeStart = parseInt(start, 10) || 0;
149 var k = relativeStart < 0 ? Math.max(len + relativeStart, 0) : Math.min(relativeStart, len);
150 var end = arguments[2];
151 var relativeEnd = end === undefined ? len : (parseInt(end) || 0) ;
152 var final = relativeEnd < 0 ? Math.max(len + relativeEnd, 0) : Math.min(relativeEnd, len);
154 for (; k < final; k++)
161 if (!Array.prototype.find) {
162 Array.prototype.find = function(predicate) {
164 throw new TypeError('Array.prototype.find called on null or undefined');
165 if (typeof predicate !== 'function')
166 throw new TypeError('predicate must be a function');
168 var list = Object(this);
169 var length = list.length >>> 0;
170 var thisArg = arguments[1];
173 for (var i = 0; i < length; i++) {
175 if (predicate.call(thisArg, value, i, list))
182 Array.prototype.shuffle = function()
184 for (var index = this.length - 1; index >= 0; --index) {
185 var randomIndex = Math.floor(Math.random() * (index + 1));
186 this.swap(index, randomIndex);
191 Point = Utilities.createClass(
198 // Used when the point object is used as a size object.
204 // Used when the point object is used as a size object.
210 // Used when the point object is used as a size object.
213 return new Point(this.x / 2, this.y / 2);
218 return "x = " + this.x + ", y = " + this.y;
224 return new Point(this.x + other, this.y + other);
225 return new Point(this.x + other.x, this.y + other.y);
228 subtract: function(other)
231 return new Point(this.x - other, this.y - other);
232 return new Point(this.x - other.x, this.y - other.y);
235 multiply: function(other)
238 return new Point(this.x * other, this.y * other);
239 return new Point(this.x * other.x, this.y * other.y);
242 move: function(angle, velocity, timeDelta)
244 return this.add(Point.pointOnCircle(angle, velocity * (timeDelta / 1000)));
248 return Math.sqrt( this.x * this.x + this.y * this.y );
251 normalize: function() {
252 var l = Math.sqrt( this.x * this.x + this.y * this.y );
259 Utilities.extendObject(Point, {
262 return new Point(0, 0);
265 pointOnCircle: function(angle, radius)
267 return new Point(radius * Math.cos(angle), radius * Math.sin(angle));
270 pointOnEllipse: function(angle, radiuses)
272 return new Point(radiuses.x * Math.cos(angle), radiuses.y * Math.sin(angle));
275 elementClientSize: function(element)
277 var rect = element.getBoundingClientRect();
278 return new Point(rect.width, rect.height);
282 Insets = Utilities.createClass(
283 function(top, right, bottom, left)
287 this.bottom = bottom;
293 return this.left + this.right;
298 return this.top + this.bottom;
303 return new Point(this.width, this.height);
307 Insets.elementPadding = function(element)
309 var styles = window.getComputedStyle(element);
311 parseFloat(styles.paddingTop),
312 parseFloat(styles.paddingRight),
313 parseFloat(styles.paddingBottom),
314 parseFloat(styles.paddingTop));
317 SimplePromise = Utilities.createClass(
320 this._chainedPromise = null;
321 this._callback = null;
324 then: function (callback)
327 throw "SimplePromise doesn't support multiple calls to then";
329 this._callback = callback;
330 this._chainedPromise = new SimplePromise;
333 this.resolve(this._resolvedValue);
335 return this._chainedPromise;
338 resolve: function (value)
340 if (!this._callback) {
341 this._resolved = true;
342 this._resolvedValue = value;
346 var result = this._callback(value);
347 if (result instanceof SimplePromise) {
348 var chainedPromise = this._chainedPromise;
349 result.then(function (result) { chainedPromise.resolve(result); });
351 this._chainedPromise.resolve(result);
357 sampleMean: function(numberOfSamples, sum)
359 if (numberOfSamples < 1)
361 return sum / numberOfSamples;
364 // With sum and sum of squares, we can compute the sample standard deviation in O(1).
365 // See https://rniwa.com/2012-11-10/sample-standard-deviation-in-terms-of-sum-and-square-sum-of-samples/
366 unbiasedSampleStandardDeviation: function(numberOfSamples, sum, squareSum)
368 if (numberOfSamples < 2)
370 return Math.sqrt((squareSum - sum * sum / numberOfSamples) / (numberOfSamples - 1));
373 geometricMean: function(values)
377 var roots = values.map(function(value) { return Math.pow(value, 1 / values.length); })
378 return roots.reduce(function(a, b) { return a * b; });